def setUp(self): self.board_full = Board(4, 3) self.board_full.create_board_without_holes(4) self.board_holes = Board(4, 3) self.board_holes.create_board_with_holes([(1, 0), (2, 0)], 3) self.player1 = PlayerData(Color.RED, 5) self.player2 = PlayerData(Color.WHITE, 10) self.players = [self.player1, self.player2] self.state_full = State(self.players, self.board_full) self.state_holes = State(self.players, self.board_holes) self.player1.add_penguin((0, 0)) self.player2.add_penguin((0, 2)) self.action1 = Move((0, 0), (1, 0)) self.game_tree = GameTree(self.state_full) self.game_tree_holes = GameTree(self.state_holes) self.state1_0 = self.state_full.move_penguin((0, 0), (1, 0)) self.state2_0 = self.state_full.move_penguin((0, 0), (2, 0)) self.state2_1 = self.state_full.move_penguin((0, 0), (2, 1)) self.state3_1 = self.state_full.move_penguin((0, 0), (3, 1))
def initialize_game(self, players): """ Start the game. Initializes the game board, the game state, and the resulting game tree with the parameters. The resulting GameTree is ready to play. 1. Takes care of what kind of Board to make (does it have holes, number of fish, etc), and makes sure that the board is big enough for the number of players and their penguins 2. Calls on Players to place their penguins EFFECT: Sets the current Game to be the GameTree. [List-of PlayerInterface] -> GameTree """ if len(players) < MIN_PLAYERS or len(players) > MAX_PLAYERS: raise ValueError("Invalid number of players") if type(self.__game) is not GameSetup: raise ValueError("Cannot initialize game: Game already started") internal_players = self.__assign_player_colors(players) board = self.__create_board() state = State(internal_players, board) self.__game = GameTree(state) self.__run_penguin_placement() self.__broadcast_current_state() return self.__game
def test_create_child_trees(self): trees = [ GameTree(self.state1_0), GameTree(self.state2_0), GameTree(self.state2_1), GameTree(self.state3_1) ] children = self.game_tree.create_child_trees() for tree in trees: self.assertTrue(tree in children.values()) self.assertEqual(len(children), len(trees))
def test_attempt_move_last_move(self): mini_board_array = [[1, 5], [2, 1], [1, 5], [1, 5], [0, 5]] player1 = PlayerData(Color.RED) player2 = PlayerData(Color.WHITE) player1.add_penguin((0, 0)) player1.add_penguin((1, 0)) player1.add_penguin((2, 0)) player1.add_penguin((3, 0)) player2.add_penguin((0, 1)) player2.add_penguin((1, 1)) player2.add_penguin((2, 1)) player2.add_penguin((3, 1)) players = [player1, player2] board = Board(5, 2) board.create_board_from_json(mini_board_array) state = State(players, board) game_tree = GameTree(state) action = Move((3, 0), (4, 1)) self.assertEqual(type(game_tree.attempt_move(action)), GameEnded)
def reset_game(self): """ Reset this referee to be reused to play another Fish game. Should only be called after the previous game has ended and returned game results. EFFECT: Reinitialize all class variables to empty. Reset game to GameSetup() """ self.__players = {} self.__kicked_players = set() self.__game = GameSetup()
def setUp(self): self.player1 = Player() self.player2 = Player() mini_board_array = [[1, 5], [2, 1], [1, 5]] self.mini_board = Board(3, 2) self.mini_board.create_board_from_json(mini_board_array) self.strategy = Strategy() self.in_player1 = PlayerData(Color.RED) self.in_player2 = PlayerData(Color.WHITE) self.players = [self.in_player1, self.in_player2] self.mini_state = State(self.players, self.mini_board) self.mini_tree = GameTree(self.mini_state)
def __run_penguin_placement(self): """ Run as many penguin placement rounds as necessary. Call on Players to place their penguins. EFFECT: Updates the current GameTree when penguins are placed void -> void """ rounds = get_max_penguin_count(len(self.__players)) for round in range(0, rounds): for color in self.get_players_as_colors(): state = self.__game.get_state() player = self.get_player_with_color(color) maybe_posn = safe_call(self.__timeout, player.choose_placement, [state]) if maybe_posn is not False: maybe_state = state.place_penguin_for_player( color, maybe_posn) if maybe_posn is False or maybe_state is False: self.__kick_player(color) else: self.__game = GameTree(maybe_state)
def __init__(self, board_config=default_board_config, timeout=TIMEOUT): """ Constructor for a Referee who supervises one game. __board_config: BoardConfiguration A python dictionary that describes how a board should be created __timeout: Positive Time in seconds to wait for each call to a player __players: {Color : PlayerInterface} Mapping of player's Color to the external player object __kicked_players: [Set PlayerInterface] Players who have cheated, and cannot play anymore __game: Game Current Game, initialized to GameSetup """ self.__board_config = board_config self.__players = {} self.__kicked_players = set() self.__game = GameSetup() self.__timeout = timeout
class Referee: default_board_config = {"row": 4, "col": 3, "fish": 3} def __init__(self, board_config=default_board_config, timeout=TIMEOUT): """ Constructor for a Referee who supervises one game. __board_config: BoardConfiguration A python dictionary that describes how a board should be created __timeout: Positive Time in seconds to wait for each call to a player __players: {Color : PlayerInterface} Mapping of player's Color to the external player object __kicked_players: [Set PlayerInterface] Players who have cheated, and cannot play anymore __game: Game Current Game, initialized to GameSetup """ self.__board_config = board_config self.__players = {} self.__kicked_players = set() self.__game = GameSetup() self.__timeout = timeout def play_game(self, players): """ Controls game mechanics and runs the game. [List-of PlayerInterface] -> GameResult """ self.initialize_game(players) self.run_game() game_result = self.__get_game_result() self.alert_players(game_result) return game_result def initialize_game(self, players): """ Start the game. Initializes the game board, the game state, and the resulting game tree with the parameters. The resulting GameTree is ready to play. 1. Takes care of what kind of Board to make (does it have holes, number of fish, etc), and makes sure that the board is big enough for the number of players and their penguins 2. Calls on Players to place their penguins EFFECT: Sets the current Game to be the GameTree. [List-of PlayerInterface] -> GameTree """ if len(players) < MIN_PLAYERS or len(players) > MAX_PLAYERS: raise ValueError("Invalid number of players") if type(self.__game) is not GameSetup: raise ValueError("Cannot initialize game: Game already started") internal_players = self.__assign_player_colors(players) board = self.__create_board() state = State(internal_players, board) self.__game = GameTree(state) self.__run_penguin_placement() self.__broadcast_current_state() return self.__game def __assign_player_colors(self, players): internal_players = [] for p in range(0, len(players)): maybe_color = self.__assign_color_to_player(p, players[p]) if maybe_color is False: self.__kicked_players.add(players[p]) else: self.__players[maybe_color] = players[p] internal_players.append(PlayerData(maybe_color)) return internal_players def run_game(self): """ Run the game until the end. """ while not self.has_game_ended(): color = self.__game.get_current_player_color() current_player = self.get_player_with_color(color) maybe_action = safe_call(self.__timeout, current_player.choose_next_move) if maybe_action is not False: maybe_game_tree = self.check_move_validity(maybe_action) if maybe_action is False or maybe_game_tree is False: self.__kick_player(color) self.__broadcast_current_state() else: self.__game = maybe_game_tree self.__broadcast_player_action(maybe_action) def check_move_validity(self, action): """ Check that the given action is valid in the current state of the game tree. If it is valid, return the next game tree with the action applied, otherwise False. Action -> [Maybe Game] """ if type(self.__game) is not GameTree: return False return self.__game.attempt_move(action) def has_game_ended(self): """ Utility method to see if the game has ended. void -> Boolean """ return self.__game.has_game_ended() def get_current_state(self): """ Get the current state of the game to inform game observers of on-going actions. Returns a JSON translation of the current state, which is read-only. void -> JSON Object """ return self.__game.state.print_json() def get_current_scores(self): """ Get scores of players. void -> List of (Player, int) """ player_score = [] for p in self.__game.state.players: ext_p = self.get_player_with_color(p.get_color()) player_score.append((ext_p, p.get_score())) return player_score def get_winners(self): """ Get the winner(s) of the game. If the game has not ended and there are no winners, return False. void -> [Maybe List of Player] """ if not self.has_game_ended(): return False internal_winners = self.__game.get_winners() ext_winners = [] for p in internal_winners: color = p.get_color() maybe_player = self.get_player_with_color(color) if maybe_player: ext_winners.append(maybe_player) return ext_winners def get_player_with_color(self, color): """ void -> [Maybe Player] """ return self.__players.get(color, False) def get_players_as_colors(self): """ Return a list of color keys for players who have not been kicked out of the Game. void -> [List-of Color] """ return [ color for color in self.__players if self.get_player_with_color(color) not in self.__kicked_players ] def get_players(self): """ Return a list of active players in the game (those who have not cheated or failed). void -> [List-of PlayerInterface] """ return [ self.get_player_with_color(color) for color in self.get_players_as_colors() ] def alert_players(self, game_result): """ Send the GameResult to the active players in the game (non-kicked players). """ if type(self.__game) is GameEnded: for player in self.get_players(): safe_call(self.__timeout, player.game_over, [game_result]) def reset_game(self): """ Reset this referee to be reused to play another Fish game. Should only be called after the previous game has ended and returned game results. EFFECT: Reinitialize all class variables to empty. Reset game to GameSetup() """ self.__players = {} self.__kicked_players = set() self.__game = GameSetup() def __get_game_result(self): """ Creates the game result, which includes a JSON representation of the terminal state, a list of the winners, and a set of the players who were kicked from the game. void -> GameResult """ winners = self.get_winners() return { "state": self.__game.get_state().print_json(), "winners": winners, "losers": [player for player in self.get_players() if player not in winners], "kicked_players": self.__kicked_players } def __run_penguin_placement(self): """ Run as many penguin placement rounds as necessary. Call on Players to place their penguins. EFFECT: Updates the current GameTree when penguins are placed void -> void """ rounds = get_max_penguin_count(len(self.__players)) for round in range(0, rounds): for color in self.get_players_as_colors(): state = self.__game.get_state() player = self.get_player_with_color(color) maybe_posn = safe_call(self.__timeout, player.choose_placement, [state]) if maybe_posn is not False: maybe_state = state.place_penguin_for_player( color, maybe_posn) if maybe_posn is False or maybe_state is False: self.__kick_player(color) else: self.__game = GameTree(maybe_state) def __assign_color_to_player(self, index, ext_player): """ Assigns color to player based on index. Returns color assigned. :index: int Index of color :ext_player: PlayerInterface Player to assign color to :returns: Maybe Color Color assigned, False if color assignment call failed """ colors = Color.get_all_colors() assign_color = colors[index] if safe_call(self.__timeout, ext_player.assign_color, [assign_color]) is False: return False else: return assign_color def __create_board(self): board = Board(self.__board_config["row"], self.__board_config["col"]) board.create_board_without_holes(self.__board_config["fish"]) return board def __broadcast_player_action(self, action): """ Update players who have not been kicked on ongoing game actions. """ for player in self.get_players(): safe_call(self.__timeout, player.update_with_action, [action]) def __broadcast_current_state(self): """ Update players with new state. Called when a player has been kicked. """ for player in self.get_players(): safe_call(self.__timeout, player.set_state, [self.__game.get_state()]) def __kick_player(self, color): """ Remove the current player from the game. Tell the current player to remove penguins. EFFECT: Adds the current player to the set of kicked players Reset the current Game instance to new Game with current player removed Color -> void """ self.__kicked_players.add(self.get_player_with_color(color)) if type(self.__game) is not GameSetup: self.__game = self.__game.remove_player(color)
def update_with_action(self, action): tree = GameTree(self.__state) self.__state = action.apply_move(tree)
def test_create_child_trees_no_children(self): children = self.game_tree_holes.create_child_trees() child_trees = list(children.values()) self.assertEqual(len(child_trees), 1) self.state_holes.set_next_players_turn() self.assertTrue(child_trees[0] in [GameTree(self.state_holes)])
class GameTreeTestCase(unittest.TestCase): def setUp(self): self.board_full = Board(4, 3) self.board_full.create_board_without_holes(4) self.board_holes = Board(4, 3) self.board_holes.create_board_with_holes([(1, 0), (2, 0)], 3) self.player1 = PlayerData(Color.RED, 5) self.player2 = PlayerData(Color.WHITE, 10) self.players = [self.player1, self.player2] self.state_full = State(self.players, self.board_full) self.state_holes = State(self.players, self.board_holes) self.player1.add_penguin((0, 0)) self.player2.add_penguin((0, 2)) self.action1 = Move((0, 0), (1, 0)) self.game_tree = GameTree(self.state_full) self.game_tree_holes = GameTree(self.state_holes) self.state1_0 = self.state_full.move_penguin((0, 0), (1, 0)) self.state2_0 = self.state_full.move_penguin((0, 0), (2, 0)) self.state2_1 = self.state_full.move_penguin((0, 0), (2, 1)) self.state3_1 = self.state_full.move_penguin((0, 0), (3, 1)) def test_attempt_move(self): self.assertEqual(type(self.game_tree.attempt_move(self.action1)), GameTree) def test_attempt_move_last_move(self): mini_board_array = [[1, 5], [2, 1], [1, 5], [1, 5], [0, 5]] player1 = PlayerData(Color.RED) player2 = PlayerData(Color.WHITE) player1.add_penguin((0, 0)) player1.add_penguin((1, 0)) player1.add_penguin((2, 0)) player1.add_penguin((3, 0)) player2.add_penguin((0, 1)) player2.add_penguin((1, 1)) player2.add_penguin((2, 1)) player2.add_penguin((3, 1)) players = [player1, player2] board = Board(5, 2) board.create_board_from_json(mini_board_array) state = State(players, board) game_tree = GameTree(state) action = Move((3, 0), (4, 1)) self.assertEqual(type(game_tree.attempt_move(action)), GameEnded) def test_attempt_move_invalid(self): self.assertFalse(self.game_tree.attempt_move(Move((0, 0), (2, 2)))) def test_create_child_trees(self): trees = [ GameTree(self.state1_0), GameTree(self.state2_0), GameTree(self.state2_1), GameTree(self.state3_1) ] children = self.game_tree.create_child_trees() for tree in trees: self.assertTrue(tree in children.values()) self.assertEqual(len(children), len(trees)) def test_create_child_trees_no_children(self): children = self.game_tree_holes.create_child_trees() child_trees = list(children.values()) self.assertEqual(len(child_trees), 1) self.state_holes.set_next_players_turn() self.assertTrue(child_trees[0] in [GameTree(self.state_holes)]) def test_apply_to_children(self): action = Move((0, 2), (1, 2)) states = [self.state1_0, self.state2_0, self.state2_1, self.state3_1] new_states = self.game_tree.apply_to_children(action.apply_move) for state in states: move_state = state.move_penguin((0, 2), (1, 2)) self.assertTrue(move_state in new_states) self.assertEqual(len(new_states), len(states)) def test_apply_to_children_create_trees(self): child_trees = self.game_tree.apply_to_children( GameTree.create_child_trees) self.assertEqual(len(child_trees), 4) num_children = 0 for c in child_trees: num_children += len(c) self.assertGreater(len(c), 0) self.assertEqual(num_children, 18)
def set_state(self, state): if state.any_remaining_moves(): self.__game = GameTree(state) else: self.__game = GameEnded(state)