board=klass() function board:init(ruleset) self.ruleset=ruleset self.cursor=cursor:new(self) self.animator=animator:new() self.checkpoint=nil self.slots={} self.wells={} -- board slots -- ...n_slots: normal for i=1,ruleset.n_slots do add(self.slots,slot:new(ruleset)) end -- n_slots+1: special add(self.slots,slot:new(ruleset,1)) -- wells: -- ...n_suits: wands, cups, swords, pentacles, etc local function add_suit(_self,suit) local w=well:new(_self.ruleset, 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) w.obscured_by_extra_slot=true add(_self.wells, w) end for suit in all(self.ruleset.deck.suits) do add_suit(self,suit) end -- n_suits+1: arcana ascending add(self.wells,well:new(self.ruleset,function(lst,new) if (new.suit!='a') return if (not lst) return new.rank==0 return new.rank==lst.rank+1 end)) -- n_suits+2: arcana descending add(self.wells,well:new(self.ruleset,function(lst,new) if (new.suit!='a') return if (not lst) return new.rank==self.ruleset.n_arcana-1 return new.rank==lst.rank-1 end)) local seed=seeds:choose(self.ruleset.pool) printh("chosen seed: "..tostr(seed,2)) self:deal(seeds:choose(self.ruleset.pool)) end function board:deal(seed) local deal=deal(self.ruleset,seed) local n_usable_slots=self.ruleset.n_slots - 1 for i=1,#self.ruleset.deck.aces do local well=self.wells[i] local ace=self.ruleset.deck.aces[i] self:animate_move_ace_to_well(ace,i) end local rows=#deal[1] for y=1,rows do for x=1,#deal do local outx=x if (x>n_usable_slots\2) outx+=1 self:animate_move_new_card_to_slot(deal[x][y],outx) end end end function board:undo() if (self.checkpoint) self.checkpoint:apply(self) print("applied") self.checkpoint=nil end function board:on_idle() self:find_automove() end function board:pre_move(card) self.checkpoint=checkpoint:new(self,card) end function board:on_move() self:find_automove() end function board:is_won() if (not self:can_take_input()) return false for s=1,#self.slots do if (self.slots[s]:peek()) return false end return true end function board:find_automove() for s=1,#self.slots do local top=self.slots[s]:peek() if top then for w=1,#self.wells do if w<=self.ruleset.n_suits and self.slots[self.ruleset.n_slots+1]:peek()!=nil then -- the top wells are blocked elseif self.wells[w]:would_accept(top) then self:animate_and_move_to_well(s,w) return true end end end end end function board:can_take_input() return self.animator:idle() end function board:update() local was_idle=self.animator:idle() self.animator:update() local is_idle=self.animator:idle() if (not was_idle and is_idle) then self:on_idle() end end function board:draw() local extra_slot_full=self.slots[self.ruleset.n_slots+1]:peek()!=nil for w_ix=1,#self.wells do local w=self.wells[w_ix] local l=self.ruleset.layouts:well(w_ix) local shadowed=nil if w.obscured_by_extra_slot and extra_slot_full then shadowed=true end for i=1,#w.contents do local x,y=l:place_card(i) self.ruleset.deck:draw_card(x,y,w.contents[i],{shadowed=shadowed}) end end local function forall_slots(cb) for s_ix=1,#self.slots do local s=self.slots[s_ix] local l=self.ruleset.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) self.ruleset.deck:draw_card(x,y,s.contents[i],{rotate=l.rotated}) end end) if self.checkpoint then local cpl=self.ruleset.layouts:checkpoint() local x,y=cpl:place_card(0) self.ruleset.deck:draw_card(x,y,self.checkpoint.card,{shadowed=true}) print("❎",x+1,y+9,7) end self.animator:draw() if self.animator:idle() then 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(l,x,y) end end) end end slot=klass() function slot:init(ruleset,max_n) self.ruleset=ruleset 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=self.ruleset.deck.cards[lst] new=self.ruleset.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(ruleset,accept_cb) self.ruleset=ruleset self.accept_cb=accept_cb self.contents={} end function well:would_accept(new) local lst=self.contents[#self.contents] if (lst) lst=self.ruleset.deck.cards[lst] assert(new,"must be populated") new=self.ruleset.deck.cards[new] assert(new,"must be populated") return self.accept_cb(lst,new) end function well:add(card) add(self.contents,card) end function well:clear() self.contents={} end