class HanabiGame: def __init__(self, players, seed, variant): self.players = prep_players(players) self.table = HanabiTable(len(players), seed, variant) self.variant = variant self.current_player = 0 def play_game(self): def pretty_print_info(info): LOGGER.debug('Player %s sees:', self.current_player) LOGGER.debug('Players: %s', info.num_players) LOGGER.debug('Cards in deck: %s', info.deck_size) LOGGER.debug('Discarded: %s', info.discarded) LOGGER.debug('Score: %s', info.score) LOGGER.debug('Progress: %s', info.scored_cards) LOGGER.debug('Sees: %s', info.hands) LOGGER.debug('Knows: %s', info.known_info) LOGGER.debug('Disclosures left: %s', info.disclosures) LOGGER.debug('Mistakes left: %s', info.mistakes_left) player_details = 'Game with {players}'.format( players=[player.__class__.__name__ for player in self.players]) LOGGER.info(player_details) while not self.table.is_game_over(): player = self.players[self.current_player] info = self.table.info_for_player(self.current_player) player_move = player.do_turn(self.current_player, info) if player_move.is_valid(info): player_move.execute(self.table) pretty_print_info(info) LOGGER.debug(str(player_move)) self.current_player = (self.current_player + 1) % self.table.num_players else: self.disqualify(player_move) for move in self.game_history(): LOGGER.debug(move) LOGGER.info('Final score: %s', self.table.score()) def game_history(self): return [str(action) for action in self.table.history] def disqualify(self, player_move): LOGGER.warning('Expected format for play card:') LOGGER.warning('{"play_type":"play", "card":<number>}') LOGGER.warning('Expected format for discard card:') LOGGER.warning('{"play_type":"discard", "card":<number>}') LOGGER.warning('Expected format for disclose color:') LOGGER.warning( '{"play_type":"disclose", "disclose_type":"color, "color":<color>}' ) LOGGER.warning('"color" cannot be "*" in a Variant 3 game') LOGGER.warning('Expected format for disclose rank:') LOGGER.warning( '{"play_type":"disclose", "disclose_type":"rank, "rank":<number>}') LOGGER.error('Received invalid move from player %s: %s', self.current_player, player_move) raise InvalidHanabiMoveException('Received invalid move from player', self.current_player)
class HanabiTableTests(unittest.TestCase): def setUp(self): self.table = HanabiTable(2, 1, HanabiVariant.basic) def test_table_play_card(self): self.assertFalse(self.table.is_game_over()) self.assertEqual(len(self.table.deck), 40) self.table.play_card(0, 0) self.assertFalse(self.table.is_game_over()) self.assertEqual(len(self.table.deck), 39) def test_table_discard_card(self): self.assertFalse(self.table.is_game_over()) self.assertEqual(len(self.table.deck), 40) self.assertEqual(len(self.table.discard), 0) self.table.discard_card(0, 0) self.assertFalse(self.table.is_game_over()) self.assertEqual(len(self.table.deck), 39) self.assertEqual(len(self.table.discard), 1) def test_table_disclose_color(self): self.assertEqual(["??", "??", "??", "??", "??"], self.table.info_for_player(0).hands[0]) self.table.disclose_color(0, 0, HanabiColor.RED) self.assertEqual(["R?", "??", "R?", "??", "??"], self.table.info_for_player(0).hands[0]) def test_table_disclose_rank(self): self.assertEqual(["??", "??", "??", "??", "??"], self.table.info_for_player(0).hands[0]) self.table.disclose_rank(0, 0, 3) self.assertEqual(["??", "??", "??", "??", "?3"], self.table.info_for_player(0).hands[0]) def test_table_game_over_mistakes(self): self.assertFalse(self.table.is_game_over()) self.table.play_card(0, 1) self.assertFalse(self.table.is_game_over()) self.assertEqual(self.table.mistakes_left, 2) self.table.play_card(1, 0) self.assertFalse(self.table.is_game_over()) self.assertEqual(self.table.mistakes_left, 1) self.table.play_card(1, 0) self.assertTrue(self.table.is_game_over()) self.assertEqual(self.table.mistakes_left, 0) def test_game_over_no_more_cards(self): self.assertFalse(self.table.is_game_over()) for i in range(0, 42): self.assertEqual(len(self.table.discard), i) self.assertFalse(self.table.is_game_over()) self.table.discard_card(0, 0) self.assertTrue(self.table.is_game_over()) def test_game_over_and_won(self): self.table.play_card(1, 2) self.assertEqual(1, self.table.score()) self.table.play_card(0, 0) self.assertEqual(2, self.table.score()) self.table.play_card(0, 4) self.assertEqual(3, self.table.score()) self.table.scored_cards[HanabiColor.RED] = 5 self.table.scored_cards[HanabiColor.BLUE] = 5 self.table.scored_cards[HanabiColor.GREEN] = 5 self.table.scored_cards[HanabiColor.YELLOW] = 5 self.table.scored_cards[HanabiColor.WHITE] = 5 self.assertEqual(25, self.table.score()) self.assertTrue(self.table.is_game_over()) def test_table_str(self): self.assertEqual('Score: 0, ' 'Cards remaining: 40, ' 'Discarded: 0, ' 'Disclosures left: 8, ' 'Mistakes left: 3', str(self.table)) self.table.play_card(1, 0) self.assertEqual('Score: 0, ' 'Cards remaining: 39, ' 'Discarded: 1, ' 'Disclosures left: 8, ' 'Mistakes left: 2', str(self.table)) self.table.disclose_color(0, 0, HanabiColor.RED) self.assertEqual('Score: 0, ' 'Cards remaining: 39, ' 'Discarded: 1, ' 'Disclosures left: 7, ' 'Mistakes left: 2', str(self.table)) self.table.discard_card(0, 0) self.assertEqual('Score: 0, ' 'Cards remaining: 38, ' 'Discarded: 2, ' 'Disclosures left: 8, ' 'Mistakes left: 2', str(self.table)) def test_table_info_for_player(self): info = self.table.info_for_player(0) self.assertEqual(info.score, 0) self.assertEqual(info.deck_size, 40) self.assertEqual(len(info.discarded), 0) self.assertEqual(info.disclosures, 8) self.assertEqual(info.mistakes_left, 3) self.assertEqual(info.num_players, 2) self.assertEqual(info.hands[0], ["??", "??", "??", "??", "??"]) self.assertEqual(info.hands[1], ["W4", "Y5", "B1", "G5", "W1"]) self.assertEqual(info.known_info[0], ["??", "??", "??", "??", "??"]) self.assertEqual(info.known_info[1], ["??", "??", "??", "??", "??"]) self.assertEqual(info.scored_cards["R"], 0) self.assertEqual(info.scored_cards["B"], 0) self.assertEqual(info.scored_cards["G"], 0) self.assertEqual(info.scored_cards["Y"], 0) self.assertEqual(info.scored_cards["W"], 0) self.assertTrue("*" not in info.scored_cards) def test_table_score(self): self.assertEqual(0, self.table.score()) def test_play_5_get_disclosure(self): self.play_to_white_5() self.table.disclose_rank(0, 0, 0) self.assertEqual(7, self.table.disclosures) self.table.play_card(0, 0) self.assertEqual(8, self.table.disclosures) def test_play_5_no_extra_disclosure(self): self.play_to_white_5() self.assertEqual(8, self.table.disclosures) self.table.play_card(0, 0) self.assertEqual(8, self.table.disclosures) def play_to_white_5(self): self.table.play_card(0, 3) for _ in range(0, 5): self.table.discard_card(0, 0) self.table.play_card(0, 4) for _ in range(0, 3): self.table.discard_card(0, 0) self.table.play_card(0, 1) self.table.play_card(1, 0)