Esempio n. 1
0
 def test_solve(self):
     fleet = Fleet({3: 1, 2: 2, 1: 2})
     puzzle = Puzzle(
         parse_board("╔═════════════════════╗\n"
                     "║  x   x   x   x   .  ║(3)\n"
                     "║  x   x   x   x   x  ║(2)\n"
                     "║  x   x   O   x   x  ║(2)\n"
                     "║  x   .   x   x   x  ║(1)\n"
                     "║  x   x   x   x   x  ║(2)\n"
                     "╚═════════════════════╝\n"
                     "  (4) (0) (2) (1) (2) "),
         Fleet(fleet),
     )
     with unittest.mock.patch.object(
             battleships.puzzle.Puzzle,
             "decide_how_to_proceed") as mock_decide_how_to_proceed:
         puzzle.solve()
     self.assertEqual(
         puzzle.board,
         parse_board("╔═════════════════════╗\n"
                     "║  x   .   x   x   .  ║(3)\n"
                     "║  x   .   x   .   x  ║(2)\n"
                     "║  x   .   x   x   x  ║(3)\n"
                     "║  x   .   x   .   x  ║(1)\n"
                     "║  x   .   x   x   x  ║(2)\n"
                     "╚═════════════════════╝\n"
                     "  (4) (0) (3) (1) (2) "),
     )
     self.assertEqual(puzzle.fleet, fleet)
     mock_decide_how_to_proceed.assert_called_once_with({Position(3, 3)})
Esempio n. 2
0
    def test_load_puzzle(self):
        actual_puzzle = Puzzle.load_puzzle(params.INPUT_FILE_PATH)
        expected_puzzle = Puzzle(
            parse_board("╔═════════════════════════════════════════╗\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(0)\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(1)\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(3)\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(3)\n"
                        "║  x   x   O   x   x   x   x   x   x   x  ║(0)\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(1)\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(2)\n"
                        "║  x   x   .   x   .   x   x   .   x   x  ║(2)\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(0)\n"
                        "║  x   x   x   x   O   x   x   x   x   x  ║(6)\n"
                        "╚═════════════════════════════════════════╝\n"
                        "  (1) (1) (3) (1) (5) (1) (0) (2) (2) (2) "),
            Fleet({
                4: 1,
                3: 2,
                2: 3,
                1: 4
            }),
        )
        self.assertEqual(actual_puzzle.board, expected_puzzle.board)
        self.assertEqual(actual_puzzle.fleet, expected_puzzle.fleet)

        actual_puzzle = Puzzle.load_puzzle()
        expected_puzzle = Puzzle(
            parse_board("╔═════════════════════════════════════════╗\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(0)\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(1)\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(3)\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(3)\n"
                        "║  x   x   O   x   x   x   x   x   x   x  ║(0)\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(1)\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(2)\n"
                        "║  x   x   .   x   .   x   x   .   x   x  ║(2)\n"
                        "║  x   x   x   x   x   x   x   x   x   x  ║(0)\n"
                        "║  x   x   x   x   O   x   x   x   x   x  ║(6)\n"
                        "╚═════════════════════════════════════════╝\n"
                        "  (1) (1) (3) (1) (5) (1) (0) (2) (2) (2) "),
            Fleet({
                4: 1,
                3: 2,
                2: 3,
                1: 4
            }),
        )
        self.assertEqual(actual_puzzle.board, expected_puzzle.board)
        self.assertEqual(actual_puzzle.fleet, expected_puzzle.fleet)
Esempio n. 3
0
    def test___eq__(self):
        puzzle1 = Puzzle(
            parse_board("╔═════════════════════╗\n"
                        "║  x   x   x   x   x  ║(0)\n"
                        "║  x   x   x   x   .  ║(2)\n"
                        "║  x   x   x   x   x  ║(2)\n"
                        "║  x   .   x   x   x  ║(2)\n"
                        "║  x   x   x   x   x  ║(3)\n"
                        "╚═════════════════════╝\n"
                        "  (2) (1) (4) (1) (4) "),
            Fleet({
                4: 1,
                3: 2,
                1: 3
            }),
        )
        puzzle2 = Puzzle(
            parse_board("╔═════════════════════╗\n"
                        "║  x   x   x   x   x  ║(0)\n"
                        "║  x   x   x   x   .  ║(2)\n"
                        "║  x   x   x   x   x  ║(2)\n"
                        "║  x   .   x   x   x  ║(2)\n"
                        "║  x   x   x   x   x  ║(3)\n"
                        "╚═════════════════════╝\n"
                        "  (2) (1) (4) (1) (4) "),
            Fleet({
                4: 1,
                3: 2,
                1: 3
            }),
        )

        puzzle2_orig = copy.deepcopy(puzzle2)
        self.assertTrue(puzzle1.__eq__(puzzle2))
        self.assertEqual(puzzle2, puzzle2_orig)

        puzzle2.board.grid[1][1] = FieldType.SEA
        puzzle2_orig = copy.deepcopy(puzzle2)
        self.assertFalse(puzzle1.__eq__(puzzle2))
        self.assertEqual(puzzle2, puzzle2_orig)

        puzzle2.board.grid[1][1] = FieldType.UNKNOWN
        puzzle2.fleet = Fleet({4: 1, 3: 1, 1: 3})
        puzzle2_orig = copy.deepcopy(puzzle2)
        self.assertFalse(puzzle1.__eq__(puzzle2))
        self.assertEqual(puzzle2, puzzle2_orig)

        self.assertFalse(puzzle1.__eq__("foo"))
Esempio n. 4
0
    def test_parse_input_data_from_file(self, mocked_print):
        with unittest.mock.patch("battleships.puzzle.open",
                                 side_effect=FileNotFoundError):
            with self.assertRaises(FileNotFoundError):
                Puzzle.parse_input_data_from_file(unittest.mock.sentinel.path)
            mocked_print.assert_called_once_with(
                "Invalid input data file path.", file=Puzzle.ofile)

        mocked_print.reset_mock()
        actual_input_data = Puzzle.parse_input_data_from_file(
            params.INPUT_FILE_PATH)
        expected_grid = parse_fieldtypegrid(
            " x   x   x   x   x   x   x   x   x   x \n"
            " x   x   x   x   x   x   x   x   x   x \n"
            " x   x   x   x   x   x   x   x   x   x \n"
            " x   x   x   x   x   x   x   x   x   x \n"
            " x   x   O   x   x   x   x   x   x   x \n"
            " x   x   x   x   x   x   x   x   x   x \n"
            " x   x   x   x   x   x   x   x   x   x \n"
            " x   x   .   x   .   x   x   .   x   x \n"
            " x   x   x   x   x   x   x   x   x   x \n"
            " x   x   x   x   O   x   x   x   x   x ")
        expected_solution_ship_fields_in_rows = [0, 1, 3, 3, 1, 1, 2, 2, 0, 7]
        expected_solution_ship_fields_in_cols = [1, 1, 4, 1, 6, 1, 0, 2, 2, 2]
        expected_fleet = Fleet({4: 1, 3: 2, 2: 3, 1: 4})
        self.assertEqual(expected_grid, actual_input_data.grid)
        self.assertEqual(
            expected_solution_ship_fields_in_rows,
            actual_input_data.solution_ship_fields_in_rows,
        )
        self.assertEqual(
            expected_solution_ship_fields_in_cols,
            actual_input_data.solution_ship_fields_in_cols,
        )
        self.assertEqual(expected_fleet, actual_input_data.fleet)
Esempio n. 5
0
    def test_ship_group_exceeds_fleet(self):
        puzzle = Puzzle(unittest.mock.Mock(), Fleet({4: 1, 3: 2, 1: 1}))
        exceeding_ship_groups = (
            {Ship(unittest.mock.Mock(), 2, unittest.mock.Mock())},
            {
                Ship(unittest.mock.Mock(), 3, unittest.mock.Mock()),
                Ship(unittest.mock.Mock(), 3, unittest.mock.Mock()),
                Ship(unittest.mock.Mock(), 3, unittest.mock.Mock()),
            },
        )
        for ship_group in exceeding_ship_groups:
            with self.subTest():
                ship_group_orig = set(ship_group)
                self.assertTrue(puzzle.ship_group_exceeds_fleet(ship_group))
                self.assertEqual(ship_group, ship_group_orig)

        nonexceeding_ship_groups = (
            set(),
            {Ship(unittest.mock.Mock(), 3, unittest.mock.Mock())},
            {
                Ship(unittest.mock.Mock(), 4, unittest.mock.Mock()),
                Ship(unittest.mock.Mock(), 3, unittest.mock.Mock()),
                Ship(unittest.mock.Mock(), 3, unittest.mock.Mock()),
                Ship(unittest.mock.Mock(), 1, unittest.mock.Mock()),
            },
        )
        for ship_group in nonexceeding_ship_groups:
            with self.subTest():
                ship_group_orig = set(ship_group)
                self.assertFalse(puzzle.ship_group_exceeds_fleet(ship_group))
                self.assertEqual(ship_group, ship_group_orig)
Esempio n. 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))
Esempio n. 7
0
 def test_get_copy_of(self):
     for fleet in self.sample_fleets:
         with self.subTest():
             fleet_orig = copy.deepcopy(fleet)
             fleet_copy = Fleet.get_copy_of(fleet)
             self.assertFalse(fleet_copy is fleet)
             self.assertEqual(fleet_copy, fleet)
             self.assertEqual(fleet_copy, fleet_orig)
Esempio n. 8
0
 def setUp(self):
     super().setUp()
     self.sample_fleets = (
         Fleet({
             4: 1,
             3: 2,
             2: 3,
             1: 4
         }),
         Fleet({
             5: 7,
             2: 10,
             1: 1
         }),
         Fleet({2: 1}),
         Fleet({}),
     )
Esempio n. 9
0
 def test_remove_ship_of_size(self):
     expected_results = (Fleet({
         4: 1,
         3: 2,
         2: 3,
         1: 3
     }), Fleet({
         5: 7,
         2: 10
     }))
     for fleet, expected_result in zip(self.sample_fleets[:2],
                                       expected_results):
         with self.subTest():
             fleet.remove_ship_of_size(1)
             self.assertEqual(expected_result, fleet)
     for fleet in self.sample_fleets[2:]:
         with self.assertRaises(InvalidShipSizeException):
             fleet.remove_ship_of_size(1)
Esempio n. 10
0
    def parse_input_data_from_file(input_file_path: pathlib.Path) -> InputData:
        """Read puzzle data from the input file.

        Args:
            input_file_path (pathlib.Path): Path to file containing
                input data.

        Returns:
            battleships.puzzle.InputData: Parsed input data.

        Raises:
            FileNotFoundError: If the input file path is invalid or
                cannot be found.

        """
        try:
            file = open(input_file_path, "r", encoding="utf-8")  # pylint: disable=R1732
        except FileNotFoundError as error:
            print(params.MESSAGES.INVALID_INPUT_FILE_PATH, file=Puzzle.ofile)
            raise error
        fleet = Fleet()
        subfleets_count = int(next(file).strip())
        for _ in range(subfleets_count):
            ship_size, number_of_ships = (int(x)
                                          for x in next(file).strip().split())
            fleet.add_ships_of_size(ship_size, number_of_ships)
        board_size = int(next(file).strip())
        solution_ship_fields_in_rows = [
            int(x) for x in next(file).strip().split()
        ]
        solution_ship_fields_in_cols = [
            int(x) for x in next(file).strip().split()
        ]
        grid = FieldTypeGrid()
        for _ in range(board_size):
            grid.append([FieldType(ch) for ch in next(file).strip()])
        file.close()
        return InputData(
            grid,
            solution_ship_fields_in_rows,
            solution_ship_fields_in_cols,
            fleet,
        )
Esempio n. 11
0
 def test_add_ships_of_size(self):
     expected_results = (
         Fleet({
             4: 1,
             3: 2,
             2: 3,
             1: 7
         }),
         Fleet({
             5: 7,
             2: 10,
             1: 4
         }),
         Fleet({
             2: 1,
             1: 3
         }),
         Fleet({1: 3}),
     )
     for fleet, expected_result in zip(self.sample_fleets,
                                       expected_results):
         with self.subTest():
             fleet.add_ships_of_size(1, 3)
             self.assertEqual(expected_result, fleet)
Esempio n. 12
0
    def test_try_to_cover_all_ship_fields_to_be(self,
                                                mocked_decide_how_to_proceed):
        board_repr = ("╔═════════════════════╗\n"
                      "║  x   x   .   x   x  ║(1)\n"
                      "║  x   x   x   x   .  ║(1)\n"
                      "║  x   x   x   x   x  ║(2)\n"
                      "║  x   .   x   x   x  ║(2)\n"
                      "║  x   x   x   x   x  ║(3)\n"
                      "╚═════════════════════╝\n"
                      "  (2) (1) (4) (1) (4) ")
        fleet = Fleet({4: 1, 3: 1, 2: 1})

        puzzle = Puzzle(parse_board(board_repr), Fleet.get_copy_of(fleet))
        positions = {Position(1, 5)}
        positions_orig = set(positions)
        puzzle.try_to_cover_all_ship_fields_to_be(positions)
        self.assertEqual(positions, positions_orig)
        mocked_decide_how_to_proceed.assert_not_called()

        puzzle = Puzzle(parse_board(board_repr), Fleet.get_copy_of(fleet))
        positions = {
            Position(2, 3),
            Position(4, 3),
            Position(3, 5),
            Position(5, 5)
        }
        positions_orig = set(positions)
        puzzle.try_to_cover_all_ship_fields_to_be(positions)
        self.assertEqual(
            puzzle,
            Puzzle(
                parse_board("╔═════════════════════╗\n"
                            "║  x   .   .   .   x  ║(1)\n"
                            "║  .   .   O   .   .  ║(0)\n"
                            "║  .   .   O   .   O  ║(0)\n"
                            "║  .   .   O   .   O  ║(0)\n"
                            "║  x   .   O   .   O  ║(1)\n"
                            "╚═════════════════════╝\n"
                            "  (2) (1) (0) (1) (1) "),
                Fleet({2: 1}),
            ),
        )
        self.assertEqual(positions, positions_orig)
        mocked_decide_how_to_proceed.assert_called_once()

        mocked_decide_how_to_proceed.reset_mock()
        puzzle = Puzzle(parse_board(board_repr), Fleet.get_copy_of(fleet))
        positions = {Position(5, 3), Position(5, 5)}
        positions_orig = set({Position(5, 3), Position(5, 5)})
        puzzle.try_to_cover_all_ship_fields_to_be(positions)
        self.assertEqual(mocked_decide_how_to_proceed.call_count, 6)
        mocked_decide_how_to_proceed.assert_has_calls(
            [
                unittest.mock.call(
                    Puzzle(
                        parse_board("╔═════════════════════╗\n"
                                    "║  x   .   .   .   x  ║(1)\n"
                                    "║  .   .   O   .   .  ║(0)\n"
                                    "║  .   .   O   .   O  ║(0)\n"
                                    "║  .   .   O   .   O  ║(0)\n"
                                    "║  x   .   O   .   O  ║(1)\n"
                                    "╚═════════════════════╝\n"
                                    "  (2) (1) (0) (1) (1) "),
                        Fleet({2: 1}),
                    )),
                unittest.mock.call(
                    Puzzle(
                        parse_board("╔═════════════════════╗\n"
                                    "║  x   x   .   x   x  ║(1)\n"
                                    "║  x   x   x   .   .  ║(1)\n"
                                    "║  x   .   .   .   O  ║(1)\n"
                                    "║  .   .   O   .   O  ║(0)\n"
                                    "║  x   .   O   .   O  ║(1)\n"
                                    "╚═════════════════════╝\n"
                                    "  (2) (1) (2) (1) (1) "),
                        Fleet({4: 1}),
                    )),
                unittest.mock.call(
                    Puzzle(
                        parse_board("╔═════════════════════╗\n"
                                    "║  x   .   .   x   x  ║(1)\n"
                                    "║  x   .   x   .   .  ║(1)\n"
                                    "║  x   .   x   .   O  ║(1)\n"
                                    "║  .   .   .   .   O  ║(1)\n"
                                    "║  .   O   O   .   O  ║(0)\n"
                                    "╚═════════════════════╝\n"
                                    "  (2) (0) (3) (1) (1) "),
                        Fleet({4: 1}),
                    )),
                unittest.mock.call(
                    Puzzle(
                        parse_board("╔═════════════════════╗\n"
                                    "║  x   .   .   .   x  ║(1)\n"
                                    "║  .   .   O   .   .  ║(0)\n"
                                    "║  x   .   O   .   .  ║(1)\n"
                                    "║  .   .   O   .   O  ║(0)\n"
                                    "║  x   .   O   .   O  ║(1)\n"
                                    "╚═════════════════════╝\n"
                                    "  (2) (1) (0) (1) (2) "),
                        Fleet({3: 1}),
                    )),
                unittest.mock.call(
                    Puzzle(
                        parse_board("╔═════════════════════╗\n"
                                    "║  x   x   .   x   x  ║(1)\n"
                                    "║  x   .   .   .   .  ║(1)\n"
                                    "║  x   .   O   .   .  ║(1)\n"
                                    "║  .   .   O   .   O  ║(0)\n"
                                    "║  x   .   O   .   O  ║(1)\n"
                                    "╚═════════════════════╝\n"
                                    "  (2) (1) (1) (1) (2) "),
                        Fleet({4: 1}),
                    )),
                unittest.mock.call(
                    Puzzle(
                        parse_board("╔═════════════════════╗\n"
                                    "║  x   x   .   .   x  ║(1)\n"
                                    "║  x   x   x   .   .  ║(1)\n"
                                    "║  x   x   x   .   x  ║(2)\n"
                                    "║  x   .   .   .   .  ║(2)\n"
                                    "║  .   .   O   O   O  ║(0)\n"
                                    "╚═════════════════════╝\n"
                                    "  (2) (1) (3) (0) (3) "),
                        Fleet({
                            4: 1,
                            2: 1
                        }),
                    )),
            ],
            any_order=True,
        )
        self.assertEqual(positions, positions_orig)
Esempio n. 13
0
    def test_mark_ship_group(self, mocked_decide_how_to_proceed):
        board_repr = ("╔═════════════════════╗\n"
                      "║  x   x   x   x   x  ║(1)\n"
                      "║  x   x   x   x   .  ║(1)\n"
                      "║  x   x   x   x   x  ║(2)\n"
                      "║  x   .   x   x   x  ║(2)\n"
                      "║  x   x   x   x   x  ║(3)\n"
                      "╚═════════════════════╝\n"
                      "  (2) (1) (4) (1) (4) ")
        fleet = Fleet({4: 1, 3: 2, 1: 2})

        puzzle = Puzzle(parse_board(board_repr), Fleet.get_copy_of(fleet))
        ship_group = {
            Ship(Position(1, 3), 4, Series.COLUMN),
            Ship(Position(2, 3), 4, Series.COLUMN),
        }
        with self.assertRaises(InvalidShipPlacementException):
            puzzle.mark_ship_group(ship_group)

        # test when board is overmarked
        puzzle = Puzzle(parse_board(board_repr), Fleet.get_copy_of(fleet))
        ship_group = {
            Ship(Position(5, 2), 1, Series.ROW),
            Ship(Position(5, 4), 1, Series.ROW),
        }
        ship_group_orig = set(ship_group)
        puzzle.mark_ship_group(ship_group)
        self.assertEqual(
            puzzle.board,
            parse_board("╔═════════════════════╗\n"
                        "║  x   .   x   .   x  ║(1)\n"
                        "║  x   .   x   .   .  ║(1)\n"
                        "║  x   .   x   .   x  ║(2)\n"
                        "║  .   .   .   .   .  ║(2)\n"
                        "║  .   O   .   O   .  ║(1)\n"
                        "╚═════════════════════╝\n"
                        "  (2) (0) (4) (0) (4) "),
        )
        self.assertEqual(ship_group, ship_group_orig)
        mocked_decide_how_to_proceed.assert_not_called()

        mocked_decide_how_to_proceed.reset_mock()
        puzzle = Puzzle(parse_board(board_repr), Fleet.get_copy_of(fleet))
        fleet = Fleet({4: 1, 3: 2, 1: 2})
        ship_group = {
            Ship(Position(3, 3), 3, Series.COLUMN),
            Ship(Position(3, 5), 3, Series.COLUMN),
        }
        ship_group_orig = set(ship_group)
        puzzle.mark_ship_group(ship_group)
        self.assertEqual(
            puzzle,
            Puzzle(
                parse_board("╔═════════════════════╗\n"
                            "║  x   x   x   x   x  ║(1)\n"
                            "║  x   .   .   .   .  ║(1)\n"
                            "║  .   .   O   .   O  ║(0)\n"
                            "║  .   .   O   .   O  ║(0)\n"
                            "║  x   .   O   .   O  ║(1)\n"
                            "╚═════════════════════╝\n"
                            "  (2) (1) (1) (1) (1) "),
                Fleet({
                    4: 1,
                    1: 2
                }),
            ),
        )
        self.assertEqual(ship_group, ship_group_orig)
        mocked_decide_how_to_proceed.assert_called_once()
Esempio n. 14
0
    def test_mark_subfleet_of_biggest_remaining_ships(self):
        board_repr = ("╔═════════════════════╗\n"
                      "║  x   x   x   x   x  ║(0)\n"
                      "║  x   x   x   x   .  ║(2)\n"
                      "║  x   x   x   x   x  ║(2)\n"
                      "║  x   .   x   x   x  ║(2)\n"
                      "║  x   x   x   x   x  ║(3)\n"
                      "╚═════════════════════╝\n"
                      "  (2) (1) (4) (1) (4) ")

        fleet1 = Fleet({})
        puzzle1 = Puzzle(parse_board(board_repr), Fleet(fleet1))
        puzzle1.mark_subfleet_of_biggest_remaining_ships()
        self.assertEqual(puzzle1.board, parse_board(board_repr))
        self.assertEqual(puzzle1.fleet, fleet1)
        self.assertEqual(
            puzzle1.__class__.solutions,
            [
                "╔═════════════════════╗\n"
                "║  x   x   x   x   x  ║\n"
                "║  x   x   x   x   .  ║\n"
                "║  x   x   x   x   x  ║\n"
                "║  x   .   x   x   x  ║\n"
                "║  x   x   x   x   x  ║\n"
                "╚═════════════════════╝"
            ],
        )

        fleet2 = Fleet({4: 1, 3: 2, 1: 1})
        puzzle2 = Puzzle(parse_board(board_repr), Fleet(fleet2))
        with unittest.mock.patch.object(
                battleships.puzzle.Puzzle,
                "mark_ship_group") as mocked_try_to_mark_ship_group:
            puzzle2.mark_subfleet_of_biggest_remaining_ships()
            self.assertEqual(puzzle2,
                             Puzzle(parse_board(board_repr), Fleet(fleet2)))
            mocked_try_to_mark_ship_group.assert_called_once_with(
                {Ship(Position(2, 3), 4, Series.COLUMN)})

        fleet3 = Fleet({3: 2, 1: 1})
        puzzle3 = Puzzle(parse_board(board_repr), Fleet(fleet3))
        with unittest.mock.patch.object(
                battleships.puzzle.Puzzle,
                "mark_ship_group") as mocked_try_to_mark_ship_group:
            puzzle3.mark_subfleet_of_biggest_remaining_ships()
            self.assertEqual(puzzle3,
                             Puzzle(parse_board(board_repr), Fleet(fleet3)))
            self.assertEqual(mocked_try_to_mark_ship_group.call_count, 15)
            mocked_try_to_mark_ship_group.assert_has_calls(
                [
                    unittest.mock.call({
                        Ship(Position(5, 1), 3, Series.ROW),
                        Ship(Position(5, 2), 3, Series.ROW),
                    }),
                    unittest.mock.call({
                        Ship(Position(5, 1), 3, Series.ROW),
                        Ship(Position(5, 3), 3, Series.ROW),
                    }),
                    unittest.mock.call({
                        Ship(Position(5, 1), 3, Series.ROW),
                        Ship(Position(2, 3), 3, Series.COLUMN),
                    }),
                    unittest.mock.call({
                        Ship(Position(5, 1), 3, Series.ROW),
                        Ship(Position(3, 3), 3, Series.COLUMN),
                    }),
                    unittest.mock.call({
                        Ship(Position(5, 1), 3, Series.ROW),
                        Ship(Position(3, 5), 3, Series.COLUMN),
                    }),
                    unittest.mock.call({
                        Ship(Position(5, 2), 3, Series.ROW),
                        Ship(Position(5, 3), 3, Series.ROW),
                    }),
                    unittest.mock.call({
                        Ship(Position(5, 2), 3, Series.ROW),
                        Ship(Position(2, 3), 3, Series.COLUMN),
                    }),
                    unittest.mock.call({
                        Ship(Position(5, 2), 3, Series.ROW),
                        Ship(Position(3, 3), 3, Series.COLUMN),
                    }),
                    unittest.mock.call({
                        Ship(Position(5, 2), 3, Series.ROW),
                        Ship(Position(3, 5), 3, Series.COLUMN),
                    }),
                    unittest.mock.call({
                        Ship(Position(5, 3), 3, Series.ROW),
                        Ship(Position(2, 3), 3, Series.COLUMN),
                    }),
                    unittest.mock.call({
                        Ship(Position(5, 3), 3, Series.ROW),
                        Ship(Position(3, 3), 3, Series.COLUMN),
                    }),
                    unittest.mock.call({
                        Ship(Position(5, 3), 3, Series.ROW),
                        Ship(Position(3, 5), 3, Series.COLUMN),
                    }),
                    unittest.mock.call({
                        Ship(Position(2, 3), 3, Series.COLUMN),
                        Ship(Position(3, 3), 3, Series.COLUMN),
                    }),
                    unittest.mock.call({
                        Ship(Position(2, 3), 3, Series.COLUMN),
                        Ship(Position(3, 5), 3, Series.COLUMN),
                    }),
                    unittest.mock.call({
                        Ship(Position(3, 3), 3, Series.COLUMN),
                        Ship(Position(3, 5), 3, Series.COLUMN),
                    }),
                ],
                any_order=True,
            )
Esempio n. 15
0
    def test_get_possible_puzzles(self):
        puzzle = Puzzle(
            parse_board("╔═════════════════════╗\n"
                        "║  x   x   x   x   x  ║(0)\n"
                        "║  x   x   x   x   .  ║(2)\n"
                        "║  x   x   x   x   x  ║(2)\n"
                        "║  x   .   x   x   x  ║(2)\n"
                        "║  x   x   x   x   x  ║(3)\n"
                        "╚═════════════════════╝\n"
                        "  (2) (1) (4) (1) (4) "),
            Fleet({5: 1}),
        )
        ships_occupying_position = puzzle.board.get_possible_ships_occupying_positions(
            {Position(3, 3)}, puzzle.fleet.distinct_ship_sizes)
        ships_occupying_position_orig = copy.deepcopy(ships_occupying_position)
        self.assertEqual(puzzle.get_possible_puzzles(ships_occupying_position),
                         [])
        self.assertEqual(ships_occupying_position,
                         ships_occupying_position_orig)

        puzzle = Puzzle(
            parse_board("╔═════════════════════╗\n"
                        "║  x   x   x   x   x  ║(0)\n"
                        "║  x   x   x   x   .  ║(2)\n"
                        "║  x   x   x   x   x  ║(2)\n"
                        "║  x   .   x   x   x  ║(2)\n"
                        "║  x   x   x   x   x  ║(3)\n"
                        "╚═════════════════════╝\n"
                        "  (2) (1) (4) (1) (4) "),
            Fleet({
                4: 1,
                3: 2,
                1: 3
            }),
        )
        ships_occupying_position = puzzle.board.get_possible_ships_occupying_positions(
            {Position(2, 1), Position(3, 3),
             Position(5, 3)},
            puzzle.fleet.distinct_ship_sizes,
        )
        ships_occupying_position_orig = copy.deepcopy(ships_occupying_position)
        actual_possible_puzzles_list = puzzle.get_possible_puzzles(
            ships_occupying_position)
        self.assertEqual(ships_occupying_position,
                         ships_occupying_position_orig)
        expected_possible_puzzles_list = [
            Puzzle(
                parse_board("╔═════════════════════╗\n"
                            "║  .   .   .   .   .  ║(0)\n"
                            "║  O   .   .   .   .  ║(1)\n"
                            "║  .   .   O   .   x  ║(1)\n"
                            "║  .   .   .   .   x  ║(2)\n"
                            "║  O   O   O   .   .  ║(0)\n"
                            "╚═════════════════════╝\n"
                            "  (0) (0) (2) (1) (4) "),
                Fleet({
                    4: 1,
                    3: 1,
                    1: 1
                }),
            ),
            Puzzle(
                parse_board("╔═════════════════════╗\n"
                            "║  .   .   .   .   .  ║(0)\n"
                            "║  O   .   .   .   .  ║(1)\n"
                            "║  .   .   O   .   x  ║(1)\n"
                            "║  .   .   .   .   .  ║(2)\n"
                            "║  .   O   O   O   .  ║(0)\n"
                            "╚═════════════════════╝\n"
                            "  (1) (0) (2) (0) (4) "),
                Fleet({
                    4: 1,
                    3: 1,
                    1: 1
                }),
            ),
            Puzzle(
                parse_board("╔═════════════════════╗\n"
                            "║  .   .   .   .   .  ║(0)\n"
                            "║  O   .   .   .   .  ║(1)\n"
                            "║  .   .   O   .   x  ║(1)\n"
                            "║  x   .   .   .   .  ║(2)\n"
                            "║  .   .   O   O   O  ║(0)\n"
                            "╚═════════════════════╝\n"
                            "  (1) (1) (2) (0) (3) "),
                Fleet({
                    4: 1,
                    3: 1,
                    1: 1
                }),
            ),
            Puzzle(
                parse_board("╔═════════════════════╗\n"
                            "║  .   .   .   .   .  ║(0)\n"
                            "║  O   .   .   .   .  ║(1)\n"
                            "║  .   .   O   .   x  ║(1)\n"
                            "║  x   .   .   .   x  ║(2)\n"
                            "║  x   .   O   .   x  ║(2)\n"
                            "╚═════════════════════╝\n"
                            "  (1) (1) (2) (1) (4) "),
                Fleet({
                    4: 1,
                    3: 2
                }),
            ),
            Puzzle(
                parse_board("╔═════════════════════╗\n"
                            "║  .   .   .   .   .  ║(0)\n"
                            "║  O   .   O   .   .  ║(0)\n"
                            "║  .   .   O   .   x  ║(1)\n"
                            "║  x   .   O   .   x  ║(1)\n"
                            "║  x   .   O   .   x  ║(2)\n"
                            "╚═════════════════════╝\n"
                            "  (1) (1) (0) (1) (4) "),
                Fleet({
                    3: 2,
                    1: 2
                }),
            ),
            Puzzle(
                parse_board("╔═════════════════════╗\n"
                            "║  .   .   .   .   .  ║(0)\n"
                            "║  O   .   .   .   .  ║(1)\n"
                            "║  .   .   O   .   x  ║(1)\n"
                            "║  x   .   O   .   x  ║(1)\n"
                            "║  x   .   O   .   x  ║(2)\n"
                            "╚═════════════════════╝\n"
                            "  (1) (1) (1) (1) (4) "),
                Fleet({
                    4: 1,
                    3: 1,
                    1: 2
                }),
            ),
        ]
        self.assertEqual(
            {
                self.__class__.hashable_object_from_puzzle(puzzle)
                for puzzle in actual_possible_puzzles_list
            },
            {
                self.__class__.hashable_object_from_puzzle(puzzle)
                for puzzle in expected_possible_puzzles_list
            },
        )
Esempio n. 16
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,
                    )