--!name Mines
--!icon puzzle
--!category Puzzles
local ax="!$%&()+,-0123456789:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnpqrst"local af=25 local ac,ab,ae,ad,aw,aA,ay=0,1,2,11,12,13,14 local M={{name="Beginner",w=9,h=9,n=10},{name="Intermediate",w=16,h=16,n=40},{name="Expert",w=16,h=30,n=99},}local p,f,k,D=1,9,9,10 local e,ag={},{}local F,A,i,O=false,false,nil,0 local H,o,m,q=0,0,1,false local r,K,t,w=0,0,0,false local n,s,u,L="menu",1,nil,false local I={{0,0,0,0},{0,0,0,0},{0,0,0,0}}local function ak()local c=os.time()return c~=nil and c>1700000000 end local function X(l)p=l local x=M[l]f,k,D=x.w,x.h,x.n local ar=(l-1)*af for c=0,af-1 do ag[c]=ax:sub(ar+c+1,ar+c+1)end end local function T(b)return("%d:%02d"):format(b//60,b%60)end local function W()local c={}if F then for a=1,f*k do local d=e[a]c[a]=string.char(65+d%16+10*(d//16))end end local b={}for l=1,3 do b[l]=table.concat(I[l],",")end snail.save(table.concat({"N1",p,m,A and 1 or 0,i=="won"and 2 or(i and 1 or 0),O,r,t,w and 1 or 0,table.concat(b,","),table.concat(c)},";"))end local function aI()local b=snail.load()if type(b)~="string"or b:sub(1,3)~="N1;"then return end local E={}for d in(b..";"):gmatch("([^;]*);")do E[#E+1]=d end local h=function(a)return tonumber(E[a])or 0 end X(math.max(1,math.min(3,h(2))))local j=0 for d in(E[10]or""):gmatch("%d+")do j=j+1 if j<=12 then I[(j-1)//4+1][(j-1)%4+1]=tonumber(d)end end local y=E[11]or""if#y~=f*k then return end H,o=0,0 for a=1,f*k do local aj=y:byte(a)-65 local d=aj%10+16*(aj//10)e[a]=d if d>=32 then o=o+1 elseif d>=16 and d~=25 then H=H+1 end end F=true m=math.max(1,math.min(f*k,h(3)))A=h(4)==1 i=h(5)==2 and"won"or(h(5)==1 and"lost"or nil)O,r,t,w=h(6),h(7),h(8),h(9)==1 end local function J(a,E)local B,C=(a-1)%f,(a-1)//f for am=-1,1 do local _=C+am if _>=0 and _<k then for al=-1,1 do local Z=B+al if(al~=0 or am~=0)and Z>=0 and Z<f then E(_*f+Z+1)end end end end end local function aC(g)if e[g]~=9 then e[g]=e[g]+1 end end local function aq(l)X(l)for a=1,f*k do e[a]=0 end for a=f*k+1,#e do e[a]=nil end m=(k//2)*f+f//2+1 F,A,i,O,H,o,q=true,false,nil,0,0,0,false r,t,w,u,L=0,0,false,nil,false n="play"W()end local function aM(av)local an={[av]=true}J(av,function(g)an[g]=true end)local h=0 while h<D do local a=math.random(f*k)if not an[a]and e[a]~=9 then e[a]=9;h=h+1 end end for a=1,f*k do if e[a]==9 then J(a,aC)end end A,w=true,ak()r,K=0,os.time()I[p][1]=I[p][1]+1 end local S,v={},0 local function aN(g)if e[g]<16 then v=v+1;S[v]=g end end local function aG(a)v=1 S[1]=a while v>0 do local g=S[v]S[v]=nil v=v-1 local d=e[g]if d<16 then e[g]=d+16 H=H+1 if d==0 then J(g,aN)end end end end local function aJ(a)i,O="lost",a e[a]=25 q=false end local function aQ()i,q="won",false for a=1,f*k do if e[a]==9 then e[a]=41 end end o=D local b=I[p]b[2]=b[2]+1 L=false if w and(b[3]==0 or r<b[3])then b[3]=r;L=true end if b[4]==0 or t<b[4]then b[4]=t if not w then L=true end end end local function at(a)if i or e[a]>=16 then return false end if not A then aM(a)end if e[a]==9 then aJ(a)else aG(a)end return true end local function Y(a)local d=e[a]if d>=32 then e[a]=d-32;o=o-1 elseif d<16 then e[a]=d+32;o=o+1 else u="Flags go on covered cells";return false end return true end local z,Q=0,0 local function aE(g)local d=e[g]if d>=32 then z=z+1 elseif d<16 then Q=Q+1 end end local function aL(g)if e[g]<16 then at(g)end end local function aF(g)if e[g]<16 then Y(g)end end local function aD(a)local h=e[a]-16 z,Q=0,0 J(a,aE)if h==0 or Q==0 then u="Nothing left to open here";return false end if z==h then J(a,aL)elseif z+Q==h then J(a,aF)elseif z>h then u="Too many flags round here";return false else u=h-z.." more mine"..(h-z==1 and""or"s").." round here"return false end return true end local function ao()if A and not i and w then local U=os.time()if U>K then r=r+(U-K)end K=U end end local function ap()local c={}if F then c[#c+1]={i and"Back to the last game"or"Back to the game","resume"}end for l=1,3 do local x=M[l]c[#c+1]={"New "..x.name..": "..x.w.." x "..x.h..", "..x.n.." mines",l}end c[#c+1]={"Statistics","stats"}c[#c+1]={"How to play","help"}return c end local function aK()ao()q=false n,s="menu",1 W()end local aa={left=-1,right=1,up=0,down=0}local az={left=0,right=0,up=-1,down=1}function key(j)if n=="menu"then local G=ap()if j=="up"then s=(s-2)%#G+1 elseif j=="down"then s=s%#G+1 elseif j=="ok"then local N=G[s][2]if N=="resume"then n,u="play",nil;K=os.time()elseif N=="stats"or N=="help"then n=N else aq(N)end end return end if n~="play"then if j=="top"or j=="ok"then n="menu"end return end u=nil if j=="top"then if q or i then aK()else q=true end return end if aa[j]then local B,C=(m-1)%f,(m-1)//f B,C=(B+aa[j])%f,(C+az[j])%k m=C*f+B+1 return end if j~="ok"then return end if i then aq(p);return end ao()local d,P=e[m],false if q then q=false P=Y(m)elseif d>=32 then P=Y(m)elseif d>=16 then P=aD(m)else P=at(m)end if P then t=t+1 if not i and H==f*k-D then aQ()end W()end end local function aP(a)local d=e[a]local y,R=d%16,d//16 if i then if R==1 then if y==9 then return a==O and aw or ad end return ae+y elseif R==2 then return y==9 and ab or aA end return y==9 and ad or ac end local c=R==1 and ae+y or(R==2 and ab or ac)if a==m then c=c+ay end return c end local function aB()local au={}for C=0,k-1 do local as={}for B=1,f do as[B]=ag[aP(C*f+B)]end au[C+1]=table.concat(as)end return table.concat(au,"/")end local function V()if w then return"Time "..T(r)end return t..(t==1 and" move"or" moves")end local function aO()if i=="lost"then return"Boom! That one was a mine."end if i=="won"then return"Cleared in "..(w and T(r)or V())..(L and" - best yet!"or"")end if u then return u end if q then return"Flag: OK marks this cell, BACK options"end if not A then return D.." mines. Open any cell to start."end return(D-o).." mines left    "..V()end local function ai(b)local c={}if b[3]>0 then c[#c+1]="best time "..T(b[3])end if b[4]>0 then c[#c+1]="fewest moves "..b[4]end return#c>0 and table.concat(c,", ")or"no wins yet"end function draw()if n=="menu"then snail.title("Mines")local G=ap()if s>#G then s=#G end for a,aH in ipairs(G)do snail.row(aH[1],a==s)end snail.gap()if F and not i and A then snail.small(M[p].name.." game in progress: "..(D-o).." mines left, "..V()..".")else snail.small("Your first cell is always safe.")end snail.hint("BACK leave OK choose UP/DN move")return end if n=="stats"then snail.title("Statistics")for l=1,3 do local b=I[l]snail.text(M[l].name..": won "..b[2].." of "..b[1])snail.small(ai(b))snail.gap()end if not ak()then snail.small("Best times need the clock: connect to Wi-Fi once to set it.")end snail.hint("BACK back OK back")return end if n=="help"then snail.title("How to play")snail.text("Open every cell that has no mine under it.")snail.text("A number says how many mines touch that cell, diagonals too.")snail.text("Arrows move, OK opens. The first cell is always safe.")snail.text("BACK then OK flags a cell you know is a mine. OK on a flag lifts it.")snail.text("OK on a number opens the cells round it once its mines are flagged.")snail.text("If its covered neighbours can only be mines, OK flags them all.")snail.text("BACK twice gets to the options. Your game is kept.")snail.hint("BACK back OK back")return end snail.title("Mines - "..M[p].name)snail.status(aO())local ah if i=="won"then ah=L and"Cleared! New best!"or"Field cleared!"end snail.board(aB(),nil,ah)if p<3 then local b=I[p]snail.small("Won "..b[2].." of "..b[1]..". "..ai(b):gsub("^%l",string.upper)..".")end if i then snail.hint("BACK options OK new game")elseif q then snail.hint("BACK options OK flag L/R move")else snail.hint("BACK flag OK open L/R move")end end function start()math.randomseed(os.time())snail.ink("fast")X(1)aI()if F and not i then n="play"K=os.time()else n="menu"s=F and p+1 or 1 end end
