fortunes_foundation/dealer.lua
2024-02-08 12:44:28 -08:00

147 lines
3.3 KiB
Lua

function deal(ruleset)
local n_usable_cards=ruleset.n_usable_cards
local n_final_slots=ruleset.n_slots-1
local n_usable_slots=n_final_slots+1
local tower_height = n_usable_cards\n_final_slots
-- prototype
local deal1,generate_wells,generate_pops,accepts
local deal1=function()
local slots,max_height={},{}
for i=0,n_final_slots do
slots[i]={}
max_height[i]=tower_height
end
max_height[0]=1
local wells=generate_wells()
local pops=generate_pops(wells)
local function pek(lst)
return lst[#lst]
end
local function pop(lst)
return deli(lst,#lst)
end
local function pop_accepted_card(lst)
local card=lst[#lst]
if (card and accepts(lst[#lst-1],card)) return pop(lst)
end
local function find_home(card,allow_too_tall)
assert(card!=0)
local acceptors={}
local start_points={}
for s=0,#slots do
local n=#slots[s]
if s==exclude then
-- don't place it here ever
elseif ruleset.deck.instantly_accepted[card] and n==max_height[s]-1 then
-- can't place an instantly accepted card at the bottom of a slot
else
if (accepts(pek(slots[s]),card)) add(acceptors,s)
if (n<max_height[s]) add(start_points,s)
end
end
local acceptor=rnd(acceptors)
local start_point=rnd(start_points)
if (acceptor and rnd()>odds) acceptor=nil
local a=acceptor or start_point or source
if (a) add(slots[a],card) return true
end
local original_pops=#pops
while #pops>0 do
local w=pop(pops)
local card=pop(wells[w])
find_home(card,true)
end
-- fix any stacks that are too tall
max_height[0]=0 -- auxiliary slot must be empty
for i=0,#slots do
while #slots[i]>max_height[i] do
local card=pop(slots[i])
if (not find_home(card,false)) return
end
end
-- get rid of auxiliary slot
slots[0]=nil
local shuffles=1
local actual_shuffles=1
for s=1,#slots do
local extra_shuf=shuffles+1
while (ruleset.deck.instantly_accepted[pek(slots[s])]) do
shuf(slots[s])
shuffles=extra_shuf
actual_shuffles+=1
if (actual_shuffles>=5) return nil
end
end
for i=shuffles,2 do
shuf(rnd(slots))
end
for s=1,#slots do
assert(#slots[s]==tower_height)
end
return slots
end
generate_wells=function()
local split_point=flr(rnd()*(ruleset.n_arcana+1))
local wells={}
for i=1,ruleset.n_suits+2 do
wells[i]={}
end
for r=2,ruleset.n_cards_per_suit do
for s=1,ruleset.n_suits do
local card=ruleset.n_cards_per_suit * (s - 1) + (r - 1) + 1
assert(card!=0)
add(wells[s],card)
end
end
local first_arcana=ruleset.n_suits*ruleset.n_cards_per_suit+1
local arcana0=ruleset.n_suits
local arcana1=arcana0+1
for r=0,split_point-1 do
assert(first_arcana+r!=0)
add(wells[arcana0],first_arcana+r)
end
for r=ruleset.n_arcana-1,split_point,-1 do
assert(first_arcana+r!=0)
add(wells[arcana1],first_arcana+r)
end
return wells
end
generate_pops=function(wells)
local pops={}
for w=1,#wells do
for _=1,#wells[w] do
add(pops,w)
end
end
shuf(pops)
return pops
end
accepts=function(c0,c1)
assert(c0!=0)
assert(c1!=0)
assert(c1!=nil)
if (c0==nil) return true
c0=ruleset.deck.cards[c0]
c1=ruleset.deck.cards[c1]
return c0.suit==c1.suit and (c1.rank == c0.rank+1 or c0.rank==c1.rank+1)
end
while true do
local d=deal1()
if (d) return d
end
end