def test_play_poacher_with_empty_supply_piles(self): for num_empty_supply_piles in (1, 2, 3): cards_in_supply = [CopperCard, SilverCard, GoldCard] starting_deck = [CopperCard] * 20 game_state = self.create_game_state( starting_deck, supply=UnorderedCardStack(cards_in_supply)) p1_agent = TestAgent('p1') p2_agent = TestAgent('p2') game_state.set_agents([p1_agent, p2_agent]) supply = game_state.get_location( Location(None, LocationName.SUPPLY)) # empty supply piles for i in range(num_empty_supply_piles): supply.extract([cards_in_supply[i]]) player = game_state.get_current_player_name() start_hand_size = 5 game_state.draw(start_hand_size) PoacherCard.play(game_state) hand = game_state.get_location(Location(player, LocationName.HAND)) self.assertEqual( hand.size(), start_hand_size + 1 - num_empty_supply_piles, 'Player should draw 1 card and then discard one for each empty supply pile.' ) discard = game_state.get_location( Location(player, LocationName.DISCARD)) self.assertEqual( discard.size(), num_empty_supply_piles, 'Player should have discarded 1 card for each empty supply pile.' ) self.assertEqual( game_state.get_counter(CounterId(None, CounterName.COINS)), 1, 'Poacher should give the player one extra coin.') self.assertEqual( game_state.get_counter(CounterId(None, CounterName.ACTIONS)), 1, 'Poacher should give the player one extra action.')
def play(cls, game_state): game_state.draw(1) game_state.update_counter(counter_id=CounterId(None, CounterName.ACTIONS), delta=1) game_state.update_counter(counter_id=CounterId(None, CounterName.COINS), delta=1) game_state.update_counter(counter_id=CounterId(None, CounterName.BUYS), delta=1)
def take_buy_action(self, player, card): """ Params: player_name: string card: Card class Moves card from supply to player discard pile """ cost = card.cost(self.game_state) self.game_state.update_counter(CounterId(None, CounterName.BUYS), -1) self.game_state.update_counter(CounterId(None, CounterName.COINS), 0 - cost) self.game_state.gain(card)
def play(cls, game_state): game_state.draw(1) game_state.update_counter(counter_id=CounterId(None, CounterName.ACTIONS), delta=1) game_state.update_counter(counter_id=CounterId(None, CounterName.COINS), delta=1) supply = game_state.get_location(Location(None, LocationName.SUPPLY)) cards_to_counts = supply.distribution.cards_to_counts() empty_supply_piles = list(cards_to_counts.values()).count(0) if empty_supply_piles == 0: return decision = PoacherDecision(game_state, empty_supply_piles) player = game_state.get_current_player_name() agent = game_state.get_agent(player) choice = agent.make_decision(decision) assert decision.is_valid(choice) game_state.discard(choice)
def play(cls, game_state): """ +2 cards +1 action """ game_state.draw(2) game_state.update_counter(counter_id=CounterId(None, CounterName.ACTIONS), delta=1)
def play(cls, game_state): """ +1 card +1 action Look through your discard pile. You may put that card on top of your deck """ game_state.draw(1) game_state.update_counter(counter_id=CounterId(None, CounterName.ACTIONS), delta=1) decision = HarbingerDecision(game_state) agent = game_state.get_agent(game_state.get_current_player_name()) chosen_cards = agent.make_decision(decision) assert decision.is_valid(chosen_cards) if chosen_cards: game_state.move(cards=chosen_cards, number=None, from_location=Location(agent.name(), LocationName.DISCARD), from_position=None, to_location=Location(agent.name(), LocationName.DRAW_PILE), to_position=StackPosition.TOP, event_type=CardEventType.MOVE)
def _create_counters(self, player_names): """ Create a dict of `CounterId` -> int for all game state counters in a starting game state given the list of player names. """ counters = {} for counter_name in GLOBAL_COUNTERS: counters[CounterId(None, counter_name)] = 0 return counters
def play(cls, game_state): decision = MoneyLenderDecision(game_state) agent = game_state.get_agent(game_state.get_current_player_name()) # No coppers to trash if decision.there_is_no_choice(): return choice = agent.make_decision(decision) if choice == [CopperCard]: game_state.trash(CopperCard) game_state.update_counter(counter_id=CounterId( None, CounterName.COINS), delta=3)
def test_play_poacher_no_empty_supply_piles(self): starting_deck = [CopperCard] * 12 game_state = self.create_game_state(starting_deck, supply=UnorderedCardStack( [PoacherCard, CopperCard])) player = game_state.get_current_player_name() PoacherCard.play(game_state) hand = game_state.get_location(Location(player, LocationName.HAND)) self.assertEqual(hand.size(), 1, 'Poacher should have the player draw a card.') discard = game_state.get_location( Location(player, LocationName.DISCARD)) self.assertEqual( discard.size(), 0, 'If no empty supply piles no cards should be discarded.') self.assertEqual( game_state.get_counter(CounterId(None, CounterName.COINS)), 1, 'Poacher should give the player one extra coin.') self.assertEqual( game_state.get_counter(CounterId(None, CounterName.ACTIONS)), 1, 'Poacher should give the player one extra action.')
def play(cls, game_state): """ Discard any number of cards, then draw that many +1 action """ game_state.update_counter(counter_id=CounterId(None, CounterName.ACTIONS), delta=1) decision = CellarDecision(game_state) agent = game_state.get_agent(game_state.get_current_player_name()) chosen_cards = agent.make_decision(decision) assert decision.is_valid(chosen_cards) game_state.discard(chosen_cards) game_state.draw(len(chosen_cards))
def run(self): """ Main control loop for game play """ ################# # Initialization ################# starting_supply = self.get_starting_supply() starting_deck = self.get_starting_deck() self.game_state = GameState( list(map(lambda x: x.name(), self.players)), starting_supply, starting_deck, self.log) # TODO: inform Players of initial state for learning agents self.game_state.set_agents(self.players) for player in self.players: # shuffle player decks self.game_state.shuffle( Location(player.name(), LocationName.DRAW_PILE)) # draw starting hands self.game_state.draw(NUM_CARDS_IN_HAND, player.name()) ################# # Gameplay loop ################# turn_number = 1 while not self.game_over(): # Fetch the next player player = self.players[self.player_index] ### Setup Phase self.game_state.reset_counters_for_new_turn() ### Action phase while (self.actions_left() > 0 and len(self.action_cards_in_hand(player)) > 0): decision = self.generate_action_decision(player) choices = self.give_decision(decision, player) action_card = choices[0] if choices else None # TODO: validate choice legality if not action_card: break self.game_state.update_counter( CounterId(None, CounterName.ACTIONS), -1) self.game_state.play(action_card) action_card.play(self.game_state) ### Buy phase decision = self.generate_play_treasures_decision(player) treasures = self.give_decision(decision, player) self.play_treasures(player, treasures) while self.buys_left() > 0: decision = self.generate_buy_decision(player) choices = self.give_decision(decision, player) choice = choices[0] if choices else None # TODO: validate choice legality if not choice: break self.take_buy_action(player, choice) ### Discard cards in play and in hand self.game_state.discard_location( Location(player.name(), LocationName.IN_PLAY)) self.game_state.discard_location( Location(player.name(), LocationName.HAND)) ### Draw next hand self.game_state.draw(NUM_CARDS_IN_HAND, player.name()) # TODO: inform Players of after turn state for learning agents # rotate player index self.player_index = self.increment_player_turn_index() turn_number += 1 # Safety to avoid bots getting stuck in infinite game. if turn_number > MAX_TURNS: break ################# # Resolve game ################# print('\nGAME OVER on Turn %d\n-----------------\n' % (turn_number / 2)) for name, vp in self.player_name_to_vp().items(): print('%s: %d' % (name, vp)) winners = self.get_winners() if len(winners) == 1: print('Winner: ' + winners[0]) elif len(winners) == 2: tied_player_names = ' and '.join(winners) print('Tie between %s' % tied_player_names) return winners
def _print_coins(self, known_state): if known_state is None: return num_coins = known_state.counters[CounterId(None, CounterName.COINS)] print('Coins: %d' % num_coins)
def money_in_play(self): return self.game_state.get_counter(CounterId(None, CounterName.COINS))
def buys_left(self): return self.game_state.get_counter(CounterId(None, CounterName.BUYS))
def actions_left(self): return self.game_state.get_counter(CounterId(None, CounterName.ACTIONS))
def reset_counters_for_new_turn(self): """ Updates all counters for the beginning of a new turn. """ for counter_name, value in COUNTER_VALUES_AT_TURN_START.items(): self.set_counter(CounterId(None, counter_name), value)
def play(cls, game_state): game_state.update_counter( counter_id=CounterId(None, CounterName.COINS), delta=cls.base_treasure_value )