--!name Five Dice
--!icon dice
--!category Games
local aa={"Ones","Twos","Threes","Fours","Fives","Sixes","Three of a Kind","Four of a Kind","Full House","Small Straight","Large Straight","Five of a Kind","Chance"}local aW={1.8,4.2,6.6,9,11.4,13.8,15,7,14,18,11,8,22}local _=1300 local t="solo"local o,x={},{}local k,i,g=1,0,"none"local m,y={0,0,0,0,0},{false,false,false,false,false}local h,S=6,6 local l,F,P=nil,nil,1 local C="roll"local B,z="menu",1 local e={games=0,sum=0,best=0,fives=0,w=0,l=0,d=0,top={}}local q={0,0,0,0,0,0}local Q,J,V=0,0,0 local function D(M)for c=1,6 do q[c]=0 end Q=0 for c=1,5 do local b=M[c]q[b]=q[b]+1 Q=Q+b end J,V=0,0 local A=0 for c=1,6 do local j=q[c]if j>J then J=j end if j>0 then A=A+1;if A>V then V=A end else A=0 end end end local function aH(d)if J==5 and o[d][12]~=false then for n=1,6 do if q[n]==5 then return n end end end end local function Z(a,d)if a<=6 then return a*q[a]end local an=aH(d)if a==7 then return J>=3 and Q or 0 end if a==8 then return J>=4 and Q or 0 end if a==9 then if an then return 25 end local aT,aU=false,false for c=1,6 do if q[c]==3 then aT=true elseif q[c]==2 then aU=true end end return(aT and aU)and 25 or 0 end if a==10 then return(V>=4 or an)and 30 or 0 end if a==11 then return(V>=5 or an)and 40 or 0 end if a==12 then return J==5 and 50 or 0 end return Q end local function ab(d,a)local u=o[d]if u[a]~=false then return false end local n=aH(d)if not n then return true end if u[n]==false then return a==n end for c=7,13 do if u[c]==false then return a>=7 end end return true end local function aw(d)local I=0 for a=1,6 do I=I+(o[d][a]or 0)end return I end local function v(d)local f=0 for a=1,13 do f=f+(o[d][a]or 0)end if aw(d)>=63 then f=f+35 end return f+100*x[d]end local function al(d)local j=0 for a=1,13 do if o[d][a]then j=j+1 end end return j end local function ac(d)local I=aw(d)local bc=I<63 local E,ay=-1e9,nil for a=1,13 do if ab(d,a)then local b=Z(a,d)local U=b-aW[a]if a<=6 and bc then U=U+(b-3*a)*0.6 end if a==12 and b==50 then U=U+30 end if U>E then E,ay=U,a end end end return E,ay end local ak={0,0,0,0,0}local function bh(d,aI,j)if aI==31 then D(m)return(ac(d))end local u=0 for W=1,j do for c=1,5 do if aI&(1<<(c-1))~=0 then ak[c]=m[c]else ak[c]=math.random(6)end end D(ak)u=u+ac(d)end return u/j end local function a7(d)D(m)local ad,aQ={},{}local function R(p)if not aQ[p]then aQ[p]=true;ad[#ad+1]=p end end local ae={q[1],q[2],q[3],q[4],q[5],q[6]}local function ah(aR)local p,aV=0,{}for c=1,5 do local b=m[c]if aR(b)and not aV[b]then p=p|(1<<(c-1))if aR(b)==1 then aV[b]=true end end end return p end R(31)R(0)for n=1,6 do if ae[n]>0 then R(ah(function(b)return b==n end))end end for ag=1,3 do local am=0 for b=ag,ag+3 do if ae[b]>0 then am=am+1 end end if am>=3 then R(ah(function(b)return(b>=ag and b<=ag+3)and 1 end))end end local ar=0 for n=1,6 do if ae[n]>=2 then ar=ar+1 end end if ar==2 then R(ah(function(b)return ae[b]>=2 end))end R(ah(function(b)return b>=5 end))local j=#ad>8 and 14 or 20 local E,aA=-1e9,31 for W,p in ipairs(ad)do local b=bh(d,p,j)if b>E then E,aA=b,p end end return aA end local function aB(d)local f={}for a=1,13 do f[a]=o[d][a]and tostring(o[d][a])or"-"end return table.concat(f,",")end local function at()local M,aG={},{}for c=1,5 do M[c]=m[c];aG[c]=y[c]and 1 or 0 end local aF=""if g~="none"then aF=table.concat({t,k,i,g,C,table.concat(M),table.concat(aG),aB(1),x[1],o[2]and aB(2)or"",x[2]or 0},";")end snail.save(table.concat({"FD1",e.games,e.sum,e.best,e.fives,e.w,e.l,e.d,table.concat(e.top,","),aF},"|"))end local function aq(d)o[d]={}for a=1,13 do o[d][a]=false end x[d]=0 end local function aO(d,u)aq(d)local a=0 for b in(u or""):gmatch("[^,]+")do a=a+1 if a<=13 then o[d][a]=tonumber(b)or false end end end local function bb()local u=snail.load()if type(u)~="string"or u:sub(1,4)~="FD1|"then return end local n={}for b in(u.."|"):gmatch("([^|]*)|")do n[#n+1]=b end local j=function(c)return tonumber(n[c])or 0 end e.games,e.sum,e.best,e.fives,e.w,e.l,e.d=j(2),j(3),j(4),j(5),j(6),j(7),j(8)e.top={}for b in(n[9]or""):gmatch("%d+")do e.top[#e.top+1]=tonumber(b)end local r={}for b in((n[10]or"")..";"):gmatch("([^;]*);")do r[#r+1]=b end if#r<11 then return end t,k,i,g,C=r[1],tonumber(r[2])or 1,tonumber(r[3])or 0,r[4],r[5]for c=1,5 do m[c]=tonumber(r[6]:sub(c,c))or 0 y[c]=r[7]:sub(c,c)=="1"end aO(1,r[8]);x[1]=tonumber(r[9])or 0 if t=="cpu"then aO(2,r[10]);x[2]=tonumber(r[11])or 0 else o[2]=nil end P=k end local function aN()return t=="cpu"and 2 or 1 end local function aM(p)t=p aq(1)if p=="cpu"then aq(2)else o[2],x[2]=nil,nil end k,i,g,P=1,0,"play",1 for c=1,5 do m[c],y[c]=0,false end h,S,l,F=6,6,nil,nil snail.tick(0)B="game"at()end local function as()for c=1,5 do if i==0 or not y[c]then m[c]=math.random(6);y[c]=false end end i=i+1 end local function aE()g="over"snail.tick(0)local T=v(1)e.games=e.games+1 e.sum=e.sum+T l=nil if T>e.best then e.best=T;l="new best score!"end table.insert(e.top,T)table.sort(e.top,function(w,X)return w>X end)while#e.top>5 do table.remove(e.top)end if t=="cpu"then local aS=v(2)if T>aS then e.w=e.w+1 elseif T<aS then e.l=e.l+1 else e.d=e.d+1 end end P=1 end local function aj(d)k,i,P=d,0,d for c=1,5 do m[c],y[c]=0,false end h,S,F=6,6,nil if d==2 then g,C="cpu","roll"snail.tick(_)else g="play"end end local function bd()if k==1 and aN()==2 then aj(2)elseif al(1)>=13 then aE()else aj(1)end end local function aP(a)D(m)local b=Z(a,k)if J==5 and o[k][12]==50 then x[k]=x[k]+1 if k==1 then e.fives=e.fives+1 end elseif a==12 and b==50 and k==1 then e.fives=e.fives+1 end o[k][a]=b return b end local function au(a)if g~="play"or i==0 then return false end D(m)return ab(k,a)end local function bg()D(m)local W,a=ac(k)if a then h=6+a end end local function a9(a)local b=aP(a)local ai="You scored "..b.." in "..aa[a]if aN()==2 then g,l="wait",ai snail.tick(_)else l=ai bd()end end local function a8()if C=="roll"then as()l="Computer rolls ("..i.." of 3)"C=i>=3 and"score"or"think"elseif C=="think"then local p=a7(2)if p==31 then l="Computer keeps these dice"C="score"else local f={}for c=1,5 do y[c]=p&(1<<(c-1))~=0 if y[c]then f[#f+1]=m[c]end end table.sort(f)l=#f>0 and("Computer holds "..table.concat(f," "))or"Computer rerolls everything"C="roll"end elseif C=="score"then D(m)local W,a=ac(2)local b=aP(a)l="Computer: "..b.." in "..aa[a]C="done"else local ai=l if al(2)>=13 then aE()else aj(1);l=ai.." - your turn"end end end function tick()if g=="wait"then aj(2)elseif g=="cpu"then a8()else snail.tick(0)return end at()end local function aD()local K={{1,2,3,4,5,6}}local L={}for a=1,6 do if au(a)then L[#L+1]=6+a end end if#L>0 then K[2]=L end for a=7,13 do if au(a)then K[#K+1]={6+a}end end return K end local function aC(d)if d<=5 then return d elseif d==6 then return 7.5 elseif d<=12 then return d-5 end return 4 end local function aL(af)local K=aD()local G=1 for c,A in ipairs(K)do for W,b in ipairs(A)do if b==h then G=c end end end if h<=6 then S=h end local A=K[(G-1+af)%#K+1]if A[1]==1 then h=S;return end local E,az=A[1],99 for W,b in ipairs(A)do local M=math.abs(aC(b)-aC(h))if M<az then E,az=b,M end end h=E end local function aK(af)if h<=6 then h=(h-1+af)%6+1;return end if h>=13 then h=S;return end local L=aD()[2]local G=1 for c,b in ipairs(L)do if b==h then G=c end end h=L[(G-1+af)%#L+1]end local function be()if h<=5 then if i==0 then as()elseif i<3 then y[h]=not y[h]else l="No rolls left - pick a box"end elseif h==6 then if i<3 then as()if i==3 then S=6;bg()end else l="No rolls left - pick a box"end else local a=h-6 if not au(a)then return end D(m)if Z(a,k)==0 and F~=a then F=a return end a9(a)end end local function aJ()local f={}if g~="none"and g~="over"then f[#f+1]={"Back to the game","resume"}end f[#f+1]={"New solo game","solo"}f[#f+1]={"New game vs computer","cpu"}f[#f+1]={"High scores","scores"}f[#f+1]={"How to play","help"}return f end function key(s)if B=="menu"then local H=aJ()if z>#H then z=#H end if s=="up"then z=(z-2)%#H+1 elseif s=="down"then z=z%#H+1 elseif s=="ok"then local w=H[z][2]if w=="resume"then B="game"if g=="cpu"or g=="wait"then snail.tick(_)end elseif w=="solo"or w=="cpu"then aM(w)else B=w end end return end if B~="game"then if s=="top"or s=="ok"then B="menu"end return end if s=="top"then B,z="menu",1 snail.tick(0)return end if g=="over"then if s=="ok"then aM(t)elseif(s=="left"or s=="right")and t=="cpu"then P=3-P end return end if g~="play"then return end local bi=F l=nil if s=="ok"then be();at()elseif s=="left"then aK(-1)elseif s=="right"then aK(1)elseif s=="up"then aL(-1)elseif s=="down"then aL(1)end if F==bi then F=nil end end local a0,a1,aZ,a_="123456","ABCDEF","abcdef","GHIJKL"local a2,a4,a3="ghijkl","mnvwxy","!$%&+-"local aX={[0]="VW","PQ","RS","TU"}local aY={[0]="XY","pq","rs","tu"}local function a5(d,N)local f={}local ap=g=="play"for c=1,5 do local b=m[c]local G=ap and h==c if b==0 then f[c]=G and"z"or"0"else local av=y[c]and(G and a_ or a1)or(G and aZ or a0)f[c]=av:sub(b,b)end end local ao=3-i if g~="play"then ao=k==2 and g=="cpu"and(3-i)or 0 end f[6]="."f[7]=(ap and h==6)and aY[ao]or aX[ao]f[8]="/."local O={"/."}for a=1,6 do local b=o[d][a]local av=b and a4 or((ap and h==6+a)and a3 or a2)f[#f+1]=av:sub(a,a)if b then O[#O+1]="{"..b.."}"elseif N and ab(d,a)then O[#O+1]="{+"..Z(a,d).."}"else O[#O+1]="."end end return table.concat(f)..table.concat(O)end local function a6(d,a,N)local b=o[d][a]local Y=aa[a]if b then if a==12 and x[d]>0 then return Y..": "..b.." +"..(100*x[d])end return Y..": "..b end if N then if ab(d,a)then return Y.."   +"..Z(a,d)end return Y.."   -"end return Y end local function bf()if F then return"Score 0 in "..aa[F].."? OK again"end if g=="over"then if t=="cpu"then local w,X=v(1),v(2)return(w>X and"You win! "or(w<X and"Computer wins. "or"A tie! "))..w.." to "..X end return"Game over: "..v(1).." points"..(l and(" - "..l)or"")end if l then return l end if g~="play"then return"Computer's turn"end if i==0 then return"Turn "..(al(1)+1).." of 13 - OK to roll"end if i<3 then return"Roll "..i.." of 3 - hold dice or pick a box"end return"Last roll - pick a box to score"end function draw()if B=="menu"then snail.title("Five Dice")local H=aJ()if z>#H then z=#H end for c,ba in ipairs(H)do snail.row(ba[1],c==z)end snail.gap()if g~="none"and g~="over"then local bj=t=="cpu"and("vs computer, you "..v(1).." - "..v(2))or("solo, "..v(1).." points")snail.small("Game in progress: "..bj..".")end if e.games>0 then snail.small("Best "..e.best..", average "..(e.sum//e.games).." over "..e.games..(e.games==1 and" game."or" games."))else snail.small("Roll five dice, fill thirteen boxes, chase a high score.")end snail.hint("BACK leave OK choose UP/DN move")return end if B=="scores"then snail.title("High scores")if#e.top==0 then snail.text("No games finished yet.")end for c,b in ipairs(e.top)do snail.text(c..".   "..b)end snail.gap()snail.rule()snail.small("Games "..e.games..", average "..(e.games>0 and e.sum//e.games or 0)..", best "..e.best)snail.small("Fives of a kind rolled: "..e.fives)snail.small("Against the computer: "..e.w.." won, "..e.l.." lost, "..e.d.." tied")snail.hint("BACK back OK back")return end if B=="help"then snail.title("How to play")snail.text("Each turn roll up to three times. Between rolls, OK on a die holds it (shown black).")snail.text("Then pick an open box. +N shows what it would score now.")snail.text("The small dice are the upper boxes (black once used): they score that face.")snail.text("63 or more in the upper boxes earns a 35 bonus.")snail.text("Full house 25, small straight (4 in a row) 30, large (5) 40.")snail.text("Five of a kind 50. More fives add 100 each and act as jokers.")snail.text("A box that scores nothing asks for a second OK.")snail.hint("BACK back OK back")return end local d=P local N=g=="play"and i>0 and d==k if N then D(m)end if t=="cpu"then snail.title(d==2 and"Five Dice - computer's card"or"Five Dice - your card")else snail.title("Five Dice")end snail.status(bf())local ax if g=="over"then ax=(d==1 and"You "or"Computer ")..v(d)end snail.board(a5(d,N),nil,ax)local I=aw(d)snail.small(I>=63 and("Upper boxes "..I..": bonus +35")or("Upper boxes "..I.." of 63 for the 35 bonus"))snail.rule()for a=7,13 do snail.row(a6(d,a,N),g=="play"and h==6+a)end snail.rule()if t=="cpu"then snail.text("You "..v(1).."   Computer "..v(2))else snail.text("Total "..v(1))end if g=="over"then snail.hint(t=="cpu"and"BACK options OK play again L/R cards"or"BACK options OK play again")elseif g~="play"then snail.hint("BACK options")elseif h<=5 then snail.hint(i==0 and"BACK options OK roll L/R move UP/DN boxes"or"BACK options OK hold L/R move UP/DN boxes")elseif h==6 then snail.hint("BACK options OK roll L/R move UP/DN boxes")elseif h<=12 then snail.hint("BACK options OK score L/R move UP/DN boxes")else snail.hint("BACK options OK score L/R dice UP/DN boxes")end end function start()math.randomseed(os.time())bb()if g~="none"and g~="over"then B="game"if g=="cpu"or g=="wait"then snail.tick(_)end end end
