def test_invalid_when_entering(self): game_state = get_game_state_for_tests() game_state.trick_points = PlayerPair(0, 0) with self.assertRaisesRegex(InvalidGameStateError, "Invalid trick points"): with GameStateValidator(game_state): pass with self.assertRaisesRegex(InvalidGameStateError, "Invalid trick points"): with GameStateValidator(game_state): # This would make the game_state valid. game_state.trick_points = PlayerPair(22, 53)
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_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_actions_when_player_is_to_lead_talon_is_empty(self): game_state = get_game_state_with_empty_talon_for_tests() 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)), PlayCardAction(PlayerId.ONE, Card(Suit.HEARTS, CardValue.KING)), PlayCardAction(PlayerId.ONE, Card(Suit.HEARTS, CardValue.TEN)), PlayCardAction(PlayerId.ONE, Card(Suit.SPADES, CardValue.TEN)), ] 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 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.DIAMONDS, CardValue.JACK)), AnnounceMarriageAction(PlayerId.TWO, Card(Suit.CLUBS, CardValue.KING)), PlayCardAction(PlayerId.TWO, Card(Suit.CLUBS, CardValue.JACK)), AnnounceMarriageAction(PlayerId.TWO, Card(Suit.CLUBS, CardValue.QUEEN)), ] self.assertSetEqual(set(expected_actions), set(actions))
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_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_both_cards_must_be_in_hand(self): game_state = get_game_state_for_tests() with GameStateValidator(game_state): game_state.next_player = PlayerId.TWO queen_diamonds = Card(Suit.DIAMONDS, CardValue.QUEEN) action = AnnounceMarriageAction(PlayerId.TWO, queen_diamonds) self.assertFalse(action.can_execute_on(game_state)) self.assertFalse(action.can_execute_on(game_state.next_player_view())) # Swap the queen of spades with the trump card with GameStateValidator(game_state): queen_spades = game_state.cards_in_hand[PlayerId.TWO].pop() game_state.cards_in_hand[PlayerId.TWO].append(game_state.trump_card) game_state.trump_card = queen_spades game_state.trump_card.public = True action = AnnounceMarriageAction(PlayerId.TWO, queen_spades) self.assertFalse(action.can_execute_on(game_state)) self.assertFalse(action.can_execute_on(game_state.next_player_view()))
def get_game_state_with_multiple_cards_in_the_talon_for_tests() -> GameState: game_state = get_game_state_for_tests() with GameStateValidator(game_state): trick = game_state.won_tricks[PlayerId.TWO].pop() 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]) return game_state
def test_invalid_when_exiting_other_exception_is_raised(self): game_state = get_game_state_for_tests() with self.assertRaisesRegex(InvalidGameStateError, "Invalid trick points"): with GameStateValidator(game_state): game_state.trick_points = PlayerPair(0, 0) raise ValueError( "This will be replaced by InvalidGameStateError")
def test_no_actions_available_when_game_is_over(self): game_state = get_game_state_with_all_tricks_played() self.assertEqual([], get_available_actions(game_state)) self.assertEqual([], get_available_actions(game_state.next_player_view())) with GameStateValidator(game_state): game_state.next_player = PlayerId.TWO self.assertEqual([], get_available_actions(game_state)) self.assertEqual([], get_available_actions(game_state.next_player_view()))
def _get_game_state_with_one_card_left() -> GameState: game_state = get_game_state_with_all_tricks_played() with GameStateValidator(game_state): trick = game_state.won_tricks.one.pop(-1) game_state.trick_points.one -= trick.one.card_value game_state.trick_points.one -= trick.two.card_value game_state.cards_in_hand.one.append(trick.one) game_state.cards_in_hand.two.append(trick.two) game_state.next_player = PlayerId.ONE return game_state
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_announcing_marriage_is_enough_to_win_the_game(self): game_state = get_game_state_for_tests() with GameStateValidator(game_state): game_state.next_player = PlayerId.TWO king_clubs = Card(Suit.CLUBS, CardValue.KING) action = AnnounceMarriageAction(PlayerId.TWO, king_clubs) self.assertTrue(action.can_execute_on(game_state)) game_state = action.execute(game_state) queen_clubs = game_state.cards_in_hand[PlayerId.TWO][4] self.assertTrue(queen_clubs.public) self.assertEqual(93, game_state.trick_points[PlayerId.TWO]) self.assertTrue(game_state.is_game_over)
def test_announce_trump_marriage(self): game_state = get_game_state_for_tests() with GameStateValidator(game_state): game_state.next_player = PlayerId.TWO queen_clubs = Card(Suit.CLUBS, CardValue.QUEEN) action = AnnounceMarriageAction(PlayerId.TWO, queen_clubs) self.assertTrue(action.can_execute_on(game_state)) game_state = action.execute(game_state) self.assertEqual(93, game_state.trick_points[PlayerId.TWO]) self.assertEqual(queen_clubs, game_state.current_trick[PlayerId.TWO]) self.assertEqual([Suit.DIAMONDS, Suit.CLUBS], game_state.marriage_suits[PlayerId.TWO]) self.assertEqual(PlayerId.ONE, game_state.next_player) king_clubs = game_state.cards_in_hand[PlayerId.TWO][1] self.assertTrue(king_clubs.public)
def test_announce_marriage_without_scoring_any_trick(self): game_state = get_game_state_for_tests() with GameStateValidator(game_state): for trick in game_state.won_tricks[PlayerId.ONE]: game_state.talon.extend([trick.one, trick.two]) for trick in game_state.won_tricks[PlayerId.TWO]: game_state.talon.extend([trick.one, trick.two]) game_state.won_tricks = PlayerPair([], []) game_state.trick_points = PlayerPair(0, 0) game_state.marriage_suits[PlayerId.TWO] = [] queen_hearts = Card(Suit.HEARTS, CardValue.QUEEN) action = AnnounceMarriageAction(PlayerId.ONE, queen_hearts) self.assertTrue(action.can_execute_on(game_state)) game_state = action.execute(game_state) self.assertEqual(0, game_state.trick_points[PlayerId.ONE]) self.assertEqual(queen_hearts, game_state.current_trick[PlayerId.ONE]) self.assertEqual([Suit.HEARTS], game_state.marriage_suits[PlayerId.ONE]) self.assertEqual(PlayerId.TWO, game_state.next_player) king_hearts = game_state.cards_in_hand[PlayerId.ONE][1] self.assertTrue(king_hearts.public)
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)
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_no_changes(): game_state = get_game_state_for_tests() with GameStateValidator(game_state): pass
def test_invalid_when_exiting(self): game_state = get_game_state_for_tests() with self.assertRaisesRegex(InvalidGameStateError, "Invalid trick points"): with GameStateValidator(game_state): game_state.trick_points = PlayerPair(0, 0)
def test_valid_when_exiting_other_exception_is_raised(self): game_state = get_game_state_for_tests() with self.assertRaisesRegex(ValueError, "This should be reraised"): with GameStateValidator(game_state): game_state.next_player = game_state.next_player.opponent() raise ValueError("This should be reraised")
def test_valid_changes(): game_state = get_game_state_for_tests() with GameStateValidator(game_state): game_state.next_player = game_state.next_player.opponent()