Example #1
0
def test_valid_place_ship():
    board = Board(BOARD_SIZE)
    board.place_ship(Battleship(), Point(4, 5), Orientation.Horizontal)

    assert board.all_ship_locations == {
        Point(4, 5), Point(5, 5),
        Point(6, 5), Point(7, 5)
    }
Example #2
0
    def __init__(self, players, game_id):

        # Set game_id
        self.game_id = game_id

        # Create boards
        self.first_player_board = Board()
        self.second_player_board = Board()

        # Import the players
        bots_path = "bots"
        self.player_bots = [
            getattr(importlib.import_module(f"{bots_path}.{p}"), p)()
            for p in players
        ]

        self.first_player = self.player_bots[0]
        self.second_player = self.player_bots[1]
Example #3
0
def test_shoot_valid():
    board = Board(BOARD_SIZE)

    board.shoot(Point(7, 4))
    board.shoot(Point(0, BOARD_SIZE - 1))

    assert board.shot_locations == {Point(7, 4), Point(0, BOARD_SIZE - 1)}
Example #4
0
def test_shoot_invalid_already_shot():
    board = Board(BOARD_SIZE)

    board.shoot(Point(1, 1))
    board.shoot(Point(5, 2))

    with pytest.raises(InvalidShotException):
        board.shoot(Point(1, 1))
Example #5
0
def test_create_board():
    board = Board(BOARD_SIZE)

    assert board.shot_locations == set()
    assert board.all_ship_locations == set()
    assert board.is_board_lost() is False
Example #6
0
def test_game_is_won():
    board = Board(BOARD_SIZE)

    assert board.is_board_lost() is False

    board.place_ship(Destroyer(), Point(5, 1), Orientation.Horizontal)

    assert board.is_board_lost() is False

    board.shoot(Point(5, 1))

    assert board.is_board_lost() is False

    board.shoot(Point(5, 2))

    assert board.is_board_lost() is True

    board.shoot(Point(BOARD_SIZE - 1, BOARD_SIZE - 1))

    assert board.is_board_lost() is True
Example #7
0
def test_shoot_invalid_off_board():
    board = Board(BOARD_SIZE)

    with pytest.raises(InvalidShotException):
        board.shoot(Point(BOARD_SIZE, BOARD_SIZE))
Example #8
0
def test_invalid_place_ship():
    board = Board(BOARD_SIZE)

    with pytest.raises(InvalidShipPlacementException):
        board.place_ship(Battleship(), Point(BOARD_SIZE - 1, BOARD_SIZE - 1),
                         Orientation.Vertical)
Example #9
0
class Game:
    def __init__(self, players, game_id):

        # Set game_id
        self.game_id = game_id

        # Create boards
        self.first_player_board = Board()
        self.second_player_board = Board()

        # Import the players
        bots_path = "bots"
        self.player_bots = [
            getattr(importlib.import_module(f"{bots_path}.{p}"), p)()
            for p in players
        ]

        self.first_player = self.player_bots[0]
        self.second_player = self.player_bots[1]

    def _check_valid_placements(self, p1_placements, p2_placements):
        if not p1_placements and p2_placements:
            logging.error(
                f'{self.first_player.name} screwed up ship placement - {self.second_player.name} wins!'
            )
            winner = self.second_player.name
        elif not p2_placements and p1_placements:
            logging.error(
                f'{self.second_player.name} screwed up ship placement - {self.first_player.name} wins!'
            )
            winner = self.first_player.name
        elif not p1_placements and not p2_placements:
            logging.error(
                'Both players screwed up ship placement - no one wins!')
            winner = None
        else:
            logging.info(
                'Ship placements were both valid - continuing the game!')
            winner = None

        return winner

    def play_game(self):

        logging.info(
            f'------------ Starting game {self.game_id} between {self.first_player.name} and {self.second_player.name} ------------'
        )

        # Perform ship placement (Keep retrying until we get a correct placement). If we don't, end the game here
        try:
            p1_placements = self._place_ships(self.first_player,
                                              self.first_player_board)
        except MaxRetriesExceededException:
            p1_placements = []

        try:
            p2_placements = self._place_ships(self.second_player,
                                              self.second_player_board)
        except MaxRetriesExceededException:
            p2_placements = []

        # Check to see if someone screwed up ship placement
        early_winner = self._check_valid_placements(
            p1_placements,
            p2_placements,
        )

        # If someone screwed up placement, end the game early
        if early_winner:
            return {
                "id": self.game_id,
                "p1_name": self.first_player.name,
                "p2_name": self.second_player.name,
                "p1_ship_placements": [],
                "p2_ship_placements": [],
                "p1_shots": [],
                "p2_shots": [],
                "winner": early_winner
            }

        p1_shots = []
        p2_shots = []

        # Game loop - get shots until a player wins
        while True:

            # Get first player shot
            try:
                p1_shot, p1_is_hit, p1_is_sunk, p1_ship_sunk, error = self._do_shot(
                    self.first_player, self.second_player_board)
                p1_shots.append(p1_shot)
                logging.info(
                    f'{self.first_player.name} shot at {p1_shot} and {"hit" if p1_is_hit else "missed"}'
                )

                if p1_is_sunk:
                    logging.info(
                        f'{self.first_player.name} sunk {self.second_player.name}\'s {p1_ship_sunk}!'
                    )

                self.first_player.last_shot_status = {
                    'shot': p1_shot,
                    'is_hit': p1_is_hit,
                    'is_sunk': p1_is_sunk,
                    'ship_sunk': p1_ship_sunk,
                    'error': error
                }

            except MaxRetriesExceededException:
                self.first_player.last_shot_status = {
                    'shot': None,
                    'is_hit': None,
                    'is_sunk': None,
                    'ship_sunk': None,
                    'error': MaxRetriesExceededException
                }

            if self.second_player_board.is_board_lost():
                winner = self.first_player.name
                break

            # Get second player shot
            try:
                p2_shot, p2_is_hit, p2_is_sunk, p2_ship_sunk, error = self._do_shot(
                    self.second_player, self.first_player_board)
                p2_shots.append(p2_shot)
                logging.info(
                    f'{self.second_player.name} shot at {p2_shot} and {"hit" if p2_is_hit else "missed"}'
                )

                if p2_is_sunk:
                    logging.info(
                        f'{self.second_player.name} sunk {self.first_player.name}\'s {p2_ship_sunk}!'
                    )

                self.second_player.last_shot_status = {
                    'shot': p2_shot,
                    'is_hit': p2_is_hit,
                    'is_sunk': p2_is_sunk,
                    'ship_sunk': p2_ship_sunk,
                    'error': error
                }

            except MaxRetriesExceededException:
                self.second_player.last_shot_status = {
                    'shot': None,
                    'is_hit': None,
                    'is_sunk': None,
                    'ship_sunk': None,
                    'error': MaxRetriesExceededException
                }

            if self.first_player_board.is_board_lost():
                winner = self.second_player.name
                break

        logging.info(f'{winner} wins!')

        # Return completed game data
        game_data = {
            "id": self.game_id,
            "p1_name": self.first_player.name,
            "p2_name": self.second_player.name,
            "p1_ship_placements": list(p1_placements),
            "p2_ship_placements": list(p2_placements),
            "p1_shots": list(p1_shots),
            "p2_shots": list(p2_shots),
            "winner": winner,
        }

        return game_data

    @retry((InvalidShipPlacementException, stopit.utils.TimeoutException))
    def _place_ships(self, player, board):

        with stopit.ThreadingTimeout(MAX_PLACE_TIME, swallow_exc=False):
            ship_placements = player.get_ship_placements(ships_to_place())

        # Ensure that the ship placements are of the correct format
        try:
            assert isinstance(ship_placements,
                              list)  # Ensure we have a list of ship placements
            assert len(ship_placements) == len(ships_to_place(
            ))  # Ensure the correct number of ships have been placed
            assert set([s[0].ship_type for s in ship_placements]) == set([
                s.ship_type for s in ships_to_place()
            ])  # Ensure the correct ships types have been placed
        except AssertionError:
            raise InvalidShipPlacementException

        for ship, location, orientation in ship_placements:
            board.place_ship(ship, location, orientation)

        return board.all_ship_locations

    @retry((InvalidShotException, stopit.utils.TimeoutException))
    def _do_shot(self, player, board_to_shoot):

        error = None

        # Call get_shot and enforce the timeout
        with stopit.ThreadingTimeout(MAX_SHOT_TIME, swallow_exc=False):
            player_shot = player.get_shot()

        # If this succeeded, ensure the player is returning a point
        try:
            assert isinstance(player_shot, Point)
        # If not - fail
        except AssertionError:
            is_hit = None
            is_sunk = False
            ship_sunk = None
            error = NotAPointError

            return player_shot, is_hit, is_sunk, ship_sunk, error

        # finally, if everything is ok, try and perform the shot
        try:
            is_hit, is_sunk, ship_sunk = board_to_shoot.shoot(player_shot)
        except InvalidShotException:
            is_hit = None
            is_sunk = False
            ship_sunk = None
            error = InvalidShotException

        return player_shot, is_hit, is_sunk, ship_sunk, error