--!name Gomoku
--!icon game
--!category Games
local bd="000000000000000011313351335355701131335133535570113133513353557000000001131335022424461335355702242446133535570224244613353557000000013353557133535571335355713353557133535571335355713353557000011302241335022424461335355702242446133535570224244613353557000133524463557244635572446355724463557244635572446355724463557000133513351335133524461336355713352446133635571335244613363557000355735573557355735573557355735573557355735573557355735573557001021302241335022424461335355702242446133535570224244613353557013243524463557244635572446355724463557244635572446355724463557013243524352435243524462436355724352446243635572435244624363557035465746574657465746574657465746574657465746574657465746574657013131313241335132424461335355713242446133535571324244613353557035353535463657354636573546365735463657354636573546365735463657035353535353535353535463536365735353546353636573535354635363657057575757575757575757575757575757575757575757575757575757575757001021302241335022424461335355702242446133535570224244613353557123243524463557244635572446355724463557244635572446355724463557123243524352435243524462436355724352446243635572435244624363557345465746574657465746574657465746574657465746574657465746574657123232323242335232424462335355723242446233535572324244623353557345454545464657454646574546465745464657454646574546465745464657345454545454545454545464546465745454546454646574545454645464657567676767676767676767676767676767676767676767676767676767676767111121312241335122424461335355712242446133535571224244613353557333343534463557344635573446355734463557344635573446355734463557333343534353435343534463436355734353446343635573435344634363557555566756675667566756675667566756675667566756675667566756675667333333333343335333434463335355733343446333535573334344633353557555555555565667555656675556566755565667555656675556566755565667555555555555555555555565556566755555556555656675555555655565667777777777777777777777777777777777777777777777777777777777777777001021302241335022424461335355702242446133535570224244613353557123243524463557244635572446355724463557244635572446355724463557123243524352435243524462436355724352446243635572435244624363557345465746574657465746574657465746574657465746574657465746574657123232323242335232424462335355723242446233535572324244623353557345454545464657454646574546465745464657454646574546465745464657345454545454545454545464546465745454546454646574545454645464657567676767676767676767676767676767676767676767676767676767676767111121312241335122424461335355712242446133535571224244613353557333343534463557344635573446355734463557344635573446355734463557333343534353435343534463436355734353446343635573435344634363557555566756675667566756675667566756675667566756675667566756675667333333333343335333434463335355733343446333535573334344633353557555555555565667555656675556566755565667555656675556566755565667555555555555555555555565556566755555556555656675555555655565667777777777777777777777777777777777777777777777777777777777777777001021302241335022424461335355702242446133535570224244613353557123243524463557244635572446355724463557244635572446355724463557123243524352435243524462436355724352446243635572435244624363557345465746574657465746574657465746574657465746574657465746574657123232323242335232424462335355723242446233535572324244623353557345454545464657454646574546465745464657454646574546465745464657345454545454545454545464546465745454546454646574545454645464657567676767676767676767676767676767676767676767676767676767676767111121312241335122424461335355712242446133535571224244613353557333343534463557344635573446355734463557344635573446355734463557333343534353435343534463436355734353446343635573435344634363557555566756675667566756675667566756675667566756675667566756675667333333333343335333434463335355733343446333535573334344633353557555555555565667555656675556566755565667555656675556566755565667555555555555555555555565556566755555556555656675555555655565667777777777777777777777777777777777777777777777777777777777777777"local bh=string.byte local A,U=18,1<<24 local i=table.pack(("\9"):rep(343):byte(1,-1))local l=table.pack(("\0"):rep(343):byte(1,-1))local m={}i[0],l[0]=9,0 for n=0,14 do for c=0,14 do local a=(n+2)*A+c+2 i[a]=0 m[#m+1]=a end end local ac={1,A,A+1,A-1}local ae={}for aV=-2,2 do for aU=-2,2 do if aV~=0 or aU~=0 then ae[#ae+1]=aV*A+aU end end end local aw,u={},0 local function ah(a,g,c)local j,k,f=0,1,a-g for S=1,5 do local h=i[f]if h==c then j=j+k elseif h~=0 then break end k=k+k f=f-g end local K,D=0,1 f=a+g for S=1,5 do local h=i[f]if h==c then K=K+D elseif h~=0 then break end D=D+D f=f+g end return bh(bd,(k-1+j)*63+D+K)-48 end local function ak(a)local w=0 for b=1,4 do local g=ac[b]w=w+(ah(a,g,1)<<(b*3-3))+(ah(a,g,2)<<(b*3+9))end l[a]=(l[a]&~(U-1))|w end local function aL(a,br)for b=1,4 do local g=ac[b]local H=b*3-3 local R=H+12 local bo=~((7<<H)|(7<<R))for aD=-g,g,g+g do local f=a for S=1,5 do f=f+aD local h=i[f]if h==9 then break end if h==0 then local w=l[f]local a_=(w&bo)|(ah(f,g,1)<<H)|(ah(f,g,2)<<R)if a_~=w then if br then u=u+1 aw[u]=f+(w<<9)end l[f]=a_ end end end end end end local function aJ(a,c)i[a]=c for b=1,24 do local f=a+ae[b]l[f]=l[f]+U end end local function aX(a)i[a]=0 for b=1,24 do local f=a+ae[b]l[f]=l[f]-U end end local function al(a,c)i[a]=c aL(a,true)aJ(a,c)end local function as(a,aa)aX(a)while u>aa do local x=aw[u]l[x&511]=x>>9 u=u-1 end end local function ar(y)local D=y&7 for S=1,3 do y=y>>3 if(y&7)>D then D=y&7 end end return D end local bf={[0]=0,4,20,30,200,260,5000,100000}local function aQ(y)if y==0 then return 0 end local s,X,am=0,0,0 for S=1,4 do local e=y&7 y=y>>3 if e==7 then return 100000 end if e==6 then X=X+2 elseif e==5 then X=X+1 elseif e==4 then am=am+1 end s=s+bf[e]end if X>=2 then return 10000 end if X==1 and am>=1 then return 9000 end if am>=2 then return 4000 end return s end local an,aY,V,ab,bc=0,0,false,0,110 local function Z(G)an=an+G if an>aY then V=true end ab=ab+G if ab>=bc then ab=0;coroutine.yield()end end local function aM(c,e,t,F)local H,R=c*12-12,12-(c*12-12)local T,Q,P,ao,k=-1,nil,-1,nil,0 for b=1,225 do local a=m[b]local w=l[a]if w>=U and i[a]==0 then local j,g=aQ((w>>H)&4095),aQ((w>>R)&4095)k=k+1 if j>T then T,Q=j,a end if g>P then P,ao=g,a end if e>0 then local h if F==1 then h=j*10+math.min(g,260)*5+math.random(0,1500)else h=j*10+g*9+(F==2 and math.random(0,60)or F==3 and math.random(0,3)or 0)end local x=(h<<9)|a local v=#t if v<e or x>t[v]then if v==e then v=v-1 end while v>0 and t[v]<x do t[v+1]=t[v];v=v-1 end t[v+1]=x end end end end Z(2+k//6)return T,Q,P,ao,k end local function aW(a,c)for b=1,4 do local g=ac[b]local bw=(c-1)*12+(b-1)*3 for aD=-g,g,g+g do local f=a for S=1,4 do f=f+aD local h=i[f]if h==9 or(h~=0 and h~=c)then break end if h==0 and((l[f]>>bw)&7)==7 then return f end end end end return nil end local function a9(c,aj)Z(2)if V then return nil end local H,R=c*12-12,12-(c*12-12)local ad,k,a2={},0,false for b=1,225 do local a=m[b]if i[a]==0 and l[a]>=U then local w=l[a]local y=(w>>H)&4095 if y~=0 then local D=ar(y)if D==7 then return a end if D>=5 then k=k+1 ad[k]=a end end if ar((w>>R)&4095)==7 then a2=true end end end if a2 or k==0 then return nil end for b=1,k do if aQ((l[ad[b]]>>H)&4095)>=10000 then return ad[b]end end if aj<=0 then return nil end for b=1,k do local a=ad[b]local aa=u al(a,c)Z(9)local n,bb=aW(a,c),nil if n then local ay=u al(n,3-c)Z(9)bb=a9(c,aj-1)as(n,ay)end as(a,aa)if V then return nil end if bb then return a end end return nil end local function aP(c,aj,bg)an,aY,V=0,bg,false local n=a9(c,aj)if V then n=nil end V=false return n end local au={"Easy","Medium","Hard"}local function bl(c,t,j,Q)local aI=3-c if j>=10000 then return Q end local G=aP(c,7,600)if G then return G end if j>=9000 then return Q end local aT,aA=t[1]&511,nil for e=1,#t do local a,ax=t[e]&511,t[e]>>9 local aa=u al(a,c)Z(9)local h=ax-10000000 if aW(a,c)then h=ax else local av={}local a0=aM(aI,3,av,0)if a0<4000 and(a0<260 or not aP(aI,5,100))then local at for v=1,#av do local n=av[v]&511 local ay=u al(n,aI)Z(9)local T,S,bp=aM(c,0)local x=T*2-bp if T>=9000 or(T>=260 and aP(c,5,60))then x=1000000 end as(n,ay)if not at or x<at then at=x end end h=ax+(at or 0)end end as(a,aa)if not aA or h>aA then aT,aA=a,h end end return aT end local function bz(F,c)if i[m[113]]==0 and l[m[113]]<U then local aS=false for b=1,225 do if i[m[b]]~=0 then aS=true;break end end if not aS then return m[113]end end ab,u,aw=0,0,{}local t={}local j,Q,g,ao,k=aM(c,F==3 and 10 or 1,t,F)if k==0 then for b=1,225 do if i[m[b]]==0 then return m[b]end end return nil end if j>=100000 and(F>1 or math.random(10)<=9)then return Q end if g>=100000 and(F>1 or math.random(4)<=3)then return ao end if F==3 then return bl(c,t,j,Q)end return t[1]&511 end local p,af={},{}local J,o,O,B=1,nil,"",0 local q,r,C,Y,W=1,1,2,false,false local _={0,0,0,0,0,0,0,0,0}local z=0 local function aB()for b=0,342 do l[b]=0 end for b=1,225 do i[m[b]]=0 end p,af={},{}J,B,O,o,u,z=1,0,"",nil,0,0 end local function ba(c)if q==1 then return c==r and"You"or"Computer"end return c==1 and"Black"or"White"end local function ai(c)return c==1 and"Black"or"White"end local function bk(a)local c=i[a]for b=1,4 do local g=ac[b]local j,K=a,a while i[j-g]==c do j=j-g end while i[K+g]==c do K=K+g end if(K-j)//g>=4 then for f=j,K,g do af[f]=true end return true end end return false end local be={[4]="an open three",[5]="a four",[6]="an open four",[7]="five in a row"}local function aK(a)local c=J local H,R=c*12-12,12-(c*12-12)local a3,a1=ar((l[a]>>H)&4095),ar((l[a]>>R)&4095)aJ(a,c)if z==0 then aL(a,false)end p[#p+1]=a B=a local d={}local bm=q==1 and(c==r and"the computer's"or"your")or(c==1 and"White's"or"Black's")if a1>=6 then d[1]="blocks "..bm..(a1==7 and" four"or" three")end if a3>=4 then d[#d+1]=(q==1 and c==r and"make "or"makes ")..be[a3]end if#d==0 then O=ba(c)..(q==1 and c==r and" played."or" played the marked stone.")else if d[1]:sub(1,1)=="b"and q==1 and c==r then d[1]="block"..d[1]:sub(7)end O=ba(c).." "..table.concat(d," and ").."."end J=3-c end local function a4(e)if q~=1 or W then return end W=true local b=(C-1)*3+e _[b]=_[b]+1 end local function aN()o=nil if B>0 and bk(B)then local G=i[B]if q==1 then o=G==r and"You win!"or"Computer wins"a4(G==r and 1 or 2)else o=ai(G).." wins"end elseif#p>=225 then o="Draw - the board is full"a4(3)end end local function bv()local d={"G1",q,r==1 and"b"or"w",C,W and 1 or 0,table.concat(_,","),":"}for b=1,#p do local a=p[b]d[#d+1]=string.char(95+a%A,95+a//A)end d[#d+1]="|"..O return table.concat(d)end local function a5()snail.save(bv())end local function bt()local s=snail.load()if type(s)~="string"then return false end local P,aE,F,bi,bx,aG,a7=s:match("^G1(%d)([bw])(%d)(%d)([%d,]+):([a-q]*)|?(.*)$")if not P then return false end local b=0 for h in bx:gmatch("%d+")do b=b+1;_[b]=tonumber(h)end q,r,C=tonumber(P),aE=="b"and 1 or 2,tonumber(F)if C<1 or C>3 then C=2 end aB()z=1 for e=1,#aG-1,2 do local a=(aG:byte(e+1)-95)*A+aG:byte(e)-95 if a>342 or i[a]~=0 or o then break end aK(a)W=true aN()end W=bi=="1"if a7~=""then O=a7:sub(1,95)end Y=true return true end local M,aq,aC=nil,false,nil local I,E,N="menu",1,m[113]local az local function bu()for b=0,342 do l[b]=0 end for b=1,225 do i[m[b]]=0 end for b=1,#p do aJ(p[b],2-b%2)end for b=1,225 do if i[m[b]]==0 then ak(m[b])end end u=0 end local function aO()if M and coroutine.status(M)~="dead"then bu()end M,aq,aC=nil,false,nil snail.tick(0)end local ap local function a6()aq=true aC=az()local c=J M=coroutine.create(function()return bz(C,c)end)snail.after(ap)snail.tick(450)end local function aF()return not o and not aq and z==0 and(q==2 or J==r)end local function ag()a5()if B>0 then N=B end if not o and q==1 and J~=r then a6()end end ap=function()if not M then return end local bq,a=coroutine.resume(M)if coroutine.status(M)~="dead"then return end aO()u=0 if not bq or not a or i[a]~=0 then for b=1,225 do if i[m[b]]==0 then ak(m[b])end end a=nil for b=1,225 do local f=m[b]if i[f]==0 and(not a or l[f]>l[a])then a=f end end end if not a then return end aK(a)aN()ag()end local function bs()local x=math.min(225,z+79)for b=z,x do local a=m[b]if i[a]==0 then ak(a)end end z=x+1 if z>225 then z=0 snail.tick(0)if I=="play"and not o and q==1 and J~=r then a6()end end end local function aH(P,aE)aO()q,r,Y,W=P,aE,true,false aB()N=m[113]I="play"ag()end local function a8()local k=#p if k==0 or z>0 then return 0 end if q==2 then return 1 end local e=1 if i[p[k]]~=r then e=2 end if e>k then return 0 end return e end local function bB()local e=a8()if e==0 then return end aO()for S=1,e do local a=table.remove(p)aX(a)ak(a)aL(a,false)end af,o={},nil J=#p%2==0 and 1 or 2 B=p[#p]or 0 O="Took back "..(e==1 and"a move."or"two moves.")I="play"ag()end local function aZ()local d={}if Y then d[#d+1]={"Back to the game","resume"}end d[#d+1]={"New game as Black (first)","b"}d[#d+1]={"New game as White","w"}d[#d+1]={"New game, two players","2"}d[#d+1]={"Computer level: "..au[C],"lvl"}if Y and a8()>0 then d[#d+1]={"Take back a move","undo"}end return d end local aR={up=-A,down=A,left=-1,right=1}function key(e)if I=="menu"then local L=aZ()if E>#L then E=#L end local j=L[E][2]if e=="up"then E=(E-2)%#L+1 elseif e=="down"then E=E%#L+1 elseif e=="ok"or((e=="left"or e=="right")and j=="lvl")then if j=="resume"then I="play"elseif j=="b"then aH(1,1)elseif j=="w"then aH(1,2)elseif j=="2"then aH(2,1)elseif j=="lvl"then C=(C+(e=="left"and 1 or 0))%3+1 if Y then a5()end elseif j=="undo"then bB()end end return end if e=="top"then I="menu";E=1;return end if o then if e=="ok"then I="menu";E=2 end return end if not aF()then return end if e=="ok"then if i[N]==0 then aK(N)aN()ag()end elseif aR[e]then local f=N+aR[e]if i[f]~=9 then N=f end end end function tick()if z>0 then bs();return end ap()if M then snail.after(ap)end end local function bj(a)local n,c=a//A-2,a%A-2 if(n==3 or n==7 or n==11)and(c==3 or c==7 or c==11)and(n==7)==(c==7)then return"J"end return string.char(65+(n==0 and 0 or(n==14 and 6 or 3))+(c==0 and 0 or(c==14 and 2 or 1)))end local function bA(a)local h=i[a]if a==N and I=="play"and aF()then if h==0 then return J==1 and"Q"or"R"end if a==B then return h==1 and"U"or"V"end return h==1 and"S"or"T"end if h==0 then return bj(a)end if af[a]then return h==1 and"O"or"P"end if a==B then return h==1 and"M"or"N"end return h==1 and"K"or"L"end az=function()local d={}for b=1,225 do d[#d+1]=bA(m[b])if b%15==0 and b<225 then d[#d+1]="/"end end return table.concat(d)end local function by()local d="Move "..(#p+(o and 0 or 1)).." - "if z>0 then return"Setting out the stones..."end if o then return d.."game over"end if aq then return d.."computer thinking..."end if q==1 then return d.."your turn ("..ai(r)..")"end return d..ai(J).." to play"end function draw()if I=="menu"then snail.title("Gomoku")local L=aZ()if E>#L then E=#L end for b,bn in ipairs(L)do snail.row(bn[1],b==E)end snail.gap()if Y then local G=q==2 and"Two players"or("You play "..ai(r)..", "..au[C])snail.small(G..(o and(". "..o..".")or(", "..#p..(#p==1 and" stone"or" stones").." down.")))else snail.small("Take turns placing stones. Five or more in a row - across, down or slanting - wins.")end local s=_ snail.small("Won-lost-drawn against the computer:")snail.small("Easy "..s[1].."-"..s[2].."-"..s[3].."   Medium "..s[4].."-"..s[5].."-"..s[6].."   Hard "..s[7].."-"..s[8].."-"..s[9])snail.hint("BACK leave OK choose UP/DN move")return end snail.title(q==1 and("Gomoku - "..au[C])or"Gomoku - two players")snail.status(by())snail.text(O~=""and O or"Move the cursor with the arrows, OK places a stone.")snail.board(aC or az(),nil,o)if o then snail.hint("BACK options OK new game")elseif not aF()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())aB()if bt()then if B>0 then N=B end if not o then I="play"end if z>0 then snail.tick(450)end end end
