def makeDecision(self, s: State, response: DecisionResponse): d: DecisionState = s.decision player: int = d.controlling_player if d.type != DecisionType.DecisionSelectCards and d.type != DecisionType.DecisionDiscreteChoice: logging.error('Invalid decision type') if not d.active_card: self.makePhaseDecision(s, response) elif s.events: event = s.events[-1] if isinstance(event, PutOnDeckDownToN): self.heuristic.makePutDownOnDeckDecision(s, response) elif isinstance(event, DiscardDownToN): self.heuristic.makeDiscardDownDecision(s, response) elif isinstance(event, RemodelExpand): if not event.trashed_card: def scoringFunction(card: Card): if isinstance(card, Curse): return 19 elif isinstance(card, Estate): return 18 elif isinstance(card, VictoryCard): return -200 + card.get_coin_cost() return -card.get_coin_cost() response.cards = heuristic_select_cards( d.card_choices, d.min_cards, scoringFunction) else: response.cards.append( self.heuristic.agenda.forceBuy(s, player, d.card_choices)) else: self.heuristic.makeBaseDecision(s, response)
def makeCopyDecision(self, s: State, response: DecisionResponse): d: DecisionState = s.decision def scoringFunction(card: Card): return card.get_coin_cost() response.cards = heuristic_select_cards(d.card_choices, d.min_cards, scoringFunction)
def makeDiscardDownDecision(self, s: State, response: DecisionResponse): d: DecisionState = s.decision def scoringFunction(card: Card): if isinstance(card, VictoryCard): return 20 elif isinstance(card, Curse): return 19 elif isinstance(card, Copper): return 18 return -card.get_coin_cost() response.cards = heuristic_select_cards(d.card_choices, d.min_cards, scoringFunction)
def makeGreedyActionDecision(self, s: State, response: DecisionResponse): d: DecisionState = s.decision assert d.min_cards == 0 and d.max_cards == 1, 'Invalid decision parameters' def scoringFunction(card: Card): '''Play all cantrips first, then greedily''' cantrip_bonus = 7 score = min(card.get_coin_cost(), 6) if is_cantrip(card): score += cantrip_bonus return score cards = heuristic_select_cards(d.card_choices, d.min_cards, scoringFunction) response.cards = cards
def makePutDownOnDeckDecision(self, s: State, response: DecisionResponse): d: DecisionState = s.decision def scoringFunction(card: Card): if has_excess_actions(s.decision.card_choices): if isinstance(card, ActionCard): return 100 - card.get_plus_actions() return -card.get_coin_cost() elif has_treasure_cards(s.decision.choices): if isinstance(card, TreasureCard): return 100 - card.get_treasure() return -card.get_coin_cost() else: return -card.get_coin_cost() response.cards = heuristic_select_cards(d.card_choices, d.min_cards, scoringFunction)
def test_event_sentry(self) -> None: self.game.new_game() # Inject Sentry in player's hand sentry = Sentry() self.game.state.inject(0, sentry) self.game.state.advance_next_decision() # Action Phase Decision r = DecisionResponse([]) r.cards = [sentry] self.game.state.process_decision(r) self.game.state.advance_next_decision() # Choose to trash one card d = self.game.state.decision trashed = d.card_choices[0] r = DecisionResponse([trashed]) self.game.state.process_decision(r) # Trash card self.game.state.advance_next_decision() self.assertEqual(self.game.state.trash, [trashed]) # Choose to discard one card d = self.game.state.decision discarded = d.card_choices[0] r = DecisionResponse([discarded]) self.game.state.process_decision(r) # Discard card self.game.state.advance_next_decision() d = self.game.state.decision p_state: PlayerState = self.game.state.player_states[0] self.assertEqual(p_state._discard, [discarded]) self.assertIsNone(d.active_card)
def makeDecision(self, s: State, response: DecisionResponse): d: DecisionState = s.decision # Do not allow RandomPlayer to purchase curses if s.phase == Phase.BuyPhase and not self.train: remove_first_card(Curse(), d.card_choices) # Ensure random player plays all treasures if s.phase == Phase.TreasurePhase: response.single_card = d.card_choices[0] return if d.type == DecisionType.DecisionSelectCards: cards_to_pick = d.min_cards if d.max_cards > d.min_cards: cards_to_pick = random.randint(d.min_cards, d.max_cards) response.cards = random.sample(d.card_choices, k=min(cards_to_pick, len(d.card_choices))) elif d.type == DecisionType.DecisionDiscreteChoice: response.choice = random.randint(0, d.min_cards) else: logging.error('Invalid decision type')
def test_moat_reveal(self) -> None: self.game.new_game() # Inject necessary cards into players' hands attack_card = Militia() moat_card = Moat() self.game.state.inject(0, attack_card) self.game.state.inject(1, moat_card) self.game.state.advance_next_decision() # Action Phase decision r = DecisionResponse([]) r.cards = [attack_card] self.game.state.process_decision(r) self.game.state.advance_next_decision() # MoatReveal reaction r = DecisionResponse([]) r.choice = 0 self.game.state.process_decision(r) self.game.state.advance_next_decision() self.assertEqual(self.game.state.events, [])
def makeBaseDecision(self, s: State, response: DecisionResponse): d: DecisionState = s.decision card = d.active_card player = s.decision.controlling_player p_state: PlayerState = s.player_states[player] if isinstance(card, Cellar): num_discarded = 0 for c in d.card_choices: if isinstance(c, VictoryCard) or c.get_coin_cost() < 2: response.cards.append(c) elif isinstance(card, Chapel): treasureValue = s.get_total_coin_count(player) trashCoppers = (treasureValue > 3) num_discarded = 0 for c in d.card_choices: trashCoppers = (treasureValue > 3) if num_discarded == 4: break if isinstance(c, Curse): response.cards.append(c) num_discarded += 1 elif isinstance(c, Copper) and trashCoppers: response.cards.append(c) num_discarded += 1 treasureValue -= 1 elif isinstance(c, Estate): response.cards.append(c) num_discarded += 1 elif isinstance(c, Chapel): response.cards.append(c) num_discarded += 1 elif isinstance(card, Moat): response.choice = 0 elif isinstance(card, Bureaucrat): response.cards.append(d.card_choices[0]) elif isinstance(card, Militia): self.makeDiscardDownDecision(s, response) elif isinstance(card, ThroneRoom): self.makeCopyDecision(s, response) elif isinstance(card, Library): if s.player_states[s.player].actions == 0: response.choice = 0 else: response.choice = 1 elif isinstance(card, Mine): event = s.events[-1] if not event.trashed_card: def scoringFunction(card: Card): if isinstance(card, Gold) and s.supply[Gold] > 0: return 20 if isinstance(card, Silver) and s.supply[Silver] > 0: return 19 if isinstance(card, Copper) and s.supply[Copper] > 0: return 18 return -card.get_coin_cost() response.cards = heuristic_select_cards(d.card_choices, d.min_cards, scoringFunction) else: response.cards.append(self.agenda.forceBuy(s, player, d.card_choices)) elif isinstance(card, Harbinger): def scoringFunction(card: Card): if has_excess_actions(p_state.hand): if isinstance(card, ActionCard): return 100 + card.get_coin_cost() else: return card.get_coin_cost() else: return card.get_coin_cost() response.cards = heuristic_select_cards(d.card_choices, d.min_cards, scoringFunction) elif isinstance(card, Artisan): event = s.events[-1] if not event.gained_card: response.cards.append(self.agenda.forceBuy(s, player, d.card_choices)) else: self.makePutDownOnDeckDecision(s, response) elif isinstance(card, Poacher): self.makeDiscardDownDecision(s, response) else: logging.error('Unexpected decision')