def test_won_tricks(self): # Cannot win with the same suit, but smaller card. trick = self.game_state.won_tricks.one[0] # K♠, Q♠ swapped_trick = PlayerPair(one=trick.two, two=trick.one) self.game_state.won_tricks.one[0] = swapped_trick with self.assertRaisesRegex(InvalidGameStateError, "ONE cannot win this trick: Q♠, K♠"): validate(self.game_state) # Cannot win with non-trump card against a trump card. self.game_state = get_game_state_for_tests() self.game_state.won_tricks.one.append( self.game_state.won_tricks.two.pop()) self.game_state.trick_points = PlayerPair(42, 33) with self.assertRaisesRegex(InvalidGameStateError, "ONE cannot win this trick: X♦, X♣"): validate(self.game_state) # Different suits, no trump. Could be valid wins for both players, since we # don't know who played the first card. self.game_state = get_game_state_for_tests() card = self.game_state.won_tricks.two[-1].two self.game_state.won_tricks.two[-1].two = \ self.game_state.cards_in_hand.one[3] self.game_state.cards_in_hand.one[3] = card validate(self.game_state) self.game_state.won_tricks.one.append( self.game_state.won_tricks.two.pop()) self.game_state.trick_points = PlayerPair(42, 33) validate(self.game_state)
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_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 __init__(self, game_options: Optional[GameOptions] = None, **kwargs): """ Instantiates a new GameWidget and all its children widgets. All the widgets are empty (i.e., no cards). """ # Store or initialize the game options. This has to be done before the call # to Widget.__init__() so the options are available when the game_widget.kv # file is parsed. self._game_options = game_options or GameOptions() super().__init__(**kwargs) # Dictionary used to store all the cards widgets. self._cards: Dict[Card, CardWidget] = {} # A reference to the area where the cards are moved when one player plays a # card. self._play_area = self.ids.play_area.__self__ # Store the current play area size in order to update the position of the # already played cards accordingly, when the window is resized. self._prev_play_area_size = self._play_area.size[ 0], self._play_area.size[1] self._prev_play_area_pos = self._play_area.pos[0], self._play_area.pos[ 1] self._play_area.bind(size=lambda *_: self._update_play_area_cards()) # The cards in the players' hands, sorted in display order. self._sorted_cards: Optional[PlayerPair[List[Card]]] = None # Widgets that store the cards. self._player_card_widgets: Optional[PlayerPair[CardSlotsLayout]] = None self._tricks_widgets: Optional[PlayerPair[CardSlotsLayout]] = None self._talon: Optional[TalonWidget] = None # Image that displays the trump suit, if the talon is empty. self._trump_suit_image: Optional[Image] = None # Labels used to display the trick points and game points. self._trick_score_labels: PlayerPair[Label] = PlayerPair( one=self.ids.human_trick_score_label.__self__, two=self.ids.computer_trick_score_label.__self__) self._game_score_labels: PlayerPair[Label] = PlayerPair( one=self.ids.human_game_score_label.__self__, two=self.ids.computer_game_score_label.__self__) # Stores the callback that is passed by the GameController when it requests # a new player action. self._action_callback: Optional[Callable[[PlayerAction], None]] = None # When a player action is requested, this dict stores the default action # associated to each card that can be double clicked. self._actions: Dict[CardWidget, PlayerAction] = {} # AnimationController that coordinates all card animations. self._animation_controller = AnimationController() self.fbind('size', self._cancel_animations) self._init_widgets()
def test_stop_is_called_with_pending_callback(self): self.render(None) game_widget = Mock() players = PlayerPair(Mock(), Mock()) players.one.is_cheater.return_value = False players.two.is_cheater.return_value = False score_view = Mock() # noinspection PyTypeChecker game_controller = GameController(game_widget, players, score_view) bummerl = Bummerl(next_dealer=PlayerId.ONE) bummerl.start_game(seed=2) actions = get_actions_for_one_complete_game(PlayerId.TWO) expected_game_state = GameState.new(dealer=PlayerId.ONE, random_seed=2) game_controller.start(bummerl) # Initializes the game widget. game_widget.reset.assert_called_once() game_widget.init_from_game_state.assert_called_once() actual_game_state, done_callback, game_points = \ game_widget.init_from_game_state.call_args.args self.assertEqual(expected_game_state, actual_game_state) self.assertEqual(PlayerPair(0, 0), game_points) game_widget.reset_mock() done_callback() # Player action is requested. action = actions[0] players[action.player_id].request_next_action.assert_called_once() actual_game_state, action_callback, actual_game_points = \ players[action.player_id].request_next_action.call_args.args self.assertEqual(bummerl.game_points, actual_game_points) expected_game_state = action.execute(expected_game_state) players[action.player_id].reset_mock() # GameController.stop() is called before the GameWidget calls # action_callback(). game_controller.stop() # Player responds with an action. action_callback(action) # GameWidget.on_action() is called to update the UI. game_widget.on_action.assert_called_once() actual_action, done_callback = game_widget.on_action.call_args.args self.assertEqual(action, actual_action) game_widget.reset_mock() done_callback() # The GameController requests no action from the next player. next_player = expected_game_state.next_player players[next_player].request_next_action.assert_not_called()
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_pickling(self): game = Game(PlayerId.ONE, seed=2) self.assertEqual(Suit.DIAMONDS, game.game_state.trump) actions = [ # Player TWO wins the first trick. Score: 0-6. PlayCardAction(PlayerId.TWO, Card(Suit.HEARTS, CardValue.JACK)), PlayCardAction(PlayerId.ONE, Card(Suit.CLUBS, CardValue.KING)), # Player TWO closes the talon. CloseTheTalonAction(PlayerId.TWO), # Player TWO wins the second trick. Score: 0-18. PlayCardAction(PlayerId.TWO, Card(Suit.DIAMONDS, CardValue.TEN)), PlayCardAction(PlayerId.ONE, Card(Suit.DIAMONDS, CardValue.JACK)), # Player TWO wins the third trick. Score: 0-31. PlayCardAction(PlayerId.TWO, Card(Suit.HEARTS, CardValue.TEN)), PlayCardAction(PlayerId.ONE, Card(Suit.SPADES, CardValue.QUEEN)), # Player ONE wins the forth trick. Score: 13-31. PlayCardAction(PlayerId.TWO, Card(Suit.CLUBS, CardValue.JACK)), PlayCardAction(PlayerId.ONE, Card(Suit.CLUBS, CardValue.ACE)), # Player TWO wins the fifth trick. Score: 13-52. PlayCardAction(PlayerId.ONE, Card(Suit.SPADES, CardValue.TEN)), PlayCardAction(PlayerId.TWO, Card(Suit.SPADES, CardValue.ACE)), # Player TWO wins the sixth trick. Score: 13-67. PlayCardAction(PlayerId.TWO, Card(Suit.HEARTS, CardValue.ACE)), PlayCardAction(PlayerId.ONE, Card(Suit.SPADES, CardValue.KING)) ] for action in actions[:8]: game.play_action(action) self.assertFalse(game.game_state.is_game_over) self.assertEqual(PlayerPair(0, 31), game.game_state.trick_points) self.assertFalse(game.game_state.is_to_lead(PlayerId.ONE)) self.assertFalse(game.game_state.is_to_lead(PlayerId.TWO)) unpickled_game: Game = loads(dumps(game)) self.assertEqual(unpickled_game.game_state, game.game_state) self.assertFalse(unpickled_game.game_state.is_game_over) self.assertFalse(unpickled_game.game_state.is_to_lead(PlayerId.ONE)) for action in actions[8:]: unpickled_game.play_action(action) self.assertTrue(unpickled_game.game_state.is_game_over) self.assertEqual(PlayerPair(13, 67), unpickled_game.game_state.trick_points) self.assertEqual(PlayerPair(0, 3), unpickled_game.game_state.game_points)
def test_two_bummerl_with_random_players(self): class TestScoreViewWithBummerlCount: # pylint: disable=too-few-public-methods def __init__(self, done_callback): self._num_bummerls = 0 self._done_callback = done_callback def score_view_delegate(self, score_history, dismiss_callback): total_game_points = PlayerPair(0, 0) for _, game_points in score_history: total_game_points.one += game_points.one total_game_points.two += game_points.two if total_game_points.one > 6 or total_game_points.two > 6: self._num_bummerls += 1 if self._num_bummerls < 2: dismiss_callback() else: self._done_callback() game_widget = GameWidget(GameOptions(enable_animations=False)) players = PlayerPair(ComputerPlayer(RandomPlayer(PlayerId.ONE)), ComputerPlayer(RandomPlayer(PlayerId.TWO))) two_bummerls_played = Mock() score_view = TestScoreViewWithBummerlCount(two_bummerls_played) self.render(game_widget) # noinspection PyTypeChecker game_controller = GameController(game_widget, players, score_view.score_view_delegate, 0) game_controller.start() self.wait_for_mock_callback(two_bummerls_played, timeout_seconds=60) game_controller.stop()
def _get_bummerl_score(bummerl, game_index): game_points = PlayerPair(0, 0) for i in range(game_index): result = bummerl.completed_games[i].game_state.game_points game_points.one += result.one game_points.two += result.two return game_points
def is_to_lead(self, player_id): """ Checks if we are at the beginning of a trick and the given player is expected to make the next move. If a player is to lead, it can exchange the trump card or close the talon. """ return self.next_player == player_id and self.current_trick == PlayerPair( None, None)
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 deep_copy(self) -> "GameState": """ Creates and returns a deep copy of this game state. This is faster than using copy.deepcopy(), see GameStateCopyTest.test_deep_copy_alternatives*. """ def copy_card(card: Card) -> Card: return None if card is None else card.copy() cards_in_hand = PlayerPair( one=[copy_card(card) for card in self.cards_in_hand.one], two=[copy_card(card) for card in self.cards_in_hand.two]) talon = [copy_card(card) for card in self.talon] won_tricks = PlayerPair( one=[PlayerPair(copy_card(trick.one), copy_card(trick.two)) for trick in self.won_tricks.one], two=[PlayerPair(copy_card(trick.one), copy_card(trick.two)) for trick in self.won_tricks.two]) marriage_suits = PlayerPair(one=list(self.marriage_suits.one), two=list(self.marriage_suits.two)) return GameState( cards_in_hand=cards_in_hand, talon=talon, trump=self.trump, trump_card=copy_card(self.trump_card), next_player=self.next_player, player_that_closed_the_talon=self.player_that_closed_the_talon, opponent_points_when_talon_was_closed= self.opponent_points_when_talon_was_closed, won_tricks=won_tricks, marriage_suits=marriage_suits, trick_points=PlayerPair(self.trick_points.one, self.trick_points.two), current_trick=PlayerPair(copy_card(self.current_trick.one), copy_card(self.current_trick.two)))
def test_auto_save_folder(self): with tempfile.TemporaryDirectory() as auto_save_folder: saved_games = [] def _verify_auto_save_data(): bummerl_filename = os.path.join(auto_save_folder, "autosave_bummerl.pickle") with open(bummerl_filename, "rb") as input_file: bummerl = pickle.load(input_file) self.assertEqual(len(saved_games), len(bummerl.completed_games)) for i, game in enumerate(bummerl.completed_games): self._assert_game_equal(game, saved_games[i]) class TestScoreViewWithBummerlCount: # pylint: disable=too-few-public-methods def __init__(self, done_callback, auto_save_folder): self._num_bummerls = 0 self._done_callback = done_callback self._auto_save_folder = auto_save_folder def score_view_delegate(self, score_history, dismiss_callback): game_filename = os.path.join(self._auto_save_folder, "autosave_game.pickle") with open(game_filename, "rb") as input_file: game = pickle.load(input_file) saved_games.append(game) _verify_auto_save_data() total_game_points = PlayerPair(0, 0) for _, game_points in score_history: total_game_points.one += game_points.one total_game_points.two += game_points.two if total_game_points.one > 6 or total_game_points.two > 6: self._num_bummerls += 1 if self._num_bummerls >= 1: dismiss_callback() else: self._done_callback() game_widget = GameWidget(GameOptions(enable_animations=False)) players = PlayerPair(ComputerPlayer(RandomPlayer(PlayerId.ONE)), ComputerPlayer(RandomPlayer(PlayerId.TWO))) one_bummerl_played = Mock() score_view = TestScoreViewWithBummerlCount(one_bummerl_played, auto_save_folder) self.render(game_widget) # noinspection PyTypeChecker game_controller = GameController(game_widget, players, score_view.score_view_delegate, 0, auto_save_folder) game_controller.start() self.wait_for_mock_callback(one_bummerl_played, timeout_seconds=60) game_controller.stop()
def execute(self, game_state: GameState) -> GameState: assert self.can_execute_on(game_state) new_game_state = copy.copy(game_state) trump_jack = Card(suit=game_state.trump, card_value=CardValue.JACK) new_player_cards = [ card for card in game_state.cards_in_hand[self.player_id] if card != trump_jack ] new_player_cards.append(new_game_state.trump_card) if self.player_id == PlayerId.ONE: cards_in_hand = PlayerPair(new_player_cards, game_state.cards_in_hand.two) else: cards_in_hand = PlayerPair(game_state.cards_in_hand.one, new_player_cards) new_game_state.cards_in_hand = cards_in_hand new_game_state.trump_card = trump_jack new_game_state.trump_card.public = True return new_game_state
def test_pickling(self): bummerl = Bummerl(next_dealer=PlayerId.ONE) _simulate_a_complete_game(bummerl.start_game(seed=2)) bummerl.finalize_game() _simulate_a_complete_game(bummerl.start_game(seed=2)) unpickled_bummerl = loads(dumps(bummerl)) self.assertIsNot(bummerl, unpickled_bummerl) self.assertIsNotNone(unpickled_bummerl.game) self.assertEqual(PlayerPair(0, 3), unpickled_bummerl.game_points) self.assertEqual(1, len(unpickled_bummerl.completed_games)) self.assertFalse(unpickled_bummerl.is_over) unpickled_bummerl.finalize_game() self.assertIsNone(unpickled_bummerl.game) self.assertEqual(PlayerPair(3, 3), unpickled_bummerl.game_points) self.assertEqual(2, len(unpickled_bummerl.completed_games)) self.assertFalse(unpickled_bummerl.is_over)
def evaluate_bummerl( bummerl: Bummerl, bummerl_id: str = "0", options: Optional[MctsPlayerOptions] = None) -> EvalResults: options = options or MctsPlayerOptions( num_processes=1, max_permutations=150, max_iterations=667, merge_scoring_info_func=average_score_with_tiebreakers) players = PlayerPair(CythonMctsPlayer(PlayerId.ONE, False, options), CythonMctsPlayer(PlayerId.TWO, False, options)) bummerl_score = PlayerPair(0, 0) eval_results = [] for game_id, game in enumerate(bummerl.completed_games): eval_results.extend( evaluate_game(game, players, bummerl_score, bummerl_id, game_id)) bummerl_score.one += game.game_state.game_points.one bummerl_score.two += game.game_state.game_points.two return eval_results
def _main(): # filename = "../autosave_bummerl.pickle" # with open(filename, "rb") as input_file: # bummerl = pickle.load(input_file) # results = evaluate_bummerl(bummerl) # print_eval_results(results, None) options = MctsPlayerOptions( num_processes=1, max_permutations=150, max_iterations=667, merge_scoring_info_func=average_score_with_tiebreakers) players = PlayerPair(CythonMctsPlayer(PlayerId.ONE, False, options), CythonMctsPlayer(PlayerId.TWO, False, options)) bummerl_score = PlayerPair(0, 0) with open("../autosave_game.pickle", "rb") as input_file: game = pickle.load(input_file) print_eval_results(evaluate_game(game, players, bummerl_score, "0", "0"), None)
def print_eval_results(eval_results: EvalResults, restrict_to_player_id: Optional[PlayerId]): column_widths = _get_column_widths(eval_results) num_actions = PlayerPair(0, 0) sum_scores = PlayerPair(0, 0) for eval_result in eval_results: player_id = eval_result[2].player_id if restrict_to_player_id is not None and player_id != restrict_to_player_id: continue _print_eval_result(eval_result, column_widths) if eval_result[3] is not None: num_actions[player_id] += 1 sum_scores[player_id] += eval_result[3] print() print("Overall performance:") if num_actions.one != 0: print("\tONE:\t" + ("{:.5f}".format(sum_scores.one / num_actions.one))) if num_actions.two != 0: print("\tTWO:\t" + ("{:.5f}".format(sum_scores.two / num_actions.two)))
def test_who_laughs_last_puzzle(self): # Part one game_state = get_game_state_for_who_laughs_last_puzzle() action = self._mcts_player.request_next_action( game_state.next_player_view()) expected_actions = { PlayCardAction(PlayerId.ONE, Card(Suit.HEARTS, CardValue.KING)), PlayCardAction(PlayerId.ONE, Card(Suit.HEARTS, CardValue.QUEEN)) } self.assertIn(action, expected_actions) game_state = action.execute(game_state) if action.card.card_value == CardValue.KING: self.assertEqual(PlayerPair(19, 26), game_state.trick_points) else: self.assertEqual(PlayerPair(18, 26), game_state.trick_points) # Part two game_state = self._play_against_another_mcts_player_until_the_end( game_state) self.assertEqual(0, game_state.game_points.two)
def test_pair_of_lists(self): test_pair: PlayerPair[List[int]] = PlayerPair() self.assertIsNone(test_pair.one) self.assertIsNone(test_pair.two) test_pair.one = [1, 2, 3] self.assertEqual([1, 2, 3], test_pair.one) self.assertIsNone(test_pair.two) test_pair.two = [2, 3, 4] self.assertEqual([1, 2, 3], test_pair.one) self.assertEqual([2, 3, 4], test_pair.two) another_test_pair: PlayerPair[List[int]] = PlayerPair() self.assertIsNone(another_test_pair.one) self.assertIsNone(another_test_pair.two) self.assertEqual([1, 2, 3], test_pair.one) self.assertEqual([2, 3, 4], test_pair.two) another_test_pair.one = [10, 20, 30] self.assertEqual([10, 20, 30], another_test_pair.one) self.assertIsNone(another_test_pair.two) self.assertEqual([1, 2, 3], test_pair.one) self.assertEqual([2, 3, 4], test_pair.two)
def test_pair_of_ints(self): test_pair: PlayerPair[int] = PlayerPair() self.assertIsNone(test_pair.one) self.assertIsNone(test_pair.two) test_pair.one = 100 self.assertEqual(100, test_pair.one) self.assertIsNone(test_pair.two) test_pair.two = 200 self.assertEqual(100, test_pair.one) self.assertEqual(200, test_pair.two) another_test_pair: PlayerPair[int] = PlayerPair() self.assertIsNone(another_test_pair.one) self.assertIsNone(another_test_pair.two) self.assertEqual(100, test_pair.one) self.assertEqual(200, test_pair.two) another_test_pair.one = 300 self.assertEqual(300, another_test_pair.one) self.assertIsNone(another_test_pair.two) self.assertEqual(100, test_pair.one) self.assertEqual(200, test_pair.two)
def score_view_delegate(self, score_history, dismiss_callback): total_game_points = PlayerPair(0, 0) for _, game_points in score_history: total_game_points.one += game_points.one total_game_points.two += game_points.two if total_game_points.one > 6 or total_game_points.two > 6: self._num_bummerls += 1 if self._num_bummerls < 2: dismiss_callback() else: self._done_callback()
def evaluate_one_player_vs_opponent_list(player: str, opponents: List[str]) -> DataFrame: rows = [] for opponent in opponents: print(f"Simulating {player} vs {opponent}") players = PlayerPair(player, opponent) metrics = evaluate_player_pair_in_parallel(players) _print_metrics(metrics) rows.append([player, opponent] + _get_results_row(metrics)) columns = ["player_one", "player_two"] + _get_metrics_column_names() return pandas.DataFrame(rows, columns=columns)
def test_who_laughs_last_part_two_player_one_always_wins(self): game_state = get_game_state_for_who_laughs_last_puzzle() mcts = Mcts(PlayerId.ONE) root_node = mcts.build_tree(game_state) actions = root_node.best_actions() self.assertEqual( [PlayCardAction(PlayerId.ONE, Card(Suit.HEARTS, CardValue.KING))], actions) game_state = actions[0].execute(game_state) self.assertEqual(PlayerPair(19, 26), game_state.trick_points) self._assert_player_one_always_wins(game_state)
def get_game_state_for_forcing_the_issue_puzzle() -> GameState: """ Generates a game view for the scenario described here: http://psellos.com/schnapsen/blog/2012/05/013-forcing.html The game state is the following: * cards_in_hand: [X♠, Q♥, X♣, A♦, K♦] [X♥, K♥, X♦, Q♦, J♦] * trump: ♥ * trump_card: J♥ * talon: [A♣], closed * next_player: PlayerId.ONE * won_tricks: [(K♠, Q♠), (A♥, A♠)], [(J♠, K♣), (J♣, Q♣)] * marriage_suits: [], [♣] * trick_points: (29, 31) * current_trick: (None, J♦) * player_that_closed_the_talon: PlayerId.TWO * opponent_points_when_talon_was_closed: 29 """ cards_in_hand = PlayerPair( one=[Card(Suit.SPADES, CardValue.TEN), Card(Suit.HEARTS, CardValue.QUEEN), Card(Suit.CLUBS, CardValue.TEN), Card(Suit.DIAMONDS, CardValue.ACE), Card(Suit.DIAMONDS, CardValue.KING)], two=[Card(Suit.HEARTS, CardValue.TEN), Card(Suit.HEARTS, CardValue.KING), Card(Suit.DIAMONDS, CardValue.TEN), Card(Suit.DIAMONDS, CardValue.QUEEN), Card(Suit.DIAMONDS, CardValue.JACK)]) trump_card = Card(Suit.HEARTS, CardValue.JACK) talon = [Card(Suit.CLUBS, CardValue.ACE)] won_tricks = PlayerPair( one=[PlayerPair(Card(Suit.SPADES, CardValue.KING), Card(Suit.SPADES, CardValue.QUEEN)), PlayerPair(Card(Suit.HEARTS, CardValue.ACE), Card(Suit.SPADES, CardValue.ACE))], two=[PlayerPair(Card(Suit.SPADES, CardValue.JACK), Card(Suit.CLUBS, CardValue.KING)), PlayerPair(Card(Suit.CLUBS, CardValue.JACK), Card(Suit.CLUBS, CardValue.QUEEN))]) marriage_suits = PlayerPair(one=[], two=[Suit.CLUBS]) trick_points = PlayerPair(one=29, two=31) game_state = GameState(cards_in_hand=cards_in_hand, trump=trump_card.suit, trump_card=trump_card, talon=talon, won_tricks=won_tricks, trick_points=trick_points, marriage_suits=marriage_suits, next_player=PlayerId.TWO) game_state.close_talon() game_state.current_trick.two = Card(Suit.DIAMONDS, CardValue.JACK) game_state.next_player = PlayerId.ONE return game_state
def test_play_a_full_game(self): game = Game(PlayerId.ONE, seed=2) self.assertEqual(PlayerId.ONE, game.dealer) self.assertEqual(2, game.seed) self.assertEqual(Suit.DIAMONDS, game.game_state.trump) actions = [ # Player TWO wins the first trick. Score: 0-6. PlayCardAction(PlayerId.TWO, Card(Suit.HEARTS, CardValue.JACK)), PlayCardAction(PlayerId.ONE, Card(Suit.CLUBS, CardValue.KING)), # Player TWO closes the talon. CloseTheTalonAction(PlayerId.TWO), # Player TWO wins the second trick. Score: 0-18. PlayCardAction(PlayerId.TWO, Card(Suit.DIAMONDS, CardValue.TEN)), PlayCardAction(PlayerId.ONE, Card(Suit.DIAMONDS, CardValue.JACK)), # Player TWO wins the third trick. Score: 0-31. PlayCardAction(PlayerId.TWO, Card(Suit.HEARTS, CardValue.TEN)), PlayCardAction(PlayerId.ONE, Card(Suit.SPADES, CardValue.QUEEN)), # Player ONE wins the forth trick. Score: 13-31. PlayCardAction(PlayerId.TWO, Card(Suit.CLUBS, CardValue.JACK)), PlayCardAction(PlayerId.ONE, Card(Suit.CLUBS, CardValue.ACE)), # Player TWO wins the fifth trick. Score: 13-52. PlayCardAction(PlayerId.ONE, Card(Suit.SPADES, CardValue.TEN)), PlayCardAction(PlayerId.TWO, Card(Suit.SPADES, CardValue.ACE)), # Player TWO wins the sixth trick. Score: 13-67. PlayCardAction(PlayerId.TWO, Card(Suit.HEARTS, CardValue.ACE)), PlayCardAction(PlayerId.ONE, Card(Suit.SPADES, CardValue.KING)) ] for i, action in enumerate(actions): game.play_action(action) self.assertEqual(actions[:i + 1], game.actions) self.assertTrue(game.game_state.is_game_over) self.assertEqual(PlayerPair(13, 67), game.game_state.trick_points) self.assertEqual(PlayerPair(0, 3), game.game_state.game_points)
def test_invalid_trick_points(self): correct_trick_points = self.game_state.trick_points for points_one in range(100): for points_two in range(100): self.game_state.trick_points = PlayerPair( points_one, points_two) if self.game_state.trick_points == correct_trick_points: continue with self.assertRaisesRegex(InvalidGameStateError, "Invalid trick points", msg=f"{points_one} {points_two}"): validate(self.game_state)
def build(self): self._game_widget = GameWidget(game_options=self._game_options) self._game_widget.padding_pct = 0.01 self._game_widget.size_hint = 1, 1 human_player: Player = self._game_widget computer_player: Player = OutOfProcessComputerPlayer( CythonMctsPlayer, (PlayerId.TWO, False, mcts_player_options_v1())) players: PlayerPair[Player] = PlayerPair(human_player, computer_player) auto_save_folder = str(Path(__file__).parent.parent.absolute()) self._game_controller = GameController(self._game_widget, players, auto_save_folder=auto_save_folder) return self._game_widget
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_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)