def dto_to_game(game_dto): """ maps a DTO to a game to deserialize a persisted instance. :param game_dto: a dictionary representing the structure of the game, created by game_to_dto :return: a Game instance whose state is equal to the DTO """ maze, leftover_card, maze_card_by_id = _dto_to_maze_cards_and_dictionary( game_dto[MAZE]) objective_maze_card = maze_card_by_id[game_dto[OBJECTIVE]] board = Board(maze, leftover_card, objective_maze_card=objective_maze_card) players = [ _dto_to_player(player_dto, board, maze_card_by_id) for player_dto in game_dto[PLAYERS] ] board._pieces = [player.piece for player in players] turns_prepare_delay = _dto_to_timedelta(game_dto[TURN_PREPARE_DELAY]) turns = _dto_to_turns(game_dto[NEXT_ACTION], players=players, prepare_delay=turns_prepare_delay) identifier = game_dto[ID] game = Game(identifier, board=board, players=players, turns=turns) for player in players: player.set_game(game) game.previous_shift_location = _dto_to_board_location( game_dto[PREVIOUS_SHIFT_LOCATION]) return game
def create_game(maze_size=7, game_id=0, with_delay=True): """ Creates a game instance with a random board. Player and piece initialization is not done here. """ if not with_delay: return Game(game_id, board=create_board(maze_size), turns=Turns()) else: return Game(game_id, board=create_board(maze_size))
def test_given_empty_game__when_player_is_added__initializes_turns(): game = Game(identifier=0) player = Player(game.unused_player_id()) game.add_player(player) game.turns.next_player_action() == PlayerAction(player, PlayerAction.PREPARE_SHIFT)
def test_get_enabled_shift_locations_with_previous_shift(): """ Tests get_enabled_shift_locations where the previous shift is (3, 0) """ board = Board() game = Game(identifier=0, board=board) game.previous_shift_location = BoardLocation(3, 0) expected_disabled = BoardLocation(3, board.maze.maze_size - 1) enabled_shift_locations = game.get_enabled_shift_locations() assert expected_disabled not in enabled_shift_locations
def _setup(game_id, player_id): game = Game(game_id, turns=Turns()) game.add_player(Player(player_id)) game_repository = game_repository_coach.when_game_repository_find_by_id_then_return( game) game_repository.update = Mock() interactor = interactors.PlayerInteractor( game_repository=game_repository) return game, interactor
def given_running_game__when_restart__then_turns_are_started(): turns = Mock() game = Game(identifier=0, turns=turns) game.add_player(Player(0)) turns.reset_mock() restart(game) turns.start.assert_called_once()
def test_move_raises_error_on_invalid_player_id(): """ Tests move validation """ game = Game(identifier=0) player_id = game.unused_player_id() game.add_player(Player(player_id)) game.start_game() with pytest.raises(PlayerNotFoundException): game.move(player_id - 1, BoardLocation(5, 5))
def test_shift_raises_error_on_invalid_player_id(): """ Tests shift validation """ game = Game(identifier=0) player_id = game.unused_player_id() game.add_player(Player(player_id)) game.start_game() with pytest.raises(PlayerNotFoundException): game.shift(player_id + 1, BoardLocation(0, 1), 90)
def test_move_does_not_raise_error_after_shift(): """ Tests turn validation """ board = Mock() turns = Mock() turns.is_action_possible.return_value = True game = Game(identifier=0, board=board, turns=turns) player_id = game.unused_player_id() game.add_player(Player(player_id)) game.start_game() player = game.get_player(player_id) game.move(player_id, BoardLocation(0, 0)) board.move.assert_called_once() turns.is_action_possible.assert_called_once_with(player, PlayerAction.MOVE_ACTION)
def _create_test_game(with_computer=False): """ Creates a Game instance by hand """ card_factory = MazeCardFactory() board = Board(leftover_card=MazeCard(0, MazeCard.T_JUNCT, 0)) for row in range(board.maze.maze_size): for column in range(board.maze.maze_size): if row == 0 and column == 0: board.maze[BoardLocation( row, column)] = card_factory.create_instance( MazeCard.STRAIGHT, 0) elif row == 1 and column == 1: board.maze[BoardLocation( row, column)] = card_factory.create_instance( MazeCard.CORNER, 0) elif row == 2 and column == 2: board.maze[BoardLocation( row, column)] = card_factory.create_instance( MazeCard.T_JUNCT, 270) else: board.maze[BoardLocation( row, column)] = card_factory.create_instance( MazeCard.T_JUNCT, 0) player_ids = [3, 4] players = [ Player(identifier=player_id, game=None) for player_id in player_ids ] if with_computer: player_ids.append(42) players.append( create_computer_player(player_id=42, compute_method="dynamic-foo", shift_url="shift-url", move_url="move-url")) for player in players: player.set_board(board) players[0].piece.maze_card = board.maze[BoardLocation(3, 3)] players[1].piece.maze_card = board.maze[BoardLocation(5, 5)] players[0].piece.piece_index = 1 players[1].piece.piece_index = 0 players[0].score = 7 players[1].score = 8 board._objective_maze_card = board.maze[BoardLocation(1, 4)] turns = Turns(players, next_action=PlayerAction(players[1], PlayerAction.MOVE_ACTION)) game = Game(identifier=7, turns=turns, board=board, players=players) for player in players: player._game = game game.previous_shift_location = BoardLocation(0, 3) return game, player_ids
def test_player_reaches_objective_increase_score(): """ Tests that the score on a player is increased once he reaches an objective """ board = Mock() turns = Mock() turns.is_action_possible.return_value = True board.move.return_value = True game = Game(identifier=0, board=board, turns=turns) player_id = game.unused_player_id() game.add_player(Player(player_id)) old_score = game.get_player(player_id).score game.move(player_id, BoardLocation(0, 1)) assert game.get_player(player_id).score == old_score + 1
def test_bot_random_algorith_when_piece_is_pushed_out(post_move, post_shift, time_sleep): """ Tests case where piece is positioned on a shift location, so that it is pushed out. Runs computation 100 times. Push-out expectation rate is 1/12. Probability that no push-out takes place in 100 runs is negligible .start() is patched so that the compute method runs sequentially. This test recreates a bug, where the pushed-out piece is not updated correctly, leading to exceptions thrown when bot makes a move. """ board = create_board() piece = board.create_piece() piece.maze_card = board.maze[BoardLocation(3, 6)] game = Game(0, board=board, turns=Turns()) bot = Bot(library_binding_factory=Mock(), move_url="move-url", shift_url="shift-url", identifier=9, piece=piece) bot.set_game(game) for _ in range(100): shift_action, move_location = bot.random_actions() shift_location, _ = shift_action allowed_coordinates = [(3, 6)] if shift_location == BoardLocation(3, 6): allowed_coordinates = [(3, 5)] elif shift_location == BoardLocation(3, 0): allowed_coordinates = [(3, 0)] allowed_moves = { BoardLocation(*coordinates) for coordinates in allowed_coordinates } assert move_location in allowed_moves
def test_random_actions_computes_valid_actions(): """ Runs computation 100 times and expects that it returns valid actions in each run """ orig_board = create_board() for _ in range(100): board = copy.deepcopy(orig_board) game = Game(0, board=board, turns=Turns()) bot = Bot(library_binding_factory=Mock(), move_url="move-url", shift_url="shift-url", identifier=9) bot.set_game(game) shift_action, move_location = bot.random_actions() shift_location, shift_rotation = shift_action assert shift_rotation in [0, 90, 180, 270] assert shift_location in board.shift_locations allowed_coordinates = [(0, 0)] if shift_location == BoardLocation(0, 1) and shift_rotation == 270: allowed_coordinates += [(0, 1)] elif shift_location == BoardLocation(0, 1) and shift_rotation == 180: allowed_coordinates += [(0, 1), (1, 1)] elif shift_location == BoardLocation(1, 0) and shift_rotation == 270: allowed_coordinates += [(1, 0)] elif shift_location == BoardLocation(1, 0) and shift_rotation == 0: allowed_coordinates += [(1, 0), (1, 1), (1, 2), (2, 1), (2, 2), (3, 1), (3, 2)] elif shift_location == BoardLocation(6, 1): allowed_coordinates += [(0, 1), (0, 2), (1, 1), (2, 1)] allowed_moves = { BoardLocation(*coordinates) for coordinates in allowed_coordinates } assert move_location in allowed_moves
def test_random_actions_should_respect_no_pushback_rule(): """ Runs computation 50 times and checks that none of the computed shifts reverts the previous shift action """ card_factory = MazeCardFactory() orig_board = Board(create_maze(MAZE_STRING, card_factory), leftover_card=card_factory.create_instance("NE", 0)) for _ in range(50): board = copy.deepcopy(orig_board) maze = board.maze piece = board.create_piece() piece.maze_card = maze[BoardLocation(0, 0)] game = Game(0, board=orig_board) game.previous_shift_location = BoardLocation(0, 3) computer_player = ComputerPlayer(library_binding_factory=Mock(), move_url="move-url", shift_url="shift-url", game=game, identifier=9, board=board, piece=piece) shift_action, _ = computer_player.random_actions() shift_location, _ = shift_action assert shift_location != BoardLocation(6, 3)
def _setup(): game = Game(5, turns=Turns()) game_repository = game_repository_coach.when_game_repository_find_by_id_then_return( game) game_repository.update = Mock() interactor = interactors.PlayerActionInteractor( game_repository=game_repository) return game, interactor, game_repository
def test_replace_board(): """ Tests replace_board. Asserts that score and all player locations are reset, and that it is the first player's turn """ turns = Mock() game = Game(identifier=0, turns=turns) player_ids = [] for _ in range(2): player_id = game.unused_player_id() game.add_player(Player(player_id)) player_ids.append(player_id) players = list(map(game.get_player, player_ids)) players[0].score = 11 players[1].score = 22 players[0].piece.maze_card = game.board.maze[BoardLocation(1, 1)] players[1].piece.maze_card = game.board.maze[BoardLocation(2, 2)] pieces = list(map(lambda player: player.piece, players)) board = Mock() type(board).pieces = PropertyMock(return_value=pieces) turns.reset_mock() game.replace_board(board) board.create_piece.assert_has_calls([call(), call()]) turns.start.assert_called_once() assert players[0].score == 0 assert players[1].score == 0
def given_game_and_player__when_set_game_on_player__creates_piece_for_player_on_board( ): board = create_board() game = Game(0, board=board) player = Player(0) player.set_game(game) assert player.piece in board.pieces
def test_add_player_validation(): """ Tests that adding more players than MAX_PLAYERS does not add another one """ game = Game(identifier=0) for _ in range(game.MAX_PLAYERS): player_id = game.unused_player_id() game.add_player(Player(player_id)) with pytest.raises(GameFullException): game.add_player(Player(42))
def test_unused_player_id_returns_new_id(): """ Tests unused_player_id """ game = Game(identifier=0) game.add_player(Player(3)) game.add_player(Player(7)) game.add_player(Player(11)) player_id = game.unused_player_id() assert player_id not in [player.identifier for player in game.players]
def test_random_actions_should_respect_no_pushback_rule(): """ Runs computation 50 times and checks that none of the computed shifts reverts the previous shift action """ orig_board = create_board() for _ in range(50): board = copy.deepcopy(orig_board) maze = board.maze piece = board.create_piece() piece.maze_card = maze[BoardLocation(0, 0)] game = Game(0, board=board, turns=Turns()) game.previous_shift_location = BoardLocation(0, 3) bot = Bot(library_binding_factory=Mock(), move_url="move-url", shift_url="shift-url", identifier=9, piece=piece) bot.set_game(game) shift_action, _ = bot.random_actions() shift_location, _ = shift_action assert shift_location != BoardLocation(6, 3)
def given_game_with_two_players__when_restart__then_score_is_reset(): game = Game(identifier=0) add_players(game, 2) game.players[0].score = 11 game.players[1].score = 22 restart(game) assert game.players[0].score == 0 assert game.players[1].score == 0
def given_game_player_with_piece_index_0_removed__when_restart__then_remaining_player_keeps_piece_index( ): game = Game(identifier=0) add_players(game, 2) remove_player_with_piece_index(game, 0) remaining_piece = game.players[0].piece restart(game) assert game.players[0].piece.piece_index == remaining_piece.piece_index
def given_game_with_two_players__when_restart__then_players_keep_piece_index(): game = Game(identifier=0) add_players(game, 2) old_player_pieces = { player.identifier: player.piece for player in game.players } restart(game) for player in game.players: expected_piece_index = old_player_pieces[player.identifier].piece_index assert player.piece.piece_index == expected_piece_index
def test_add_player_start_game_calls_methods_on_turns(): """ Tests add_player, start_game and Player """ board = Board() turns = Mock() game = Game(identifier=0, board=board, turns=turns) for _ in range(4): player_id = game.unused_player_id() game.add_player(Player(player_id)) game.start_game() expected_turn_calls = [call.init(game.players)] + [call.start()] assert turns.mock_calls[-2:] == expected_turn_calls
def test_unused_player_id_raises_exception_on_full_game(): """ Tests unused_player_id """ game = Game(identifier=0) for _ in range(game.MAX_PLAYERS): player_id = game.unused_player_id() game.add_player(Player(player_id)) with pytest.raises(GameFullException): player_id = game.unused_player_id()
def test_add_player_with_duplicate_id_does_not_add(): """ Tests add_player """ game = Game(identifier=0) game.add_player(Player(1)) other_player = Player(1) game.add_player(other_player) assert len(game.players) == 1 assert other_player not in game.players
def test_add_player_start_game_calls_methods_on_board(): """ Tests add_player, start_game and Player """ board = Board() turns = Mock() game = Game(identifier=0, board=board, turns=turns) with patch.object(board, 'create_piece', wraps=board.create_piece) as board_create_piece: for _ in range(4): player_id = game.unused_player_id() game.add_player(Player(player_id)) game.start_game() expected_board_calls = [ call.create_piece(), call.create_piece(), call.create_piece(), call.create_piece() ] assert board_create_piece.mock_calls == expected_board_calls
def test_get_enabled_shift_locations_without_previous_shift(): """ Tests get_enabled_shift_locations where the previous shift is None """ board = Board() game = Game(identifier=0, board=board) enabled_shift_locations = game.get_enabled_shift_locations() assert set(enabled_shift_locations) == set(board.shift_locations)
def test_add_get_player(): """ Tests add_player and get_player """ game = Game(identifier=0) player = Player(7) game.add_player(player) assert game.get_player(7) is player
def test_get_player_raises_exception_for_unknown_id(): """ Tests get_player """ game = Game(identifier=0) with pytest.raises(PlayerNotFoundException): game.get_player(0)