def test_next_player_action_after_perform_shift_should_be_move(): """ Tests next_player_action and perform_action """ player = Player(7, 0) turns = Turns(players=[player]) turns.perform_action(player, PlayerAction.SHIFT_ACTION) assert turns.next_player_action() == PlayerAction(player, PlayerAction.MOVE_ACTION)
def given_delay__when_waiting_long_enough__then_is_in_shift_state(): player = Player(0) turns = Turns(prepare_delay=timedelta(milliseconds=5), players=[player]) turns.start() time.sleep(timedelta(milliseconds=20).total_seconds()) assert turns.next_player_action().action == PlayerAction.SHIFT_ACTION
def test_callback_with_one_player(): """ Tests that the callback on a player is called after each move, even if he plays alone """ turns = Turns() player1 = Mock() turns.add_player(player1, turn_callback=player1.callback) turns.start() player1.callback.assert_called_once() player1.callback.reset_mock() turns.perform_action(player1, PlayerAction.SHIFT_ACTION) turns.perform_action(player1, PlayerAction.MOVE_ACTION) player1.callback.assert_called_once()
def test_remove_first_player_next_player_action_should_be_remaining_player(): """ Tests after removing the first of two players, next_player_action() should not return None """ turns = Turns() player1, player2 = Mock(), Mock() turns.add_player(player1, turn_callback=player1.callback) turns.add_player(player2, turn_callback=player2.callback) turns.start() turns.remove_player(player1) assert turns.next_player_action() is not None assert turns.next_player_action().player is player2 player2.callback.assert_called_once()
def test_remove_player__with_only_one_player__no_callback_called(): turns = Turns() player = Mock() turns.add_player(player, turn_callback=player.callback) turns.start() player.callback.reset_mock() turns.remove_player(player) assert turns.next_player_action() is None player.callback.assert_not_called()
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 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_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_given_delay_with_two_players__when_player1_leaves_during_wait__player1_callback_is_not_called( ): turns = Turns(prepare_delay=timedelta(milliseconds=30)) player1, player2 = Mock(), Mock() turns.add_player(player1, turn_callback=player1.callback) turns.add_player(player2, turn_callback=player2.callback) turns.start() time.sleep(timedelta(milliseconds=50).total_seconds()) turns.perform_action(player1, PlayerAction.SHIFT_ACTION) time.sleep(timedelta(milliseconds=5).total_seconds()) player1.callback.reset_mock() turns.remove_player(player1) time.sleep(timedelta(milliseconds=50).total_seconds()) player1.callback.assert_not_called()
def _dto_to_turns(next_action_dto, players, prepare_delay=timedelta(0)): """ Maps a DTO to a Turns instance :param next_action_dto: a dictionary representing the next player action, as created by _turns_to_next_action_dto :param players: a list of players. The value of the PLAYER_ID field in the dto has to match one of the player's id :return: an instance of Turns """ if not players: return Turns(prepare_delay=prepare_delay) player = next(player for player in players if player.identifier == next_action_dto[PLAYER_ID]) action = next_action_dto[ACTION] return Turns(players=players, next_action=PlayerAction(player, action), prepare_delay=prepare_delay)
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 _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 _turns_to_next_player_action_dto(turns: Turns): """ Maps an instance of Turns to a DTO, representing only the next player's action. """ next_player_action = turns.next_player_action() if not next_player_action: return None return {PLAYER_ID: next_player_action.player.identifier, ACTION: next_player_action.action}
def given_prepare_state__when_player_tries_shift__raises_exception(): player = Player(0) turns = Turns(prepare_delay=timedelta(milliseconds=10), players=[player]) turns.start() with pytest.raises(TurnActionViolationException): turns.perform_action(player, PlayerAction.SHIFT_ACTION)
def given_delay__when_start__calls_callback_on_player(): turns = Turns(prepare_delay=timedelta(milliseconds=5)) player = Mock() turns.add_player(player, turn_callback=player.callback) turns.start() assert player.callback.call_count == 1 player.callback.assert_called_with(PlayerAction.PREPARE_SHIFT)
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_start__calls_callback_if_present(): """ Tests start for player with callback """ turns = Turns() player = Player(7, 0) callback = Mock() turns.add_player(player, turn_callback=callback) callback.assert_not_called() turns.start() callback.assert_called_once_with(PlayerAction.SHIFT_ACTION)
def test_given_delay__when_waiting_long_enough__then_calls_callback_on_player( ): turns = Turns(prepare_delay=timedelta(milliseconds=5)) player = Mock() turns.add_player(player, turn_callback=player.callback) turns.start() time.sleep(timedelta(milliseconds=20).total_seconds()) assert player.callback.call_count == 2 player.callback.assert_called_with(PlayerAction.SHIFT_ACTION)
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 test_removed_player_no_callback_called(): """ Tests remove_player """ turns = Turns() player1 = Mock() player2 = Mock() turns.add_player(player1, turn_callback=player1.callback) turns.add_player(player2, turn_callback=player2.callback) turns.start() turns.remove_player(player2) turns.perform_action(player1, PlayerAction.SHIFT_ACTION) turns.perform_action(player1, PlayerAction.MOVE_ACTION) player2.callback.assert_not_called()
def test_is_action_possible_with_complete_cycle(): """ Tests is_action_possible """ players = [Player(id, 0) for id in [2, 1]] turns = Turns(players=players) assert turns.is_action_possible(players[0], PlayerAction.SHIFT_ACTION) assert not turns.is_action_possible(players[0], PlayerAction.MOVE_ACTION) assert not turns.is_action_possible(players[1], PlayerAction.SHIFT_ACTION) assert not turns.is_action_possible(players[1], PlayerAction.MOVE_ACTION) turns.perform_action(players[0], PlayerAction.SHIFT_ACTION) turns.perform_action(players[0], PlayerAction.MOVE_ACTION) turns.perform_action(players[1], PlayerAction.SHIFT_ACTION) assert not turns.is_action_possible(players[0], PlayerAction.SHIFT_ACTION) assert not turns.is_action_possible(players[0], PlayerAction.MOVE_ACTION) assert not turns.is_action_possible(players[1], PlayerAction.SHIFT_ACTION) assert turns.is_action_possible(players[1], PlayerAction.MOVE_ACTION) turns.perform_action(players[1], PlayerAction.MOVE_ACTION) assert turns.is_action_possible(players[0], PlayerAction.SHIFT_ACTION) assert not turns.is_action_possible(players[0], PlayerAction.MOVE_ACTION) assert not turns.is_action_possible(players[1], PlayerAction.SHIFT_ACTION) assert not turns.is_action_possible(players[1], PlayerAction.MOVE_ACTION) turns.perform_action(players[0], PlayerAction.SHIFT_ACTION)
def test_is_action_possible_with_next_action(): """ Tests is_action_possible and constructor parameter next_action """ players = [Player(id, 0) for id in [9, 0, 3]] turns = Turns(players=players, next_action=PlayerAction(players[2], PlayerAction.MOVE_ACTION)) assert not turns.is_action_possible(players[0], PlayerAction.SHIFT_ACTION) assert not turns.is_action_possible(players[1], PlayerAction.SHIFT_ACTION) assert turns.is_action_possible(players[2], PlayerAction.MOVE_ACTION) turns.perform_action(players[2], PlayerAction.MOVE_ACTION) assert turns.is_action_possible(players[0], PlayerAction.SHIFT_ACTION) assert not turns.is_action_possible(players[1], PlayerAction.SHIFT_ACTION) turns.perform_action(players[0], PlayerAction.SHIFT_ACTION) turns.perform_action(players[0], PlayerAction.MOVE_ACTION) assert not turns.is_action_possible(players[0], PlayerAction.SHIFT_ACTION) assert turns.is_action_possible(players[1], PlayerAction.SHIFT_ACTION)
def test_perform_invalid_action_does_not_alter_turns(): """ Tests perform_action """ player1, player2 = Player(7, 0), Player(11, 0) turns = Turns(players=[player1, player2]) with pytest.raises(TurnActionViolationException): turns.perform_action(player2, PlayerAction.SHIFT_ACTION) turns.perform_action(player1, PlayerAction.SHIFT_ACTION) turns.perform_action(player1, PlayerAction.MOVE_ACTION) turns.perform_action(player2, PlayerAction.SHIFT_ACTION) with pytest.raises(TurnActionViolationException): turns.perform_action(player2, PlayerAction.SHIFT_ACTION)
def test_perform_invalid_action_should_raise(): """ Tests perform_action """ player1, player2 = Player(7, 0), Player(11, 0) turns = Turns(players=[player1, player2]) with pytest.raises(TurnActionViolationException): turns.perform_action(player2, PlayerAction.SHIFT_ACTION)
def test_given_delay_with_two_players__when_player1_leaves_during_wait__another_wait_leads_to_player2_shift( ): turns = Turns(prepare_delay=timedelta(milliseconds=30)) player1, player2 = Player(1), Player(2) callback1, callback2 = Mock(), Mock() turns.add_player(player1, turn_callback=callback1) turns.add_player(player2, turn_callback=callback2) turns.start() time.sleep(timedelta(milliseconds=50).total_seconds()) turns.perform_action(player1, PlayerAction.SHIFT_ACTION) time.sleep(timedelta(milliseconds=5).total_seconds()) turns.remove_player(player1) callback2.reset_mock() time.sleep(timedelta(milliseconds=50).total_seconds()) assert callback2.call_count == 1 assert turns.next_player_action().player == player2 assert turns.next_player_action().action == PlayerAction.SHIFT_ACTION
def test_remove_second_player_next_player_action_should_be_remaining_player(): """ Tests after removing the second of two players, next_player_action() should not return None """ turns = Turns() player1, player2 = Mock(), Mock() turns.add_player(player1, turn_callback=player1.callback) turns.add_player(player2, turn_callback=player2.callback) turns.start() turns.perform_action(player1, PlayerAction.SHIFT_ACTION) turns.perform_action(player1, PlayerAction.MOVE_ACTION) player1.callback.reset_mock() turns.remove_player(player2) assert turns.next_player_action() is not None assert turns.next_player_action().player is player1 player1.callback.assert_called_once()
def test_given_delay_with_two_players__when_player1_leaves_during_wait__has_prepare_player2_state( ): turns = Turns(prepare_delay=timedelta(milliseconds=30)) player1, player2 = Mock(), Mock() turns.add_player(player1, turn_callback=player1.callback) turns.add_player(player2, turn_callback=player2.callback) turns.start() time.sleep(timedelta(milliseconds=50).total_seconds()) turns.perform_action(player1, PlayerAction.SHIFT_ACTION) time.sleep(timedelta(milliseconds=2).total_seconds()) turns.remove_player(player1) assert player2.callback.call_count == 1 assert turns.next_player_action().player == player2 assert turns.next_player_action().action == PlayerAction.PREPARE_SHIFT
def test_next_player_action_should_be_none_without_players(): """ Tests next_player_action """ turns = Turns() assert turns.next_player_action() is None
def test_next_player_action_with_one_player_should_be_shift(): """ Tests next_player_action """ player = Player(7, 0) turns = Turns(players=[player]) assert turns.next_player_action() == PlayerAction( player, PlayerAction.SHIFT_ACTION)
def given_delay__when_start__is_in_prepare_state(): player = Player(0) turns = Turns(prepare_delay=timedelta(milliseconds=10), players=[player]) turns.start() assert turns.next_player_action().action == PlayerAction.PREPARE_SHIFT