--!name Four in a Row
--!icon game
--!category Games
local aQ="ABCDEFGHIJ"local aw={}for b=1,#aQ do aw[b-1]=aQ:sub(b,b)end local G,ar,k,L={},{},{},{}for b=0,50 do G[b]=9;ar[b]=0 end for a=0,6 do k[a]=0 L[a]=0 for n=0,5 do G[a*7+n+1]=0 end end local M,aa,ag,af,P={},{},{},{},{}do local aq={}for b=1,50 do aq[b]={}end local bn={{1,0},{0,1},{1,1},{1,-1}}for a=0,6 do for n=0,5 do for aS,B in ipairs(bn)do local be,aV=a+3*B[1],n+3*B[2]if be<=6 and aV>=0 and aV<=5 then local d=#M+1 M[d]=a*7+n+1 aa[d]=B[1]*7+B[2]for f=0,3 do local e=aq[M[d]+f*aa[d]]e[#e+1]=d end end end end end for b=1,50 do ag[b]=#P+1 af[b]=#aq[b]for aS,d in ipairs(aq[b])do P[#P+1]=d end end end local aL,aM,aN,aO={},{},{},{}local Y,D={[1]=aL,[-1]=aM},{[1]=aN,[-1]=aO}local ax={[0]=0,1,5,25,0}local y,O,J=1,0,0 local aR={}local function a6(c,m)local n=(m-1)%7 if(n+(c==1 and 0 or 1))%2==0 then return 14 end return 4 end local function al(a,u)local c=y local H=k[a]local g=a*7+H+1 G[g]=c k[a]=H+1 aR[u]=O local K,au,aP,_=Y[c],Y[-c],D[c],D[-c]local l,a8=0,false local I=ag[g]for f=I,I+af[g]-1 do local d=P[f]local i,R=K[d],au[d]if R==0 then l=l+ax[i+1]-ax[i]if i==2 then local m,B=M[d],aa[d]while G[m]~=0 do m=m+B end aP[m]=aP[m]+1 l=l+a6(c,m)elseif i==3 then a8=true end elseif i==0 then l=l+ax[R]if R==3 then _[g]=_[g]-1 l=l+a6(-c,g)end end K[d]=i+1 end O=O+c*l y=-c J=J+1 return a8 end local function aK(a,u)local c=-y y=c local H=k[a]-1 k[a]=H local g=a*7+H+1 local K,au=Y[c],Y[-c]local I=ag[g]for f=I,I+af[g]-1 do local d=P[f]local i=K[d]-1 K[d]=i local R=au[d]if R==0 then if i==2 then local m,B=M[d],aa[d]while G[m]~=0 do m=m+B end D[c][m]=D[c][m]-1 end elseif i==0 and R==3 then D[-c][g]=D[-c][g]+1 end end G[g]=0 O=aR[u]J=J-1 end local as,ab=1000000,100000 local av={3,2,4,1,5,0,6}local an,a_,Q,ae,a9=0,0,false,0,280 local aD={}local function bm()an=an+1 if an>a_ then Q=true end ae=ae+1 if ae>=a9 then ae=0;coroutine.yield()end end local function a4(U,E,aT,u)bm()if Q then return 0 end local c=y local bb,_=D[c],D[-c]local N,aW=0,0 for a=0,6 do local H=k[a]if H<6 then local g=a*7+H+1 if bb[g]>0 then return ab-u end if _[g]>0 then N=N+1;aW=a end end end if J>=42 then return 0 end if N>1 then return-ab+u+1 end if U<=0 and N==0 then return O*c end local bh=N==1 and U or U-1 local aZ=aD[u]or-1 local r=-as for b=0,7 do local a=-1 if N==1 then if b>0 then break end a=aW elseif b==0 then a=aZ elseif av[b]~=aZ then a=av[b]end if a>=0 and k[a]<6 then local h if N==0 and _[a*7+k[a]+2]>0 then h=-ab+u+2 else al(a,u)h=-a4(bh,-aT,-E,u+1)aK(a,u)if Q then return 0 end end if h>r then r=h if h>E then E=h if h>=aT then aD[u]=a break end end end end end return r end local ba={{1,100,60},{6,1800,8},{42,11000,0}}local at={"Easy","Medium","Hard"}local C={}local function bo(ak)local V=ba[ak]local v={}for b=1,7 do local a=av[b]if k[a]<6 then v[#v+1]=a end end local j=#v if j==0 then return nil end for b=1,j do if D[y][v[b]*7+k[v[b]]+1]>0 then return v[b]end end if j==1 then return v[1]end an,a_,Q,ae=0,V[2],false,0 aD={}if ak==1 and math.random(10)<=3 then local r,aA for b=1,j do local a=v[b]al(a,0)local h=-O*y+math.random(0,V[3])aK(a,0)if not aA or h>aA then r,aA=a,h end end return r end local r=v[1]for U=1,V[1]do local E,ai,aU=-as,nil,1 for b=1,j do local a=v[b]al(a,0)local h=-a4(U-1,-as,V[3]-E,1)aK(a,0)if Q then break end if V[3]>0 then h=h+math.random(0,V[3])end if h>E then E,ai,aU=h,a,b end end if ai then r=ai table.remove(v,aU)table.insert(v,1,ai)end if Q or E>ab//2 or E<-ab//2 or U>42-J then break end end return r end local w,ay={},{}local X,o,aj,aE=1,nil,"",0 local q,s,t,W,T=1,1,2,false,false local Z={0,0,0,0,0,0,0,0,0}local ac=false local function aI()for a=0,6 do k[a]=0 for n=0,5 do G[a*7+n+1]=0 end end for b=1,50 do aN[b]=0;aO[b]=0 end for d=1,#M do aL[d]=0;aM[d]=0 end y,O,J,ac=1,0,0,false w,ay={},{}aE,aj,o=0,"",nil end local function bq(c)if q==1 then return c==s and"You"or"Computer"end return c==1 and"Black"or"White"end local function aG(a)local c=y local g=a*7+k[a]+1 ac=al(a,0)aE=g w[#w+1]=a aj=bq(c)..": column "..(a+1)if ac then local K=Y[c]local I=ag[g]for f=I,I+af[g]-1 do local d=P[f]if K[d]==4 then for bg=0,3 do ay[M[d]+bg*aa[d]]=true end end end end end local function a2(f)if q~=1 or T then return end T=true local b=(t-1)*3+f Z[b]=Z[b]+1 end local function ad()X=y C={}for a=0,6 do L[a]=k[a]if k[a]<6 then C[#C+1]=a end for n=0,5 do ar[a*7+n+1]=G[a*7+n+1]end end o=nil if ac then local d=-y if q==1 then o=d==s and"You win!"or"Computer wins"a2(d==s and 1 or 2)else o=(d==1 and"Black"or"White").." wins"end elseif J>=42 then o="Draw - the board is full"a2(3)end end local function bk()local e={"F1",q,s==1 and"b"or"w",t,T and 1 or 0,table.concat(Z,","),":"}for b=1,#w do e[#e+1]=w[b]end return table.concat(e)end local function a3()snail.save(bk())end local function aH(A,j)aI()for b=1,j do local a=A[b]if ac or not a or a<0 or a>6 or k[a]>=6 then break end aG(a)end ad()end local function bj()local c=snail.load()if type(c)~="string"then return false end local am,aB,ak,bd,B,a1=c:match("^F1(%d)([bw])(%d)(%d)([%d,]+):(%d*)$")if not am then return false end local b=0 for h in B:gmatch("%d+")do b=b+1;Z[b]=tonumber(h)end q,s,t=tonumber(am),aB=="b"and 1 or-1,tonumber(ak)if t<1 or t>3 then t=2 end local A={}for f=1,#a1 do A[f]=a1:byte(f)-48 end T=true aH(A,#A)T=bd=="1"W=true return true end local S,ap=nil,false local F,p,x="menu",1,3 local function aJ()S,ap=nil,false snail.tick(0)end local ao local function a5()ap=true S=coroutine.create(function()return bo(t)end)snail.after(ao)snail.tick(450)end local function aC()return not o and not ap and(q==2 or X==s)end local function aX()if L[x]<6 or#C==0 then return end local r,az for b=1,#C do local l=math.abs(C[b]-x)if not az or l<az then r,az=C[b],l end end x=r end local function ah()a3()if not o and q==1 and X~=s then a5()else aX()end end ao=function()if not S then return end local bi,a=coroutine.resume(S)if coroutine.status(S)~="dead"then return end aJ()if not bi or not a or a<0 or a>6 or L[a]>=6 then local A=w aH(A,#A)a=C[math.random(#C)]end if not a then return end aG(a)ad()ah()end local function aF(am,aB)aJ()q,s,W,T=am,aB,true,false aI()ad()x=3 F="play"ah()end local function a7()local j=#w-1 if q==1 then while j>=0 and((j%2==0)and 1 or-1)~=s do j=j-1 end end if j<0 then return nil end return j end local function bp()local j=a7()if not j then return end aJ()local A=w aH(A,j)F="play"ah()end local function a0()local e={}if W then e[#e+1]={"Back to the game","resume"}end e[#e+1]={"New game, you go first","b"}e[#e+1]={"New game, computer first","w"}e[#e+1]={"New game, two players","2"}e[#e+1]={"Computer level: "..at[t],"lvl"}if W and a7()then e[#e+1]={"Take back a move","undo"}end return e end function key(f)if F=="menu"then local z=a0()if p>#z then p=#z end if f=="up"then p=(p-2)%#z+1 elseif f=="down"then p=p%#z+1 elseif f=="ok"or((f=="left"or f=="right")and z[p][2]=="lvl")then local i=z[p][2]if i=="resume"then F="play"elseif i=="b"then aF(1,1)elseif i=="w"then aF(1,-1)elseif i=="2"then aF(2,1)elseif i=="lvl"then t=(t+(f=="left"and 1 or 0))%3+1 if W then a3()end elseif i=="undo"then bp()end end return end if f=="top"then F="menu";p=1;return end if o then if f=="ok"then F="menu";p=2 end return end if not aC()then return end if f=="ok"then if L[x]<6 then aG(x)ad()ah()end elseif f=="left"or f=="right"then local l=f=="left"and-1 or 1 for aS=1,7 do x=(x+l)%7 if L[x]<6 then break end end end end function tick()ao()if S then snail.after(ao)end end local function bc()local aY=aC()and F=="play"local e={}for a=0,6 do e[#e+1]=(aY and a==x)and aw[X==1 and 8 or 9]or" "end for n=5,0,-1 do e[#e+1]="/"for a=0,6 do local g=a*7+n+1 local h=ar[g]local b=0 if h==0 then if aY and a==x and n==L[a]then b=7 end elseif ay[g]then b=h==1 and 5 or 6 elseif g==aE then b=h==1 and 3 or 4 else b=h==1 and 1 or 2 end e[#e+1]=aw[b]end end e[#e+1]="/1234567"return table.concat(e)end local function bl()if o then return"Game over after "..#w.." discs"end if ap then return"Computer is thinking..."end if q==1 then return"Your move - you have the "..(s==1 and"solid"or"ringed").." discs"end return(X==1 and"Black (solid)"or"White (ringed)").." to move"end function draw()if F=="menu"then snail.title("Four in a Row")local z=a0()if p>#z then p=#z end for b,bf in ipairs(z)do snail.row(bf[1],b==p)end snail.gap()if W then local d=q==2 and"Two players"or("You go "..(s==1 and"first"or"second")..", "..at[t])snail.small(d..(o and(". "..o..".")or(", "..#w..(#w==1 and" disc"or" discs").." down.")))else snail.small("Drop discs in turn. Four in a line - across, up or slanted - wins.")end local c=Z snail.small("Won-lost-drawn against the computer:")snail.small("Easy "..c[1].."-"..c[2].."-"..c[3].."   Medium "..c[4].."-"..c[5].."-"..c[6].."   Hard "..c[7].."-"..c[8].."-"..c[9])snail.hint("BACK leave OK choose UP/DN move")return end snail.title(q==1 and("Four in a Row - "..at[t])or"Four in a Row - two players")snail.status(bl())snail.text(aj~=""and aj or"Left and right pick a column, OK drops.")snail.board(bc(),nil,o)if o then snail.hint("BACK options OK new game")elseif not aC()then snail.hint("BACK options")else snail.hint("BACK options OK drop L/R column")end end function start()math.randomseed(os.time())aI()ad()if bj()then if not o then F="play"if q==1 and X~=s then a5()else aX()end end end end
