--!name 2048
--!icon puzzle
--!category Puzzles
local Z="!$%&()+,-0123456789:"local _=18 local d,D={},{}local h,q,F,m=0,0,0,false local j,t,E=0,0,0 local o,n,k=false,false,false local v,A=0,0 local i,l,r,B="play",1,false,nil local function u(e)local g=tostring(e)while true do local f,c=g:gsub("^(%d+)(%d%d%d)","%1,%2")g=f if c==0 then return g end end end local function T(f)local w={}for a=1,16 do w[a]=string.char(97+f[a])end return table.concat(w)end local function I()snail.save(table.concat({"T1",h,q,F,m and 1 or 0,j,t,E,o and 1 or 0,n and 1 or 0,k and 1 or 0,v,A,T(d),T(D)},";"))end local function ad()local g=snail.load()if type(g)~="string"or g:sub(1,3)~="T1;"then return false end local x={}for b in(g..";"):gmatch("([^;]*);")do x[#x+1]=b end local e=function(a)return tonumber(x[a])or 0 end local p,R=x[14]or"",x[15]or""if#p~=16 or#R~=16 then return false end h,q,F,m=e(2),e(3),e(4),e(5)==1 j,t,E=e(6),e(7),e(8)o,n,k=e(9)==1,e(10)==1,e(11)==1 v,A=e(12),e(13)for a=1,16 do d[a]=math.max(0,math.min(17,p:byte(a)-97))D[a]=math.max(0,math.min(17,R:byte(a)-97))end return true end local function O()local y={}for a=1,16 do if d[a]==0 then y[#y+1]=a end end if#y==0 then return 0 end local a=y[math.random(#y)]d[a]=math.random(10)==1 and 2 or 1 return a end local function ab()for a=1,16 do local b=d[a]if b==0 then return true end if a%4~=0 and d[a+1]==b then return true end if a<=12 and d[a+4]==b then return true end end return false end local Q={left={1,1,5,1,9,1,13,1},right={4,-1,8,-1,12,-1,16,-1},up={1,4,2,4,3,4,4,4},down={13,-4,14,-4,15,-4,16,-4},}local function af(M)local P=Q[M]local H,z=false,0 for U=1,7,2 do local g,L=P[U],P[U+1]local G,C={},{}for c=0,3 do local b=d[g+c*L]if b>0 then G[#G+1]=b end end local a=1 while a<=#G do local b=G[a]if G[a+1]==b then C[#C+1]=b+1 z=z+(1<<(b+1))a=a+2 else C[#C+1]=b a=a+1 end end for c=0,3 do local b=C[c+1]or 0 if d[g+c*L]~=b then H=true;d[g+c*L]=b end end end return H,z end local function N()for a=1,16 do d[a],D[a]=0,0 end h,F,m,j=0,0,false,0 o,n,k,B,r=false,false,false,nil,false A=A+1 O()t=O()E=0 i="play"I()end local function ae(M)local S={}for a=1,16 do S[a]=d[a]end local H,z=af(M)if not H then B="Nothing moves that way";return end D,F,E,m=S,h,t,true h=h+z if h>q then q=h end j=j+1 t=O()for a=1,16 do if d[a]>v then v=d[a]end if d[a]>=11 and not o then o=true end end k=not ab()I()end local function Y()if not m then B="Only one move can be taken back";return end for a=1,16 do d[a]=D[a]end h,t,m=F,E,false j=math.max(0,j-1)k=false i="play"I()end local function V()local f={{"Back to the game","resume"}}if m then f[#f+1]={"Take back the last move","undo"}end f[#f+1]={r and"Press OK again to start over"or"New game","new"}f[#f+1]={"How to play","help"}return f end function key(c)if i=="menu"then local s=V()if c=="up"then l=(l-2)%#s+1;r=false elseif c=="down"then l=l%#s+1;r=false elseif c=="ok"then local p=s[l][2]if p=="resume"then i="play"elseif p=="undo"then Y()elseif p=="help"then i="help"elseif p=="new"then if r or k or j==0 then N()else r=true end end end return end if i=="help"then if c=="top"or c=="ok"then i="menu"end return end B=nil if c=="top"then i,l,r="menu",1,false return end if k then if c=="ok"then N()end return end if o and not n then if c=="ok"then n=true;I();return end n=true end if c=="ok"then Y()elseif Q[c]then ae(c)end end local function aa()local X={}for W=0,3 do local f={}for w=1,4 do local a=W*4+w local b=d[a]local K=b if a==t and b<=2 and b>0 then K=_+b-1 end f[w]=Z:sub(K+1,K+1)end X[W+1]=table.concat(f)end return table.concat(X,"/")end function draw()if i=="menu"then snail.title("2048")local s=V()if l>#s then l=#s end for a,ac in ipairs(s)do snail.row(ac[1],a==l)end snail.gap()snail.small("Best score "..u(q)..". Biggest tile "..(v>0 and u(1<<v)or"none yet")..".")snail.small("Games played: "..A..".")snail.hint("BACK leave OK choose UP/DN move")return end if i=="help"then snail.title("How to play")snail.text("Every arrow slides all the tiles as far as they go that way.")snail.text("Two tiles with the same number that meet merge into one.")snail.text("Merges add to your score. A new 2 or 4 appears after each move.")snail.text("The notch marks the tile that just appeared.")snail.text("Make a 2048 tile to win, then keep going if you like.")snail.text("OK takes back your last move. The game ends when nothing can move.")snail.hint("BACK back OK back")return end snail.title("2048")snail.status(B or("Score "..u(h).."    Best "..u(q)))local J if k then J="No moves left! Score "..u(h)elseif o and not n then J="You made 2048!"end snail.board(aa(),nil,J)if k then snail.small("Moves "..j..". Your best is "..u(q)..".")snail.hint("BACK options OK new game")elseif o and not n then snail.small("Keep going for 4096, or start over from the options.")snail.hint("BACK options OK keep going")else snail.small("Moves "..j..(m and". OK takes back the last one."or"."))snail.hint("BACK options OK undo L/R slide UP/DN slide")end end function start()math.randomseed(os.time())if not ad()then N()end i="play"end
