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