--!name Score Keeper
--!icon notes
--!category Games
local T,ao,aI,H=8,10,8,8 local d={"Player 1","Player 2"}local n={{}}local J={}local u=0 local M,v,s,D=true,0,false,false local w={}local m,g,ac,p="setup",1,nil,nil local A=1 local _,ab local function L(Z,aV,aP)return math.max(aV,math.min(aP,Z))end local function aA(j)return(j>=0 and"+"or"")..j end local function V()return n[#n]end local function K()return#n+u end local function S(a)local b=J[a]or 0 for U,l in ipairs(n)do b=b+(l[a]or 0)end return b end local function aj()local x={table.concat({"S1",M and 1 or 0,v,s and 1 or 0,D and 1 or 0,u},"|")}for U,j in ipairs(d)do x[#x+1]="p"..j end local function az(e,l)local b={}for a=1,#d do b[a]=l[a]or""end x[#x+1]=e..table.concat(b,",")end az("b",J)for U,l in ipairs(n)do az("r",l)end return table.concat(x,"\n")end local function y()local h=aj()while#h>1023 and#n>1 do local l=table.remove(n,1)for a=1,#d do J[a]=(J[a]or 0)+(l[a]or 0)end u=u+1 h=aj()end snail.save(h)end local function at(h)if type(h)~="string"or h:sub(1,3)~="S1|"then return end local j,l,C,i={},{},{},{}for W in h:gmatch("[^\n]+")do local e,Z=W:sub(1,1),W:sub(2)if e=="S"then for q in(Z.."|"):gmatch("([^|]*)|")do i[#i+1]=q end elseif e=="p"and#j<T then j[#j+1]=Z:sub(1,ao)elseif e=="b"or e=="r"then local b,a={},0 for q in(Z..","):gmatch("([^,]*),")do a=a+1 if a<=T then b[a]=tonumber(q)and L(math.floor(tonumber(q)),-9999999,9999999)end end if e=="b"then C=b else l[#l+1]=b end end end d,J,n=j,C,#l>0 and l or{{}}M,v,s=i[2]~="0",L(math.floor(tonumber(i[3])or 0),0,99999),i[4]=="1"D,u=i[5]=="1"and#j>0,math.max(0,math.floor(tonumber(i[6])or 0))return true end local function R(a9,Y)w[#w+1]={s=aj(),what=a9,tag=Y,d=0}if#w>aI then table.remove(w,1)end end local function aN()local B=table.remove(w)if not B then p="Nothing to undo";return end at(B.s)y()p="Undone: "..B.what end local function ar(a,j,Y)local B=w[#w]if not(Y and B and B.tag==Y)then R("",Y)B=w[#w]end B.d=B.d+j B.what=d[a].." "..aA(B.d)V()[a]=L((V()[a]or 0)+j,-99999,99999)y()end local function av(i)i(J)for U,l in ipairs(n)do i(l)end end local function a6(I,C)d[I],d[C]=d[C],d[I]av(function(b)b[I],b[C]=b[C],b[I]end)end local function a_(a)for E=a,T do d[E]=d[E+1]end av(function(b)for E=a,T do b[E]=b[E+1]end end)end local function aw()local O,c=nil,{}for a=1,#d do local b=S(a)if not O or(M and b>O)or(not M and b<O)then O,c=b,{d[a]}elseif b==O then c[#c+1]=d[a]end end return c,O end local function aC(c)return#c==1 and c[1]or#c==2 and c[1].." and "..c[2]or#c.." players"end local function a5()local c,C=aw()if#c==0 then return""end if#c>1 and#c==#d then return"All tied on "..C end return aC(c)..(#c>1 and" tie on "or s and" wins with "or" leads with ")..C end local function ax()n,J,u,s={{}},{},0,false end local function aW()local ah=false for a=1,#d do ah=ah or V()[a]~=nil end if not ah then p="No points in round "..K().." yet";return end R("round "..K())if v>0 then for a=1,#d do s=s or S(a)>=v end end n[#n+1]={}g=1 p=not s and"Round "..K()or nil y()end local aF="!$%&()+,-0123456789:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnpqrstuvwxyz|~"local function a8(a)return aF:sub(a+1,a+1)end local aH={"1234567890<","QWERTYUIOP'","ASDFGHJKL-+","^ZXCVBNM,./","()[_____]?>"}local aJ={"1234567890<","-+        >"}local aK="'-+,./()?"local k local o,t,G=1,2,1 local function al()return k.num and aJ or aH end local function F(q,N)return al()[N]:sub(q,q)end local function ae(i,a)_=a local h=i=="n"and d[a]or""k={f=i,s=h,at=#h,num=i~="n",max=i=="n"and ao or 6}G=(h==""and i=="n")and 1 or 0 if k.num then o,t=1,1 elseif t==1 or t>5 then o,t=1,2 end m="kb"end local function as()local i,a=k.f,_ local h=(k.s:gsub("^%s+",""):gsub("%s+$",""))m,k=i=="p"and"table"or"setup",nil if i=="n"then if h~=""then d[a]=h else if not ab then if D then R("remove "..d[a])end p=d[a].." is out"end a_(a)g=L(g,1,#d+1)end ab=nil elseif h~=""then local a3,j=h:match("^([+-]?)(%d+)$")if not j then p="That isn't a number: "..h elseif i=="t"then v=L(tonumber(j),0,99999)else ar(a,a3=="-"and-tonumber(j)or tonumber(j))g,p=a+1,d[a].." now has "..S(a)end end y()end local function aZ()local e=F(o,t)local h,r=k.s,k.at if e=="<"then if r>0 then k.s,k.at=h:sub(1,r-1)..h:sub(r+1),r-1 end elseif e=="^"then G=(G+1)%3 elseif e=="["then k.at=math.max(0,r-1)elseif e=="]"then k.at=math.min(#h,r+1)elseif e==">"then as()elseif#h>=k.max then p="That's as long as it can be"else if e=="_"then e=" "elseif G==0 then e=e:lower()end k.s,k.at=h:sub(1,r)..e..h:sub(r+1),r+1 if G==1 and e:match("%a")then G=0 end end end local function aS(f)if f=="top"then as()elseif f=="ok"then aZ()elseif f=="left"or f=="right"then local ai,e=f=="left"and-1 or 1,F(o,t)local j repeat o=(o-1+ai)%11+1 j=F(o,t)until j~=" "and(e~="_"or j~="_")elseif f=="up"or f=="down"then t=(t-1+(f=="up"and-1 or 1))%#al()+1 if F(o,t)==" "then for Q=1,10 do if o-Q>=1 and F(o-Q,t)~=" "then o=o-Q;break end if o+Q<=11 and F(o+Q,t)~=" "then o=o+Q;break end end end end end local function aT()local an=al()local X,aX={},F(o,t)=="_"for N=1,#an do local b={}for q=1,11 do local e=an[N]:sub(q,q)local af=N==t and(q==o or(aX and e=="_"))local P,ay,a=af and 1 or 0,aK:find(e,1,true)if e==" "then a=nil;e="."elseif e:match("%u")then a=af and e:byte()-65 elseif e:match("%d")then a=af and 26+e:byte()-48 elseif ay then a=(af and 35+ay)or(e=="/"and 45)elseif e=="<"then a=46+P elseif e=="^"then a=48+G*2+P elseif e=="["then a=54+P elseif e=="]"then a=56+P elseif e=="_"then a=58+P*3+(F(q-1,N)~="_"and 0 or F(q+1,N)~="_"and 2 or 1)else a=64+P end b[q]=a and a8(a)or e=="."and"."or("{"..e.."}")end X[N]=table.concat(b)end return table.concat(X,"/")end local function aa()local b={s and"playon"or"next"}if not s then b[2]="end"end for U,c in ipairs({"undo","hist","again","setup","fresh"})do b[#b+1]=c end return b end local function ap(c)for E,q in ipairs(aa())do if q==c then return#d+E end end end local function ag()local b={}for a=1,#d do b[a]=a end if#d<T then b[#b+1]="add"end b[#b+1]="win"b[#b+1]="target"b[#b+1]="start"return b end local function a0()if m=="table"then return#d+#aa()end if m=="setup"then return#ag()end return 1 end local function a7(f,I)local aq=aa()local j=#d+#aq local z=(f=="left"and-1)or(f=="right"and 1)if f=="up"then g=(g-2)%j+1 elseif f=="down"then g=g%j+1 elseif d[g]then if f=="ok"then ae("p",g)elseif z then ar(g,z,g.."."..K())end elseif f=="ok"then local c=aq[g-#d]if c=="next"then aW()elseif c=="end"then R("end the game");s=true;y()elseif c=="playon"then R("play on");s=false;p="Playing on";y()elseif c=="undo"then aN()elseif c=="hist"then m,A="hist",math.max(1,#n-H+1)elseif c=="setup"then m,g="setup",1 elseif I~=c then ac=c elseif c=="again"then R("new game")ax()g,p=1,"New game: round 1"y()else ax()D,w,m,g=false,{},"setup",1 y()end end end local function a1(f)local b=ag()local c=b[g]local z=(f=="left"and-1)or(f=="right"and 1)if f=="top"then m,g="table",ap("setup")elseif f=="up"then g=(g-2)%#b+1 elseif f=="down"then g=g%#b+1 elseif type(c)=="number"then if f=="ok"then ae("n",c)elseif z and d[c+z]then a6(c,c+z);g=c+z;y()end elseif c=="add"then if f=="ok"then d[#d+1]=""ab=true ae("n",#d)end elseif c=="win"then if f=="ok"or z then M=not M;y()end elseif c=="target"then if f=="ok"then ae("t")elseif z then local r=v+(z<0 and-1 or 0)local ai=r<50 and 5 or r<200 and 10 or 50 v=L(v+z*ai,0,99999)y()end elseif f=="ok"then if#d==0 then p="Add a player first"else D,m,g=true,"table",1 y()end end end local function aQ(f)local ak=math.max(1,#n-H+1)if f=="top"then m,g="table",ap("hist")elseif f=="up"or f=="left"then A=math.max(1,A-H)elseif f=="down"or f=="right"then A=math.min(ak,A+H)end end function key(f)if m=="kb"then p=nil;aS(f);return end if f=="top"and(m=="table"or(m=="setup"and not D))then return end p=nil local I=ac ac=nil if m=="table"then a7(f,I)elseif m=="setup"then a1(f)else aQ(f)end if m~="kb"then g=L(g,1,a0())end end local aE=("...................."):rep(3,"/")local function X(b,r,am,a4)local aB=math.max(1,math.min(r-am//2,#b-am+1))for a=aB,math.min(#b,aB+am-1)do snail.row(b[a]:sub(1,44),a==r)if a==a4 and a<#b then snail.rule()end end end local aD={next="Next round",["end"]="End the game now",playon="Play on",hist="Round history",again="New game, same players",setup="Players and rules",fresh="Set up a new game",}local function aL(c)if ac==c then return c=="again"and"Press OK again: scores back to 0"or"Press OK again: start over"end if c=="undo"then return#w>0 and"Undo: "..w[#w].what or"Undo (nothing yet)"end return aD[c]end local function aY(a)local au=V()[a]return d[a].."  "..S(a)..(au and"  ("..aA(au)..")"or"")end local function a2(c)if type(c)=="number"then return c..". "..(d[c]~=""and d[c]or"(new)")end if c=="add"then return"+ Add a player"end if c=="win"then return"Winner: "..(M and"highest"or"lowest").." score"end if c=="target"then return"Play to: "..(v>0 and v or"no target")end return D and"Back to the scores"or"Start the game"end local function ad(h)return"{"..tostring(h):gsub("[{}/]",""):sub(1,5).."}"end local function aR()local x,b={},{"{#}"}for a=1,#d do b[#b+1]=ad(d[a]:sub(1,4))end x[1]=table.concat(b)local function W(aU,l)b={ad(aU)}for a=1,#d do b[#b+1]=l[a]and ad(l[a])or"."end x[#x+1]=table.concat(b)end if u>0 and A==1 then W(u>1 and"1-"..u or 1,J)end for l=A,math.min(#n,A+H-1)do W(l+u,n[l])end b={"{=}"}for a=1,#d do b[#b+1]=ad(S(a))end x[#x+1]=table.concat(b)return table.concat(x,"/")end local aG={n="Up to 10 letters. Clear the name to take the player out.",t="The game ends after the round where someone reaches it. 0: no target.",}local function aO()local i=k.f snail.title(i=="p"and d[_].." - round "..K()or i=="t"and"Play to"or ab and"New player"or"Name")snail.status(p or(k.num and""or G==2 and"CAPS"or G==1 and"Shift"or""))snail.text(k.s:sub(1,k.at).."|"..k.s:sub(k.at+1))snail.small(i=="p"and d[_].." has "..S(_)..". Type 12 to add, -5 to take away."or aG[i])snail.board(aT())snail.hint("arrows move  BACK done  OK press")end function draw()if m=="kb"then return aO()end if m=="setup"then snail.title(D and"Players and rules"or"New game")snail.status(p or(#d..(#d==1 and" player"or" players")..(v>0 and", to "..v or"")))local b={}for E,c in ipairs(ag())do b[E]=a2(c)end X(b,g,14)local c=ag()[g]if type(c)=="number"then snail.small("OK renames (clear the name to take them out). LEFT/RIGHT changes the seating order.")elseif c=="target"then snail.small("The game ends after the round where someone reaches this score.")elseif c=="win"then snail.small("Lowest wins suits golf and hearts-style games.")end local aM=D and"BACK scores"or"BACK leave"snail.hint(aM..(type(c)=="number"and"  OK rename  L/R seat"or c=="target"and"  OK type  L/R change"or c=="win"and"  OK change"or"  OK choose").."  UP/DN move")return end if m=="hist"then local ak=math.min(#n,A+H-1)snail.title("Round history")snail.status("Rounds "..(A+u).."-"..(ak+u).." of "..K()..(u>0 and A==1 and", 1-"..u.." merged"or""))snail.board(aR())if#n>H then snail.small("UP/DOWN: earlier and later rounds.")end snail.hint("BACK scores"..(#n>H and"  UP/DN rounds"or""))return end snail.title(s and"Game over"or"Round "..K())snail.status((p or a5()..(v>0 and not s and", to "..v or"")):sub(1,47))if s then local c=aw()snail.board(aE,nil,(#c==1 and c[1].." wins!"or aC(c).." tie"):upper():sub(1,32))end local b={}for a=1,#d do b[a]=aY(a)end for U,c in ipairs(aa())do b[#b+1]=aL(c)end X(b,g,14,#d)if K()==1 and not next(V())then snail.small("OK types a player's points, LEFT/RIGHT adds or takes 1. Then Next round.")end snail.hint(d[g]and"BACK leave  OK points  L-1  R+1  UP/DN move"or"BACK leave  OK choose  UP/DN move")end function start()if at(snail.load())then m=D and"table"or"setup"end g=1 end
