コード例 #1
0
ファイル: bot.py プロジェクト: jan-g/remoter
    async def get_ships(self, pn):
        self.pn = pn
        print("Player {} pick your ships!".format(pn))
        print(
            "Use: R C D for input (R: row; C: column; D = A for across, D for down)"
        )

        b = Board()
        for ship, size in ships:
            possibles = [
                p for p in possible_placements(b, size)
                if all({(c.x + dx, c.y + dy) not in b.sea
                        for dx in (-1, 0, 1) for dy in (-1, 0, 1)
                        for c in p.cells})
            ]

            place = random.choice(possibles)
            b.add_ship(place.x, place.y, place.dx, place.dy, place.size)

            print('Adding {} at ({}, {}) - ({}, {})'.format(
                ship, place.x, place.y, place.x + place.dx * (place.size - 1),
                place.y + place.dy * (place.size - 1)))
            assert await self.game.add_ship(pn, place.x, place.y, place.dx,
                                            place.dy, place.size)
            print()
            await self.display()

        print()
        print("Your final board:")
        await self.display()
コード例 #2
0
    def __init__(self, **kwargs):
        self.grid_size = kwargs.get('grid_size', 10)
        self.max_index = kwargs.get('N', 10)
        self.end_game = False
        self.left_board = Board(self.max_index,
                                self.grid_size,
                                draw_all_ships=False,
                                offset_x=10,
                                offset_y=30)
        self.right_board = Board(self.max_index,
                                 self.grid_size,
                                 draw_all_ships=False,
                                 offset_x=30 + self.grid_size * self.max_index,
                                 offset_y=30)

        if VERBOSE:
            self.number_of_players = 0
        else:
            self.number_of_players = self.welcome_screen()

        if self.number_of_players == 2:
            self.left_player = Human(self.left_board, arrow_keymap)
            self.right_player = Human(self.right_board, wasd_keymap)
        elif self.number_of_players == 1:
            self.left_player = Human(self.left_board, arrow_keymap)
            self.right_player = Enemy(self.right_board)
        else:
            self.left_player = Enemy(self.left_board)
            self.right_player = Enemy(self.right_board)

        pyxel.init(*SCREEN_SIZE,
                   caption=DEFAULT_CAPTION,
                   fps=FPS,
                   quit_key=pyxel.KEY_Q)
        pyxel.run(self.update, self.draw)
コード例 #3
0
ファイル: test_board.py プロジェクト: jan-g/remoter
def test_ship_must_lie_on_board():
    b = Board()

    with pytest.raises(ValueError):
        b.add_ship(0, 0, -1, 0, 2)

    with pytest.raises(ValueError):
        b.add_ship(1, 1, 0, -1, 3)

    with pytest.raises(ValueError):
        b.add_ship(9, 9, 1, 0, 2)

    with pytest.raises(ValueError):
        b.add_ship(0, 0, 0, 1, 11)
コード例 #4
0
ファイル: test_board.py プロジェクト: jan-g/remoter
def test_hit():
    b = Board()
    b.add_counter(5, 5)

    assert b.potshot(5, 5) == Board.HIT
    assert b.defeated()
    b.display()
コード例 #5
0
def parse_board(board_repr):
    visible_grid_repr = ""
    number_of_ship_fields_to_mark_in_rows = []
    repr_string_lines = board_repr.split("\n")
    for line_number, line in enumerate(repr_string_lines[1:], 1):
        if re.fullmatch(r"^╚═+╝$", line):
            number_of_ship_fields_to_mark_in_columns = [
                int(x) for x in re.findall(r"\d+", repr_string_lines[line_number + 1])
            ]
            break
        match = re.fullmatch(r"^║ (.+) ║\((\d+)\)$", line)
        visible_grid_repr += match.group(1) + "\n"
        number_of_ship_fields_to_mark_in_rows.append(int(match.group(2)))
    visible_grid = parse_fieldtypegrid(visible_grid_repr.strip("\n"))
    grid = FieldTypeGrid(
        [[FieldType.SEA] * (len(visible_grid[0]) + 2)]
        + [[FieldType.SEA, *row, FieldType.SEA] for row in visible_grid]
        + [[FieldType.SEA] * (len(visible_grid[0]) + 2)]
    )
    return Board(
        grid,
        {
            Series.ROW: [0, *number_of_ship_fields_to_mark_in_rows, 0],
            Series.COLUMN: [0, *number_of_ship_fields_to_mark_in_columns, 0],
        },
    )
コード例 #6
0
    def mark_subfleet_of_biggest_remaining_ships(self) -> None:
        """Determine the size of the largest ship remaining in the
        Puzzle fleet and mark the entire subfleet of those ships onto
        the Puzzle board.

        If the board offers more available "slots" in which all subfleet
        ships can be marked, then branch the puzzle solving by marking
        the subfleet in each possible slot combination.

        If no ships are remaining in the fleet, that means a new puzzle
        solution has been found.

        """
        if self.fleet.has_ships_remaining():
            ship_size = self.fleet.longest_ship_size
            max_possible_subfleet = self.board.get_possible_ships_of_size(
                ship_size)
            if len(max_possible_subfleet) == self.fleet.size_of_subfleet(
                    ship_size):
                with contextlib.suppress(InvalidShipPlacementException):
                    self.mark_ship_group(max_possible_subfleet)
            else:
                for possible_subfleet in itertools.combinations(
                        max_possible_subfleet,
                        self.fleet.size_of_subfleet(ship_size)):
                    puzzle_branch = Puzzle(Board.get_copy_of(self.board),
                                           Fleet.get_copy_of(self.fleet))
                    with contextlib.suppress(InvalidShipPlacementException):
                        puzzle_branch.mark_ship_group(set(possible_subfleet))
        else:
            self.__class__.solutions.append(self.board.repr(False))
コード例 #7
0
ファイル: game.py プロジェクト: jan-g/remoter
    async def new_player(self, plr):
        if len(self.players) < 2:
            self.players.append(plr)

            # Keep tabs on this player's id number
            pn = plr.pn = len(self.players)

            self.boards[pn] = Board()

            # This player isn't yet ready. We track this because multiple
            # players can join in parallel; when each becomes ready, we check
            # to determine if everyone is good to go.
            plr.ready = False

            # Tell the player who they are, and ask them for their ship placements.
            await plr.get_ships(pn)

            # Once they're ready, check if everyone else is too.
            plr.ready = True

            # Is everyone ready?
            if len(self.players) > 1 and all(p.ready for p in self.players):
                # Could just use "await self.game_loop()" here.
                asyncio.create_task(self.game_loop())
        else:
            await plr.print("Too many players already connected to this game.")
            await plr.exit(0)
コード例 #8
0
    def load_puzzle(cls, path: Optional[pathlib.Path] = None) -> "Puzzle":
        """Read the puzzle input file and create a Puzzle object from
        its data.

        Args:
            path (Optional[pathlib.Path]): Path to the input file.
                Defaults to None, in which case the default input file
                location is used.

        Returns:
            battleships.puzzle.Puzzle: Newly created Puzzle object.

        """
        if path:
            input_data = Puzzle.parse_input_data_from_file(path)
        else:
            input_data = Puzzle.parse_input_data_from_file(
                params.INPUT_FILE_PATH)
        return Puzzle(
            Board.parse_board(
                input_data.grid,
                input_data.solution_ship_fields_in_rows,
                input_data.solution_ship_fields_in_cols,
            ),
            input_data.fleet,
        )
コード例 #9
0
ファイル: test_board.py プロジェクト: jan-g/remoter
def test_board():
    b = Board()
    b.add_counter(0, 0)
    b.add_counter(9, 9)

    b.display()

    assert not b.defeated()
コード例 #10
0
 def test___init__(self):
     board = Board(
         unittest.mock.sentinel.grid,
         unittest.mock.sentinel.number_of_ship_fields_to_mark_in_series,
     )
     self.assertTrue(board.grid is unittest.mock.sentinel.grid)
     self.assertTrue(
         board.number_of_ship_fields_to_mark_in_series
         is unittest.mock.sentinel.number_of_ship_fields_to_mark_in_series
     )
コード例 #11
0
    def test___eq__(self):
        other_board = parse_board(self.sample_board_repr)
        other_board_orig = Board.get_copy_of(other_board)
        self.assertTrue(self.sample_board.__eq__(other_board))
        self.assertEqual(other_board, other_board_orig)

        other_board.grid[1][1] = FieldType.UNKNOWN
        other_board_orig = Board.get_copy_of(other_board)
        self.assertFalse(self.sample_board.__eq__(other_board))
        self.assertEqual(other_board, other_board_orig)

        other_board.grid[1][1] = FieldType.SEA
        other_board_orig = Board.get_copy_of(other_board)
        self.assertTrue(self.sample_board.__eq__(other_board))
        self.assertEqual(other_board, other_board_orig)

        other_board.number_of_ship_fields_to_mark_in_series[Series.ROW][6] = 5
        other_board_orig = Board.get_copy_of(other_board)
        self.assertFalse(self.sample_board.__eq__(other_board))
        self.assertEqual(other_board, other_board_orig)

        self.assertFalse(self.sample_board.__eq__(self.sample_board.grid))
コード例 #12
0
ファイル: test_board.py プロジェクト: jan-g/remoter
def test_ship_must_not_be_adjacent_to_another():
    b = Board()
    b.add_ship(5, 5, 0, 0, 1)

    for dx in -1, 0, 1:
        for dy in -1, 0, 1:
            with pytest.raises(ValueError):
                b.add_ship(5 + dx, 5 + dy, 0, 0, 1)
コード例 #13
0
    def test_parse_board(self):
        input_grid = parse_fieldtypegrid(
            " .   .   .   .   .   .   .   .   .   . \n"
            " x   .   .   .   O   .   .   .   .   . \n"
            " .   .   O   .   O   .   .   x   .   . \n"
            " .   .   O   .   O   .   x   .   .   O \n"
            " x   .   O   .   .   .   x   .   .   . \n"
            " .   .   .   .   O   .   x   .   .   . \n"
            " .   x   .   .   O   .   x   .   .   . \n"
            " x   .   .   x   .   .   .   .   O   O \n"
            " .   .   .   .   .   .   .   .   .   . \n"
            " O   .   O   O   O   O   .   O   O   . "
        )
        input_number_of_ship_fields_to_mark_in_rows = [0, 2, 2, 6, 1, 5, 1, 2, 5, 7]
        input_number_of_ship_fields_to_mark_in_cols = [2, 1, 5, 4, 9, 5, 5, 1, 2, 3]

        input_grid_orig = input_grid
        input_number_of_ship_fields_to_mark_in_rows_orig = (
            input_number_of_ship_fields_to_mark_in_rows
        )
        input_number_of_ship_fields_to_mark_in_cols_orig = (
            input_number_of_ship_fields_to_mark_in_cols
        )

        parsed_board = Board.parse_board(
            input_grid,
            input_number_of_ship_fields_to_mark_in_rows,
            input_number_of_ship_fields_to_mark_in_cols,
        )
        self.assertEqual(self.sample_board, parsed_board)
        self.assertFalse(input_grid is parsed_board.grid)
        self.assertEqual(input_grid, input_grid_orig)
        self.assertFalse(
            input_number_of_ship_fields_to_mark_in_rows
            is parsed_board.number_of_ship_fields_to_mark_in_series[Series.ROW]
        )
        self.assertEqual(
            input_number_of_ship_fields_to_mark_in_rows,
            input_number_of_ship_fields_to_mark_in_rows_orig,
        )
        self.assertFalse(
            input_number_of_ship_fields_to_mark_in_cols
            is parsed_board.number_of_ship_fields_to_mark_in_series[Series.COLUMN]
        )
        self.assertEqual(
            input_number_of_ship_fields_to_mark_in_cols,
            input_number_of_ship_fields_to_mark_in_cols_orig,
        )
コード例 #14
0
class Game:
    def __init__(self, **kwargs):
        self.grid_size = kwargs.get('grid_size', 10)
        self.max_index = kwargs.get('N', 10)
        self.end_game = False
        self.left_board = Board(self.max_index,
                                self.grid_size,
                                draw_all_ships=False,
                                offset_x=10,
                                offset_y=30)
        self.right_board = Board(self.max_index,
                                 self.grid_size,
                                 draw_all_ships=False,
                                 offset_x=30 + self.grid_size * self.max_index,
                                 offset_y=30)

        if VERBOSE:
            self.number_of_players = 0
        else:
            self.number_of_players = self.welcome_screen()

        if self.number_of_players == 2:
            self.left_player = Human(self.left_board, arrow_keymap)
            self.right_player = Human(self.right_board, wasd_keymap)
        elif self.number_of_players == 1:
            self.left_player = Human(self.left_board, arrow_keymap)
            self.right_player = Enemy(self.right_board)
        else:
            self.left_player = Enemy(self.left_board)
            self.right_player = Enemy(self.right_board)

        pyxel.init(*SCREEN_SIZE,
                   caption=DEFAULT_CAPTION,
                   fps=FPS,
                   quit_key=pyxel.KEY_Q)
        pyxel.run(self.update, self.draw)

    @staticmethod
    def welcome_screen(linewidth=80, fillchar='='):
        print("Battleships!".center(linewidth, fillchar))
        print("\n\n")
        pre = "! Human Error Detected !\n"
        while True:
            try:
                humans = int(input("How many HUMAN players (0, 1, 2)?"))
            except ValueError:
                print(pre + "Integer number of humans only")
                continue

            if 0 <= humans <= 2:
                return humans
            elif humans < 0:
                print(pre + "Real quantities of humans only")
            elif humans > 2:
                print(pre + "Excess of humans, cannot comply")

    def game_over(self):
        winner = 'left' if not self.left_board.ships else 'right'
        sx, sy = SCREEN_SIZE
        sx //= 3
        sy = self.grid_size * self.max_index + 32
        blurb = [
            'Game Over!', f'Player {winner} wins!',
            f'Player One Score : {self.left_board.score():03}',
            f'Player Two Score : {self.right_board.score():03}'
        ]
        blurb = "\n".join(blurb)

        pyxel.text(sx, sy, blurb, TEXT_COLOUR)

    def update(self):
        if self.end_game:
            return
        self.left_player.update()
        self.right_player.update()

        if not self.left_board.ships or not self.right_board.ships:
            self.end_game = True

    def draw(self):
        pyxel.cls(BACKGROUND_COLOUR)
        if self.end_game:
            self.game_over()

        self.left_board.draw()
        self.right_board.draw()

        self.left_player.draw()
        self.right_player.draw()
コード例 #15
0
ファイル: test_board.py プロジェクト: jan-g/remoter
def test_near_miss():
    b = Board()
    b.add_counter(3, 3)

    assert Board.NEAR == b.potshot(2, 2)
    assert not b.defeated()
コード例 #16
0
 def test_get_copy_of(self):
     sample_board_orig = copy.deepcopy(self.sample_board)
     actual_copy = Board.get_copy_of(self.sample_board)
     self.assertFalse(actual_copy is self.sample_board)
     self.assertEqual(actual_copy, self.sample_board)
     self.assertEqual(self.sample_board, sample_board_orig)
コード例 #17
0
ファイル: test_board.py プロジェクト: jan-g/remoter
def test_miss():
    b = Board()
    b.add_counter(3, 3)

    assert Board.MISS == b.potshot(1, 2)
    assert not b.defeated()
コード例 #18
0
        def find_puzzles(  # pylint: disable=W9015
            ship_group: Set[Ship],
            covered_positions: Set[Position],
            positions_to_cover: List[Position],
            available_coverings: Dict[Ship, Set[Position]],
            puzzle: Puzzle,
        ) -> None:
            """Build a list of possible Puzzle objects created by
            branching a given Puzzle object and covering all given
            positions with a single ship from a given ship set.

            Depth-First Search (DFS) algorithm is used.

            Args:
                ship_group (Set[battleships.ship.Ship]): Group of
                    previously selected ships from available_coverings.
                covered_positions (Set[battleships.grid.Position]): Set
                    of positions covered by ships in ship_group.
                positions_to_cover (List[battleships.grid.Position]):
                    Positions remaining to be covered by ships.
                available_coverings(Dict[
                    battleships.ship.Ship, Set[battleships.grid.Position
                    ]]): Mapping of remaining ships and sets of
                    positions that each ship covers. Does not contain
                    any of the ships in ship_group. The sets of ships do
                    not contain any of the positions in
                    covered_positions.
                puzzle (battleships.puzzle.Puzzle): Current Puzzle
                    object.

            """
            if not positions_to_cover:
                puzzles.append(puzzle)
                return
            remaining_position = positions_to_cover[0]
            ship_candidates = [
                ship for ship, positions in available_coverings.items()
                if remaining_position in positions
            ]
            if not ship_candidates:
                return
            for ship_candidate in ship_candidates:
                if puzzle.board.can_fit_ship(
                        ship_candidate
                ) and not puzzle.ship_group_exceeds_fleet([ship_candidate]):
                    ship_candidate_positions = available_coverings[
                        ship_candidate]
                    new_positions_to_cover = [
                        position for position in positions_to_cover
                        if position not in ship_candidate_positions
                    ]
                    new_coverings = {
                        ship: {*ship_positions}
                        for ship, ship_positions in
                        available_coverings.items() if ship != ship_candidate
                    }
                    new_puzzle = Puzzle(Board.get_copy_of(puzzle.board),
                                        Fleet.get_copy_of(puzzle.fleet))
                    new_puzzle.board.mark_ship_and_surrounding_sea(
                        ship_candidate)
                    new_puzzle.board.mark_sea_in_series_with_no_rem_ship_fields(
                    )
                    new_puzzle.fleet.remove_ship_of_size(ship_candidate.size)
                    find_puzzles(
                        ship_group.union({ship_candidate}),
                        covered_positions.union(ship_candidate_positions),
                        new_positions_to_cover,
                        new_coverings,
                        new_puzzle,
                    )