BattleShip Part 7: Ship Placement Tests

While running the code, I did find a bug where ships could overlap.

As I started trying to write a test for the ShipPlacer class, I found it was a little hard to do. There is a bunch of randomizing involved and because the primary thing that happens is updating the ship coordinate state, it was a little hard to test things in isolation.

I refactored ShipPlacer so it no longer directly owned any of the logic for generating coordinates for a random ship placement. The only thing this class now does is go through the ship placements provided by the _get_valid_ship_placements function and set the coordinates for the ship.

class ShipPlacer:

    def __init__(self, ship_configs, rows, columns, ship_coords, get_valid_ship_placements = get_valid_ship_placements):
        self.ship_configs = ship_configs
        self.rows = rows
        self.columns = columns
        self.ship_coords = ship_coords
        self._get_valid_ship_placements = get_valid_ship_placements

...

    def _attempt_placement(self, ship_coords, ship_config):
        for potential_placement in self._get_valid_ship_placements(ship_coords, ship_config, self.rows, self.columns):
            if potential_placement is not None:
                for coord in potential_placement:
                    ship_coords[coord] = ship_config['name']
                return True

        return False

This allows me to write a more simple unit test. I can control the results returned by _get_valid_ship_placements and verify it uses those values in the ship_coords.

                                                                                                                                                                                                                                              
    def test_valid_placment_sets_coordinates(self):                                                                                                                                                                                           
        config = {                                                                                                                                                                                                                            
            'columns': 5,                                                                                                                                                                                                                     
            'rows': 5,                     
            'ships': [                     
                {'name': 'B', 'length': 2},
                {'name': 'C', 'length': 2}
            ]
        }

        def placement(*args, **kwargs):
            ship_config = args[1]
            if ship_config['name'] == 'B':
                yield [(0,1), (0,2)]
            elif ship_config['name'] == 'C':
                yield [(2,4), (3,4)]

        placement_fn = Mock(side_effect=placement)

        placer = ShipPlacer(config['ships'], config['rows'], config['columns'], {}, placement_fn)
        ship_coords  = placer.build_ship_coords()

        self.assertTrue(ship_coords[('a', '2')], 'B')
        self.assertTrue(ship_coords[('a', '3')], 'B')
        self.assertTrue(ship_coords[('c', '5')], 'C')
        self.assertTrue(ship_coords[('d', '5')], 'C')

I can also write a unit test for the function that actually generates the possible ship placements and test it in isolation.

    def test_get_valid_ship_placements(self):
        ship_coords = {
            (0, 0): 'B',
            (0, 1): 'B',
        }
        ship_config = {'name': 'C', 'length': 2}
        placements = list(get_valid_ship_placements(ship_coords, ship_config, 2, 2))

        self.assertTrue(len(placements) > 1)

        # currently not deduping based on where we first placed the ship
        placement = placements[0]

        # ship can only be placed in the following cooords
        self.assertTrue((1,0) in placement)
        self.assertTrue((1,1) in placement)

Project changeset.

One response to “BattleShip Part 7: Ship Placement Tests”

  1. […] BattleShip Part 7: Ship Placement Tests […]

    Like

Leave a reply to BattleShip Project Review – Level up SE Cancel reply