--!name Chess
--!icon game
--!category Games
local bf="!$%&()+,-09:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`ijklmnpqrstuvwxyz"local bn={}for a=1,#bf do bn[a-1]=bf:sub(a,a)end local bm="KKKKKKKKUUUUUUUUMMOQQOMMLLMPPMLLKKKOOKKKLJIKKIJLLMMGGMMLKKKKKKKKACEEEECACGKKKKGCEKMNNMKEELNOONLEEKNOONKEELMNNMLECGKLLKGCACEEEECAGIIIIIIGIKKKKKKIIKLMMLKIILLMMLLIIKMMMMKIIMMMMMMIILKKKKLIGIIIIIIGKKKKKKKKLMMMMMMLJKKKKKKJJKKKKKKJJKKKKKKJJKKKKKKJJKKKKKKJKKKLLKKKGIIJJIIGIKKKKKKIIKLLLLKIJKLLLLKJKKLLLLKJILLLLLKIIKLKKKKIGIIJJIIGECCAACCEECCAACCEECCAACCEECCAACCEGEECCEEGIGGGGGGIOOKKKKOOOQMKKMQOACEGGECAEGIKKIGEEIOQQOIEEIQSSQIEEIQSSQIEEIOQQOIEEEKKKKEEAEEEEEEA"local br=string.byte local aU={0,64,128,192,256,320}local aT={100,320,330,500,900,0}local al={100,320,330,500,900,2000}local bl={"Pawn","Knight","Bishop","Rook","Queen","King"}local aj=99 local g,bh,L,ak,bk={},{},{},{},{}for a=0,119 do g[a]=aj end for r=0,7 do for q=0,7 do local l=21+q+10*r L[#L+1]=l ak[l]=r*8+q+1 bk[l]=(7-r)*8+q+1 end end local bi={-21,-19,-12,-8,8,12,19,21}local au={-11,-10,-9,-1,1,9,10,11}local bY={true,false,true,false,false,true,false,true}local bZ={[3]={-11,-9,9,11},[4]={-10,-1,1,10},[5]=au}local bg={[95]=12,[98]=14,[91]=13,[25]=3,[28]=11,[21]=7}local s,Q,Z,R,ag=1,15,0,0,0 local S={}local bR,bU,bS,bT,bV={},{},{},{},{}local F=524288 local av={}for a=0,40 do av[a]={}end local function aa(e,l)if e>0 then return aT[e]+(br(bm,aU[e]+ak[l])-75)*5 end return-aT[-e]-(br(bm,aU[-e]+bk[l])-75)*5 end local function I(l,ae)if ae==1 then if g[l+9]==1 or g[l+11]==1 then return true end elseif g[l-9]==-1 or g[l-11]==-1 then return true end local c,m=2*ae,6*ae for a=1,8 do local k=g[l+bi[a]]if k==c then return true end if g[l+au[a]]==m then return true end end local b1,r,p=3*ae,4*ae,5*ae for a=1,8 do local Y=au[a]local d=l+Y local k=g[d]while k==0 do d=d+Y;k=g[d]end if k==p then return true end if bY[a]then if k==b1 then return true end elseif k==r then return true end end return false end local function aW(j,aw)local c,h=0,s for a=1,64 do local b=L[a]local e=g[b]*h if e>0 and e<aj then if e==1 then local bw=-10*h local i=b+bw local bG=(h==1 and i<30)or(h==-1 and i>90)for Y=-1,1,2 do local d=i+Y local k=g[d]if k~=aj and k*h<0 then if k*h==-6 then return-1 end local o=al[-k*h]*10-1 if bG then for p=5,2,-1 do c=c+1;j[c]=b+d*128+p*16384+(o+al[p])*F end else c=c+1;j[c]=b+d*128+(o)*F end elseif d==Z then c=c+1;j[c]=b+d*128+131072+(999)*F end end if g[i]==0 then if bG then for p=5,2,-1 do c=c+1;j[c]=b+i*128+p*16384+(al[p])*F end elseif not aw then c=c+1;j[c]=b+i*128 if(h==1 and b>80)or(h==-1 and b<39)then local bQ=i+bw if g[bQ]==0 then c=c+1;j[c]=b+bQ*128+393216 end end end end elseif e==2 or e==6 then local at=e==2 and bi or au for A=1,8 do local d=b+at[A]local k=g[d]if k==0 then if not aw then c=c+1;j[c]=b+d*128 end elseif k~=aj and k*h<0 then if k*h==-6 then return-1 end c=c+1;j[c]=b+d*128+(al[-k*h]*10-e)*F end end if e==6 and not aw and b==(h==1 and 95 or 25)then local by=h==1 and 1 or 4 if Q&by~=0 and g[b+1]==0 and g[b+2]==0 and g[b+3]==4*h and not I(b,-h)and not I(b+1,-h)and not I(b+2,-h)then c=c+1;j[c]=b+(b+2)*128+262144 end if Q&(by*2)~=0 and g[b-1]==0 and g[b-2]==0 and g[b-3]==0 and g[b-4]==4*h and not I(b,-h)and not I(b-1,-h)and not I(b-2,-h)then c=c+1;j[c]=b+(b-2)*128+262144 end end else local at=bZ[e]for A=1,#at do local Y=at[A]local d=b+Y while true do local k=g[d]if k~=0 then if k~=aj and k*h<0 then if k*h==-6 then return-1 end c=c+1;j[c]=b+d*128+(al[-k*h]*10-e)*F end break end if not aw then c=c+1;j[c]=b+d*128 end d=d+Y end end end end end return c end local function ao(f,n)local b,i=f%128,(f//128)%128 local p,z=(f//16384)%8,f//131072 local h=s local e,k=g[b],g[i]bR[n],bU[n],bS[n],bT[n],bV[n]=k,Q,Z,R,ag local E=ag-aa(e,b)if k~=0 then E=E-aa(k,i)end local ce=e==h if p>0 then e=p*h end g[b]=0 g[i]=e E=E+aa(e,i)if z==1 then local ac=i+10*h E=E-aa(-h,ac)g[ac]=0 elseif z==2 then local aJ,bc=b+3,b+1 if i<b then aJ,bc=b-4,b-1 end local r=g[aJ]E=E-aa(r,aJ)+aa(r,bc)g[aJ]=0 g[bc]=r end if e==6*h then S[h]=i end if k~=0 or ce then R=0 else R=R+1 end Z=z==3 and(b+i)//2 or 0 Q=Q&(bg[b]or 15)&(bg[i]or 15)ag=E s=-h end local function aN(f,n)local h=-s s=h local b,i=f%128,(f//128)%128 local p,z=(f//16384)%8,f//131072 local e=g[i]if p>0 then e=h end g[b]=e g[i]=bR[n]if z==1 then g[i+10*h]=-h elseif z==2 then if i>b then g[b+3]=g[b+1];g[b+1]=0 else g[b-4]=g[b-1];g[b-1]=0 end end if e==6*h then S[h]=b end Q,Z,R,ag=bU[n],bS[n],bT[n],bV[n]end local function bA(a8)local j=av[39]local c=aW(j,false)local m=0 for a=1,c do local f=j[a]%F ao(f,40)if not I(S[-s],s)then m=m+1;a8[m]=f end aN(f,40)end for a=#a8,m+1,-1 do a8[a]=nil end return m end local aR,bj,as=1000000,100000,500000 local aF,bB,P,O,bX=0,0,false,0,520 local a_={}local function bJ()local a7,E=0,0 for a=1,64 do local e=g[L[a]]if e~=0 and e~=-1 and e~=1 then a7=a7+aT[e<0 and-e or e]end end aU[6]=a7<=2600 and 384 or 320 for a=1,64 do local l=L[a]if g[l]~=0 then E=E+aa(g[l],l)end end ag=E end local function bD(j,c,n)local m=0 for a=1,c do if j[a]>=F then m=m+1 j[a],j[m]=j[m],j[a]end end for a=2,m do local o,A=j[a],a-1 while A>0 and j[A]<o do j[A+1]=j[A];A=A-1 end j[A+1]=o end local bz=a_[n]if bz then for a=m+1,c do if j[a]==bz then j[a],j[m+1]=j[m+1],j[a];break end end end end local function bP(cj)aF=aF+1 if aF>bB then P=true end O=O+cj if O>=bX then O=0;coroutine.yield()end end local function bI(t,X,n)bP(2)if P then return 0 end if I(S[-s],s)then return as end local aq=ag*s if aq>=X or n>30 then return aq end local j=av[n]local c=aW(j,true)O=O+5+c if aq>t then t=aq end bD(j,c,0)for a=1,c do if aq+j[a]//F//10+200>t then local f=j[a]%F ao(f,n)local o=-bI(-X,-t,n+1)aN(f,n)if P then return 0 end if o~=-as then if o>=X then return o end if o>t then t=o end end end end return t end local function bL(az,t,X,n)if az<=0 then return bI(t,X,n)end bP(8)if P then return 0 end local j=av[n]local c=aW(j,false)if c<0 then return as end bD(j,c,n)local a2,x=0,-aR for a=1,c do local f=j[a]%F O=O+1 ao(f,n)local o=-bL(az-1,-X,-t,n+1)aN(f,n)if P then return 0 end if o~=-as then a2=a2+1 if o>x then x=o end if o>t then t=o if o>=X then if j[a]<F then a_[n]=f end return o end end end end if a2==0 then if I(S[s],-s)then return-bj+n end return 0 end return x end local b_={{1,200,90},{3,1500,20},{6,4000,0}}local aS={"Easy","Medium","Hard"}local function ch(a3)local aK=b_[a3]local N={}local c=bA(N)if c==0 then return nil end for a=c,2,-1 do local A=math.random(a)N[a],N[A]=N[A],N[a]end bJ()aF,bB,P,O=0,aK[2],false,0 a_={}local x=N[1]for az=1,aK[1]do local t,aB,bp=-aR,nil,1 for a=1,c do local f=N[a]ao(f,0)local o=-bL(az-1,-aR,-t,1)aN(f,0)if P then break end if aK[3]>0 then o=o+math.random(0,aK[3])end if o>t then t,aB,bp=o,f,a end end if aB then x=aB table.remove(N,bp)table.insert(N,1,aB)end if P or t>bj-100 then break end end return x end local G,K,V={},{},{}local ai,u,aZ,aC=1,nil,false,""local a0,a1=0,0 local w,C,H,ah=1,1,2,false local function bF()local aX=s*7+Q*131+Z for a=1,64 do aX=aX*31+g[L[a]]+7 end return aX end local function aI()local bo={4,2,3,5,6,3,2,4}for q=0,7 do g[21+q]=-bo[q+1]g[31+q]=-1 for r=2,5 do g[21+q+10*r]=0 end g[81+q]=1 g[91+q]=bo[q+1]end S[1],S[-1]=95,25 s,Q,Z,R=1,15,0,0 G,K={},{}a0,a1,aC=0,0,""bJ()K[1]=bF()end local function bM(l)local q,r=(l-21)%10,(l-21)//10 return string.char(97+q)..(8-r)end local function b5(f)local b,i=f%128,(f//128)%128 local p,z=(f//16384)%8,f//131072 local ar=s==1 and"White"or"Black"if w==1 then ar=s==C and"You"or"Computer"end if z==2 then return ar..": castles "..(i>b and"kingside"or"queenside")end local e=g[b]local d=ar..": "..bl[e<0 and-e or e].." "..bM(b)..((g[i]~=0 or z==1)and"x"or"-")..bM(i)if p>0 then d=d.." = "..bl[p]end return d end local function aH(f,b9)aC=b5(f)a0,a1=f%128,(f//128)%128 ao(f,40)G[#G+1]=f if R==0 then K={}end if b9 then K[#K+1]=bF()end end local function b6()local a4=0 for a=1,64 do local e=g[L[a]]if e<0 then e=-e end if e==1 or e==4 or e==5 then return false end if e==2 or e==3 then a4=a4+1 end end return a4<=1 end local function ap()local c=bA(V)ai=s aZ=I(S[s],-s)u=nil if c==0 then u=aZ and("Checkmate - "..(s==1 and"Black"or"White").." wins")or"Stalemate - draw"elseif R>=100 then u="Draw - fifty moves"elseif b6()then u="Draw - too few pieces"else local ba,b4=0,K[#K]for a=1,#K do if K[a]==b4 then ba=ba+1 end end if ba>=3 then u="Draw - repetition"end end for a=1,64 do bh[a]=g[L[a]]end end local function cb(b,i,p)local e=g[b]local z=0 if(e==1 or e==-1)then if i==Z and(i-b)%10~=0 then z=1 elseif i-b==20 or b-i==20 then z=3 end elseif(e==6 or e==-6)and(i-b==2 or b-i==2)then z=2 end return b+i*128+p*16384+z*131072 end local function cf()local d={"C1",w,C==1 and"w"or"b",H,":"}for a=1,#G do local f=G[a]d[#d+1]=string.char(47+ak[f%128],47+ak[(f//128)%128])local p=(f//16384)%8 if p>0 then d[#d+1]=string.char(110+p)end end return table.concat(d)end local function bK()local h=cf()if#h<=1020 then snail.save(h)end end local function bN(k)return L[k-47]end local function a9(M,c)aI()for a=1,c do aH(M[a],a>c-110)end ap()end local function ca()local h=snail.load()if type(h)~="string"then return false end local aD,aA,a3,aE=h:match("^C1(%d)([wb])(%d):(.*)$")if not aD then return false end w,C,H=tonumber(aD),aA=="w"and 1 or-1,tonumber(a3)if H<1 or H>3 then H=2 end aI()local a,c=1,0 local M={}while a<#aE do local b,i=bN(aE:byte(a)),bN(aE:byte(a+1))a=a+2 local p=0 local k=aE:byte(a)if k and k>111 then p=k-110;a=a+1 end if not b or not i or g[b]==0 then break end local f=cb(b,i,p)c=c+1 M[c]=f aH(f,false)end a9(M,c)ah=true return true end local af,aM=nil,false local B,D,v,y="menu",1,nil,85 local U={}local _,bH=1,nil local function bd()af,aM=nil,false snail.tick(0)end local aL,am local function bO()aM=true af=coroutine.create(function()return ch(H)end)snail.after(aL)snail.tick(450)end local function be()local d,ab={},{}for a=1,#V do local f=V[a]local b,i=f%128,(f//128)%128 local l if v then if b==v then l=i end else l=b end if l and not ab[l]then ab[l]=true;d[#d+1]=l end end if v then d[#d+1]=v end return d,ab end local an=false local function aO(l)local q,r=(l-21)%10,(l-21)//10 if an then return 7-q,7-r end return q,r end local function bv()local d,ab=be()if ab[y]or y==v or#d==0 then return end local ax,ay=aO(y)local x,W for a=1,#d do local ac,aP=aO(d[a])local k=(ac-ax)^2+(aP-ay)^2 if not W or k<W then x,W=d[a],k end end y=x end local function aY()return not u and not aM and(w==2 or ai==C)end am=function()v=nil an=w==1 and C==-1 bK()if not u and w==1 and ai~=C then bO()else bv()end end aL=function()if not af then return end local cd,f=coroutine.resume(af)if coroutine.status(af)~="dead"then return end bd()if not cd or not f then local M=G a9(M,#M)f=V[math.random(#V)]end if not f then return end aH(f,true)ap()am()end local function a6(aD,aA)bd()w,C,ah=aD,aA,true aI()ap()y=aA==1 and 85 or 35 B="play"am()end local function ci()bd()local c=#G-1 if w==1 then while c>0 and((c%2==0)and 1 or-1)~=C do c=c-1 end end if c<0 then c=0 end local M=G a9(M,c)B="play"am()end local function bC()local d={}if ah then d[#d+1]={"Back to the game","resume"}end d[#d+1]={"New game as White","w"}d[#d+1]={"New game as Black","b"}d[#d+1]={"New game, two players","2"}d[#d+1]={"Computer level: "..aS[H],"lvl"}if ah and#G>0 and not(w==1 and#G==1 and C==-1)then d[#d+1]={"Take back a move","undo"}end return d end local function b8(bt,bu)local d=be()local ax,ay=aO(y)local x,W for bE=1,2 do for a=1,#d do local ac,aP=aO(d[a])local e=(ac-ax)*bt+(aP-ay)*bu local h=math.abs((ac-ax)*bu)+math.abs((aP-ay)*bt)if e>0 and(bE==2 or h<=e)then local k=bE==1 and e*2+h or e+h*3 if not W or k<W then x,W=d[a],k end end end if x then break end end if x then y=x end end local function b3(l)v=l U={}for a=1,#V do local f=V[a]if f%128==l then local i=(f//128)%128 U[i]=U[i]or{}table.insert(U[i],f)end end end local function bx(f)v=nil B="play"aH(f,true)ap()y=(f//128)%128 am()end local aQ={up={0,-1},down={0,1},left={-1,0},right={1,0}}function key(m)if B=="menu"then local J=bC()if D>#J then D=#J end if m=="up"then D=(D-2)%#J+1 elseif m=="down"then D=D%#J+1 elseif m=="ok"or((m=="left"or m=="right")and J[D][2]=="lvl")then local ad=J[D][2]if ad=="resume"then B="play"elseif ad=="w"then a6(1,1)elseif ad=="b"then a6(1,-1)elseif ad=="2"then a6(2,1)elseif ad=="lvl"then H=(H+(m=="left"and 1 or 0))%3+1 if ah then bK()end elseif ad=="undo"then ci()end end return end if B=="promo"then if m=="top"then B="play"elseif m=="up"then _=(_-2)%4+1 elseif m=="down"then _=_%4+1 elseif m=="ok"then for b0,f in ipairs(bH)do if(f//16384)%8==6-_ then bx(f)end end end return end if m=="top"then if v then v=nil else B="menu";D=1 end return end if u then if m=="ok"then B="menu";D=1 end return end if not aY()then return end if m=="ok"then if v then if y==v then v=nil elseif U[y]then local a5=U[y]if#a5>1 then B,_,bH="promo",1,a5 else bx(a5[1])end end else local b0,ab=be()if ab[y]then b3(y)end end elseif aQ[m]then b8(aQ[m][1],aQ[m][2])end end function tick()aL()if af then snail.after(aL)end end local function b2(l,q,r,aG)local e=bh[ak[l]]local T=e>0 and e or(e<0 and 6-e or 0)if l==y and aY()and B=="play"then return 26+T end if l==v then return 38+T end if not aG then if v then if U[l]then return T==0 and 51 or 51+T end elseif l==a1 and T>0 then return 51+T elseif l==a0 and T==0 then return 64 end end return((r+q)%2==1 and 13 or 0)+T end local function bq(aG)local d,bW,aV={},{},0 for bb=0,7 do local r=an and 7-bb or bb for bs=0,7 do local q=an and 7-bs or bs local a=b2(21+q+10*r,q,r,aG)if not bW[a]then bW[a]=true;aV=aV+1 end d[#d+1]=bn[a]end if bb<7 then d[#d+1]="/"end end if aV>32 and not aG then return bq(true)end return table.concat(d)end local function cg()if u then if w==1 and u:find("Checkmate")then return ai==C and"Checkmate - you lost"or"Checkmate - you won!"end return u end local d if aM then d="Computer is thinking..."elseif w==1 then d="Your move"else d=(ai==1 and"White"or"Black").." to move"end if aZ then d=d.." - check!"end return d end function draw()if B=="menu"then snail.title("Chess")local J=bC()if D>#J then D=#J end for a,b7 in ipairs(J)do snail.row(b7[1],a==D)end snail.gap()if ah then local ar=w==2 and"Two players"or("You play "..(C==1 and"White"or"Black")..", "..aS[H])snail.small(ar..(u and(". "..u..".")or(", move "..(#G//2+1)..".")))else snail.small("Pick a side to start. The computer level can be changed at any time.")end snail.hint("BACK leave OK choose UP/DN move")return end if B=="promo"then snail.title("Promote pawn")for a,cc in ipairs({"Queen","Rook","Bishop","Knight"})do snail.row(cc,a==_)end snail.hint("BACK cancel OK choose UP/DN move")return end snail.title(w==1 and("Chess - "..aS[H])or"Chess - two players")snail.status(cg())snail.text(aC~=""and aC or"Move with the arrows, OK picks a piece.")snail.board(bq(false),nil,u)if u then snail.hint("BACK options OK new game")elseif not aY()then snail.hint("BACK options")elseif v then snail.hint("BACK cancel OK move 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())aI()ap()if ca()then an=w==1 and C==-1 if not u then B="play"if w==1 and ai~=C then bO()else bv()end end end end
