Initial code
This commit is contained in:
commit
cf1aa522aa
206
board.lua
Normal file
206
board.lua
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
board=klass()
|
||||||
|
|
||||||
|
function board:init()
|
||||||
|
self.cursor=cursor:new(self)
|
||||||
|
self.slots={}
|
||||||
|
self.wells={}
|
||||||
|
|
||||||
|
-- board slots
|
||||||
|
-- 1,11: normal
|
||||||
|
for i=1,11 do
|
||||||
|
add(self.slots,slot:new())
|
||||||
|
end
|
||||||
|
-- 12: special
|
||||||
|
add(self.slots,slot:new(1))
|
||||||
|
assert(#self.slots==12)
|
||||||
|
|
||||||
|
-- wells:
|
||||||
|
-- 1,2,3,4: wands, cups, swords, pentacles
|
||||||
|
local function add_suit(_self,suit)
|
||||||
|
add(_self.wells,well:new(function(lst,new)
|
||||||
|
assert(lst) -- the ace is always present initially
|
||||||
|
if (new.suit!=suit) return
|
||||||
|
return new.suit==suit and new.rank==lst.rank+1
|
||||||
|
end))
|
||||||
|
end
|
||||||
|
for suit in all(deck.suits) do
|
||||||
|
add_suit(self,suit)
|
||||||
|
end
|
||||||
|
-- 5: arcana ascending
|
||||||
|
add(self.wells,well:new(function(lst,new)
|
||||||
|
if (new.suit!='a') return
|
||||||
|
if (not lst) return new.rank==0
|
||||||
|
return new.rank==lst.rank+1
|
||||||
|
end))
|
||||||
|
-- 6: arcana descending
|
||||||
|
add(self.wells,well:new(function(lst,new)
|
||||||
|
if (new.suit!='a') return
|
||||||
|
if (not lst) return new.rank==21
|
||||||
|
return new.rank==lst.rank-1
|
||||||
|
end))
|
||||||
|
|
||||||
|
self:deal()
|
||||||
|
end
|
||||||
|
|
||||||
|
function board:deal()
|
||||||
|
local n_conventional_wells=4
|
||||||
|
local n_slots=10
|
||||||
|
-- first, pull the aces
|
||||||
|
assert(#deck.aces==n_conventional_wells)
|
||||||
|
assert(#self.wells==n_conventional_wells+2)
|
||||||
|
local available={}
|
||||||
|
for card=1,#deck.cards do
|
||||||
|
available[card]=true
|
||||||
|
end
|
||||||
|
for i=1,n_conventional_wells do
|
||||||
|
local well=self.wells[i]
|
||||||
|
local ace=deck.aces[i]
|
||||||
|
well:add(ace)
|
||||||
|
available[ace]=false
|
||||||
|
end
|
||||||
|
|
||||||
|
local eligible_bottom_row={}
|
||||||
|
for card=1,#deck.cards do
|
||||||
|
local skip
|
||||||
|
if not available[card] then
|
||||||
|
skip=true
|
||||||
|
else
|
||||||
|
for w in all(self.wells) do
|
||||||
|
if (w:would_accept(card)) skip=true break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if (not skip) add(eligible_bottom_row,card)
|
||||||
|
end
|
||||||
|
|
||||||
|
function i_to_slot(i)
|
||||||
|
if (i<n_slots\2) return i+1
|
||||||
|
return i+2
|
||||||
|
end
|
||||||
|
|
||||||
|
local bottom_row={}
|
||||||
|
shuf(eligible_bottom_row)
|
||||||
|
for i=1,n_slots do
|
||||||
|
local card=eligible_bottom_row[i]
|
||||||
|
add(bottom_row,card)
|
||||||
|
available[card]=false
|
||||||
|
end
|
||||||
|
|
||||||
|
eligible={}
|
||||||
|
for card=1,#deck.cards do
|
||||||
|
if (available[card]) add(eligible,card)
|
||||||
|
end
|
||||||
|
shuf(eligible)
|
||||||
|
for card in all(bottom_row) do
|
||||||
|
add(eligible,card)
|
||||||
|
end
|
||||||
|
for i=1,#eligible do
|
||||||
|
local ix=i_to_slot((i-1)%n_slots)
|
||||||
|
local slot=self.slots[ix]
|
||||||
|
slot:add(eligible[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function board:on_move()
|
||||||
|
-- TODO: Make checkpoint
|
||||||
|
while true do
|
||||||
|
if (not self:_on_move_1()) break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function board:_on_move_1()
|
||||||
|
for s=1,#self.slots do
|
||||||
|
local top=self.slots[s]:peek()
|
||||||
|
if top then
|
||||||
|
for w=1,#self.wells do
|
||||||
|
if w<=4 and self.slots[12]:peek()!=nil then
|
||||||
|
-- the top wells are blocked
|
||||||
|
elseif self.wells[w]:would_accept(top) then
|
||||||
|
self.wells[w]:add(self.slots[s]:pop())
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function board:draw()
|
||||||
|
for w_ix=1,#self.wells do
|
||||||
|
local w=self.wells[w_ix]
|
||||||
|
local l=layouts:well(w_ix)
|
||||||
|
|
||||||
|
for i=1,#w.contents do
|
||||||
|
local x,y=l:place_card(i)
|
||||||
|
deck:draw_card(x,y,w.contents[i],i<#w.contents)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function forall_slots(cb)
|
||||||
|
for s_ix=1,#self.slots do
|
||||||
|
local s=self.slots[s_ix]
|
||||||
|
local l=layouts:slot(s_ix)
|
||||||
|
|
||||||
|
local n=#s.contents
|
||||||
|
if (self.cursor.grabbed==s_ix) n-=1
|
||||||
|
|
||||||
|
cb(x,y,s_ix,s,l,n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
forall_slots(function(x,y,s_ix,s,l,n)
|
||||||
|
for i=1,n do
|
||||||
|
local x,y=l:place_card(i)
|
||||||
|
deck:draw_card(x,y,s.contents[i],i<n)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local hover_slot=self.cursor:hover_slot()
|
||||||
|
forall_slots(function(x,y,s_ix,s,l,n)
|
||||||
|
if hover_slot==s_ix then
|
||||||
|
local x,y=l:place_card(n+1)
|
||||||
|
self.cursor:draw_at(x,y)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
slot=klass()
|
||||||
|
function slot:init(max_n)
|
||||||
|
self.contents={}
|
||||||
|
self.max_n=max_n
|
||||||
|
end
|
||||||
|
function slot:would_accept(new)
|
||||||
|
local n=#self.contents
|
||||||
|
if (n==self.max_n) return
|
||||||
|
|
||||||
|
local lst=self.contents[n]
|
||||||
|
if (not lst) return true
|
||||||
|
lst=deck.cards[lst]
|
||||||
|
new=deck.cards[new]
|
||||||
|
return lst.suit==new.suit and (lst.rank==new.rank-1 or lst.rank==new.rank+1)
|
||||||
|
end
|
||||||
|
function slot:add(card)
|
||||||
|
add(self.contents,card)
|
||||||
|
end
|
||||||
|
function slot:peek()
|
||||||
|
return self.contents[#self.contents]
|
||||||
|
end
|
||||||
|
function slot:pop()
|
||||||
|
return deli(self.contents,#self.contents)
|
||||||
|
end
|
||||||
|
|
||||||
|
well=klass()
|
||||||
|
function well:init(accept_cb)
|
||||||
|
self.accept_cb=accept_cb
|
||||||
|
self.contents={}
|
||||||
|
end
|
||||||
|
function well:would_accept(new)
|
||||||
|
local lst=self.contents[#self.contents]
|
||||||
|
if (lst) lst=deck.cards[lst]
|
||||||
|
assert(new,"must be populated")
|
||||||
|
new=deck.cards[new]
|
||||||
|
assert(new,"must be populated")
|
||||||
|
return self.accept_cb(lst,new)
|
||||||
|
end
|
||||||
|
function well:add(card)
|
||||||
|
add(self.contents,card)
|
||||||
|
end
|
91
cursor.lua
Normal file
91
cursor.lua
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
acceptance_state={
|
||||||
|
not_grabbed=0,
|
||||||
|
would_accept=1,
|
||||||
|
would_not_accept=2,
|
||||||
|
no_move=3
|
||||||
|
}
|
||||||
|
cursor=klass()
|
||||||
|
function cursor:init(board)
|
||||||
|
self.board=board
|
||||||
|
self.hover_x=0
|
||||||
|
self.hover_y=1
|
||||||
|
self.grabbed=nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function cursor:acceptance_state()
|
||||||
|
if self.grabbed then
|
||||||
|
local hover=self:hover_slot()
|
||||||
|
if hover==self.grabbed then
|
||||||
|
return acceptance_state.no_move
|
||||||
|
end
|
||||||
|
local source=self.board.slots[self.grabbed]
|
||||||
|
local target=self.board.slots[self:hover_slot()]
|
||||||
|
local card=source:peek()
|
||||||
|
if target:would_accept(card) then
|
||||||
|
return acceptance_state.would_accept,source,target
|
||||||
|
else
|
||||||
|
return acceptance_state.would_not_accept
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return acceptance_state.not_grabbed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function cursor:toggle_grab()
|
||||||
|
local acc,src,tar=self:acceptance_state()
|
||||||
|
if acc==acceptance_state.not_grabbed then
|
||||||
|
self.grabbed=self:hover_slot()
|
||||||
|
elseif acc==acceptance_state.would_accept then
|
||||||
|
local card=src:pop()
|
||||||
|
tar:add(card)
|
||||||
|
self.grabbed=nil
|
||||||
|
self.board:on_move(card)
|
||||||
|
elseif acc==acceptance_state.no_move or acc==acceptance_state.would_not_accept then
|
||||||
|
self.grabbed=nil
|
||||||
|
else
|
||||||
|
assert(false,"invalid acceptance state")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function cursor:move_x(dx)
|
||||||
|
if (self.hover_y==0) return
|
||||||
|
self.hover_x+=dx
|
||||||
|
self.hover_x%=11 -- TODO: Don't hard-code
|
||||||
|
end
|
||||||
|
|
||||||
|
function cursor:move_y(dy)
|
||||||
|
if (self.hover_y==0 and dy==1) self.hover_y=1
|
||||||
|
if (self.hover_y==1 and dy==-1) self.hover_y=0
|
||||||
|
end
|
||||||
|
|
||||||
|
function cursor:hover_slot()
|
||||||
|
if (self.hover_y==0) return 12
|
||||||
|
return self.hover_x+1
|
||||||
|
end
|
||||||
|
|
||||||
|
function cursor:grabbed_card()
|
||||||
|
if self.grabbed then
|
||||||
|
local slot=self.board.slots[self.grabbed]
|
||||||
|
return slot:peek()
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function cursor:draw_at(x,y)
|
||||||
|
local card=self:grabbed_card()
|
||||||
|
local acc=self:acceptance_state()
|
||||||
|
|
||||||
|
if card and acc!=acceptance_state.would_accept and acc!=acceptance_state.no_move then
|
||||||
|
x+=sin(time()/2)*2+0.5
|
||||||
|
y+=sin(time()/4)+0.5+1
|
||||||
|
end
|
||||||
|
|
||||||
|
if card then
|
||||||
|
local card_fg=deck:draw_card(x,y,card,false)
|
||||||
|
local fg=card_fg
|
||||||
|
if (acc==acceptance_state.would_accept) fg=9
|
||||||
|
if (fg) rect(x-1,y-1,x+9,y+16,fg)
|
||||||
|
else
|
||||||
|
rectfill(x,y,x+8,y+15,9)
|
||||||
|
end
|
||||||
|
end
|
67
deck.lua
Normal file
67
deck.lua
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
deck={
|
||||||
|
suits={"p","s","c","w"},
|
||||||
|
cards={},
|
||||||
|
|
||||||
|
rank_name="a23456789tjqk"
|
||||||
|
}
|
||||||
|
-- pentacles, swords, cups, wands
|
||||||
|
for suit in all(deck.suits) do
|
||||||
|
for rank=1,13 do
|
||||||
|
add(deck.cards,{suit=suit,rank=rank})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- arcana
|
||||||
|
for rank=0,21 do
|
||||||
|
add(deck.cards,{suit="a",rank=rank})
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(#deck.cards==74)
|
||||||
|
deck.aces={1,14,27,40}
|
||||||
|
for i in all(deck.aces) do
|
||||||
|
assert(deck.cards[i].rank==1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function deck:draw_card(x,y,c,shadow)
|
||||||
|
local meta=deck.cards[c]
|
||||||
|
|
||||||
|
local s,fg
|
||||||
|
|
||||||
|
local bg,shadowbg
|
||||||
|
if meta.suit=='a' then
|
||||||
|
bg,shadowbg=1,1
|
||||||
|
if (shadow) bg,shadowbg=1,0
|
||||||
|
else
|
||||||
|
bg,shadowbg=7,7
|
||||||
|
--if (shadow) bg,shadowbg=7,6
|
||||||
|
end
|
||||||
|
|
||||||
|
rectfill(x,y,x+8,y+4,bg)
|
||||||
|
rectfill(x,y+5,x+8,y+15,shadowbg)
|
||||||
|
|
||||||
|
if (meta.suit=='p') s,fg=0,4
|
||||||
|
if (meta.suit=='s') s,fg=1,12
|
||||||
|
if (meta.suit=='c') s,fg=2,2
|
||||||
|
if (meta.suit=='w') s,fg=3,3
|
||||||
|
if (meta.suit=='a') fg=15
|
||||||
|
|
||||||
|
if meta.suit=='a' then
|
||||||
|
local rank=""..meta.rank
|
||||||
|
pal(7,15)
|
||||||
|
print(meta.rank,x+5-#rank*2,y+1,7)
|
||||||
|
spr(4,x,y+8)
|
||||||
|
pal()
|
||||||
|
else
|
||||||
|
local name=sub(deck.rank_name,meta.rank,meta.rank)
|
||||||
|
rectfill(x,y,x+3,y+6,fg)
|
||||||
|
print(name,x,y+1,7)
|
||||||
|
pal(7,fg)
|
||||||
|
spr(s,x+5,y+1)
|
||||||
|
if not shadow then
|
||||||
|
spr(15+meta.rank,x,y+8)
|
||||||
|
end
|
||||||
|
pal()
|
||||||
|
end
|
||||||
|
|
||||||
|
return fg
|
||||||
|
end
|
81
engine.lua
Normal file
81
engine.lua
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
-- setup
|
||||||
|
modules={}
|
||||||
|
|
||||||
|
function _init()
|
||||||
|
-- printh("restarting")
|
||||||
|
_doall("init")
|
||||||
|
end
|
||||||
|
|
||||||
|
function _update60()
|
||||||
|
_doall("update") end
|
||||||
|
function _draw()
|
||||||
|
cls(0)
|
||||||
|
_doall("draw") end
|
||||||
|
|
||||||
|
function _doall(x)
|
||||||
|
for n in all{x.."0",x,x.."2",x.."3"} do
|
||||||
|
for mod in all(modules) do
|
||||||
|
local f=mod[n]
|
||||||
|
if (f) f(mod)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function klass()
|
||||||
|
local k={}
|
||||||
|
k.__index=k
|
||||||
|
function k:new(...)
|
||||||
|
local n={}
|
||||||
|
setmetatable(n,k)
|
||||||
|
n:init(...)
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
return k
|
||||||
|
end
|
||||||
|
|
||||||
|
function alives(tbl)
|
||||||
|
local tbl2={}
|
||||||
|
for i in all(tbl) do
|
||||||
|
if (not i.dead) add(tbl2,i)
|
||||||
|
end
|
||||||
|
return tbl2
|
||||||
|
end
|
||||||
|
|
||||||
|
function trunc4(x)
|
||||||
|
if (x < 0) return -trunc4(-x)
|
||||||
|
return x\0.25/4
|
||||||
|
end
|
||||||
|
|
||||||
|
function stepstep(by,x0,x1,f)
|
||||||
|
local x=x0
|
||||||
|
if (x==x1) return
|
||||||
|
if x0>x1 then
|
||||||
|
return stepstep(by,-x0,-x1,function(x) return f(-x) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
x=x\by*by
|
||||||
|
|
||||||
|
while true do
|
||||||
|
x+=by
|
||||||
|
if (x>=x1) f(x1) break
|
||||||
|
if (not f(x)) break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function lerp(x,x0,x1)
|
||||||
|
return x0+x*(x1-x0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function csv(s)
|
||||||
|
local ret=split(s,"\n")
|
||||||
|
for i,v in ipairs(ret) do
|
||||||
|
ret[i] = type(v) == "string" and split(v) or {v} end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
function shuf(t)
|
||||||
|
for i=#t,1,-1 do
|
||||||
|
local j=flr(rnd(i))+1
|
||||||
|
t[i],t[j]=t[j],t[i]
|
||||||
|
end
|
||||||
|
end
|
31
layout.lua
Normal file
31
layout.lua
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
layouts={}
|
||||||
|
function layouts:well(i)
|
||||||
|
if (i<=4) return layout:new(69+i*10,1,layout_mode.obscured)
|
||||||
|
if (i==5) return layout:new(9,1,layout_mode.obscured)
|
||||||
|
if (i==6) return layout:new(19,1,layout_mode.obscured)
|
||||||
|
assert(false,"shouldn't be > 6")
|
||||||
|
end
|
||||||
|
function layouts:slot(i)
|
||||||
|
if (i<=11) return layout:new(9+(i-1)*10,18,layout_mode.vertical)
|
||||||
|
if (i==12) return layout:new(94,1,layout_mode.obscured)
|
||||||
|
assert(false,"shouldn't be > 12")
|
||||||
|
end
|
||||||
|
|
||||||
|
layout_mode={
|
||||||
|
obscured=0, -- for wells
|
||||||
|
vertical=1, -- for conventional slots
|
||||||
|
-- todo: sideways
|
||||||
|
}
|
||||||
|
|
||||||
|
layout=klass()
|
||||||
|
function layout:init(x,y,mode)
|
||||||
|
self.x=x
|
||||||
|
self.y=y
|
||||||
|
self.mode=mode
|
||||||
|
end
|
||||||
|
|
||||||
|
function layout:place_card(i)
|
||||||
|
if (self.mode==layout_mode.obscured) return self.x,self.y
|
||||||
|
if (self.mode==layout_mode.vertical) return self.x,self.y+(i-1)*6
|
||||||
|
assert(false,"unexpected mode")
|
||||||
|
end
|
19
main.lua
Normal file
19
main.lua
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
main={}
|
||||||
|
add(modules,main)
|
||||||
|
|
||||||
|
function main:init()
|
||||||
|
self.board=board:new()
|
||||||
|
end
|
||||||
|
|
||||||
|
function main:update()
|
||||||
|
if (btnp(0)) self.board.cursor:move_x(-1)
|
||||||
|
if (btnp(1)) self.board.cursor:move_x(1)
|
||||||
|
if (btnp(2)) self.board.cursor:move_y(-1)
|
||||||
|
if (btnp(3)) self.board.cursor:move_y(1)
|
||||||
|
if (btnp(4)) self.board.cursor:toggle_grab()
|
||||||
|
end
|
||||||
|
|
||||||
|
function main:draw()
|
||||||
|
cls(13)
|
||||||
|
self.board:draw()
|
||||||
|
end
|
25
main.p8
Normal file
25
main.p8
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
pico-8 cartridge // http://www.pico-8.com
|
||||||
|
version 41
|
||||||
|
__lua__
|
||||||
|
#include engine.lua
|
||||||
|
#include board.lua
|
||||||
|
#include cursor.lua
|
||||||
|
#include deck.lua
|
||||||
|
#include layout.lua
|
||||||
|
#include main.lua
|
||||||
|
__gfx__
|
||||||
|
00000000070000007770000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
70700000070000007770000007000000070707000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
07000000070000007770000007000000007777000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
70700000777000000700000070000000077070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
00000000070000007770000070000000007777700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
00000000000000000000000000000000070077700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
00000000000000000000000000000000000000000000000000000000000070000000700000007000000000000000000000000000000000000000000000000000
|
||||||
|
00000000000070000000700000700070007000700070007000700070007000700070007000700070070707000707070007070700000000000000000000000000
|
||||||
|
00000000000000000000000000000000000000000000000000000000000070000000700000007000007777000077770000777700000000000000000000000000
|
||||||
|
00007000000000000000700000000000000070000070007000707070000000000070007000700070077070000770700007707000000000000000000000000000
|
||||||
|
00000000000000000000000000000000000000000000000000000000000070000000000000007000007777700077777000777770000000000000000000000000
|
||||||
|
00000000000070000000700000700070007000700070007000700070007000700070007000700070070077700700777007007770000000000000000000000000
|
||||||
|
00000000000000000000000000000000000000000000000000000000000070000000700000007000000000000000000000000000000000000000000000000000
|
Loading…
Reference in New Issue
Block a user