--!name Last Card
--!icon cards
--!category Games
local a8="!$%&()+,-0123456789:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnpqrstuvwx"local function u(f)return a8:sub(f+1,f+1)end local bb,aB,bc,aO,be,bd,aP,a6,a7,a5,a9,a4,aN,bf=15,20,22,24,26,30,34,36,40,55,69,72,76,78 local ae={"Sun","Moon","Star","Leaf"}local C={[0]="You","Fox","Owl","Cub"}local am={[10]="Skip",[11]="Reverse",[12]="Draw Two",[13]="Wild",[14]="Wild Draw Four"}local bh={2400,1400,700}local bg={"relaxed","normal","fast"}local an={0,200,500}local P=8 local function V(b)return b<52 and b//13+1 or 0 end local function E(b)return b<52 and b%13 or b-39 end local function ah(b)local d=E(b)if d>=13 then return am[d]end return ae[V(b)].." "..(am[d]or d)end local function aq(b)local d=E(b)return d<10 and d or(d<13 and 20 or 50)end local j={opp=2,target=3,rule=1,speed=2}local i={rp=0,rw=0,mp=0,mw=0,best=0}local s,Y=3,3 local h={[0]={},{},{},{}}local z,y={},{}local D,R,p,aa=1,1,0,0 local G={[0]=0,0,0,0}local n="none"local w local L,aw=0,0 local F={}local H,J,l,U="menu",1,1,0 local A,_=nil,1 local ak local function a2(e)for f=#e,2,-1 do local aj=math.random(f)e[f],e[aj]=e[aj],e[f]end end local function t(g)F[#F+1]=g if#F>4 then table.remove(F,1)end end local function bu()local g=""for f=#F,1,-1 do local e=g==""and F[f]or(F[f].." "..g)if#e>92 then break end g=e end return g end local function Z(a,m)return(a+R*m)%s end local function as()return y[#y]end local function al(a,d)return C[a].." "..d..(a==0 and""or(d=="miss"and"es"or"s"))end local function aU(c)return c==1 and"a card"or(c.." cards")end local function bq(a,g)for M,q in ipairs(h[a])do if V(q)==g then return true end end return false end local function Q(b,a)local d=E(b)if d==13 then return true end if d==14 then return not bq(a,D)end local a3=E(as())return V(b)==D or(d==a3 and a3<13)end local function a_(b)if w then return b==w end return Q(b,0)end local function aW(e,b)for f=1,#e do if e[f]==b then return f end end end local function av(a)if#z==0 then if#y<=1 then return nil end local e=table.remove(y)z,y=y,{e}a2(z)end local b=table.remove(z)local k=h[a]k[#k+1]=b return b end local function aV(a,m)local c=0 for M=1,m do if av(a)then c=c+1 end end if a==0 then table.sort(h[0])end return c end local function at(a,ar)local N,aR,aS={0,0,0,0},nil,0 for f,q in ipairs(h[a])do local g=V(q)if f~=ar and g>0 then N[g]=N[g]+1 if N[g]>aS then aR,aS=g,N[g]end end end return aR,N end local function aF()local c=#h[0]+1 if l>c then l=c end if l<1 then l=1 end if l<=U then U=l-1 end if l>U+P then U=l-P end if U>c-P then U=c-P end if U<0 then U=0 end end local function aA()local k=h[0]if not(k[l]and Q(k[l],0))then l=#k+1 for f=1,#k do if Q(k[f],0)then l=f;break end end end aF()end local function bn(X)local ad=0 for a=0,s-1 do for M,b in ipairs(h[a])do ad=ad+aq(b)end end L,aw=X,ad i.rp=i.rp+1 if X==0 then i.rw=i.rw+1 if ad>i.best then i.best=ad end end n="over"local W=an[Y]if W>0 then G[X]=G[X]+ad if G[X]>=W then n="match"i.mp=i.mp+1 if X==0 then i.mw=i.mw+1 end end else G[X]=G[X]+1 end t(al(X,"win").." the round"..(W>0 and(": "..ad.." points for the cards left.")or"!"))end local function ax(a,f,bk)local k=h[a]local b=table.remove(k,f)y[#y+1]=b w=nil local d=E(b)if d>=13 then D=bk or math.random(4)t(C[a].." played "..am[d]..": "..ae[D]..".")else D=V(b)t(C[a].." played "..ah(b)..".")end if#k==1 then if a>0 and math.random(5)==1 then aV(a,2)t(C[a].." forgot to call Last card: +2.")else t(al(a,"call").." Last card!")end end local ar=false if d==10 or(d==11 and s==2)then ar=true t(al(Z(a,1),"miss").." a turn.")elseif d==11 then R=-R t("Play turns around.")elseif d==12 or d==14 then local e=Z(a,1)local c=aV(e,d==12 and 2 or 4)ar=true t(al(e,"draw").." "..c.." and "..(e==0 and"miss"or"misses").." a turn.")end if#k==0 then bn(a)return end p=Z(a,ar and 2 or 1)if p==0 then aA()end end local function bo()local r={}for g=0,3 do for d=0,12 do r[#r+1]=g*13+d if d>0 then r[#r+1]=g*13+d end end end for M=1,4 do r[#r+1]=52 r[#r+1]=53 end a2(r)return r end local function aZ()z,y=bo(),{}for a=0,3 do h[a]={}end for M=1,7 do for a=0,s-1 do av(a)end end table.sort(h[0])local f=#z while E(z[f])>9 do f=f-1 end local b=table.remove(z,f)y[1]=b D,R,w,A=V(b),1,nil,nil aa=aa+1 p=(aa-1)%s n="play"F={}t("Round "..aa.." starts on "..ah(b)..".")if p==0 then l=1 aA()else t(C[p].." goes first.")end end local function ai(e)local g={}for f=1,#e do g[f]=string.char(65+e[f])end return table.concat(g)end local function aE(g)local e={}for f=1,#g do local b=g:byte(f)-65 if b<0 or b>53 then return nil end e[f]=b end return e end local function aK()local x={"L1",j.opp,j.target,j.rule,j.speed,i.rp,i.rw,i.mp,i.mw,i.best}if n~="none"then local ac={s,p,R,D,aa,Y,n,G[0],G[1],G[2],G[3],w or-1,L,aw,ai(z),ai(y),ai(h[0]),ai(h[1]),ai(h[2]),ai(h[3]),table.concat(F,"|")}for M,d in ipairs(ac)do x[#x+1]=d end end snail.save(table.concat(x,";"))end local function bt()local g=snail.load()if type(g)~="string"or g:sub(1,3)~="L1;"then return end local x={}for d in(g..";"):gmatch("([^;]*);")do x[#x+1]=d end local function c(f,bs,bp,bl)local q=math.tointeger(tonumber(x[f]))if not q or q<bs or q>bp then return bl end return q end j.opp,j.target,j.rule,j.speed=c(2,1,3,2),c(3,1,3,3),c(4,1,2,1),c(5,1,3,2)i.rp,i.rw,i.mp,i.mw,i.best=c(6,0,1e9,0),c(7,0,1e9,0),c(8,0,1e9,0),c(9,0,1e9,0),c(10,0,1e9,0)if#x<31 or not(x[17]=="play"or x[17]=="over"or x[17]=="match")then return end s,p,R,D,aa,Y=c(11,2,4,3),c(12,0,3,0),c(13,-1,1,1),c(14,1,4,1),c(15,1,1e6,1),c(16,1,3,3)if R==0 then R=1 end for a=0,3 do G[a]=c(18+a,0,1e6,0)end local r=c(22,-1,53,-1)w=r>=0 and r or nil L,aw=c(23,0,3,0),c(24,0,1e6,0)z,y=aE(x[25]),aE(x[26])local aM=(z and#z or-999)+(y and#y or-999)for a=0,3 do h[a]=aE(x[27+a])aM=aM+(h[a]and#h[a]or-999)end if aM~=108 or#y<1 or p>=s then z,y,n={},{},"none"for a=0,3 do h[a]={}end return end F={}for e in(x[31]or""):gmatch("[^|]+")do F[#F+1]=e end n=x[17]if w and not aW(h[0],w)then w=nil end if n=="play"and p==0 then aA()end end local function ay()if n=="play"and p~=0 then snail.tick(bh[j.speed])else snail.tick(0)end end local function aY()s,Y,aa=j.opp+1,j.target,0 for a=0,3 do G[a]=0 end aZ()H="game"aK()ay()end local function bi(a,b)local d,g=E(b),V(b)local bw=Z(a,1)local aI=#h[bw]local aH=99 for aJ=0,s-1 do if aJ~=a and#h[aJ]<aH then aH=#h[aJ]end end local aG=aI<=aH or aI<=3 local K if d<10 then K=20+d elseif d==11 and s>2 then local by=#h[Z(a,-1)]K=(aG and by>aI)and 45 or 16 elseif d<13 then K=(aG and 48 or 18)+(d==12 and 4 or 0)elseif d==13 then K=#h[a]<=2 and 60 or 4 else K=(aG or#h[a]<=2)and 56 or 2 end if g>0 then for M,q in ipairs(h[a])do if V(q)==g then K=K+3 end end end return K+math.random(0,4)end local function af()aF()aK()ay()end local function aQ()local a=p local k=h[a]local au,aT=nil,-1 for f=1,#k do if Q(k[f],a)then local K=bi(a,k[f])if K>aT then au,aT=f,K end end end if au then ax(a,au,at(a,au))else local c,b=0,nil repeat b=av(a)if b then c=c+1 end until not b or j.rule==1 or Q(b,a)if b and Q(b,a)then t(al(a,"draw").." "..aU(c)..".")ax(a,#k,at(a,#k))else t(c>0 and(al(a,"draw").." "..aU(c).." and passes.")or(C[a].." can't draw and passes."))p=Z(a,1)if p==0 then aA()end end end af()end local function bB()local c,b=0,nil repeat b=av(0)if b then c=c+1 end until not b or j.rule==1 or Q(b,0)table.sort(h[0])if b and Q(b,0)then w=b l=aW(h[0],b)t("You drew "..(c>1 and(c.." cards, the last ")or"")..ah(b)..". Play it or pass.")else if c==0 then t("Nothing left to draw. You pass.")elseif c==1 then t("You drew "..ah(b).." and pass.")else t("You drew "..c.." cards and pass.")end p=Z(0,1)end af()end local function bA()local d=E(as())if d>=13 then return am[d].." ("..ae[D]..")"end return ah(as())end local function aX()local e={}if n~="none"then e[#e+1]={"Continue the game","resume"}end e[#e+1]={"New game","new"}e[#e+1]={"Opponents: "..j.opp,"opp"}local W=an[j.target]e[#e+1]={"Play to: "..(W>0 and(W.." points")or"single rounds"),"target"}e[#e+1]={"Drawing: "..(j.rule==1 and"one card"or"until you can play"),"rule"}e[#e+1]={"Computer speed: "..bg[j.speed],"speed"}e[#e+1]={"Statistics","stats"}e[#e+1]={"How to play","help"}return e end local function bv(m)local S=aX()if J>#S then J=#S end if m=="up"then J=(J-2)%#S+1;return end if m=="down"then J=J%#S+1;return end local v=S[J][2]local r=m=="left"and-1 or((m=="right"or m=="ok")and 1 or 0)if r==0 then return end if v=="opp"then j.opp=(j.opp-1+r)%3+1 elseif v=="target"then j.target=(j.target-1+r)%3+1 elseif v=="rule"then j.rule=3-j.rule elseif v=="speed"then j.speed=(j.speed-1+r)%3+1 elseif m=="ok"then if v=="resume"then H="game";ay()elseif v=="new"then aY()else H=v end return else return end aK()end function key(m)ak=nil if H=="menu"then return bv(m)end if H~="game"then if m=="top"or m=="ok"then H="menu"end return end if A then if m=="top"then A=nil elseif m=="left"then _=(_-2)%4+1 elseif m=="right"then _=_%4+1 elseif m=="ok"then local f=A A=nil ax(0,f,_)af()end return end if m=="top"then H,J="menu",1 return end if n=="over"or n=="match"then if m=="ok"then if n=="match"then aY()else aZ();af()end end return end if p~=0 then if m=="ok"then aQ()end return end local k=h[0]local c=#k+1 if m=="left"then l=(l-2)%c+1 elseif m=="right"then l=l%c+1 elseif m=="ok"then if l>=c then if w then w=nil t("You keep the card.")p=Z(0,1)af()else bB()end return end local b=k[l]if not a_(b)then if w then ak="Play the card you drew, or pass"elseif E(b)==14 then ak="Wild Draw Four: you still hold a "..ae[D]else ak=ah(b).." can't go on "..bA()end return end if E(b)>=13 then A=l _=at(0,l)or D else ax(0,l)af()end return end aF()end function tick()if H=="game"and n=="play"and p~=0 then aQ()end end local function bj()local ac={}for O=1,10 do ac[O]={".",".",".",".",".",".",".",".",".","."}end local function o(O,b,g)ac[O+1][b+1]=g end local ao=n=="play"local a0=({{4},{2,6},{0,4,8}})[s-1]for a=1,s-1 do local ag=a0[a]o(0,ag,u((ao and a==p)and a4+a or a9+a))o(0,ag+1,"{"..#h[a].."}")if ao then o(1,ag,"{"..C[a].."}")end if a<s-1 then for b=ag+2,a0[a+1]-1 do o(0,b,u(R==1 and aN or aN+1))end end end local B=ao and 2 or 1 if#z>0 then o(B,2,u(aB))o(B+1,2,u(aB+1))else o(B,2,"o")o(B+1,2,"o")end o(B+2,2,"{"..#z.."}")local b,g=as(),D if A then b,g=h[0][A],_ end local d=E(b)o(B,4,u(be+g-1));o(B,5,u(a6+g-1));o(B,6,u(aO))o(B+1,4,u(aP));o(B+1,5,u(a7+d));o(B+1,6,u(aP+1))o(B+2,4,u(aO+1));o(B+2,5,u(a5+d));o(B+2,6,u(bd+g-1))local k=h[0]local ap=ao and p==0 and not A local I,aL={},{}if not ao and#k==0 then for a=1,s-1 do for M,q in ipairs(h[a])do I[#I+1]=q end end table.sort(I,function(v,ab)return aq(v)>aq(ab)or(aq(v)==aq(ab)and v<ab)end)else for f=1,#k do if f~=A then I[#I+1]=k[f]aL[#I]=f end end if ap then I[#I+1]=-1 aL[#I]=#k+1 end end local c=#I local T=ap and U or 0 if T>c-P then T=c-P end if T<0 then T=0 end local az=c<P and c or P local ag=c>P and 1 or(10-az)//2 for aj=1,az do local q=I[T+aj]local aD=ag+aj-1 local O=(ap and aL[T+aj]==l)and 6 or 7 local v,ab if q<0 then v=w and bc or aB ab=v+1 else v=E(q)ab=bb+(q>=52 and 4 or V(q)-1)if ap and a_(q)then o(O+2,aD,u(bf))end end o(O,aD,u(v))o(O+1,aD,u(ab))end if T>0 then o(7,0,"{<}")end if T+az<c then o(7,9,ap and"{>}"or("{+"..c-T-az.."}"))end for O=1,10 do ac[O]=table.concat(ac[O])end return table.concat(ac,"/")end local function bz()if ak then return ak end if n=="match"then return L==0 and"You win the game!"or(C[L].." wins the game")end if n=="over"then if L>0 then return C[L].." wins the round"end return"You win the round!"..(an[Y]>0 and(" +"..aw.." points")or"")end if A then return"Choose a suit: < "..ae[_].." >"end if p~=0 then return C[p].."'s turn..."end if w then return"Play the card you drew, or pass"end for M,q in ipairs(h[0])do if Q(q,0)then local d=E(as())return"Your turn: play on "..ae[D]..(d<13 and(" or "..(am[d]or d))or"")end end return"Your turn: no match, draw a card"end local function a1()local e={}for a=0,s-1 do e[#e+1]=C[a].." "..G[a]end return(an[Y]>0 and"Points: "or"Rounds won: ")..table.concat(e,", ")end local function bm()local W=an[Y]snail.title("Last Card - round "..aa..(W>0 and(", to "..W)or""))snail.status(bz())if A then local M,N=at(0,A)snail.text("You hold Sun "..N[1]..", Moon "..N[2]..", Star "..N[3]..", Leaf "..N[4]..".")else snail.text(bu())end local aC if n=="match"then aC=L==0 and"You win the game!"or(C[L].." wins the game")elseif n=="over"then aC=L==0 and"You win the round!"or(C[L].." wins the round")end snail.board(bj(),nil,aC)snail.small(a1())if n=="match"then snail.hint("BACK options OK new game")elseif n=="over"then snail.hint("BACK options OK next round")elseif A then snail.hint("BACK cancel OK choose L/R suit")elseif p~=0 then snail.hint("BACK options OK hurry")elseif l>#h[0]then snail.hint(w and"BACK options OK pass L/R card"or"BACK options OK draw L/R card")else snail.hint("BACK options OK play L/R card")end end local ba={"Match the top card by suit, number or symbol. Empty your hand first to win the round.","Suits: Sun, Moon, Star, Leaf. Skip, Reverse and Draw Two hit the next player.","Wild picks a new suit. Wild Draw Four too, but only if you hold no card of the suit.","Can't play? Pick the card back at the end of your hand to draw. A drawn card may be played.","The raised card is chosen. A small arrow marks the cards you can play.","Last card is called for you. The computer sometimes forgets and draws two.","The winner scores the cards left: numbers face value, actions 20, wilds 50.",}function draw()if H=="game"then return bm()end if H=="stats"then snail.title("Statistics")local bx=i.rp>0 and(i.rw*100//i.rp)or 0 snail.text("Rounds played: "..i.rp)snail.text("Rounds won: "..i.rw.." ("..bx.."%)")snail.text("Best round: "..i.best.." points")snail.gap()snail.text("Games to 200 or 500 played: "..i.mp)snail.text("Games won: "..i.mw)snail.hint("BACK back OK back")return end if H=="help"then snail.title("How to play")for M,g in ipairs(ba)do snail.text(g)end snail.hint("BACK back OK back")return end snail.title("Last Card")local S=aX()if J>#S then J=#S end for f,br in ipairs(S)do snail.row(br[1],f==J)end snail.gap()if n~="none"and(s~=j.opp+1 or Y~=j.target)then snail.small("Opponents and Play to take effect in a new game.")elseif n~="none"then snail.small(a1())else snail.small("Shed your cards before Fox, Owl and Cub do.")end snail.hint("BACK leave OK choose L/R change UP/DN move")end function start()math.randomseed(os.time())bt()if n~="none"then H="game"ay()end end
