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 (nodds) 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