def test_play_trick_winner_has_pending_marriage_points(self): game_state = get_game_state_for_tests() with GameStateValidator(game_state): for trick in game_state.won_tricks[PlayerId.TWO]: game_state.trick_points[PlayerId.TWO] -= trick.one.card_value game_state.trick_points[PlayerId.TWO] -= trick.two.card_value game_state.talon.extend([trick.one, trick.two]) game_state.trick_points = PlayerPair(22, 0) game_state.won_tricks[PlayerId.TWO] = [] first_talon_card = game_state.talon[0] second_talon_card = game_state.talon[1] queen_hearts = game_state.cards_in_hand[PlayerId.ONE][0] action = PlayCardAction(PlayerId.ONE, queen_hearts) game_state = action.execute(game_state) self.assertEqual(queen_hearts, game_state.current_trick[PlayerId.ONE]) self.assertEqual(PlayerId.TWO, game_state.next_player) self.assertEqual([Suit.DIAMONDS], game_state.marriage_suits[PlayerId.TWO]) self.assertEqual(0, game_state.trick_points[PlayerId.TWO]) queen_clubs = game_state.cards_in_hand[PlayerId.TWO][4] action = PlayCardAction(PlayerId.TWO, queen_clubs) game_state = action.execute(game_state) self.assertEqual(PlayerPair(None, None), game_state.current_trick) self.assertEqual(PlayerPair(22, 26), game_state.trick_points) self.assertEqual(PlayerPair(queen_hearts, queen_clubs), game_state.won_tricks[PlayerId.TWO][-1]) self.assertFalse(queen_hearts in game_state.cards_in_hand[PlayerId.ONE]) self.assertFalse(queen_clubs in game_state.cards_in_hand[PlayerId.TWO]) self.assertTrue(second_talon_card in game_state.cards_in_hand[PlayerId.ONE]) self.assertTrue(first_talon_card in game_state.cards_in_hand[PlayerId.TWO]) self.assertEqual(PlayerId.TWO, game_state.next_player)
def test_play_trick_talon_closed_opponent_cannot_follow_suit_or_trump(self): game_state = get_game_state_for_tests() with GameStateValidator(game_state): trick = game_state.won_tricks.one.pop(0) game_state.talon.append(trick.one) game_state.talon.append(trick.two) game_state.trick_points.one -= trick.one.card_value game_state.trick_points.one -= trick.two.card_value trick = game_state.won_tricks.two.pop(-1) game_state.talon.append(trick.one) game_state.talon.append(trick.two) game_state.trick_points.two -= trick.one.card_value game_state.trick_points.two -= trick.two.card_value game_state.next_player = PlayerId.TWO game_state.close_talon() self.assertEqual([False, False, False, False, False], [card.public for card in game_state.talon]) action = PlayCardAction(PlayerId.TWO, Card(Suit.DIAMONDS, CardValue.QUEEN)) self.assertTrue(action.can_execute_on(game_state)) game_state = action.execute(game_state) self.assertEqual([False, False, False, False, False], [card.public for card in game_state.talon]) action = PlayCardAction(PlayerId.ONE, Card(Suit.HEARTS, CardValue.QUEEN)) self.assertTrue(action.can_execute_on(game_state)) game_state = action.execute(game_state) self.assertEqual([True, False, False, True, True], [card.public for card in game_state.talon])
def test_play_trick_talon_has_more_than_one_card(self): game_state = get_game_state_with_multiple_cards_in_the_talon_for_tests() first_talon_card = game_state.talon[0] second_talon_card = game_state.talon[1] queen_hearts = game_state.cards_in_hand[PlayerId.ONE][0] action = PlayCardAction(PlayerId.ONE, queen_hearts) game_state = action.execute(game_state) self.assertEqual(queen_hearts, game_state.current_trick[PlayerId.ONE]) self.assertEqual(PlayerId.TWO, game_state.next_player) queen_diamonds = game_state.cards_in_hand[PlayerId.TWO][0] action = PlayCardAction(PlayerId.TWO, queen_diamonds) game_state = action.execute(game_state) self.assertEqual(PlayerPair(None, None), game_state.current_trick) self.assertEqual(PlayerPair(28, 33), game_state.trick_points) self.assertEqual(PlayerPair(queen_hearts, queen_diamonds), game_state.won_tricks[PlayerId.ONE][-1]) self.assertFalse(queen_hearts in game_state.cards_in_hand[PlayerId.ONE]) self.assertFalse(queen_diamonds in game_state.cards_in_hand[PlayerId.TWO]) self.assertTrue(first_talon_card in game_state.cards_in_hand[PlayerId.ONE]) self.assertTrue(second_talon_card in game_state.cards_in_hand[PlayerId.TWO]) self.assertEqual([Card(Suit.CLUBS, CardValue.TEN)], game_state.talon) self.assertEqual(PlayerId.ONE, game_state.next_player)
def test_actions_after_the_opponent_played_one_card_talon_is_empty(self): game_state = get_game_state_with_empty_talon_for_tests() action = PlayCardAction(PlayerId.ONE, Card(Suit.SPADES, CardValue.TEN)) game_state = action.execute(game_state) actions = get_available_actions(game_state) self.assertEqual(set(actions), set(get_available_actions(game_state.next_player_view()))) expected_actions = [ PlayCardAction(PlayerId.TWO, Card(Suit.CLUBS, CardValue.KING)), PlayCardAction(PlayerId.TWO, Card(Suit.CLUBS, CardValue.JACK)), PlayCardAction(PlayerId.TWO, Card(Suit.CLUBS, CardValue.QUEEN)), ] self.assertSetEqual(set(expected_actions), set(actions)) game_state = get_game_state_with_empty_talon_for_tests() with GameStateValidator(game_state): game_state.next_player = PlayerId.TWO action = PlayCardAction(PlayerId.TWO, Card(Suit.CLUBS, CardValue.JACK)) game_state = action.execute(game_state) actions = get_available_actions(game_state) self.assertEqual(set(actions), set(get_available_actions(game_state.next_player_view()))) expected_actions = [ PlayCardAction(PlayerId.ONE, Card(Suit.CLUBS, CardValue.ACE)), ] self.assertSetEqual(set(expected_actions), set(actions))
def test_must_follow_suit_must_use_higher_trump(self): """ Player.ONE plays the trump Queen. Player.TWO has three trump cards. The valid cards are only the trump King and Ace. """ game_state = get_game_state_with_empty_talon_for_tests() with GameStateValidator(game_state): ace_clubs = game_state.cards_in_hand.one[0] queen_clubs = game_state.cards_in_hand.two[3] game_state.cards_in_hand.two[3] = ace_clubs game_state.cards_in_hand.one[0] = queen_clubs action = PlayCardAction(PlayerId.ONE, queen_clubs) self.assertTrue(action.can_execute_on(game_state)) game_state = action.execute(game_state) num_legal_cards = 0 valid_cards = [Card(Suit.CLUBS, CardValue.KING), Card(Suit.CLUBS, CardValue.ACE)] for card in game_state.cards_in_hand[PlayerId.TWO]: action = PlayCardAction(PlayerId.TWO, card) is_legal_card = action.can_execute_on(game_state) self.assertEqual(card in valid_cards, is_legal_card, msg=f"{card}") self.assertEqual(is_legal_card, action.can_execute_on(game_state.next_player_view())) if is_legal_card: num_legal_cards += 1 self.assertEqual(2, num_legal_cards)
def test_must_follow_suit_can_play_any_higher_card_same_suit(self): """ Player.ONE has two cards of the same suit as the card played by Player.TWO, both higher. Player.ONE also has a trump card. Any of the two cards from the same suit as the card played by Player.TWO are valid. """ game_state = get_game_state_for_tests() with GameStateValidator(game_state): queen_hearts = game_state.cards_in_hand.one[0] queen_clubs = game_state.cards_in_hand.two[4] game_state.cards_in_hand.two[4] = queen_hearts game_state.cards_in_hand.one[0] = queen_clubs game_state.close_talon() game_state.next_player = PlayerId.TWO self.assertTrue(game_state.must_follow_suit()) jack_spades = Card(Suit.SPADES, CardValue.JACK) action = PlayCardAction(PlayerId.TWO, jack_spades) self.assertTrue(action.can_execute_on(game_state)) game_state = action.execute(game_state) num_legal_cards = 0 for card in game_state.cards_in_hand[PlayerId.ONE]: action = PlayCardAction(PlayerId.ONE, card) is_legal_card = action.can_execute_on(game_state) self.assertEqual(card.suit == Suit.SPADES, is_legal_card, msg=f"{card}") self.assertEqual(is_legal_card, action.can_execute_on(game_state.next_player_view())) if is_legal_card: num_legal_cards += 1 self.assertEqual(2, num_legal_cards)
def test_leaf_node(self): game_state = _get_game_state_with_one_card_left() play_jack_clubs = PlayCardAction(PlayerId.ONE, Card(Suit.CLUBS, CardValue.JACK)) game_state = play_jack_clubs.execute(game_state) mcts = Mcts(PlayerId.TWO) root_node = mcts.build_tree(game_state) self.assertIsNone(root_node.parent) self.assertEqual(1, len(root_node.children)) self.assertEqual([], root_node.untried_actions) self.assertTrue(root_node.fully_expanded) self.assertFalse(root_node.terminal) self.assertEqual(PlayerId.TWO, root_node.player) self.assertFalse(root_node.fully_simulated) action = list(root_node.children.keys())[0] self.assertEqual( PlayCardAction(PlayerId.TWO, Card(Suit.SPADES, CardValue.JACK)), action) leaf: Node = root_node.children[action] self.assertIs(root_node, leaf.parent) self.assertIsNone(leaf.children) self.assertIsNone(leaf.untried_actions) self.assertTrue(leaf.fully_expanded) self.assertTrue(leaf.terminal) self.assertEqual(PlayerId.ONE, leaf.player) self.assertAlmostEqual(0.33, leaf.ucb, delta=0.01) self.assertTrue(leaf.fully_simulated)
def test_elimination_play_player_two(self): game_state = get_game_state_for_elimination_play_puzzle() action = PlayCardAction(PlayerId.ONE, Card(Suit.CLUBS, CardValue.JACK)) game_state = action.execute(game_state) action = PlayCardAction(PlayerId.TWO, Card(Suit.CLUBS, CardValue.KING)) game_state = action.execute(game_state) self.assertEqual(34, game_state.trick_points.one) self.assertEqual(40, game_state.trick_points.two) mcts = Mcts(PlayerId.TWO) root_node = mcts.build_tree(game_state) for action, child in root_node.children.items(): print(action, child) for action, child in root_node.children.items(): self.assertTrue(child.fully_simulated) self.assertEqual(PlayerId.ONE, child.player) self.assertAlmostEqual(0.33, child.ucb, delta=0.01, msg=action)
def test_must_follow_trump_can_discard_any_card(self): game_state = get_game_state_for_tests() with GameStateValidator(game_state): game_state.next_player = PlayerId.TWO game_state.close_talon() action = PlayCardAction(PlayerId.TWO, game_state.cards_in_hand[PlayerId.TWO][0]) game_state = action.execute(game_state) for card in game_state.cards_in_hand[PlayerId.ONE]: action = PlayCardAction(PlayerId.ONE, card) self.assertTrue(action.can_execute_on(game_state)) self.assertTrue(action.can_execute_on(game_state.next_player_view()))
def test_play_trick_talon_is_empty(self): game_state = get_game_state_with_empty_talon_for_tests() ace_clubs = game_state.cards_in_hand[PlayerId.ONE][0] action = PlayCardAction(PlayerId.ONE, ace_clubs) game_state = action.execute(game_state) self.assertEqual(ace_clubs, game_state.current_trick[PlayerId.ONE]) self.assertEqual(PlayerId.TWO, game_state.next_player) jack_clubs = game_state.cards_in_hand[PlayerId.TWO][2] action = PlayCardAction(PlayerId.TWO, jack_clubs) game_state = action.execute(game_state) self.assertEqual(PlayerPair(None, None), game_state.current_trick) self.assertEqual(PlayerPair(48, 59), game_state.trick_points) self.assertEqual(PlayerPair(ace_clubs, jack_clubs), game_state.won_tricks[PlayerId.ONE][-1]) self.assertFalse(ace_clubs in game_state.cards_in_hand[PlayerId.ONE]) self.assertFalse(jack_clubs in game_state.cards_in_hand[PlayerId.TWO]) self.assertEqual(3, len(game_state.cards_in_hand[PlayerId.ONE])) self.assertEqual(3, len(game_state.cards_in_hand[PlayerId.TWO])) self.assertEqual(PlayerId.ONE, game_state.next_player)
def test_play_trick_talon_is_closed(self): game_state = get_game_state_for_tests() game_state.close_talon() queen_hearts = game_state.cards_in_hand[PlayerId.ONE][0] action = PlayCardAction(PlayerId.ONE, queen_hearts) game_state = action.execute(game_state) self.assertEqual(queen_hearts, game_state.current_trick[PlayerId.ONE]) self.assertEqual(PlayerId.TWO, game_state.next_player) queen_clubs = game_state.cards_in_hand[PlayerId.TWO][4] action = PlayCardAction(PlayerId.TWO, queen_clubs) game_state = action.execute(game_state) self.assertEqual(PlayerPair(None, None), game_state.current_trick) self.assertEqual(PlayerPair(22, 59), game_state.trick_points) self.assertEqual(PlayerPair(queen_hearts, queen_clubs), game_state.won_tricks[PlayerId.TWO][-1]) self.assertFalse(queen_hearts in game_state.cards_in_hand[PlayerId.ONE]) self.assertFalse(queen_clubs in game_state.cards_in_hand[PlayerId.TWO]) self.assertEqual(4, len(game_state.cards_in_hand[PlayerId.ONE])) self.assertEqual(4, len(game_state.cards_in_hand[PlayerId.TWO])) self.assertEqual(PlayerId.TWO, game_state.next_player)
def test_play_trick_talon_closed_opponent_cannot_follow_suit(self): game_state = get_game_state_for_tests() with GameStateValidator(game_state): trick = game_state.won_tricks.two.pop(0) game_state.talon.append(trick.one) game_state.talon.append(trick.two) trick = game_state.won_tricks.two.pop(0) game_state.talon.append(trick.one) game_state.talon.append(trick.two) game_state.trick_points.two = 0 game_state.close_talon() self.assertEqual([False, False, False, False, False], [card.public for card in game_state.talon]) action = PlayCardAction(PlayerId.ONE, Card(Suit.HEARTS, CardValue.TEN)) self.assertTrue(action.can_execute_on(game_state)) game_state = action.execute(game_state) self.assertEqual([False, False, False, False, False], [card.public for card in game_state.talon]) action = PlayCardAction(PlayerId.TWO, Card(Suit.CLUBS, CardValue.JACK)) self.assertTrue(action.can_execute_on(game_state)) game_state = action.execute(game_state) self.assertEqual([False, True, True, False, False], [card.public for card in game_state.talon])
def test_can_only_execute_on_players_turn(self): game_state = get_game_state_for_tests() card = game_state.cards_in_hand[PlayerId.ONE][0] action_one = PlayCardAction(PlayerId.ONE, card) self.assertTrue(action_one.can_execute_on(game_state)) self.assertTrue(action_one.can_execute_on(game_state.next_player_view())) action_two = PlayCardAction(PlayerId.TWO, game_state.cards_in_hand[PlayerId.TWO][0]) self.assertFalse(action_two.can_execute_on(game_state)) self.assertFalse(action_two.can_execute_on(game_state.next_player_view())) game_state = action_one.execute(game_state) self.assertFalse(action_one.can_execute_on(game_state)) self.assertFalse(action_one.can_execute_on(game_state.next_player_view())) self.assertTrue(action_two.can_execute_on(game_state)) self.assertTrue(action_two.can_execute_on(game_state.next_player_view()))
def test_know_your_opponent_puzzle(self): game_state = get_game_state_for_know_your_opponent_puzzle() game_state = self._play_against_another_mcts_player_until_the_end( game_state) self.assertEqual(0, game_state.game_points.two) # If we just run the MctsPlayer for Player.TWO, it doesn't play the Ace # of Spades as the second card to challenge Player.ONE as described in the # puzzle, so we run this scenario manually here. game_state = get_game_state_for_know_your_opponent_puzzle() action = PlayCardAction(PlayerId.ONE, Card(Suit.HEARTS, CardValue.KING)) game_state = action.execute(game_state) action = PlayCardAction(PlayerId.TWO, Card(Suit.SPADES, CardValue.ACE)) game_state = action.execute(game_state) action = self._mcts_player.request_next_action( game_state.next_player_view()) self.assertEqual( PlayCardAction(PlayerId.ONE, Card(Suit.SPADES, CardValue.KING)), action) game_state = action.execute(game_state) game_state = self._play_against_another_mcts_player_until_the_end( game_state) self.assertEqual(0, game_state.game_points.two)
def test_play_trick_player_two_wins(self): game_state = get_game_state_for_tests() trump_card = game_state.trump_card last_talon_card = game_state.talon[0] queen_hearts = game_state.cards_in_hand[PlayerId.ONE][0] action = PlayCardAction(PlayerId.ONE, queen_hearts) game_state = action.execute(game_state) self.assertEqual(queen_hearts, game_state.current_trick[PlayerId.ONE]) self.assertEqual(PlayerId.TWO, game_state.next_player) jack_clubs = game_state.cards_in_hand[PlayerId.TWO][2] action = PlayCardAction(PlayerId.TWO, jack_clubs) game_state = action.execute(game_state) self.assertEqual(PlayerPair(None, None), game_state.current_trick) self.assertEqual(PlayerPair(22, 58), game_state.trick_points) self.assertEqual(PlayerPair(queen_hearts, jack_clubs), game_state.won_tricks[PlayerId.TWO][-1]) self.assertFalse(queen_hearts in game_state.cards_in_hand[PlayerId.ONE]) self.assertFalse(jack_clubs in game_state.cards_in_hand[PlayerId.TWO]) self.assertTrue(trump_card in game_state.cards_in_hand[PlayerId.ONE]) self.assertTrue(last_talon_card in game_state.cards_in_hand[PlayerId.TWO]) self.assertEqual([], game_state.talon) self.assertIsNone(game_state.trump_card) self.assertEqual(PlayerId.TWO, game_state.next_player)
def test_cannot_play_cards_not_in_hand(self): game_state = get_game_state_for_tests() for card in Card.get_all_cards(): action = PlayCardAction(PlayerId.ONE, card) self.assertEqual(card in game_state.cards_in_hand[PlayerId.ONE], action.can_execute_on(game_state)) self.assertEqual(card in game_state.cards_in_hand[PlayerId.ONE], action.can_execute_on(game_state.next_player_view())) action = PlayCardAction(PlayerId.ONE, game_state.cards_in_hand[PlayerId.ONE][0]) self.assertTrue(action.can_execute_on(game_state)) self.assertTrue(action.can_execute_on(game_state.next_player_view())) game_state = action.execute(game_state) for card in Card.get_all_cards(): action = PlayCardAction(PlayerId.TWO, card) self.assertEqual(card in game_state.cards_in_hand[PlayerId.TWO], action.can_execute_on(game_state)) self.assertEqual(card in game_state.cards_in_hand[PlayerId.TWO], action.can_execute_on(game_state.next_player_view()))
def test_must_follow_suit_must_use_trump(self): """Player.TWO has no hearts, so they must use one of the trump cards.""" game_state = get_game_state_for_tests() game_state.close_talon() self.assertTrue(game_state.must_follow_suit()) action = PlayCardAction(PlayerId.ONE, game_state.cards_in_hand[PlayerId.ONE][0]) self.assertTrue(action.can_execute_on(game_state)) game_state = action.execute(game_state) num_legal_cards = 0 for card in game_state.cards_in_hand[PlayerId.TWO]: action = PlayCardAction(PlayerId.TWO, card) is_legal_card = action.can_execute_on(game_state) self.assertEqual(card.suit == game_state.trump, is_legal_card, msg=f"{card}") self.assertEqual(is_legal_card, action.can_execute_on(game_state.next_player_view())) if is_legal_card: num_legal_cards += 1 self.assertEqual(3, num_legal_cards)
def test_must_follow_suit_cannot_play_lower_card_same_suit(self): """ Player.ONE has two cards of the same suit as the card played by Player.TWO, one lower and one higher. Player.ONE also has a trump card. The only valid card is the higher card having the same suit as the card played by Player.TWO. """ game_state = get_game_state_for_tests() with GameStateValidator(game_state): ten_spades = game_state.cards_in_hand.one[3] jack_spades = game_state.cards_in_hand.two[3] game_state.cards_in_hand.two[3] = ten_spades game_state.cards_in_hand.one[3] = jack_spades queen_hearts = game_state.cards_in_hand.one[0] queen_clubs = game_state.cards_in_hand.two[4] game_state.cards_in_hand.two[4] = queen_hearts game_state.cards_in_hand.one[0] = queen_clubs game_state.close_talon() game_state.next_player = PlayerId.TWO self.assertTrue(game_state.must_follow_suit()) action = PlayCardAction(PlayerId.TWO, ten_spades) self.assertTrue(action.can_execute_on(game_state)) game_state = action.execute(game_state) ace_spades = Card(Suit.SPADES, CardValue.ACE) num_legal_cards = 0 for card in game_state.cards_in_hand[PlayerId.ONE]: action = PlayCardAction(PlayerId.ONE, card) is_legal_card = action.can_execute_on(game_state) self.assertEqual(card == ace_spades, is_legal_card, msg=f"{card}") self.assertEqual(is_legal_card, action.can_execute_on(game_state.next_player_view())) if is_legal_card: num_legal_cards += 1 self.assertEqual(1, num_legal_cards)