--!name Sudoku
--!icon puzzle
--!category Puzzles
local bp="!$%&()+,-0123456789:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnpqrstuvwxyz|~"local function H(a)return bp:sub(a+1,a+1)end local bt,bs,bv,bl,aL,br,bm,bo,bn=0,9,18,27,36,45,46,47,56 local aP,aO,av,aR,bA,aN,aQ,by,bz,bx=65,66,67,68,69,70,71,80,81,82 local bk={"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"}local ai=(20)local R={"Easy","Medium","Hard","Expert"}local aK="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-"local aM={}for a=1,64 do aM[aK:byte(a)]=a-1 end local function bf(f)local aG,D,w=1,0,0 return function(d)while w<d do D=(D<<6)|(aM[f:byte(aG)]or 0)aG,w=aG+1,w+6 end w=w-d local o=D>>w D=D&((1<<w)-1)return o end end local function Z(a)return((a-1)//27)*3+((a-1)%9)//3 end local function bH(bS)local al=bf(bS)local a8,t,f={},{},{}for a=0,40 do a8[a]=al(1)end local ah,ag,aj={},{},{}for c=0,8 do ah[c],ag[c],aj[c]=0,0,0 end for a=0,80 do local v,r,p=a//9,a%9,Z(a+1)local h=a+1 if a>=9 then local n=~(ah[v]|ag[r]|aj[p])&511 local d=0 for c=0,8 do d=d+(n>>c&1)end local an,au=0,0 if d>1 then while(1<<au)<d do au=au+1 end an=al(au)end for c=0,8 do if n>>c&1==1 then if an==0 then h=c+1;break end an=an-1 end end end local X=1<<(h-1)ah[v],ag[r],aj[p]=ah[v]|X,ag[r]|X,aj[p]|X f[a+1]=h end for a=0,80 do t[a+1]=a8[math.min(a,80-a)]==1 and f[a+1]or 0 end return t,f end local ad=1 local function as(d)ad=(ad*1103515245+12345)%2147483648 return(ad>>8)%d+1 end local function bh(d)local e={}for a=1,d do e[a]=a end for a=d,2,-1 do local j=as(a)e[a],e[j]=e[j],e[a]end return e end local function a7()local n,a4={},bh(3)for c=1,3 do n[c],n[10-c]=a4[c],10-a4[c]end if as(2)==2 then n[4],n[6]=6,4 else n[4],n[6]=4,6 end n[5]=5 if as(2)==2 then for c=1,3 do n[c],n[10-c]=n[10-c],n[c]end end return n end local function bG(e)local h=os.date("*t",e)return os.time({year=h.year,month=h.month,day=h.day,hour=12})//86400 end local function ae()local e=os.time()if not e or e<1700000000 then return nil end return bG(e)end local bB={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}local bw={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}local bq={4,1,1,2,2,3,3}local function a_(_)return os.date("*t",_*86400+43200)end local function a0(_)local h=a_(_)return bB[h.wday].." "..bw[h.month].." "..h.day end local function aZ(_)return bq[a_(_).wday]end local Y,P,i,s,F={},{},{},{},{}local b local K={}local g={}for a=1,16 do g[a]=0 end local ac={1,1,1,1}local U,aa=false,false local L={}local k,B,q,a6,I=41,"grid",1,1,false local m,x,aT="menu",1,"menu"local E,am,ap=nil,nil,nil local function Q(f)f=math.floor(f or 0)if f>=3600 then return string.format("%d:%02d:%02d",f//3600,f//60%60,f%60)end return string.format("%d:%02d",f//60,f%60)end local function ao(t)if t.kind=="d"then return"Daily, "..a0(t.num)end return R[t.diff].." #"..t.num end local function aq(a5,A,O)local aV=bk[A]local d=#aV//ai local ak if a5=="d"then ak,ad=(O*37+11)%d,O*7+1 else ak,ad=(O-1)%d,A*100003+O*7919 end local t,f=bH(aV:sub(ak*ai+1,ak*ai+ai))local a1,C,bE=bh(9),a7(),a7()local bK=as(2)==2 for v=1,9 do for r=1,9 do local aF,aC=C[v],bE[r]if bK then aF,aC=aC,aF end local aB,a=(aF-1)*9+aC,(v-1)*9+r Y[a]=t[aB]>0 and a1[t[aB]]or 0 P[a]=a1[f[aB]]i[a],s[a],F[a]=Y[a],0,Y[a]>0 end end b={kind=a5,diff=A,num=O,el=0,hints=0,done=false}L,B,I,aa,E={},"grid",false,false,nil k=41 for a=1,81 do if Y[a]==0 then k=a;break end end end local function bO()local e,D,w,aS={},0,0,false for a=1,82 do local n=s[a]or 0 if n~=0 then aS=true end D,w=(D<<9)|n,w+9 while w>=6 do w=w-6 local o=D>>w e[#e+1]=aK:sub(o+1,o+1)D=D&((1<<w)-1)end end return aS and table.concat(e)or""end local function bU()local o={}for a=1,81 do o[a]=(F[a]and Y[a]==0)and string.char(96+i[a])or tostring(i[a])end return table.concat({b.kind,b.diff,b.num,b.el,b.hints,b.done and 1 or 0,table.concat(o),bO()},",")end local function bg(f)local l={}for W in(f..","):gmatch("([^,]*),")do l[#l+1]=W end local A,O=tonumber(l[2]),tonumber(l[3])if(l[1]~="d"and l[1]~="b")or not A or A<1 or A>4 or not O then return end aq(l[1],A,O)b.el,b.hints,b.done=tonumber(l[4])or 0,tonumber(l[5])or 0,l[6]=="1"local o=l[7]or""for a=1,81 do local z=o:byte(a)or 48 if Y[a]==0 then if z>=97 and z<=105 then i[a],F[a]=z-96,true elseif z>=49 and z<=57 then i[a]=z-48 end end end if l[8]and l[8]~=""then local al=bf(l[8])for a=1,81 do s[a]=al(9)end end return true end local function G()if b then K[b.kind]=bU()end snail.save(table.concat({"SU1",table.concat(g,","),table.concat(ac,","),U and 1 or 0,b and b.kind or"",K.d or"",K.b or""},";"))end local function bN()local f=snail.load()if type(f)~="string"or f:sub(1,4)~="SU1;"then return end local l={}for W in(f..";"):gmatch("([^;]*);")do l[#l+1]=W end local c=0 for W in(l[2]or""):gmatch("[^,]+")do c=c+1 if c<=16 then g[c]=tonumber(W)or 0 end end c=0 for W in(l[3]or""):gmatch("[^,]+")do c=c+1 if c<=4 then ac[c]=math.max(1,tonumber(W)or 1)end end U=l[4]=="1"if l[6]and l[6]~=""then K.d=l[6]end if l[7]and l[7]~=""then K.b=l[7]end local ax=l[5]if ax and K[ax]then local aD,aI=pcall(bg,K[ax])if not(aD and aI)then b=nil end end end local function bT(a,j)return(a-1)//9==(j-1)//9 or(a-1)%9==(j-1)%9 or Z(a)==Z(j)end local function aH(u)L[#L+1]=u if#L>60 then table.remove(L,1)end end local function aJ(a,o,u)u=u or{}u[#u+1],u[#u+2],u[#u+3]=a,i[a],s[a]i[a],s[a]=o,0 if o>0 then local X=1<<(o-1)for j=1,81 do if s[j]&X~=0 and bT(a,j)then u[#u+1],u[#u+2],u[#u+3]=j,i[j],s[j]s[j]=s[j]&~X end end end return u end local function ba()local d=0 for a=1,81 do if i[a]>0 and i[a]~=P[a]then d=d+1 end end return d end local function a3()for a=1,81 do if i[a]~=P[a]then return end end b.done,B,aa=true,"grid",false local p=(b.diff-1)*3 g[p+1]=g[p+1]+1 if g[p+2]==0 or b.el<g[p+2]then g[p+2]=b.el end g[p+3]=g[p+3]+b.el if b.kind=="d"and g[16]~=b.num then g[13]=g[13]+1 g[14]=g[16]==b.num-1 and g[14]+1 or 1 if g[14]>g[15]then g[15]=g[14]end g[16]=b.num end end local function bW()local t=table.remove(L)if not t then return false end for c=#t-2,1,-3 do local j=t[c]if not F[j]then i[j],s[j]=t[c+1],t[c+2]end end k,B=t[1],"grid"return true end local function bL()local a=k if F[a]or i[a]==P[a]then local M={}for j=1,81 do if i[j]>0 then for aw,bj in ipairs({"r"..(j-1)//9,"c"..(j-1)%9,"b"..Z(j)})do M[bj]=(M[bj]or 0)|(1<<(i[j]-1))end end end local aW,aX=nil,10 for j=1,81 do if i[j]~=P[j]then local n=(M["r"..(j-1)//9]or 0)|(M["c"..(j-1)%9]or 0)|(M["b"..Z(j)]or 0)local d=9 for c=0,8 do d=d-(n>>c&1)end if d<aX then aW,aX=j,d end end end a=aW end if not a then return end aJ(a,P[a])F[a],k=true,a b.hints=b.hints+1 E="Revealed a "..P[a]a3()end local function bF()local d={}for h=1,9 do d[h]=0 end for a=1,81 do if i[a]>0 then d[i[a]]=d[i[a]]+1 end end return d end local function at()local h=ae()if h and g[16]<h-1 then return 0 end return g[14]end local function J()m,ap,x="play",os.time(),1 end local function bi(A)if b then G()end local d=ac[A]ac[A]=d+1 aq("b",A,d)J()G()end local function bV(h)if b then G()end b=nil if K.d then local aD,aI=pcall(bg,K.d)if not(aD and aI)or b.num~=h then b=nil end end if not b then aq("d",aZ(h),h)end J()G()end local function bD()local bd=os.time()if b and m=="play"and ap and not b.done then local a2=bd-ap if a2>0 then b.el=b.el+math.min(a2,600)end end ap=bd end local function a9()local e={}if b and not b.done then e[#e+1]={"Continue: "..ao(b).." ("..Q(b.el)..")","cont"}end local h=ae()if h then local f="Daily puzzle: "..a0(h)..", "..R[aZ(h)]if g[16]==h then f=f.." (solved)"end e[#e+1]={f,"daily"}else e[#e+1]={"Daily puzzle: needs the clock","noclock"}end for c=1,4 do e[#e+1]={"New "..R[c].." puzzle (#"..ac[c]..")",c}end e[#e+1]={"Statistics","stats"}e[#e+1]={"How to play","help"}return e end local function be()return{{"Back to the puzzle","cont"},{#L>0 and("Undo ("..#L..(#L==1 and" move)"or" moves)"))or"Undo (nothing to undo)","undo"},{"Hint: reveal a cell","hint"},{"Check for mistakes","check"},{"Always show mistakes: "..(U and"on"or"off"),"wrong"},{"Restart this puzzle","restart"},{"New puzzle","menu"},{"Statistics","stats"},{"How to play","help"},}end local function bC(y)if y=="cont"then J()elseif y=="undo"then if bW()then J()else E="Nothing to undo"end G()elseif y=="hint"then if b.done then J()else J();bL();G()end elseif y=="check"then J()local d=ba()aa=d>0 E=d==0 and"No mistakes so far"or(d==1 and"1 mistake, struck through"or d.." mistakes, struck through")elseif y=="wrong"then U=not U;G()elseif y=="restart"then aq(b.kind,b.diff,b.num)J()G()elseif y=="menu"then m,x="menu",1 elseif y=="daily"then bV(ae())elseif type(y)=="number"then if b and b.kind=="b"and not b.done and am~=y then am,E=y,"Press OK again to leave "..ao(b)else am=nil bi(y)end else aT,m=m,y end end local function bb(c)local v,r=(k-1)//9,(k-1)%9 if c=="up"then v=(v+8)%9 elseif c=="down"then v=(v+1)%9 elseif c=="left"then r=(r+8)%9 elseif c=="right"then r=(r+1)%9 end k=v*9+r+1 end local function bP()if q==10 then I=not I return end if q==11 then if i[k]~=0 or s[k]~=0 then aH(aJ(k,0))end B="grid"elseif I then aH({k,i[k],s[k]})i[k]=0 s[k]=s[k]~(1<<(q-1))else a6=q aH(aJ(k,i[k]==q and 0 or q))B="grid"a3()end G()end local function bQ(c)if b.done then if c=="top"then m,x="opts",1 elseif c=="ok"then if b.kind=="b"then bi(b.diff)else m,x="menu",1 end end return end if B=="pad"then if c=="top"then B="grid"elseif c=="left"then q=(q+9)%11+1 elseif c=="right"then q=q%11+1 elseif c=="ok"then bP()else B="grid";bb(c)end return end if c=="top"then m,x="opts",1 G()elseif c=="ok"then if F[k]then E="That number is part of the puzzle"else B="pad"q=i[k]>0 and i[k]or a6 end else bb(c)end end function key(c)bD()E,aa=nil,false if m=="play"then bQ(c)return end if m=="menu"or m=="opts"then local N=m=="menu"and a9()or be()if c~="ok"then am=nil end if c=="up"then x=(x-2)%#N+1 elseif c=="down"then x=x%#N+1 elseif c=="ok"then bC(N[math.min(x,#N)][2])elseif c=="top"and m=="menu"and b and not b.done then m,x="opts",1 end return end if c=="top"or c=="ok"then m=aT end end local function bc(n)local e={}for h=1,9 do if n>>(h-1)&1==1 then e[#e+1]=h end end return e end local function aY(bR)local ar=b.done or bR local ay=nil if not ar then if B=="pad"then ay=q<=9 and q or nil elseif i[k]>0 then ay=i[k]end end local M,aA={},0 local function V(a)local z=H(a)if not M[z]then M[z],aA=true,aA+1 end return z end local S,az={},{}for a=1,81 do local o=i[a]local aU=not ar and not F[a]and o>0 and o~=P[a]and(U or aa)if a==k and not ar then S[a]=V(o==0 and bm or((aU and bl)or(F[a]and bo)or bn)+o-1)elseif o>0 then if o==ay then S[a]=V(bv+o-1)elseif aU then az[#az+1]=a else S[a]=V((F[a]and bt or bs)+o-1)end elseif s[a]~=0 then local e=bc(s[a])S[a]="{"..(#e<=3 and table.concat(e)or(e[1]..e[2].."+")).."}"else S[a]=V(br)end end for aw,r in ipairs({aP,aO,av,aR,aN})do V(r)end V(aQ)for aw,a in ipairs(az)do local z=H(aL+i[a]-1)S[a]=(M[z]or aA<32)and V(aL+i[a]-1)or"@"end local C={}local ab=string.rep(H(aO),3)ab=ab..H(av)..ab..H(av)..ab for v=0,8 do if v==3 or v==6 then C[#C+1]=ab end local e={}for r=1,9 do if r==4 or r==7 then e[#e+1]=H(aP)end e[#e+1]=S[v*9+r]end C[#C+1]=table.concat(e)end if ar then return table.concat(C,"/")end C[#C+1]="..........."local T,d={},bF()local aE=B=="pad"for h=1,9 do if aE and q==h then T[h]=H(aQ+h-1)elseif d[h]>=9 then T[h]="{}"else T[h]="{"..h.."}"end end if aE and q==10 then T[10]=H(I and bz or by)else T[10]=H(I and bA or aR)end T[11]=H(aE and q==11 and bx or aN)C[#C+1]=table.concat(T)return table.concat(C,"/")end local function bI()snail.title("Sudoku - "..ao(b))local f if b.done then local p=(b.diff-1)*3 f="Solved! Best "..R[b.diff].." time "..Q(g[p+2])if b.hints>0 then f=f..", "..b.hints..(b.hints==1 and" hint"or" hints")end elseif E then f=E else f=Q(b.el)if U then local d=ba()if d>0 then f=f.."  "..d..(d==1 and" mistake"or" mistakes")end end if I then f=f.."  Notes on"end if i[k]==0 and s[k]~=0 then f=f.."  Marks "..table.concat(bc(s[k]))end end snail.status(f:sub(1,47))snail.board(aY(),nil,b.done and("Solved in "..Q(b.el))or nil)if b.done then local p=(b.diff-1)*3 snail.text(R[b.diff].." puzzles solved: "..g[p+1]..", average "..Q(g[p+3]/math.max(1,g[p+1])))if b.kind=="d"then snail.text("Daily streak: "..at()..(at()==1 and" day"or" days"))end snail.hint(b.kind=="b"and"BACK options OK next puzzle"or"BACK options OK new puzzle")elseif B=="pad"then local af="place"if q==10 then af=I and"notes off"or"notes on"elseif q==11 then af="erase"elseif I then af="mark"elseif i[k]==q then af="clear"end snail.hint("BACK grid OK "..af.." L/R number UP/DN grid")else snail.hint("BACK options OK enter L/R move UP/DN move")end end local function bJ()snail.title("Sudoku statistics")for c=1,4 do local p=(c-1)*3 local d=g[p+1]if d>0 then snail.text(R[c]..": "..d.." solved, best "..Q(g[p+2])..", average "..Q(g[p+3]/d))else snail.text(R[c]..": none solved yet")end end snail.gap()snail.text("Daily puzzles solved: "..g[13])snail.text("Daily streak "..at()..", best "..g[15])snail.hint("BACK back OK back")end local bu={"Fill the grid so every row, column and 3x3 box holds 1 to 9 once each.","Arrows move. OK opens the number pad: pick with LEFT/RIGHT, then OK.","The pencil key turns notes on, for small pencil marks. The last key erases.","Dotted numbers are part of the puzzle. Picking a number shows all its places.","BACK opens options: undo, a hint, checking for mistakes and new puzzles.","The daily puzzle is easy early in the week and expert on Sunday.",}function draw()if m=="play"and b then bI()return end if m=="stats"then bJ()return end if m=="help"then snail.title("How to play")for aw,f in ipairs(bu)do snail.text(f)end snail.hint("BACK back OK back")return end if m=="noclock"then snail.title("Daily puzzle")snail.text("The daily puzzle needs the date. Connect to Wi-Fi once so the clock gets set, then come back.")snail.text("The Easy to Expert puzzles work any time.")snail.hint("BACK back OK back")return end local N=m=="menu"and a9()or be()if x>#N then x=#N end snail.title(m=="menu"and"Sudoku"or("Sudoku - "..ao(b)))if E then snail.status(E)end for a,bM in ipairs(N)do snail.row(bM[1],a==x)end snail.gap()if m=="menu"then local d=g[1]+g[4]+g[7]+g[10]snail.small("Daily streak "..at()..", "..d..(d==1 and" puzzle"or" puzzles").." solved.")else snail.small("Time "..Q(b.el)..(b.hints>0 and(", hints "..b.hints)or""))end if b and not b.done then snail.board(aY(true),"small")end snail.hint("BACK leave OK choose UP/DN move")end function start()snail.ink("fast")bN()if b and not b.done and not(b.kind=="d"and ae()and b.num~=ae())then J()else m,x="menu",1 end end
