--!name Checkers
--!icon game
--!category Games
local bq="ABCDEFGHIJKLMNOPQRSTUVW"local bp={}for a=1,#bq do bp[a-1]=bq:sub(a,a)end local aj=9 local k,a0,F,a1,aB,aA={},{},{},{},{},{}for a=0,99 do k[a]=aj;a0[a]=0;aB[a]=0;aA[a]=0 end for E=0,7 do for r=0,7 do if(E+r)%2==1 then local c=11+10*E+r F[#F+1]=c a1[c]=#F-1 k[c]=0 local d=(7-E)*3 if E==7 then d=8 end if r>=2 and r<=5 and E>=3 and E<=4 then d=d+4 end if r==0 or r==7 then d=d-3 end aB[c]=d aA[c]=8-(math.abs(2*E-7)+math.abs(2*r-7))//2 end end end local T={[0]=-11,-9,9,11}local aZ={[-11]=0,[-9]=1,[9]=2,[11]=3}local x,V,H,I=1,0,12,12 local W,ag=0,0 local br,bs,aC,bt,Q,bo={},{},{},{},{},{}local function aR(h,c)if h==1 then return 100+aB[c]elseif h==-1 then return-100-aB[99-c]elseif h==2 then return 150+aA[c]end return-150-aA[c]end local function cf()V,H,I=0,0,0 for a=1,32 do local c=F[a]local h=k[c]if h~=0 then V=V+aR(h,c)if h>0 then H=H+1 else I=I+1 end end end end local function bI(ai,g,m,n,h,i,b5,e)local an,ao=0,3 if h==1 then if i==1 then ao=1 else an=2 end end for aq=an,ao do local v=T[aq]local D=n+v local j=k[D]if j~=0 and j~=aj and j*i<0 and k[D+v]==0 then local a8=D+v local bP=b5|(aq<<(11+2*e))k[D]=aj local cb=g if e<9 and not(h==1 and(a8<19 or a8>80))then g=bI(ai,g,m,a8,h,i,bP,e+1)end if g==cb then g=g+1;ai[g]=m|((e+1)<<7)|bP end k[D]=j end end return g end local function bG(ai,p)local i,g,as=x,p,0 for a=1,32 do local c=F[a]local d=k[c]local h=d*i if h>0 then local an,ao=0,3 if h==1 then if i==1 then ao=1 else an=2 end end local bH=false for aq=an,ao do local v=T[aq]local j=k[c+v]if j==0 then if g==p then as=as+1;bo[as]=c|(aq<<11)end elseif not bH and j~=aj and j*i<0 and k[c+v+v]==0 then bH=true k[c]=0 g=bI(ai,g,c,c,h,i,0,0)k[c]=d end end end end if g>p then return g-p,true end for a=1,as do ai[p+a]=bo[a]end return as,false end local function bb(f,z)local m=f&127 local e=(f>>7)&15 local h=k[m]local bj=V-aR(h,m)bt[z]=V k[m]=0 local n=m if e==0 then n=m+T[(f>>11)&3]else for o=0,e-1 do local v=T[(f>>(11+2*o))&3]local D=n+v local j=k[D]W=W+1 br[W]=j bj=bj-aR(j,D)k[D]=0 n=D+v end if x==1 then I=I-e else H=H-e end end bs[z]=h aC[z]=n if h==1 and n<19 then h=2 elseif h==-1 and n>80 then h=-2 end k[n]=h V=bj+aR(h,n)x=-x end local function b0(f,z)x=-x local m=f&127 local e=(f>>7)&15 k[aC[z]]=0 k[m]=bs[z]V=bt[z]if e>0 then local n=m local p=W-e for o=0,e-1 do local v=T[(f>>(11+2*o))&3]local D=n+v k[D]=br[p+o+1]n=D+v end W=p if x==1 then I=I+e else H=H+e end end end local a2,a4=1000000,100000 local aQ,bK,ac,ay,b1=0,0,false,0,140 local a6={}local bm,bn={},{}local function ci()aQ=aQ+1 if aQ>bK then ac=true end ay=ay+1 if ay>=b1 then ay=0;coroutine.yield()end end local function b8()local d=V if H~=I then d=d+(H-I)*(24-H-I)*3 if H+I<=8 then local aT=H>I and 1 or-1 local K,aE=0,0 for a=1,32 do local c=F[a]local h=k[c]*aT if h==2 then K=K+1;bm[K]=c elseif h<0 then aE=aE+1;bn[aE]=c end end local bl=0 for a=1,K do local aa=bm[a]for o=1,aE do local ab=bn[o]local bB=math.abs(aa//10-ab//10)local bA=math.abs(aa%10-ab%10)bl=bl+(bB>bA and bB or bA)end end d=d-aT*bl*2 end end return d*x end local function bV(ap,R,bu,z)ci()if ac then return 0 end local p=ag local g,am=bG(Q,p)if g==0 then return-a4+z end if(ap<=0 and not am)or z>40 then return b8()end ag=p+g if not am then local a7=a6[z]if a7 then for a=p+2,p+g do if Q[a]==a7 then Q[a],Q[p+1]=Q[p+1],a7;break end end end end local cc=g==1 and ap or ap-1 local A=-a2 for a=p+1,p+g do local f=Q[a]bb(f,z)local d=-bV(cc,-bu,-R,z+1)b0(f,z)if ac then ag=p;return 0 end if d>A then A=d if d>R then R=d if d>=bu then if not am then a6[z]=f end break end end end end ag=p return A end local b2={{2,300,60},{5,2000,10},{20,5000,0}}local a3={"Easy","Medium","Hard"}local J={}local function cj(ba)local av=b2[ba]local M={}for a=1,#J do M[a]=J[a]end local g=#M if g==0 then return nil end if g==1 then return M[1]end for a=g,2,-1 do local o=math.random(a)M[a],M[o]=M[o],M[a]end aQ,bK,ac,ay,ag,W=0,av[2],false,0,0,0 a6={}local A=M[1]for ap=1,av[1]do local R,aK,bv=-a2,nil,1 for a=1,g do local f=M[a]bb(f,0)local d=-bV(ap-1,-a2,av[3]-R,1)b0(f,0)if ac then break end if av[3]>0 then d=d+math.random(0,av[3])end if d>R then R,aK,bv=d,f,a end end if aK then A=aK table.remove(M,bv)table.insert(M,1,aK)end if ac or R>a4//2 or R<-a4//2 then break end end return A end local N,U,ak,az={},{},{},{}local aw,w,aL,a9,aM=1,nil,"",0,0 local _,aO,aP,bc=0,12,12,false local y,B,G,ah,ae=1,1,2,false,false local al={0,0,0,0,0,0,0,0,0}local X=""local function bS()local af=x for a=1,32 do af=af*5+k[F[a]]+2 end return af end local function bh()local Z=X for a=1,32 do local c=F[a]local d=0 if#Z>0 then local j=Z:sub(a,a)d=j=="b"and 1 or j=="B"and 2 or j=="w"and-1 or j=="W"and-2 or 0 elseif c<41 then d=-1 elseif c>60 then d=1 end k[c]=d end x,_=1,0 if#Z>0 then x=Z:sub(33,33)=="w"and-1 or 1 _=tonumber(Z:sub(34))or 0 end W,ag=0,0 cf()N,U,az={},{bS()},{}a9,aM,aL,w=0,0,"",nil end local function ce()local b={}for a=1,32 do local d=k[F[a]]b[a]=d==1 and"b"or d==2 and"B"or d==-1 and"w"or d==-2 and"W"or"."end return table.concat(b)..(x==1 and"b"or"w").._ end local function cl(i)if y==1 then return i==B and"You"or"Computer"end return i==1 and"Black"or"White"end local function bR(f)local m=f&127 local e=(f>>7)&15 local l={m}if e==0 then l[2]=m+T[(f>>11)&3]else local n=m for o=0,e-1 do n=n+2*T[(f>>(11+2*o))&3]l[o+2]=n end end return l end local function bN(l)local m=l[1]if#l==2 and aZ[l[2]-m]then return m|(aZ[l[2]-m]<<11)end local f=m|((#l-1)<<7)for o=2,#l do local bz=aZ[(l[o]-l[o-1])//2]if not bz then return nil end f=f|(bz<<(11+2*(o-2)))end return f end local function bf(f)local i=x local m=f&127 local e=(f>>7)&15 local bL=k[m]==i az={}if e>0 then local n=m for o=0,e-1 do local v=T[(f>>(11+2*o))&3]az[n+v]=true n=n+2*v end end bb(f,0)W=0 a9,aM=m,aC[0]local b6=bL and k[aM]==2*i aL=cl(i)..": "..(bL and"man"or"king")..(e==0 and" moved"or(" took "..e))..(b6 and", crowned!"or"")N[#N+1]=f if e>0 then _=0;U={}else _=_+1 end U[#U+1]=bS()end local function bT(e)if y~=1 or ae then return end ae=true local a=(G-1)*3+e al[a]=al[a]+1 end local function au()local g,am=bG(Q,0)J,ak={},{}for a=1,g do J[a]=Q[a];ak[a]=bR(Q[a])end aw,bc=x,am aO,aP=0,0 for a=1,32 do local c=F[a]local d=k[c]a0[c]=d if d>0 then aO=aO+1 elseif d<0 then aP=aP+1 end end w=nil if g==0 then local ax=-x if y==1 then w=ax==B and"You win!"or"Computer wins"bT(ax==B and 1 or 2)else w=(ax==1 and"Black"or"White").." wins"end else local bg,n=0,U[#U]for a=1,#U do if U[a]==n then bg=bg+1 end end if bg>=3 then w="Draw - same position 3 times"elseif _>=80 then w="Draw - 40 moves, no jump"end if w then bT(3)end end end local function bW()local b={"K1",y,B==1 and"b"or"w",G,ae and 1 or 0,table.concat(al,","),":",X,":"}for a=1,#N do local l=bR(N[a])b[#b+1]=string.char(65+a1[l[1]])for o=2,#l do b[#b+1]=string.char(33+a1[l[o]])end end return table.concat(b)end local function at(q,g)bh()for a=1,g do local f=q[a]local h=k[f&127]if h==0 or h==aj or h*x<0 then break end bf(f)end au()end local function bU()local i=bW()if#i>1000 then local q=N local af=#q//2 at(q,af)X=ce()local aV={}for a=af+1,#q do aV[#aV+1]=q[a]end at(aV,#aV)i=bW()end snail.save(i)end local function cg()local i=snail.load()if type(i)~="string"then return false end local aN,aI,ba,b7,aT,Z,bO=i:match("^K1(%d)([bw])(%d)(%d)([%d,]+):([^:]*):(.*)$")if not aN then return false end local a=0 for d in aT:gmatch("%d+")do a=a+1;al[a]=tonumber(d)end y,B,G=tonumber(aN),aI=="b"and 1 or-1,tonumber(ba)if G<1 or G>3 then G=2 end X=Z local q,l={},nil for e=1,#bO do local j=bO:byte(e)if j>=65 then if l then q[#q+1]=bN(l)end l={F[j-64]}elseif l then l[#l+1]=F[j-32]end end if l then q[#q+1]=bN(l)end ae=true at(q,#q)ae=b7=="1"ah=true return true end local ad,aX=nil,false local S,C,u="menu",1,76 local s,t=nil,{}local ar=false local function bk()ad,aX=nil,false snail.tick(0)end local aU local function bX()aX=true ad=coroutine.create(function()return cj(G)end)snail.after(aU)snail.tick(450)end local function aJ()return not w and not aX and(y==2 or aw==B)end local function bw()local be={}for a=1,#J do local l=ak[a]if l[1]==s then local bF=true for o=1,#t do if l[o+1]~=t[o]then bF=false;break end end if bF then be[#be+1]=a end end end return be end local function aW()local b,P={},{}if not s then for a=1,#J do local r=ak[a][1]if not P[r]then P[r]=true;b[#b+1]=r end end else local j=bw()for a=1,#j do local aS=ak[j[a]][#t+2]if aS and not P[aS]then P[aS]=true;b[#b+1]=aS end end if#t==0 then b[#b+1]=s end end return b,P end local function aY(c)local r,E=(c-11)%10,(c-11)//10 if ar then return 7-r,7-E end return r,E end local function aH()local b,P=aW()if P[u]or#b==0 then return end local aF,aG=aY(u)local A,Y for a=1,#b do local aa,ab=aY(b[a])local j=(aa-aF)^2+(ab-aG)^2 if b[a]~=s and(not Y or j<Y)then A,Y=b[a],j end end u=A or u end local function aD()s,t=nil,{}ar=y==1 and B==-1 bU()if not w and y==1 and aw~=B then bX()else aH()end end aU=function()if not ad then return end local cd,f=coroutine.resume(ad)if coroutine.status(ad)~="dead"then return end bk()local bJ=false for a=1,#J do if J[a]==f then bJ=true end end if not cd or not bJ then local q=N at(q,#q)f=J[math.random(#J)]end if not f then return end bf(f)au()aD()end local function bd(aN,aI)bk()y,B,ah,ae,X=aN,aI,true,false,""bh()au()u=aI==1 and 76 or 23 S="play"aD()end local function b_()local g=#N-1 local bE=#X>0 and(X:sub(33,33)=="w"and-1 or 1)or 1 if y==1 then while g>=0 and((g%2==0)and bE or-bE)~=B do g=g-1 end end if g<0 then return nil end return g end local function ck()local g=b_()if not g then return end bk()local q=N at(q,g)S="play"aD()end local function bM()local b={}if ah then b[#b+1]={"Back to the game","resume"}end b[#b+1]={"New game as Black (first)","b"}b[#b+1]={"New game as White","w"}b[#b+1]={"New game, two players","2"}b[#b+1]={"Computer level: "..a3[G],"lvl"}if ah and b_()then b[#b+1]={"Take back a move","undo"}end return b end local function ca(bC,bD)local b=aW()local aF,aG=aY(u)local A,Y for bQ=1,2 do for a=1,#b do local aa,ab=aY(b[a])local h=(aa-aF)*bC+(ab-aG)*bD local i=math.abs((aa-aF)*bD)+math.abs((ab-aG)*bC)if h>0 and(bQ==2 or i<=h)then local j=bQ==1 and h*2+i or h+i*3 if not Y or j<Y then A,Y=b[a],j end end end if A then break end end if A then u=A end end local a_={up={0,-1},down={0,1},left={-1,0},right={1,0}}function key(e)if S=="menu"then local O=bM()if C>#O then C=#O end if e=="up"then C=(C-2)%#O+1 elseif e=="down"then C=C%#O+1 elseif e=="ok"or((e=="left"or e=="right")and O[C][2]=="lvl")then local K=O[C][2]if K=="resume"then S="play"elseif K=="b"then bd(1,1)elseif K=="w"then bd(1,-1)elseif K=="2"then bd(2,1)elseif K=="lvl"then G=(G+(e=="left"and 1 or 0))%3+1 if ah then bU()end elseif K=="undo"then ck()end end return end if e=="top"then if#t>0 then u=t[#t]t[#t]=nil elseif s then u=s s=nil else S="menu"C=1 end return end if w then if e=="ok"then S="menu";C=2 end return end if not aJ()then return end if e=="ok"then local cm,P=aW()if not s then if P[u]then s=u;aH()end elseif u==s and#t==0 then s=nil elseif P[u]then t[#t+1]=u local j=bw()if#j==1 then local f=J[j[1]]bf(f)au()u=aC[0]aD()else aH()end end elseif a_[e]then ca(a_[e][1],a_[e][2])end end function tick()aU()if ad then snail.after(aU)end end local b3={[1]=1,[2]=2,[-1]=3,[-2]=4}local function b4()local a5=aJ()and S=="play"local bY,bx={},{}if a5 and s then local b=aW()for a=1,#b do bY[b[a]]=true end for a=1,#t do bx[t[a]]=true end end local b={}for bi=0,7 do local E=ar and 7-bi or bi for by=0,7 do local r=ar and 7-by or by local c=11+10*E+r local a=0 if(E+r)%2==1 then local L=b3[a0[c]]or 0 if a5 and c==u then a=L==0 and 6 or 6+L elseif a5 and s then if c==s then a=10+L elseif bx[c]then a=22 elseif bY[c]then a=15 else a=L==0 and 1 or 1+L end elseif c==aM and L>0 then a=16+L elseif L==0 and c==a9 then a=16 elseif L==0 and az[c]then a=21 else a=L==0 and 1 or 1+L end end b[#b+1]=bp[a]end if bi<7 then b[#b+1]="/"end end return table.concat(b)end local function ch()local b="Black "..aO.."  White "..aP.." - "if w then return b.."game over"end if aX then return b.."thinking..."end if y==1 then return b..(bc and"you must jump"or"your move")end return b..(aw==1 and"Black"or"White")..(bc and" must jump"or" to move")end function draw()if S=="menu"then snail.title("Checkers")local O=bM()if C>#O then C=#O end for a,b9 in ipairs(O)do snail.row(b9[1],a==C)end snail.gap()if ah then local ax=y==2 and"Two players"or("You play "..(B==1 and"Black"or"White")..", "..a3[G])snail.small(ax..(w and(". "..w..".")or(", move "..(#N//2+1)..".")))else snail.small("Black moves first. Jumps are compulsory and a jump goes on while it can.")end local i=al snail.small("Won-lost-drawn against the computer:")snail.small("Easy "..i[1].."-"..i[2].."-"..i[3].."   Medium "..i[4].."-"..i[5].."-"..i[6].."   Hard "..i[7].."-"..i[8].."-"..i[9])snail.hint("BACK leave OK choose UP/DN move")return end snail.title(y==1 and("Checkers - "..a3[G])or"Checkers - two players")snail.status(ch())local bZ=aL~=""and aL or"Move with the arrows, OK picks a piece."if s and aJ()then bZ=#t>0 and"Keep jumping: pick the next square."or"Pick a square for it, or OK on it to cancel."end snail.text(bZ)snail.board(b4(),nil,w)if w then snail.hint("BACK options OK new game")elseif not aJ()then snail.hint("BACK options")elseif s then snail.hint("BACK undo step OK here L/R move UP/DN move")else snail.hint("BACK options OK pick L/R move UP/DN move")end end function start()math.randomseed(os.time())bh()au()if cg()then ar=y==1 and B==-1 if not w then S="play"if y==1 and aw~=B then bX()else aH()end end end end
