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), )
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 ), )
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)
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)
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)
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)
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()
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, )
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), )
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), )
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))