--!name Sea Battle
--!icon game
--!category Games
local p={5,4,3,3,2}local W={"Carrier","Battleship","Cruiser","Submarine","Destroyer"}local ah={"Easy","Normal","Hard"}local aq=1100 local aL,aK,bc="w","x","h"local as,ap,bb="LRTBHV","acdefg","mnpqsu"local aI,a8,a7,be,bd="W","X","K","M","N"local ba,bf="12134","456"local q={{},{}}local A={{},{}}local w={{},{}}local m,h=2,"none"local E,F,z,O,V=4,4,0,0,0 local o=nil local y,B,G="menu",1,1 local t,S,M,N,aC=1,1,0,0,1 local i={p={0,0,0},w={0,0,0},best={0,0,0},shots=0,hits=0}local function r(e,f)return f*10+e+1 end local aJ={{1,0},{0,1},{-1,0},{0,-1}}local function Q(d)local n=A[d]for a=1,100 do n[a]=0 end for b,g in ipairs(q[d])do for u=0,g.n-1 do n[r(g.x+u*(1-g.d),g.y+u*g.d)]=b end end end local function aT(d,e,f,j,k)local an,ao=e+(k-1)*(1-j),f+(k-1)*j if e<0 or f<0 or an>9 or ao>9 then return false end local n=A[d]for aH=math.max(0,f-1),math.min(9,ao+1)do for aG=math.max(0,e-1),math.min(9,an+1)do if n[r(aG,aH)]~=0 then return false end end end return true end local function aM(d,e,f,j,k)local c=q[d]c[#c+1]={x=e,y=f,d=j,n=k,h=0}Q(d)end local function ae(d)repeat q[d]={}Q(d)local ac=true for b=1,5 do local k,a0=p[b],false for at=1,200 do local j=math.random(0,1)local e,f=math.random(0,9-(k-1)*(1-j)),math.random(0,9-(k-1)*j)if aT(d,e,f,j,k)then aM(d,e,f,j,k);a0=true;break end end if not a0 then ac=false;break end end until ac end local function P(d,b)local g=q[d][b];return g.h>=g.n end local function X(d)local k=0 for b=1,5 do if not P(d,b)then k=k+1 end end return k end local function aS(d,a)local b=A[d][a]if b==0 then w[d][a]=1;return"miss"end w[d][a]=2 local g=q[d][b]g.h=g.h+1 return g.h>=g.n and"sunk"or"hit",b end local U,ar,T={},{},{}local function bo()local n,g=A[1],w[1]for a=1,100 do U[a]=g[a]~=0;ar[a]=false end for b=1,5 do if P(1,b)then local C=q[1][b]local an,ao=C.x+(C.n-1)*(1-C.d),C.y+(C.n-1)*C.d for aH=math.max(0,C.y-1),math.min(9,ao+1)do for aG=math.max(0,C.x-1),math.min(9,an+1)do U[r(aG,aH)]=true end end end end local s={}for a=1,100 do if g[a]==2 and not P(1,n[a])then s[#s+1]=a;ar[a]=true end end return s end local function ad(ay)if#ay==0 then return nil end return ay[math.random(#ay)]end local function aF(aP)local c={}for a=1,100 do if w[1][a]==0 and(aP==nil or aP(a))then c[#c+1]=a end end return c end local function ab(e,f)return e>=0 and e<=9 and f>=0 and f<=9 and not U[r(e,f)]end local function aN(s)local c={}if#s>=2 then local x,D=s[1]-1,s[#s]-1 local Y,Z,_,aa=x%10,x//10,D%10,D//10 if Z==aa then if ab(Y-1,Z)then c[#c+1]=r(Y-1,Z)end if ab(_+1,aa)then c[#c+1]=r(_+1,aa)end elseif Y==_ then if ab(Y,Z-1)then c[#c+1]=r(Y,Z-1)end if ab(_,aa+1)then c[#c+1]=r(_,aa+1)end end end if#c==0 then for at,a in ipairs(s)do local e,f=(a-1)%10,(a-1)//10 for at,l in ipairs(aJ)do if ab(e+l[1],f+l[2])then c[#c+1]=r(e+l[1],f+l[2])end end end end if#c>0 then return ad(c)end local az=9 for b=1,5 do if not P(1,b)and p[b]<az then az=p[b]end end return ad(aF(function(a)local e,f=(a-1)%10,(a-1)//10 return not U[a]and(e+f)%az==0 end))or ad(aF(function(a)return not U[a]end))end local function bh(s)local g=w[1]for a=1,100 do T[a]=0 end local bu=#s>0 local aQ={}for b=1,5 do local k=p[b]if not P(1,b)and not aQ[k]then local aA=0 for aW=b,5 do if p[aW]==k and not P(1,aW)then aA=aA+1 end end aQ[k]=true for j=0,1 do local am=j==1 and 10 or 1 for f=0,9-(k-1)*j do for e=0,9-(k-1)*(1-j)do local al=f*10+e+1 local ac,aj=true,0 for a=al,al+(k-1)*am,am do if ar[a]then aj=aj+1 elseif U[a]then ac=false;break end end if ac and(aj>0 or not bu)then local ag=(1+aj*12)*aA for a=al,al+(k-1)*am,am do if g[a]==0 then T[a]=T[a]+ag end end end end end end end end local ai,c=0,{}for a=1,100 do if T[a]>ai then ai,c=T[a],{a}elseif T[a]==ai and ai>0 then c[#c+1]=a end end return ad(c)or aN(s)end local function bg()local s=bo()if m==1 then return ad(aF())end if m==2 then return aN(s)end return bh(s)end local function a5(d)local c={}for b,g in ipairs(q[d])do c[b]=g.x..g.y..g.d end return table.concat(c)end local function a6(d)return table.concat(w[d])end local function R()local aU=""if h~="none"and h~="place"then aU=table.concat({m,h,E,F,z,O,V,a5(1),a5(2),a6(1),a6(2)},";")end snail.save(table.concat({"SB1",table.concat(i.p,","),table.concat(i.w,","),table.concat(i.best,","),i.shots,i.hits,m,aU},"|"))end local function a1(d,g)q[d]={}for b=1,5 do local e,f,j=tonumber(g:sub(3*b-2,3*b-2)),tonumber(g:sub(3*b-1,3*b-1)),tonumber(g:sub(3*b,3*b))if not(e and f and j)or j>1 or e+(p[b]-1)*(1-j)>9 or f+(p[b]-1)*j>9 then return false end q[d][b]={x=e,y=f,d=j,n=p[b],h=0}end Q(d)local k=0 for a=1,100 do if A[d][a]>0 then k=k+1 end end return k==17 end local function a2(d,g)for a=1,100 do local l=tonumber(g:sub(a,a))or 0 w[d][a]=l if l==2 and A[d][a]>0 then local C=q[d][A[d][a]]C.h=C.h+1 end end end local function aB(g)local c={}for l in(g or""):gmatch("%d+")do c[#c+1]=tonumber(l)end return c end local function bp()local g=snail.load()if type(g)~="string"or g:sub(1,4)~="SB1|"then return end local I={}for l in(g.."|"):gmatch("([^|]*)|")do I[#I+1]=l end local K,ag,D=aB(I[2]),aB(I[3]),aB(I[4])for a=1,3 do i.p[a],i.w[a],i.best[a]=K[a]or 0,ag[a]or 0,D[a]or 0 end i.shots,i.hits=tonumber(I[5])or 0,tonumber(I[6])or 0 m=math.max(1,math.min(3,tonumber(I[7])or 2))local n={}for l in((I[8]or"")..";"):gmatch("([^;]*);")do n[#n+1]=l end if#n<11 then return end if not(a1(1,n[8])and a1(2,n[9]))then q[1],q[2]={},{}Q(1)Q(2)return end m,h=tonumber(n[1])or 2,n[2]E,F,z,O,V=tonumber(n[3])or 0,tonumber(n[4])or 0,tonumber(n[5])or 0,tonumber(n[6])or 0,tonumber(n[7])or 0 a2(1,n[10])a2(2,n[11])end local function aO()for d=1,2 do for a=1,100 do w[d][a]=0 end end end local function aY()snail.tick(0)ae(1)ae(2)aO()h,y,G="deploy","game",1 z,O,V,o=0,0,0,nil E,F=4,4 R()end local function bk()ae(2)aO()z,O,V=0,0,0 h,o="you","Fire at the enemy waters (top grid)"R()end local function aR(bv)h="over"snail.tick(0)i.p[m]=i.p[m]+1 if bv then i.w[m]=i.w[m]+1 if i.best[m]==0 or z<i.best[m]then i.best[m]=z end end end local function bs()local a=r(E,F)if w[2][a]~=0 then o="Already fired there - pick another square";return end local L,b=aS(2,a)z,i.shots=z+1,i.shots+1 if L~="miss"then O,i.hits=O+1,i.hits+1 end if L=="miss"then o="You missed."elseif L=="hit"then o="You hit a ship!"else o="You sank the "..W[b].."!"end if X(2)==0 then aR(true)else h="enemy"snail.tick(aq)end R()end local function bm()local a=bg()if not a then return end local L,b=aS(1,a)V=a if L=="miss"then o="Enemy missed. Your shot."elseif L=="hit"then o="Enemy hit your "..W[b].."! Your shot."else o="Enemy sank your "..W[b].."!"end if X(1)==0 then aR(false)else h="you"snail.tick(0)end R()end function tick()if h=="enemy"and y=="game"then bm()else snail.tick(0)end end local function bt()q[1]={}Q(1)h,t,S,M,N,o="place",1,1,0,0,nil end local function ak()local k=p[t]local l=aJ[aC]local e,f=M,N if l[1]<0 then e=M-k+1 end if l[2]<0 then f=N-k+1 end local j=l[2]~=0 and 1 or 0 if aT(1,e,f,j,k)then return e,f,j end end local function br(b)if S==1 then if b=="top"then if t>1 then t=t-1 table.remove(q[1])Q(1)else ae(1)h="deploy"end elseif b=="ok"then for j=1,4 do aC=j if ak()then S=2;return end end o="No room for the "..W[t].." there"elseif b=="left"then M=(M+9)%10 elseif b=="right"then M=(M+1)%10 elseif b=="up"then N=(N+9)%10 elseif b=="down"then N=(N+1)%10 end return end if b=="top"then S=1 elseif b=="ok"then local e,f,j=ak()if not e then o="It doesn't fit that way";return end aM(1,e,f,j,p[t])t,S=t+1,1 if t>5 then h,G,o="deploy",1,"Fleet placed. Ready?";R()end else aC=({right=1,down=2,left=3,up=4})[b]if not ak()then o="It doesn't fit that way"end end end local function aV()return h~="none"and h~="over"end local function aX()local c={}if aV()then c[#c+1]={h=="deploy"and"Back to your fleet"or"Back to the battle","resume"}end c[#c+1]={"New battle","new"}c[#c+1]={"Level: "..ah[m],"level"}c[#c+1]={"Statistics","stats"}c[#c+1]={"How to play","help"}return c end local a9={"Start the battle","Shuffle the fleet","Place ships by hand"}function key(b)if y=="menu"then local J=aX()if B>#J then B=#J end local x=J[B][2]if b=="up"then B=(B-2)%#J+1 elseif b=="down"then B=B%#J+1 elseif x=="level"and(b=="ok"or b=="left"or b=="right")then m=(m-1+(b=="left"and-1 or 1))%3+1 R()elseif b=="ok"then if x=="resume"then y="game"if h=="enemy"then snail.tick(aq)end elseif x=="new"then aY()else y=x end end return end if y~="game"then if b=="top"or b=="ok"then y="menu"end return end if h=="place"then o=nil;br(b);return end if b=="top"then y,B="menu",1 snail.tick(0)return end if h=="deploy"then o=nil if b=="up"then G=(G+1)%3+1 elseif b=="down"then G=G%3+1 elseif b=="ok"then if G==1 then bk()elseif G==2 then ae(1);R()else bt()end end elseif h=="over"then if b=="ok"then aY()end elseif h=="you"then o=nil if b=="left"then E=(E+9)%10 elseif b=="right"then E=(E+1)%10 elseif b=="up"then F=(F+9)%10 elseif b=="down"then F=(F+1)%10 elseif b=="ok"then bs()end end end local function aD(af,g,u)local K if g.d==0 then K=u==0 and 1 or(u==g.n-1 and 2 or 5)else K=u==0 and 3 or(u==g.n-1 and 4 or 6)end return af:sub(K,K)end local function aE(d,a,af)local b=A[d][a]local g=q[d][b]local e,f=(a-1)%10,(a-1)//10 return aD(af,g,(e-g.x)+(f-g.y))end local function bl(a,av,a3)local l=w[2][a]local b=A[2][a]if l==0 then if av then return aI end if a3 and b>0 then return aE(2,a,as)end return aL end if l==1 then return av and a8 or aK end if av then return a7 end if P(2,b)or a3 then return aE(2,a,ap)end return bc end local function aZ(a,H)local b=A[1][a]local l=w[1][a]if a==V and h~="deploy"then return l==2 and bd or be end if H and H[a]then return H[a]end if b>0 then return aE(1,a,l==2 and ap or as)end return l==1 and aK or aL end local function a4(d,L)if L==1 then return d==2 and ba or bf end local b=L-2 if b<1 or b>5 then return""end local aw={d=0,n=p[b]}local af=P(d,b)and ap or as local c={}for u=0,p[b]-1 do c[#c+1]=aD(af,aw,u)end return table.concat(c)end local function bj()local v={}local bq=h=="over"local bi=h=="you"for f=0,9 do local c={}for e=0,9 do c[#c+1]=bl(r(e,f),bi and e==E and f==F,bq)end v[#v+1]=table.concat(c).."."..a4(2,f+1)end v[#v+1]="."for f=0,9 do local c={}for e=0,9 do c[#c+1]=aZ(r(e,f))end v[#v+1]=table.concat(c).."."..a4(1,f+1)end return table.concat(v,"/")end local function ax()local H=nil if h=="place"then H={}if S==2 then local e,f,j=ak()if e then local aw={d=j,n=p[t]}for u=0,p[t]-1 do H[r(e+u*(1-j),f+u*j)]=aD(bb,aw,u)end end end local x=r(M,N)if not H[x]then H[x]=aI end end local v={}for f=0,9 do local c={}for e=0,9 do c[#c+1]=aZ(r(e,f),H)end v[#v+1]=table.concat(c)end for at=1,7 do v[#v+1]="."end return table.concat(v,"/")end local function a_(x,D)return D>0 and(x*100//D)or 0 end function draw()if y=="menu"then snail.title("Sea Battle")local J=aX()if B>#J then B=#J end for a,bn in ipairs(J)do snail.row(bn[1],a==B)end snail.gap()local K,ag=i.p[m],i.w[m]if K>0 then snail.small(ah[m]..": won "..ag.." of "..K..(i.best[m]>0 and(", best "..i.best[m].." shots.")or"."))else snail.small("Five ships each. Find theirs before they find yours.")end snail.hint("BACK leave OK choose UP/DN move")return end if y=="stats"then snail.title("Statistics")for a=1,3 do local D=i.best[a]>0 and(", best win "..i.best[a].." shots")or""snail.text(ah[a]..": won "..i.w[a].." of "..i.p[a]..D)end snail.gap()snail.small("Shots fired "..i.shots..", hits "..i.hits.." ("..a_(i.hits,i.shots).."% accuracy).")snail.hint("BACK back OK back")return end if y=="help"then snail.title("How to play")snail.text("Each side hides five ships: lengths 5, 4, 3, 3 and 2. Ships never touch.")snail.text("Top grid: enemy waters. Move the sights with the arrows and fire with OK.")snail.text("A ring is a miss, a black square a hit. Sunk ships show their shape.")snail.text("Bottom grid: your fleet. The framed square is the enemy's last shot.")snail.text("The columns at the right show the ships still afloat on each side.")snail.hint("BACK back OK back")return end if h=="deploy"or h=="place"then snail.title("Sea Battle - your fleet")if h=="deploy"then snail.status(o or"Ready to sail? Ships may not touch.")for a,c in ipairs(a9)do snail.row(c,a==G)end snail.board(ax())snail.hint("BACK options OK choose UP/DN move")else snail.status(o or("Place the "..W[t].." ("..p[t].." long)"))if S==1 then snail.text("Arrows move, OK picks the bow square.")snail.text("Then an arrow points the ship that way.")snail.text(t>1 and"BACK takes back the last ship."or"BACK goes back to the random fleet.")snail.board(ax())snail.hint("BACK undo OK pick L/R move UP/DN move")else snail.text("Point the ship with the arrows.")snail.text("OK drops it there.")snail.text("BACK picks another bow square.")snail.board(ax())snail.hint("BACK cancel OK place L/R turn UP/DN turn")end end return end snail.title("Sea Battle - "..ah[m].." - "..z..(z==1 and" shot"or" shots"))local au if h=="over"then if X(2)==0 then snail.status("Victory! "..O.." hits, "..a_(O,z).."% accuracy")au="Victory in "..z.." shots!"else snail.status("Defeat. "..X(2).." enemy ships were still afloat.")au="Your fleet is sunk"end else snail.status(o or(h=="you"and"Your shot"or"Enemy is aiming..."))end snail.board(bj(),nil,au)if h=="over"then snail.hint((X(2)==0 and"Well fought, admiral!"or"Better luck next time.").." BACK options OK new battle")elseif h=="you"then snail.hint("BACK options OK fire L/R move UP/DN move")else snail.hint("Enemy is aiming... BACK options")end end function start()math.randomseed(os.time())for d=1,2 do for a=1,100 do w[d][a],A[d][a]=0,0 end end bp()if aV()then y="game"if h=="enemy"then snail.tick(aq)end end end
