Ejemplo n.º 1
0
 def setUp(self):
     self.sample_ships = (
         Ship(Position(2, 7), 3, Series.ROW),
         Ship(Position(1, 1), 1, Series.ROW),
         Ship(Position(7, 10), 4, Series.COLUMN),
         Ship(Position(3, 3), 2, Series.COLUMN),
     )
Ejemplo n.º 2
0
 def test_no_disallowed_overlapping_fields_for_ship(self):
     parameters_vector = (
         (
             "╔═════════════════════╗\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) (1) (2) (1) (2) ",
             Ship(Position(1, 2), 3, Series.ROW),
             True,
         ),
         (
             "╔═════════════════════╗\n"
             "║  x   x   x   x   .  ║(3)\n"
             "║  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) (1) (2) (1) (2) ",
             Ship(Position(1, 2), 3, Series.ROW),
             True,
         ),
         (
             "╔═════════════════════╗\n"
             "║  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) (1) (2) (1) (2) ",
             Ship(Position(1, 2), 3, Series.ROW),
             False,
         ),
         (
             "╔═════════════════════╗\n"
             "║  x   x   x   O   .  ║(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) (1) (2) (1) (2) ",
             Ship(Position(1, 2), 3, Series.ROW),
             False,
         ),
     )
     for board_repr, ship, expected_result in parameters_vector:
         with self.subTest():
             self.assertEqual(
                 expected_result,
                 parse_board(board_repr).no_disallowed_overlapping_fields_for_ship(
                     ship
                 ),
             )
Ejemplo n.º 3
0
    def test_mark_ship_and_surrounding_sea(self):
        actual_board = parse_board(
            "╔═════════════════════════════════════════╗\n"
            "║  .   x   x   x   x   x   x   x   x   x  ║(0)\n"
            "║  x   x   .   x   x   x   x   x   x   x  ║(2)\n"
            "║  x   .   x   x   x   x   x   x   x   x  ║(0)\n"
            "║  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  ║(1)\n"
            "║  x   x   x   x   x   x   x   x   x   x  ║(1)\n"
            "║  x   x   x   x   .   x   x   x   x   x  ║(1)\n"
            "║  x   x   x   x   x   x   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   x   x   x   x   x   x  ║(0)\n"
            "╚═════════════════════════════════════════╝\n"
            "  (1) (1) (0) (0) (0) (4) (0) (0) (0) (0) "
        )
        actual_board.mark_ship_and_surrounding_sea(
            Ship(Position(5, 6), 4, Series.COLUMN)
        )
        expected_board = parse_board(
            "╔═════════════════════════════════════════╗\n"
            "║  .   x   x   x   x   x   x   x   x   x  ║(0)\n"
            "║  x   x   .   x   x   x   x   x   x   x  ║(2)\n"
            "║  x   .   x   x   x   x   x   x   x   x  ║(0)\n"
            "║  x   x   x   x   .   .   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   .   O   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   .   O   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   .   O   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   .   O   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   .   .   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   x   x   x   x   x   x  ║(0)\n"
            "╚═════════════════════════════════════════╝\n"
            "  (1) (1) (0) (0) (0) (0) (0) (0) (0) (0) "
        )
        self.assertEqual(expected_board, actual_board)

        actual_board.mark_ship_and_surrounding_sea(Ship(Position(2, 1), 2, Series.ROW))
        expected_board = parse_board(
            "╔═════════════════════════════════════════╗\n"
            "║  .   .   .   x   x   x   x   x   x   x  ║(0)\n"
            "║  O   O   .   x   x   x   x   x   x   x  ║(0)\n"
            "║  .   .   .   x   x   x   x   x   x   x  ║(0)\n"
            "║  x   x   x   x   .   .   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   .   O   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   .   O   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   .   O   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   .   O   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   .   .   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   x   x   x   x   x   x  ║(0)\n"
            "╚═════════════════════════════════════════╝\n"
            "  (0) (0) (0) (0) (0) (0) (0) (0) (0) (0) "
        )
        self.assertEqual(expected_board, actual_board)
Ejemplo n.º 4
0
 def test_fieldtype_positions_in_series(self):
     params_vector = (
         (FieldType.SHIP, Series.ROW, 2),
         (FieldType.SHIP, Series.COLUMN, 2),
     )
     expected_results_vectors = (
         ({Position(2, 1)}, set()),
         (set(), {Position(1, 2)}),
         (set(), set()),
         (
             {
                 Position(2, 2),
                 Position(2, 3),
                 Position(2, 6),
                 Position(2, 7)
             },
             {Position(1, 2), Position(2, 2)},
         ),
     )
     for sample_grid, expected_result_vector in zip(
             self.sample_fieldtypegrids, expected_results_vectors):
         for params, expected_result in zip(params_vector,
                                            expected_result_vector):
             with self.subTest():
                 self.assertEqual(
                     expected_result,
                     sample_grid.fieldtype_positions_in_series(*params),
                 )
Ejemplo n.º 5
0
    def test_simulate_marking_of_ships(self):
        actual_board = parse_board(
            "╔═════════════════════╗\n"
            "║  x   x   x   x   .  ║(3)\n"
            "║  x   x   x   x   x  ║(0)\n"
            "║  x   x   O   x   x  ║(2)\n"
            "║  x   .   x   x   x  ║(1)\n"
            "║  x   x   x   x   x  ║(2)\n"
            "╚═════════════════════╝\n"
            "  (4) (1) (2) (1) (2) "
        )
        ships = {
            Ship(Position(1, 1), 3, Series.ROW),
            Ship(Position(3, 5), 2, Series.COLUMN),
        }
        ships_orig = set(ships)
        actual_board.mark_ship_group(ships)
        expected_board = parse_board(
            "╔═════════════════════╗\n"
            "║  O   O   O   .   .  ║(0)\n"
            "║  .   .   .   .   .  ║(0)\n"
            "║  x   .   O   .   O  ║(1)\n"
            "║  .   .   .   .   O  ║(0)\n"
            "║  x   .   x   .   .  ║(2)\n"
            "╚═════════════════════╝\n"
            "  (3) (0) (1) (1) (0) "
        )
        self.assertEqual(actual_board, expected_board)
        self.assertEqual(ships, ships_orig)

        actual_board = parse_board(
            "╔═════════════════════╗\n"
            "║  x   x   x   x   .  ║(3)\n"
            "║  x   x   x   x   x  ║(0)\n"
            "║  x   x   O   x   x  ║(2)\n"
            "║  x   .   x   x   x  ║(1)\n"
            "║  x   x   x   x   x  ║(2)\n"
            "╚═════════════════════╝\n"
            "  (4) (1) (2) (1) (2) "
        )
        ships = {
            Ship(Position(1, 1), 3, Series.ROW),
            Ship(Position(3, 5), 2, Series.COLUMN),
            Ship(Position(5, 1), 3, Series.ROW),
        }
        ships_orig = set(ships)
        with self.assertRaises(InvalidShipPlacementException):
            actual_board.mark_ship_group(ships)
Ejemplo n.º 6
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)})
Ejemplo n.º 7
0
    def test_decide_how_to_proceed(
        self,
        mocked_find_definite_ship_fields_positions,
        mocked_try_to_cover_all_ship_fields_to_be,
        mocked_mark_subfleet_of_biggest_remaining_ships,
    ):
        mocked_find_definite_ship_fields_positions.side_effect = (
            set(),
            set(),
            {Position(3, 3)},
            {Position(3, 3)},
        )

        positions = set()
        positions_orig = set()
        self.sample_puzzle.decide_how_to_proceed(positions)
        self.assertEqual(positions, positions_orig)
        mocked_try_to_cover_all_ship_fields_to_be.assert_not_called()
        mocked_mark_subfleet_of_biggest_remaining_ships.assert_called_once_with(
        )
        mocked_try_to_cover_all_ship_fields_to_be.reset_mock()
        mocked_mark_subfleet_of_biggest_remaining_ships.reset_mock()

        positions = {Position(5, 3)}
        positions_orig = set(positions)
        self.sample_puzzle.decide_how_to_proceed(positions)
        self.assertEqual(positions, positions_orig)
        mocked_try_to_cover_all_ship_fields_to_be.assert_called_once_with(
            {Position(5, 3)})
        mocked_mark_subfleet_of_biggest_remaining_ships.assert_not_called()
        mocked_try_to_cover_all_ship_fields_to_be.reset_mock()
        mocked_mark_subfleet_of_biggest_remaining_ships.reset_mock()

        positions = set()
        positions_orig = set(positions)
        self.sample_puzzle.decide_how_to_proceed(positions)
        self.assertEqual(positions, positions_orig)
        mocked_try_to_cover_all_ship_fields_to_be.assert_called_once_with(
            {Position(3, 3)})
        mocked_mark_subfleet_of_biggest_remaining_ships.assert_not_called()
        mocked_try_to_cover_all_ship_fields_to_be.reset_mock()
        mocked_mark_subfleet_of_biggest_remaining_ships.reset_mock()

        positions = set({Position(5, 3)})
        positions_orig = set(positions)
        self.sample_puzzle.decide_how_to_proceed(positions)
        self.assertEqual(positions, positions_orig)
        mocked_try_to_cover_all_ship_fields_to_be.assert_called_once_with(
            {Position(5, 3), Position(3, 3)})
        mocked_mark_subfleet_of_biggest_remaining_ships.assert_not_called()
Ejemplo n.º 8
0
 def test_mark_diagonal_sea_fields_for_positions(self):
     actual_board = parse_board(
         "╔═════════════════════════════════════════╗\n"
         "║  .   x   x   x   x   x   x   x   x   x  ║(0)\n"
         "║  x   x   .   x   x   x   x   x   x   x  ║(2)\n"
         "║  x   .   x   x   x   x   x   x   x   x  ║(0)\n"
         "║  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  ║(1)\n"
         "║  x   x   x   x   x   x   x   x   x   x  ║(1)\n"
         "║  x   x   x   x   .   x   x   x   x   x  ║(1)\n"
         "║  x   x   x   x   x   x   .   x   x   x  ║(0)\n"
         "║  x   x   x   x   x   x   x   x   x   x  ║(0)\n"
         "╚═════════════════════════════════════════╝\n"
         "  (1) (1) (0) (0) (0) (4) (0) (0) (0) (0) "
     )
     positions = {Position(5, 9), Position(2, 1), Position(10, 10)}
     positions_orig = set(positions)
     actual_board.mark_diagonal_sea_fields_for_positions(positions)
     expected_board = parse_board(
         "╔═════════════════════════════════════════╗\n"
         "║  .   .   x   x   x   x   x   x   x   x  ║(0)\n"
         "║  x   x   .   x   x   x   x   x   x   x  ║(2)\n"
         "║  x   .   x   x   x   x   x   x   x   x  ║(0)\n"
         "║  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   .  ║(1)\n"
         "║  x   x   x   x   x   x   x   x   x   x  ║(1)\n"
         "║  x   x   x   x   .   x   x   x   x   x  ║(1)\n"
         "║  x   x   x   x   x   x   .   x   .   x  ║(0)\n"
         "║  x   x   x   x   x   x   x   x   x   x  ║(0)\n"
         "╚═════════════════════════════════════════╝\n"
         "  (1) (1) (0) (0) (0) (4) (0) (0) (0) (0) "
     )
     self.assertEqual(expected_board, actual_board)
     self.assertEqual(positions, positions_orig)
Ejemplo n.º 9
0
 def test_set_ship_fields_as_unknown(self):
     actual_board = parse_board(
         "╔═════════════════════════════════════════╗\n"
         "║  .   .   .   .   .   .   .   .   .   .  ║(0)\n"
         "║  x   .   .   .   O   .   .   .   .   .  ║(0)\n"
         "║  .   .   O   .   O   .   .   x   .   .  ║(0)\n"
         "║  .   .   O   .   O   .   x   .   .   O  ║(0)\n"
         "║  x   .   O   .   .   .   x   .   .   .  ║(0)\n"
         "║  .   .   .   .   O   .   x   .   .   .  ║(0)\n"
         "║  .   x   .   .   O   .   x   .   .   .  ║(0)\n"
         "║  x   .   .   x   .   .   .   .   O   O  ║(0)\n"
         "║  .   .   .   .   .   .   .   .   .   .  ║(0)\n"
         "║  O   .   O   O   O   O   .   O   O   .  ║(0)\n"
         "╚═════════════════════════════════════════╝\n"
         "  (0) (0) (0) (0) (0) (0) (0) (0) (0) (0) "
     )
     ship_fields = {
         Position(3, 3),
         Position(4, 3),
         Position(5, 3),
         Position(10, 3),
         Position(10, 6),
         Position(8, 9),
         Position(8, 10),
     }
     ship_fields_orig = set(ship_fields)
     actual_board.set_ship_fields_as_unknown(ship_fields)
     expected_board = parse_board(
         "╔═════════════════════════════════════════╗\n"
         "║  .   .   .   .   .   .   .   .   .   .  ║(0)\n"
         "║  x   .   .   .   O   .   .   .   .   .  ║(0)\n"
         "║  .   .   x   .   O   .   .   x   .   .  ║(1)\n"
         "║  .   .   x   .   O   .   x   .   .   O  ║(1)\n"
         "║  x   .   x   .   .   .   x   .   .   .  ║(1)\n"
         "║  .   .   .   .   O   .   x   .   .   .  ║(0)\n"
         "║  .   x   .   .   O   .   x   .   .   .  ║(0)\n"
         "║  x   .   .   x   .   .   .   .   x   x  ║(2)\n"
         "║  .   .   .   .   .   .   .   .   .   .  ║(0)\n"
         "║  O   .   x   O   O   x   .   O   O   .  ║(2)\n"
         "╚═════════════════════════════════════════╝\n"
         "  (0) (0) (4) (0) (0) (1) (0) (0) (1) (1) "
     )
     self.assertEqual(expected_board, actual_board)
     self.assertEqual(ship_fields, ship_fields_orig)
Ejemplo n.º 10
0
 def test_find_definite_ship_fields_positions(self):
     board = parse_board(
         "╔═════════════════════╗\n"
         "║  x   x   x   .   .  ║(3)\n"
         "║  x   x   .   x   x  ║(0)\n"
         "║  x   x   O   x   .  ║(2)\n"
         "║  x   .   .   x   x  ║(1)\n"
         "║  x   x   x   x   x  ║(2)\n"
         "╚═════════════════════╝\n"
         "  (4) (1) (2) (1) (0) "
     )
     expected_result = {
         Position(1, 3),
         Position(5, 3),
         Position(1, 1),
         Position(1, 2),
         Position(3, 1),
         Position(4, 1),
         Position(5, 1),
         Position(3, 4),
     }
     self.assertEqual(expected_result, board.find_definite_ship_fields_positions())
Ejemplo n.º 11
0
 def test_get_possible_ships_occupying_positions(self):
     actual_board = parse_board(
         "╔═════════════════════╗\n"
         "║  x   x   x   x   .  ║(3)\n"
         "║  x   x   x   x   x  ║(0)\n"
         "║  x   x   O   x   x  ║(2)\n"
         "║  x   .   x   x   x  ║(1)\n"
         "║  x   x   x   x   x  ║(2)\n"
         "╚═════════════════════╝\n"
         "  (4) (1) (2) (1) (2) "
     )
     positions = {Position(1, 3), Position(5, 1)}
     positions_orig = set(positions)
     ship_sizes = {3, 2, 1}
     ship_sizes_orig = set(ship_sizes)
     expected_ship_occupying_positions = {
         Position(1, 3): {
             Ship(Position(1, 1), 3, Series.ROW),
             Ship(Position(1, 2), 3, Series.ROW),
             Ship(Position(1, 2), 2, Series.ROW),
             Ship(Position(1, 3), 2, Series.ROW),
             Ship(Position(1, 3), 1, Series.ROW),
         },
         Position(5, 1): {
             Ship(Position(5, 1), 2, Series.ROW),
             Ship(Position(5, 1), 1, Series.ROW),
             Ship(Position(3, 1), 3, Series.COLUMN),
             Ship(Position(4, 1), 2, Series.COLUMN),
         },
     }
     self.assertEqual(
         expected_ship_occupying_positions,
         actual_board.get_possible_ships_occupying_positions(positions, ship_sizes),
     )
     self.assertEqual(positions, positions_orig)
     self.assertEqual(ship_sizes, ship_sizes_orig)
Ejemplo 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)
Ejemplo 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()
Ejemplo 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,
            )
Ejemplo 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
            },
        )
Ejemplo n.º 16
0
 def test_get_ship_fields_positions(self):
     board = parse_board(
         "╔═════════════════════════════════════════╗\n"
         "║  .   .   .   .   .   .   .   .   .   .  ║(0)\n"
         "║  x   .   .   .   O   .   .   .   .   .  ║(0)\n"
         "║  .   .   x   .   O   .   .   x   .   .  ║(1)\n"
         "║  .   .   x   .   O   .   x   .   .   O  ║(1)\n"
         "║  x   .   x   .   .   .   x   .   .   .  ║(1)\n"
         "║  .   .   .   .   O   .   x   .   .   .  ║(0)\n"
         "║  .   x   .   .   O   .   x   .   .   .  ║(0)\n"
         "║  x   .   .   x   .   .   .   .   x   x  ║(2)\n"
         "║  .   .   .   .   .   .   .   .   .   .  ║(0)\n"
         "║  O   .   x   O   O   x   .   O   O   .  ║(2)\n"
         "╚═════════════════════════════════════════╝\n"
         "  (0) (0) (4) (0) (0) (1) (0) (0) (1) (1) "
     )
     self.assertEqual(
         {
             Position(10, 1),
             Position(10, 4),
             Position(10, 5),
             Position(10, 8),
             Position(10, 9),
             Position(4, 10),
             Position(2, 5),
             Position(3, 5),
             Position(4, 5),
             Position(6, 5),
             Position(7, 5),
         },
         board.get_ship_fields_positions(),
     )
Ejemplo n.º 17
0
 def test_get_possible_ships_of_size(self):
     board = 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) (1) (2) (1) (2) "
     )
     self.assertEqual(
         {
             Ship(Position(1, 1), 3, Series.ROW),
             Ship(Position(1, 2), 3, Series.ROW),
             Ship(Position(1, 1), 3, Series.COLUMN),
             Ship(Position(2, 1), 3, Series.COLUMN),
             Ship(Position(3, 1), 3, Series.COLUMN),
         },
         board.get_possible_ships_of_size(3),
     )
     self.assertEqual(
         {
             Ship(Position(1, 1), 2, Series.ROW),
             Ship(Position(1, 2), 2, Series.ROW),
             Ship(Position(1, 3), 2, Series.ROW),
             Ship(Position(5, 1), 2, Series.ROW),
             Ship(Position(5, 2), 2, Series.ROW),
             Ship(Position(5, 3), 2, Series.ROW),
             Ship(Position(5, 4), 2, Series.ROW),
             Ship(Position(1, 1), 2, Series.COLUMN),
             Ship(Position(2, 1), 2, Series.COLUMN),
             Ship(Position(3, 1), 2, Series.COLUMN),
             Ship(Position(4, 1), 2, Series.COLUMN),
             Ship(Position(2, 5), 2, Series.COLUMN),
             Ship(Position(3, 5), 2, Series.COLUMN),
             Ship(Position(4, 5), 2, Series.COLUMN),
         },
         board.get_possible_ships_of_size(2),
     )
     self.assertEqual(
         {
             Ship(Position(1, 1), 1, Series.ROW),
             Ship(Position(1, 2), 1, Series.ROW),
             Ship(Position(1, 3), 1, Series.ROW),
             Ship(Position(1, 4), 1, Series.ROW),
             Ship(Position(2, 1), 1, Series.ROW),
             Ship(Position(2, 5), 1, Series.ROW),
             Ship(Position(3, 1), 1, Series.ROW),
             Ship(Position(3, 5), 1, Series.ROW),
             Ship(Position(4, 1), 1, Series.ROW),
             Ship(Position(4, 5), 1, Series.ROW),
             Ship(Position(5, 1), 1, Series.ROW),
             Ship(Position(5, 2), 1, Series.ROW),
             Ship(Position(5, 3), 1, Series.ROW),
             Ship(Position(5, 4), 1, Series.ROW),
             Ship(Position(5, 5), 1, Series.ROW),
         },
         board.get_possible_ships_of_size(1),
     )
Ejemplo n.º 18
0
 def test_sufficient_remaining_ship_fields_to_mark_ship(self):
     parameters_vector = (
         (
             "╔═════════════════════╗\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) (1) (2) (1) (2) ",
             Ship(Position(1, 1), 3, Series.ROW),
             True,
         ),
         (
             "╔═════════════════════╗\n"
             "║  x   x   x   x   .  ║(2)\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) (1) (2) (1) (2) ",
             Ship(Position(1, 1), 3, Series.ROW),
             False,
         ),
         (
             "╔═════════════════════╗\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) ",
             Ship(Position(1, 1), 3, Series.ROW),
             False,
         ),
         (
             "╔═════════════════════╗\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) (1) (2) (1) (2) ",
             Ship(Position(1, 1), 3, Series.COLUMN),
             True,
         ),
         (
             "╔═════════════════════╗\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"
             "  (2) (1) (2) (1) (2) ",
             Ship(Position(1, 1), 3, Series.COLUMN),
             False,
         ),
         (
             "╔═════════════════════╗\n"
             "║  x   x   x   x   .  ║(3)\n"
             "║  x   x   x   x   x  ║(2)\n"
             "║  x   x   O   x   x  ║(0)\n"
             "║  x   .   x   x   x  ║(1)\n"
             "║  x   x   x   x   x  ║(2)\n"
             "╚═════════════════════╝\n"
             "  (4) (1) (2) (1) (2) ",
             Ship(Position(1, 1), 3, Series.COLUMN),
             False,
         ),
     )
     for board_repr, ship, expected_result in parameters_vector:
         with self.subTest():
             self.assertEqual(
                 expected_result,
                 parse_board(
                     board_repr
                 ).sufficient_remaining_ship_fields_to_mark_ship(ship),
             )
Ejemplo n.º 19
0
    def test_ship_is_within_playable_grid(self):
        board = parse_board(
            "╔═════════════════════════════════════════╗\n"
            "║  .   x   x   x   x   x   x   x   x   x  ║(0)\n"
            "║  x   x   .   x   x   x   x   x   x   x  ║(2)\n"
            "║  x   .   x   x   x   x   x   x   x   x  ║(0)\n"
            "║  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  ║(1)\n"
            "║  x   x   x   x   x   x   x   x   x   x  ║(1)\n"
            "║  x   x   x   x   .   x   x   x   x   x  ║(1)\n"
            "║  x   x   x   x   x   x   .   x   x   x  ║(0)\n"
            "║  x   x   x   x   x   x   x   x   x   x  ║(0)\n"
            "╚═════════════════════════════════════════╝\n"
            "  (1) (1) (0) (0) (0) (4) (0) (0) (0) (0) "
        )

        ships_within_playable_grid = (
            Ship(Position(1, 1), 3, Series.ROW),
            Ship(Position(1, 8), 3, Series.ROW),
            Ship(Position(5, 5), 3, Series.ROW),
            Ship(Position(10, 5), 3, Series.ROW),
            Ship(Position(1, 1), 3, Series.COLUMN),
            Ship(Position(8, 1), 3, Series.COLUMN),
            Ship(Position(5, 5), 3, Series.COLUMN),
            Ship(Position(5, 10), 3, Series.COLUMN),
            Ship(Position(1, 1), 1, Series.ROW),
            Ship(Position(6, 6), 1, Series.ROW),
            Ship(Position(10, 10), 1, Series.ROW),
        )
        for ship in ships_within_playable_grid:
            with self.subTest(ship):
                self.assertTrue(board.ship_is_within_playable_grid(ship))

        ships_outside_playable_grid = (
            Ship(Position(-1, 4), 3, Series.ROW),
            Ship(Position(0, 4), 3, Series.ROW),
            Ship(Position(1, 0), 3, Series.ROW),
            Ship(Position(1, 9), 3, Series.ROW),
            Ship(Position(11, 5), 3, Series.ROW),
            Ship(Position(4, -1), 3, Series.COLUMN),
            Ship(Position(4, 0), 3, Series.COLUMN),
            Ship(Position(0, 1), 3, Series.COLUMN),
            Ship(Position(9, 1), 3, Series.COLUMN),
            Ship(Position(5, 11), 3, Series.COLUMN),
            Ship(Position(0, 1), 1, Series.ROW),
            Ship(Position(1, 0), 1, Series.ROW),
            Ship(Position(11, 10), 1, Series.ROW),
            Ship(Position(10, 11), 1, Series.ROW),
        )
        for ship in ships_outside_playable_grid:
            with self.subTest(ship):
                self.assertFalse(board.ship_is_within_playable_grid(ship))