def __init__(self, stage_data, player_data): logger.info("StageController is Created") # Stage Data self.start_stage_time = datetime.datetime.now() self.stage_data = stage_data self.player_data = player_data self.game_state = GameState(self.player_data) self.user_sprite = UserSprite(self.stage_data, self.player_data, self.game_state.user_state.user_id) self.finish_wave = True # controllers self.sprite_controller = SpriteController(self.stage_data.background, self.user_sprite) self.player_controller = PlayerController(self.user_sprite, self.game_state) self.collision_controller = CollisionController(self.sprite_controller) # views self.user_info_view = UserInfoView(USER_INFO_SIZE, self.player_data) self.stage_headline = StageHeadline(self.stage_data.stage_name) # events dispatcher.connect(self.handle_collision_event, signal=SignalMapper.COLLISION_UPDATE, sender=dispatcher.Any) dispatcher.connect(self.handle_combo_event, signal=SignalMapper.COMBO_ATTACK, sender=dispatcher.Any)
class Game: """ Interface for game state """ def __init__(self): self.currentPlayer = 1 self.gameState = GameState(empty_board(), 1, (6, 4)) self.grid_shape = (48, 8) self.input_shape = (2, 48, 8) self.name = 'paper_soccer' def reset(self): self.gameState = GameState(empty_board(), 1, (6, 4)) self.currentPlayer = 1 return self.gameState def get_all_allowed_moves(self, type='deep', params=None): if type == 'deep': return self.gameState.get_full_moves_deep() if type == 'simple': return self.gameState.get_full_moves_simple() if type == 'random': return self.gameState.get_random_move() def make_move(self, move): return self.gameState.make_move(move) def change_player(self): self.currentPlayer = -self.currentPlayer self.gameState.playerTurn = -self.gameState.playerTurn self.gameState.board = turn_board(self.gameState.board) self.gameState.current_position = (12 - self.gameState.current_position[0], 8 - self.gameState.current_position[1])
def can_execute_on(self, game_state: GameState) -> bool: if game_state.next_player != self.player_id: return False if self._card not in game_state.cards_in_hand[self.player_id]: return False if not game_state.is_to_lead(self.player_id): if game_state.must_follow_suit(): if not self._is_following_suit(game_state): return False return True
def __init__(self): self.__current_state = GameState() self.__past_moves = [] self.__all_possible_moves = {} self.__moves_getter = MovesGetter() self.__state_verifier = StateVerifier() self.__predict_filter = PredictiveFilter() self.__current_move_color = PieceColor.WHITE
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 get_game_state_for_know_your_opponent_puzzle() -> GameState: """ Generates a game state for the scenario described here: http://psellos.com/schnapsen/blog/2020/09/147-know.html The game state is the following: * cards_in_hand: [K♠, J♠, K♥, A♣, K♣] [A♠, Q♠, A♥, X♣, Q♣] * trump: ♥ * trump_card: Q♥ * talon: [J♣], closed * next_player: PlayerId.ONE * won_tricks: [(X♥, X♦), (J♥, J♦)], [(Q♦, X♠), (K♦, A♦)] * marriage_suits: [], [] * trick_points: (24, 28) * current_trick: (None, A♥) * player_that_closed_the_talon: PlayerId.TWO * opponent_points_when_talon_was_closed: 24 """ cards_in_hand = PlayerPair( one=[Card(Suit.SPADES, CardValue.KING), Card(Suit.SPADES, CardValue.JACK), Card(Suit.HEARTS, CardValue.KING), Card(Suit.CLUBS, CardValue.ACE), Card(Suit.CLUBS, CardValue.KING)], two=[Card(Suit.SPADES, CardValue.ACE), Card(Suit.SPADES, CardValue.QUEEN), Card(Suit.HEARTS, CardValue.ACE), Card(Suit.CLUBS, CardValue.TEN), Card(Suit.CLUBS, CardValue.QUEEN)]) trump_card = Card(Suit.HEARTS, CardValue.QUEEN) talon = [Card(Suit.CLUBS, CardValue.JACK)] won_tricks = PlayerPair( one=[PlayerPair(Card(Suit.HEARTS, CardValue.TEN), Card(Suit.DIAMONDS, CardValue.TEN)), PlayerPair(Card(Suit.HEARTS, CardValue.JACK), Card(Suit.DIAMONDS, CardValue.JACK))], two=[PlayerPair(Card(Suit.DIAMONDS, CardValue.QUEEN), Card(Suit.SPADES, CardValue.TEN)), PlayerPair(Card(Suit.DIAMONDS, CardValue.KING), Card(Suit.DIAMONDS, CardValue.ACE))]) trick_points = PlayerPair(one=24, two=28) 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, next_player=PlayerId.TWO) game_state.close_talon() game_state.current_trick.two = Card(Suit.HEARTS, CardValue.ACE) game_state.next_player = PlayerId.ONE return game_state
def mcts_ci_widths_across_multiple_game_states(use_player: bool, options: MctsPlayerOptions, num_samples: int, num_game_states: int): data = [] overlap_count = 0 for seed in range(num_game_states): print(f"Evaluating on GameState.new(random_seed={seed})") for _ in range(num_samples): if use_player: dataframe = run_mcts_player_step_by_step( GameState.new(random_seed=seed).next_player_view(), options, options.max_iterations) else: dataframe = run_mcts_and_collect_data( GameState.new(random_seed=seed), options, options.max_iterations) dataframe[ "ci_width"] = dataframe["score_upp"] - dataframe["score_low"] dataframe = dataframe[dataframe.iteration.eq( dataframe.iteration.max())].sort_values("score", ascending=False) if _is_ci_overlap(dataframe.iloc[0].score_low, dataframe.iloc[0].score_upp, dataframe.iloc[1].score_low, dataframe.iloc[1].score_upp): overlap_count += 1 ci_widths = list(dataframe["ci_width"].values) while len(ci_widths) < 7: ci_widths.append(np.nan) data.append(tuple(ci_widths)) overlap_pct = np.round(overlap_count / num_samples / num_game_states * 100, 2) print(f"Overlap in the CIs for the best two actions in {overlap_count} " f"cases out of {num_samples * num_game_states} ({overlap_pct}%)") dataframe = DataFrame(data, columns=["BestAction"] + [f"Action #{i}" for i in range(2, 8)]) suffix = "_player" if use_player else "" csv_path = f"mcts_ci_widths_across_game_states{suffix}.csv" # noinspection PyTypeChecker dataframe.to_csv(csv_path, index=False) # dataframe = pandas.read_csv(csv_path) dataframe.boxplot() plt.xticks(rotation=45, ha='right') plt.gcf().set_size_inches((5, 5)) plt.tight_layout() plt.savefig(f"mcts_ci_widths_across_game_states{suffix}.png")
def debug_game(filename: str, game_index: int, options: MctsPlayerOptions): with open(filename, "rb") as binary_file: bummerl = pickle.load(binary_file) game = bummerl.completed_games[game_index] game_points = _get_bummerl_score(bummerl, game_index) game_state = GameState.new(dealer=game.dealer, random_seed=game.seed) num_actions = len( [action for action in game.actions if action.player_id == PlayerId.ONE]) action_counter = 0 fig, ax = plt.subplots(nrows=num_actions, ncols=2, squeeze=False) for action in game.actions: if action.player_id == PlayerId.ONE: # cheater = False dataframe = run_mcts_player_step_by_step(game_state.next_player_view(), options, iterations_step=100, game_points=game_points) _plot_data(dataframe, "score", ax[action_counter, 0], _hlines_for_scores) # cheater = True dataframe = run_mcts_player_step_by_step(game_state, options, iterations_step=100, game_points=game_points) _plot_data(dataframe, "score", ax[action_counter, 1], _hlines_for_scores) action_counter += 1 game_state = action.execute(game_state) fig.set_size_inches(20, 5 * num_actions) fig.suptitle(f"Debug game: dealer={game.dealer}, seed={game.seed}") plt.tight_layout() plt.savefig("debug_game.png")
def get_available_actions(game_state: GameState) -> List[PlayerAction]: """ Returns a list of all the valid actions that can be performed in a given game state. """ actions: List[PlayerAction] = [] player_id = game_state.next_player is_to_lead = game_state.is_to_lead(player_id) for card in game_state.cards_in_hand[player_id]: # TODO(options): Allow Queen/King to be played without announcing marriage? if card.card_value in [CardValue.QUEEN, CardValue.KING] and is_to_lead: marriage = AnnounceMarriageAction(player_id, card) if marriage.can_execute_on(game_state): actions.append(marriage) continue play_card = PlayCardAction(player_id, card) if play_card.can_execute_on(game_state): actions.append(play_card) if is_to_lead: exchange_trump = ExchangeTrumpCardAction(player_id) if exchange_trump.can_execute_on(game_state): actions.append(exchange_trump) close_talon = CloseTheTalonAction(player_id) if close_talon.can_execute_on(game_state): actions.append(close_talon) return actions
def _on_lead_action(self, game_view: GameState) -> PlayerAction: # Store the available actions. available_actions = get_available_actions(game_view) self._actions = {} for action in available_actions: # Always exchange the trump card if possible. if isinstance(action, ExchangeTrumpCardAction): return action if isinstance(action, (AnnounceMarriageAction, PlayCardAction)): self._actions[action.card] = action assert len(self._actions) == len(self._my_cards) # Check if we have a preferred marriage in hand, but don't play it yet # because we might have higher cards that cannot be beaten by the opponent # (e.g., Trump Ace) and can secure the necessary points such that showing # the marriage will end the game. self._marriage_suit = None marriage_action = get_best_marriage(available_actions, game_view.trump) if marriage_action is not None: logging.debug("HeuristicPlayer: Storing marriage suit: %s", marriage_action.card.suit) self._marriage_suit = marriage_action.card.suit # Maybe close the talon. close_talon_action = self._should_close_talon(game_view) if close_talon_action is not None: return close_talon_action # Get the preferred action to be played depending on the game state. if game_view.must_follow_suit(): return self._on_lead_follow_suit(game_view) return self._on_lead_do_not_follow_suit(game_view)
def new_init(self): self.game_finished_timestamp = None game_state = GameState.get_default_with_field(3, 3) self.game = TicTacToeGame(game_state, TicTacToeDefaultWinnerCheckStrategy(), self.game_changed_callback) self.user_cursor_controller.cursor_position = Coordinate(1, 1)
def test_max_iterations(self): class TestMcts(Mcts): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.counter = 0 def run_one_iteration(self, root_node: Node, select_best_child: bool = False) -> bool: self.counter += 1 return False game_state = GameState.new(random_seed=0) # Run ten iterations. mcts = TestMcts(game_state.next_player) self.assertEqual(0, mcts.counter) mcts.build_tree(game_state, 10) self.assertEqual(10, mcts.counter) # Run one hundred iterations. mcts = TestMcts(game_state.next_player) self.assertEqual(0, mcts.counter) mcts.build_tree(game_state, 100) self.assertEqual(100, mcts.counter) # Negative or zero max_iterations are not allowed. with self.assertRaisesRegex(AssertionError, "max_iterations must be positive"): mcts.build_tree(game_state, 0) with self.assertRaisesRegex(AssertionError, "max_iterations must be positive"): mcts.build_tree(game_state, -1)
def mcts_variance_across_multiple_game_states(cheater: bool, options: MctsPlayerOptions, num_samples: int, num_game_states: int): data = [] for seed in range(num_game_states): print(f"Evaluating on GameState.new(random_seed={seed})") dataframe = _get_dataframe(GameState.new(random_seed=seed), cheater, options, num_samples) details = dataframe.describe().T.sort_values(by="mean", ascending=False) std_dev = list(details["std"].values) while len(std_dev) < 7: std_dev.append(np.nan) data.append(tuple(std_dev)) dataframe = DataFrame(data, columns=["BestAction"] + [f"Action #{i}" for i in range(2, 8)]) csv_path = "mcts_variance_across_game_states.csv" # noinspection PyTypeChecker dataframe.to_csv(csv_path, index=False) # dataframe = pandas.read_csv(csv_path) dataframe.boxplot() plt.xticks(rotation=45, ha='right') plt.gcf().set_size_inches((5, 5)) plt.tight_layout() plt.savefig("mcts_variance_across_game_states.png")
def _not_on_lead_action(self, game_view: GameState) -> PlayerAction: if game_view.must_follow_suit(): card = self._not_on_lead_follow_suit(game_view) else: card = self._not_on_lead_do_not_follow_suit(game_view) logging.debug("HeuristicPlayer: Playing %s", card) return PlayCardAction(self.id, card)
def evaluate_game(game, players, bummerl_score, bummerl_id, game_id): eval_results = [] game_state = GameState.new(game.dealer, game.seed) for evaluated_action in game.actions: player = players[game_state.next_player] mcts_actions_and_scores = player.get_actions_and_scores( game_state.next_player_view(), bummerl_score) if len(mcts_actions_and_scores) == 1: # The player didn't have to make a decision. There is only one valid # action at this point. eval_results.append( (bummerl_id, str(game_id), evaluated_action, None, [])) else: evaluated_action_score = None max_score = max(score for action, score in mcts_actions_and_scores) max_actions = [ action for action, score in mcts_actions_and_scores if score == max_score ] if isinstance(max_score, tuple): max_score = max_score[0] for action, score in mcts_actions_and_scores: if action == evaluated_action: evaluated_action_score = score if isinstance(evaluated_action_score, tuple): evaluated_action_score = evaluated_action_score[0] break eval_results.append( (bummerl_id, str(game_id), evaluated_action, 3 * (evaluated_action_score - max_score), max_actions)) game_state = evaluated_action.execute(game_state) return eval_results
def profile(max_permutations: int, max_iterations: int = 1000): profiler = cProfile.Profile() for seed in range(NUM_SEEDS): game_state = GameState.new(random_seed=seed) if max_permutations == 1: closure_to_profile, cleanup = _get_algorithm_closure( game_state, max_iterations) else: closure_to_profile, cleanup = _get_player_closure( game_state, max_iterations, max_permutations) profiler.enable() closure_to_profile() profiler.disable() cleanup() # Save and print the profile info. folder = os.path.join(os.path.dirname(__file__), "data") suffix = _get_file_suffix(max_permutations) profiler_path = os.path.join(folder, f"iterations_and_time{suffix}.profile") profiler.dump_stats(profiler_path) with open(profiler_path + ".txt", "w") as output_file: stats = pstats.Stats(profiler, stream=output_file).strip_dirs().sort_stats( SortKey.CUMULATIVE) stats.print_stats()
def _generate_data(cheater: bool, options: MctsPlayerOptions, constant_budget: bool): dataframes = [] fully_simulated_game_states = { "You first. No, you first": get_game_state_for_you_first_no_you_first_puzzle(), "Elimination play": get_game_state_for_elimination_play_puzzle(), "Playing to win the last trick": get_game_state_for_playing_to_win_the_last_trick_puzzle(), "Tempo": get_game_state_for_tempo_puzzle(), "Who laughs last": get_game_state_for_who_laughs_last_puzzle(), "Forcing the issue": get_game_state_for_forcing_the_issue_puzzle(), "Game state for tests": get_game_state_for_tests(), } dataframes.extend( _run_simulations(fully_simulated_game_states, cheater, options, constant_budget)) partially_simulated_game_states = {} for seed in [0, 20, 40, 60, 100]: partially_simulated_game_states[f"Random GameState (seed={seed})"] = \ GameState.new(dealer=PlayerId.ONE, random_seed=seed) dataframes.extend( _run_simulations(partially_simulated_game_states, cheater, options, constant_budget)) dataframes.extend( _run_simulations(same_game_state_after_each_trick_scenarios(20), cheater, options, constant_budget)) dataframe = pandas.concat(dataframes, ignore_index=True) # noinspection PyTypeChecker dataframe.to_csv(_get_csv_path(cheater), index=False)
def test_max_iterations_less_than_available_actions(): # Here not all of the root node's children will be expanded. options = MctsPlayerOptions(max_iterations=1, max_permutations=10, num_processes=1) game_state = GameState.new(random_seed=0) mcts_player = CythonMctsPlayer(game_state.next_player, False, options) mcts_player.request_next_action(game_state.next_player_view())
def can_execute_on(self, game_state: GameState) -> bool: if not game_state.is_to_lead(self.player_id): return False if game_state.is_talon_closed: return False if len(game_state.talon) == 0: return False return True
def same_game_state_after_each_trick_scenarios(seed): game_state = GameState.new(dealer=PlayerId.ONE, random_seed=seed) same_game_state_after_each_trick = {} for i in range(5): game_state = _play_one_trick(game_state) same_game_state_after_each_trick[ f"GameState (seed={seed}), after {i + 1} trick(s) played"] = game_state return same_game_state_after_each_trick
def _run_test(self, player_class): options = MctsPlayerOptions(num_processes=1, max_permutations=10, max_iterations=10, merge_scoring_info_func=functools.partial( self._assert_num_iterations, 10 * 10), reallocate_computational_budget=False) game_view = GameState.new(random_seed=0).next_player_view() player = player_class(game_view.next_player, False, options) player.get_actions_and_scores(game_view) # There is one card left in the talon, the opponent played already a card # from their hand, so there are only 4 unknown cards in the opponent's hand. # This means there are only 4 permutations possible. game_view = get_game_view_for_duck_puzzle() # Without reallocating the computational budget, the player runs 4 # permutations of 100 iterations each. options = MctsPlayerOptions(num_processes=1, max_permutations=100, max_iterations=100, merge_scoring_info_func=functools.partial( self._assert_num_iterations, 4 * 100), reallocate_computational_budget=False) player = player_class(game_view.next_player, False, options) player.get_actions_and_scores(game_view) # When reallocating the computational budget, the player runs 4 # permutations of 2500 iterations each, but 5784 are enough to simulate the # entire game tree. options = MctsPlayerOptions(num_processes=1, max_permutations=100, max_iterations=100, merge_scoring_info_func=functools.partial( self._assert_num_iterations, 5784), reallocate_computational_budget=True) player = player_class(game_view.next_player, False, options) player.get_actions_and_scores(game_view) # If max_iterations is None, the computational budget is unlimited, so # reallocate_computational_budget has no effect. options = MctsPlayerOptions(num_processes=1, max_permutations=100, max_iterations=None, merge_scoring_info_func=functools.partial( self._assert_num_iterations, 5784), reallocate_computational_budget=False) player = player_class(game_view.next_player, False, options) player.get_actions_and_scores(game_view) options = MctsPlayerOptions(num_processes=1, max_permutations=100, max_iterations=None, merge_scoring_info_func=functools.partial( self._assert_num_iterations, 5784), reallocate_computational_budget=True) player = player_class(game_view.next_player, False, options) player.get_actions_and_scores(game_view)
def iterations_and_time(max_permutations: int): # pylint: disable=too-many-locals data = [] for seed in range(NUM_SEEDS): game_state = GameState.new(random_seed=seed) iterations = 10 duration_sec = 0 while duration_sec < 10: if max_permutations == 1: closure_to_profile, cleanup = _get_algorithm_closure( game_state, iterations) else: closure_to_profile, cleanup = _get_player_closure( game_state, iterations, max_permutations) timer = timeit.Timer(closure_to_profile) number, time_taken = timer.autorange() duration_sec = time_taken / number logging.info("Run %s iterations in %.5f seconds (seed=%s)", iterations, duration_sec, seed) data.append((seed, iterations, duration_sec)) cleanup() iterations *= 2 suffix = _get_file_suffix(max_permutations) # Save the dataframe with the timing info. dataframe = DataFrame(data, columns=["seed", "iterations", "duration_sec"]) folder = os.path.join(os.path.dirname(__file__), "data") csv_path = os.path.join(folder, f"iterations_and_time{suffix}.csv") # noinspection PyTypeChecker dataframe.to_csv(csv_path, index=False) # Plot the timing data obtained. for seed in sorted(dataframe.seed.drop_duplicates()): filtered_dataframe = dataframe[dataframe["seed"].eq(seed)] plt.plot(filtered_dataframe.iterations, filtered_dataframe.duration_sec, label=f"seed={seed}", alpha=0.5) plt.scatter(filtered_dataframe.iterations, filtered_dataframe.duration_sec, s=10) mean = dataframe.groupby("iterations").mean().sort_index() plt.plot(mean.index, mean.duration_sec, label="Average", color="r", linewidth=3) plt.grid(which="both", linestyle="--") plt.legend(loc=0) plt.xlabel("Iterations") plt.ylabel("Duration (seconds)") plt.xscale("log") plt.yscale("log") plt.title(cpuinfo.get_cpu_info()["brand_raw"]) plt.savefig(os.path.join(folder, f"iterations_and_time{suffix}.png"))
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 request_next_action( self, game_view: GameState, game_points: Optional[PlayerPair[int]] = None) -> PlayerAction: """Returns the action that this player chose to play next.""" assert game_view.next_player == self.id, "Not my turn" self._cache_game_state(game_view) if game_view.is_to_lead(self.id): return self._on_lead_action(game_view) return self._not_on_lead_action(game_view)
def can_execute_on(self, game_state: GameState) -> bool: if not game_state.is_to_lead(self.player_id): return False queen = Card(self._card.suit, CardValue.QUEEN) king = Card(self._card.suit, CardValue.KING) if queen not in game_state.cards_in_hand[self.player_id]: return False if king not in game_state.cards_in_hand[self.player_id]: return False return True
def test_function_with_invalid_changes(self): @validate_game_states def func(game_state: GameState) -> int: game_state.trick_points = PlayerPair(0, 0) return 42 self.assertEqual(42, func(GameState.new(PlayerId.ONE))) with self.assertRaisesRegex(InvalidGameStateError, "Invalid trick points"): func(get_game_state_for_tests())
def __setstate__(self, state): """ Restore the instance variable and recreate the game state by executing all the player actions. """ self.__dict__.update(state) self._game_state: GameState = GameState.new(dealer=self._dealer, random_seed=self._seed) for action in self._actions: self._game_state = action.execute(self._game_state)
def test_the_player_to_lead_must_have_won_a_trick(self): for trick in self.game_state.won_tricks.two: self.game_state.talon.extend([trick.one, trick.two]) self.game_state.trick_points.two -= trick.one.card_value self.game_state.trick_points.two -= trick.two.card_value self.game_state.won_tricks.two = [] self.game_state.trick_points.two = 0 validate(self.game_state) self.game_state.next_player = PlayerId.TWO with self.assertRaisesRegex( InvalidGameStateError, "player that is to lead did not win any trick"): validate(self.game_state) # At the beginning of a game, any player can lead without winning any trick. self.game_state = GameState.new(PlayerId.ONE) validate(self.game_state) self.game_state = GameState.new(PlayerId.TWO) validate(self.game_state)
def can_execute_on(self, game_state: GameState) -> bool: if not game_state.is_to_lead(self.player_id): return False if game_state.is_talon_closed: return False if game_state.trump_card is None: return False trump_jack = Card(suit=game_state.trump, card_value=CardValue.JACK) if trump_jack not in game_state.cards_in_hand[self.player_id]: return False return True
def __init__(self, game_changed_callback, exit_usecase, ui_show_strategy): game_state = GameState.get_default_with_field(3, 3) self.scene_changed_callback = game_changed_callback self.ui_show_strategy = ui_show_strategy self.game = TicTacToeGame(game_state, TicTacToeDefaultWinnerCheckStrategy(), self.game_changed_callback) self.user_cursor_controller = UserCursorController(game_state) self.exit_usecase = exit_usecase self.game_begin_timestamp = time.time() self.game_finished_timestamp = None