#!/opt/bin/lua -- a game of very relaxed ridiculously cheating spider -- run in an UTF-8 capable terminal -- uses xterm wheel mouse support -- requires os command stty -- -- Copyright (c) 2005 Malete Partner, Berlin, partner@malete.org -- Available under "Lua 5.0 license", see http://www.lua.org/license.html#5 -- $Id: slnspider,v 1.1 2005/02/28 16:24:11 krip Exp $ local mod,floor,random,randomseed = math.mod,math.floor,math.random,math.randomseed function clearscr () io.write("\27[2J") end function moveto (c, r) io.write(string.format("\27[%d;%dH", r, c)) end function utf8 (i) if i<128 then return string.format("%c", i) end if i<2048 then return string.format("%c%c", 192+i/64, 128+mod(i,64)) end local j=floor(i/4096) i = i-j*4096 return string.format("%c%c%c", 224+j, 128+i/64, 128+mod(i,64)) end -- constants local names = {" A"," 2"," 3"," 4"," 5"," 6"," 7"," 8"," 9","10"," B"," D"," K"} -- black club, spade, heart and diamond local types = {utf8(9827),utf8(9824),utf8(9829),utf8(9830)} local colours = {"34","36","31","35"} local keys = {"y","x","c","v","b","n","m",",",".","-"} local vert,horz = utf8(9474), string.rep(utf8(9472),6) local top,bot = utf8(9581)..horz..utf8(9582), utf8(9584)..horz..utf8(9583) local head,body = vert.."\27[%s;%sm%s %s \27[0m"..vert, vert.." "..vert local background = os.getenv("BG") or "40" local invisible = "8" local pile,deck,stack,fini local mc, mr -- marked col, row local move -- history -- drawing -- function card (c, r, n, m) -- print card value n at logical col,row local t = 1+floor((n-1)/13) local red,clr = 2 p.n then -- clear old for i=2*p.n+7,2*on+6 do moveto(x, i) io.write(" ") end end end function redraw () clearscr() for c=1,10 do printpile(c) end end function unhide () if "8" == invisible then invisible = background else invisible = "8" end redraw() end function game () pile = {{},{},{},{},{},{},{},{},{},{}} move = {n=0, h=0} deck = {n=104} fini = {n=0} mc = nil for i=1,52 do deck[i]=i; deck[i+52]=i end local rand,j = random() local card = 52 -- deck[104] for i=1,10000 do -- shuffle j = random(103) -- in [1,103] card, deck[j] = deck[j], card end deck[104] = card -- deal 10*5 for c=1,10 do local i,p = 5*(c+9),pile[c] for r=1,5 do p[r]=deck[i+r] end p.n = 5 end -- deal additional 4 for c=0,3 do local p = pile[1+3*c] p[6]=deck[101+c] p.n = 6 end for c=1,10 do pile[c].vis = pile[c].n end stack=50 redraw() end -- history function pushmove (v) move.n=move.n+1; move.h=move.n move[move.n]=v; end function deal (redo) if 0 == stack then return end for c=1,10 do local p=pile[c] p.n = p.n+1 p[p.n] = deck[stack+1-c] printpile(c) end stack=stack-10 if not redo then pushmove() end end function undeal () stack=stack+10 for c=1,10 do local p=pile[c] deck[stack+1-c] = p[p.n] p.n = p.n-1 printpile(c, p.n+1) end end function mark (c, r, automove) local p=pile[c] if mc then local o,on = pile[mc] if mc == c then mr = mr+1 printpile(mc) if mr > o.n then mc=nil end return end -- move mc to c local card=o[mr] local n = 1+mod(card-1, 13) if 0==p.n or n == mod(p[p.n]-1, 13) then -- move pushmove({from=mc, to=c, n=o.n-mr+1, vis=o.vis}) for m=mr,o.n do p.n = p.n+1 p[p.n] = o[m] end on = o.n o.n = mr-1 if o.vis > o.n then o.vis = o.n end if o.vis == 0 then o.vis = 1 end end mr=nil printpile(mc, on) mc = nil return printpile(c) end if r < p.vis then r = p.vis end if r > p.n then r = p.n elseif r < p.n then for i=p.n-1,r,-1 do if p[i] ~= p[i+1]+1 or 0 == mod(p[i]-1, 13) then r=i+1 break end end end mc=c mr=r if not automove then return printpile(c) end -- find dest local need,dest = 1+mod(p[r]-1,13) for i=1,10 do local bot=pile[i][pile[i].n] if i~=c then if not bot then -- empty col if not dest then dest=i end elseif 13==need then -- king if p.n==r+12 then fini.n = fini.n+1 fini[fini.n] = p[p.n] p.n = p.n-13 mc = nil return printpile(c, p.n+13) end elseif bot==1+p[r] then -- best match dest=i break elseif need==mod(bot-1,13) and (not dest or 0==pile[dest].n) then dest=i end end end if dest then return mark(dest) end -- move it return mark(c,r+1,true) -- retry end -- mark function undo () if 0 == move.n then return end local mv = move[move.n] move.n = move.n-1 if not mv then return undeal() end -- unmove: local o,p = pile[mv.from],pile[mv.to] o.vis=mv.vis for i=p.n-mv.n+1,p.n do o.n = o.n+1 o[o.n] = p[i] end p.n=p.n-mv.n printpile(mv.from) printpile(mv.to, p.n+mv.n) mc = nil end function redo () mc = nil if move.h <= move.n then return deal() end move.n = move.n+1 local mv = move[move.n] if not mv then return deal(true) end -- remove: local o,p = pile[mv.from],pile[mv.to] for i=o.n-mv.n+1,o.n do p.n = p.n+1 p[p.n] = o[i] end o.n=o.n-mv.n if o.vis > o.n then o.vis = o.n end if o.vis == 0 then o.vis = 1 end printpile(mv.from, o.n+mv.n) printpile(mv.to) end local keyhandler do --- keyhandlers local keymap = { d = deal, r = redraw, u = undo, n = game, [" "] = unhide } -- mouse reporting ESC [ M local basic, esc, seq, mouse local b,m,x,y -- mouse function basic (k) if (not k) or "x" == k or "q" == k then return end if "\27" == k then return esc end if keymap[k] then keymap[k]() end return basic end function esc (k) -- io.write("ESC"..k) if "[" == k then return seq end return basic end function seq (k) -- io.write("SEQ"..k) if "M" == k then b,m,x,y = nil return mouse end return basic end function mouse (k) if not b then k=string.byte(k)-32 b=floor(mod(k+1,4)) -- 0:release, buttons 1..3 m=floor(k/4) -- modifiers 1=shift,2=meta,4=ctrl,16=wheel if 16 <= m then -- wheel b=b+3 -- "buttons" 4(up),5(down) -- XTerm does not send release m=m-16 end return mouse end if not x then x=string.byte(k)-32; return mouse end y=string.byte(k)-32 -- io.write("mouse "..b..","..m..":"..x..","..y.." ") local col=floor((x+7)/8) if 0 == b then if mc and mc ~= col then mark(col, 0) end -- drop elseif 1 == b then mark(col, floor(y/2)) elseif 2 == b then deal() elseif 3 == b then mark(col, floor(y/2), true) elseif 4 == b then undo() elseif 5 == b then redo() end return basic end keyhandler = basic end -- keyhandlers -- init os.execute("stty raw -echo") io.write("\27[?1000h") -- mouse tracking randomseed(os.time()) game() -- main loop while true do moveto(1,1) io.write(stack/10," ",move.n) moveto(73,1) io.write(fini.n) io.flush() local k = io.read(1) keyhandler = keyhandler(k) if not keyhandler then break end end -- fini -- reset clearscr() io.write("\27[?1000l") -- mouse tracking off io.flush() os.execute("stty sane")