--!name Reversi
--!icon game
--!category Games
local a0="ABCDEFGHIJ"local a_={}for a=1,#a0 do a_[a-1]=a0:sub(a,a)end local bk,an=9,100 local h,k,y,J,aE,R={},{},{},{},{},{}for a=0,an do h[a]=bk;k[a]=0;y[a]=0;J[a]=0;aE[a]=0 end do local bl={100,-20,10,5,5,10,-20,100,-20,-50,-2,-2,-2,-2,-50,-20,10,-2,1,1,1,1,-2,10,5,-2,1,0,0,1,-2,5,5,-2,1,0,0,1,-2,5,10,-2,1,1,1,1,-2,10,-20,-50,-2,-2,-2,-2,-50,-20,100,-20,10,5,5,10,-20,100}for ae=0,7 do for as=0,7 do local b=11+10*ae+as R[#R+1]=b k[b]=bl[ae*8+as+1]end end end local aI={}for a=1,64 do aI[a]=R[a]end table.sort(aI,function(o,T)if k[o]~=k[T]then return k[o]>k[T]end return o<T end)local aZ={-11,-10,-9,-1,1,9,10,11}local aj={11,22,12,21,18,27,17,28,81,72,82,71,88,77,87,78}local g,G,M,B,w=1,0,0,60,0 local aF,a1,al={},{},{}local function aL(b,e)local Y=-e for a=1,8 do local r=aZ[a]local c=b+r if h[c]==Y then repeat c=c+r until h[c]~=Y if h[c]==e then return true end end end return false end local function av(b,F)local e,Y=g,-g local ag,l=w,G for a=1,8 do local r=aZ[a]local c=b+r if h[c]==Y then repeat c=c+r until h[c]~=Y if h[c]==e then c=c-r repeat h[c]=e ag=ag+1 aF[ag]=c l=l+2*e*k[c]c=c-r until c==b end end end local d=ag-w if d==0 then return 0 end a1[F]=w w=ag h[b]=e G=l+e*k[b]M=M+e*(d+d+1)local o,T=J[b],y[b]y[o]=T J[T]=o B=B-1 g=Y return d end local function aY(b,F)local e=-g g=e local aQ=a1[F]local l=G-e*k[b]for a=w,aQ+1,-1 do local c=aF[a]h[c]=-e l=l-2*e*k[c]end local d=w-aQ w=aQ G=l h[b]=0 M=M-e*(d+d+1)y[J[b]]=b J[y[b]]=b B=B+1 end local function aM(K)local d,e=0,g local ab=y[0]while ab~=an do if aL(ab,e)then d=d+1;al[K+d]=ab end ab=y[ab]end return d end local aG,ao=1000000,100000 local bj=8 local ax,a8,S,ai,bh,ac=0,0,false,0,1700,0 local function bA(I)ax=ax+I if ax>a8 then S=true end ai=ai+I if ai>=bh then ai=0;coroutine.yield()end end local function bp(O,aT)local f=G for a=1,16,4 do if h[aj[a]]~=0 then local P,a3,a4=aj[a+1],aj[a+2],aj[a+3]f=f-k[P]*h[P]-k[a3]*h[a3]-k[a4]*h[a4]end end f=f*g+bj*(O-aT)if B<16 then f=f+M*g*(16-B)//2 end return f end local function bq()local r=M*g if r>0 then return ao+r elseif r<0 then return-ao+r end return 0 end local function aW(W,v,aJ,F,aT,bu)bA(B+4)if S then return 0 end local K=ac local d=aM(K)if d==0 then if bu then return bq()end g=-g local f=-aW(W,-aJ,-v,F+1,0,true)g=-g return f end if W<=0 then return bp(d,aT)end ac=K+d local i=-aG for a=1,d do local b=al[K+a]av(b,F)local f=-aW(W-1,-aJ,-v,F+1,d,false)aY(b,F)if S then ac=K;return 0 end if f>i then i=f if f>v then v=f if f>=aJ then break end end end end ac=K return i end local bi={{1,0,0},{3,3000,12},{12,52000,0}}local aH={"Easy","Medium","Hard"}local n={}local function bB(au)local ah=bi[au]local H={}for a=1,#n do H[a]=n[a]end local d=#H if d==0 then return nil end if d==1 then return H[1]end if au==1 then local i,aK for a=1,d do local b=H[a]local f=av(b,0)+math.random(0,4)aY(b,0)if k[b]==100 and math.random(2)==1 then f=f+6 end if not aK or f>aK then i,aK=b,f end end return i end ax,a8,S,ai,ac=0,ah[2],false,0,0 local i=H[1]for W=1,ah[1]do local v,at,a2=-aG,nil,1 for a=1,d do local b=H[a]av(b,0)local f=-aW(W-1,-aG,ah[3]-v,1,d,false)aY(b,0)if S then break end if ah[3]>0 then f=f+math.random(0,ah[3])end if f>v then v,at,a2=f,b,a end end if at then i=at table.remove(H,a2)table.insert(H,1,at)end if S or v>ao//2 or v<-ao//2 or W>=B then break end end return i end local t,am,Q,ak={},{},{},{}local _,m,X,aP=1,nil,"",0 local A,C=2,2 local q,u,s,Z,V=1,1,2,false,false local aa={0,0,0,0,0,0,0,0,0}local function aV()for a=1,64 do h[R[a]]=0 end h[44],h[55],h[45],h[54]=-1,-1,1,1 local ad=0 for a=1,64 do local b=aI[a]if h[b]==0 then y[ad]=b;J[b]=ad;ad=b end end y[ad]=an J[an]=ad g,w,B,G,M=1,0,60,0,0 for a=1,64 do local b=R[a]G=G+k[b]*h[b]end t,am,ak={},{},{}aP,X,m=0,"",nil end local function bx(b)return string.char(97+(b-11)%10)..((b-11)//10+1)end local function bg(e)if q==1 then return e==u and"You"or"Computer"end return e==1 and"Black"or"White"end local function aS(b)local e=g local d=av(b,0)ak={}for a=1,w do ak[aF[a]]=true end w=0 t[#t+1]=b am[#am+1]=e aP=b X=bg(e)..": "..bx(b)..", "..d..(d==1 and" disc"or" discs").." turned"end local function bc(j)if q~=1 or V then return end V=true local a=(s-1)*3+j aa[a]=aa[a]+1 end local function af()local d=aM(0)if d==0 then g=-g d=aM(0)if d==0 then g=-g elseif#t>0 then local l=bg(-g)X=X..". "..l..(l=="You"and" have"or" has").." no move"end end n,Q={},{}for a=1,d do n[a]=al[a];Q[al[a]]=true end _=g A,C=0,0 for a=1,64 do local b=R[a]local f=h[b]aE[b]=f if f==1 then A=A+1 elseif f==-1 then C=C+1 end end m=nil if d==0 then local ay=A.."-"..C if C>A then ay=C.."-"..A end if A==C then m="Draw "..ay bc(3)else local I=A>C and 1 or-1 if q==1 then m=(I==u and"You win "or"Computer wins ")..ay bc(I==u and 1 or 2)else m=(I==1 and"Black"or"White").." wins "..ay end end end end local function bw()local c={"R1",q,u==1 and"b"or"w",s,V and 1 or 0,table.concat(aa,","),":"}for a=1,#t do local b=t[a]c[#c+1]=string.char(48+(b-11)//10*8+(b-11)%10)end return table.concat(c)end local function bd()snail.save(bw())end local function aU(N,d)aV()for a=1,d do local b=N[a]if not aL(b,g)then g=-g end if h[b]~=0 or not aL(b,g)then break end aS(b)end af()end local function bv()local e=snail.load()if type(e)~="string"then return false end local aw,aN,au,bo,by,ba=e:match("^R1(%d)([bw])(%d)(%d)([%d,]+):(.*)$")if not aw then return false end local a=0 for f in by:gmatch("%d+")do a=a+1;aa[a]=tonumber(f)end q,u,s=tonumber(aw),aN=="b"and 1 or-1,tonumber(au)if s<1 or s>3 then s=2 end local N={}for j=1,#ba do local E=ba:byte(j)-48 N[j]=11+(E//8)*10+E%8 end V=true aU(N,#N)V=bo=="1"Z=true return true end local U,aA=nil,false local D,p,z="menu",1,34 local function aX()U,aA=nil,false snail.tick(0)end local az local function be()aA=true U=coroutine.create(function()return bB(s)end)snail.after(az)snail.tick(450)end local function aO()return not m and not aA and(q==2 or _==u)end local function aB(b)return(b-11)%10,(b-11)//10 end local function a7()if Q[z]or#n==0 then return end local aq,ar=aB(z)local i,L for a=1,#n do local P,aC=aB(n[a])local E=(P-aq)^2+(aC-ar)^2 if not L or E<L then i,L=n[a],E end end z=i end local function ap()bd()if not m and q==1 and _~=u then be()else a7()end end az=function()if not U then return end local bt,O=coroutine.resume(U)if coroutine.status(U)~="dead"then return end aX()if not bt or not O or not Q[O]then aU(t,#t)O=n[math.random(#n)]end if not O then return end aS(O)af()ap()end local function aR(aw,aN)aX()q,u,Z,V=aw,aN,true,false aV()af()z=34 D="play"ap()end local function bf()local d=#t-1 if q==1 then while d>=0 and am[d+1]~=u do d=d-1 end end if d<0 then return nil end return d end local function bC()local d=bf()if not d then return end aX()local N=t aU(N,d)D="play"ap()end local function a9()local c={}if Z then c[#c+1]={"Back to the game","resume"}end c[#c+1]={"New game as Black (first)","b"}c[#c+1]={"New game as White","w"}c[#c+1]={"New game, two players","2"}c[#c+1]={"Computer level: "..aH[s],"lvl"}if Z and bf()then c[#c+1]={"Take back a move","undo"}end return c end local function bs(a5,a6)local aq,ar=aB(z)local i,L for bb=1,2 do for a=1,#n do local P,aC=aB(n[a])local l=(P-aq)*a5+(aC-ar)*a6 local e=math.abs((P-aq)*a6)+math.abs((aC-ar)*a5)if l>0 and(bb==2 or e<=l)then local E=bb==1 and l*2+e or l+e*3 if not L or E<L then i,L=n[a],E end end end if i then break end end if i then z=i end end local aD={up={0,-1},down={0,1},left={-1,0},right={1,0}}function key(j)if D=="menu"then local x=a9()if p>#x then p=#x end if j=="up"then p=(p-2)%#x+1 elseif j=="down"then p=p%#x+1 elseif j=="ok"or((j=="left"or j=="right")and x[p][2]=="lvl")then local o=x[p][2]if o=="resume"then D="play"elseif o=="b"then aR(1,1)elseif o=="w"then aR(1,-1)elseif o=="2"then aR(2,1)elseif o=="lvl"then s=(s+(j=="left"and 1 or 0))%3+1 if Z then bd()end elseif o=="undo"then bC()end end return end if j=="top"then D="menu";p=1;return end if m then if j=="ok"then D="menu";p=2 end return end if not aO()then return end if j=="ok"then if Q[z]then aS(z)af()ap()end elseif aD[j]then bs(aD[j][1],aD[j][2])end end function tick()az()if U then snail.after(az)end end local function bn(b)local f=aE[b]if f==0 then if Q[b]and aO()and D=="play"then if b==z then return _==1 and 4 or 5 end return 3 end return 0 end if b==aP then return f==1 and 6 or 7 end if ak[b]then return f==1 and 8 or 9 end return f==1 and 1 or 2 end local function bm()local c={" abcdefgh"}for ae=0,7 do c[#c+1]="/"..(ae+1)for as=0,7 do c[#c+1]=a_[bn(11+10*ae+as)]end end return table.concat(c)end local function bz()local c="Black "..A.."  White "..C.." - "if m then return c.."game over"end if aA then return c.."thinking..."end if q==1 then return c.."your move"end return c..(_==1 and"Black"or"White").." to move"end function draw()if D=="menu"then snail.title("Reversi")local x=a9()if p>#x then p=#x end for a,br in ipairs(x)do snail.row(br[1],a==p)end snail.gap()if Z then local I=q==2 and"Two players"or("You play "..(u==1 and"Black"or"White")..", "..aH[s])snail.small(I..(m and(". "..m..".")or(", move "..(#t+1)..".")))else snail.small("Black moves first. Turn over discs by trapping them between two of yours.")end local e=aa snail.small("Won-lost-drawn against the computer:")snail.small("Easy "..e[1].."-"..e[2].."-"..e[3].."   Medium "..e[4].."-"..e[5].."-"..e[6].."   Hard "..e[7].."-"..e[8].."-"..e[9])snail.hint("BACK leave OK choose UP/DN move")return end snail.title(q==1 and("Reversi - "..aH[s])or"Reversi - two players")snail.status(bz())snail.text(X~=""and X or"Pick a dot with the arrows, OK places a disc.")snail.board(bm(),nil,m)if m then snail.hint("BACK options OK new game")elseif not aO()then snail.hint("BACK options")else snail.hint("BACK options OK place L/R move UP/DN move")end end function start()math.randomseed(os.time())aV()af()if bv()then if not m then D="play"if q==1 and _~=u then be()else a7()end end end end
