Back to Home

Battleship Library in Python: Field and Ship Classes

The article breaks down the creation of a Battleship game logic library in Python. Describes Field and Ship classes, coordinate validation methods, ship placement and typical errors. Provides code examples and refactoring recommendations for optimal architecture.

Creating a Battleship Library: Field and Ship in Python
Advertisement 728x90

Developing a Battleship Game Logic Library in Python: Architecture and Common Pitfalls

The library implements the logic of the classic Battleship game with a focus on mathematically optimal algorithms. The main classes are Field for representing the game board and Ship for managing ships. The field is stored as a 10x10 matrix using lists of lists, where symbols denote states: space for an empty cell, '1' for an intact ship part, '.' for a miss, 'X' for a destroyed cell.

The Field class handles board creation, coordinate validation, ship placement, and shot processing. Coordinates are input in the format 'a1', where letters A-J correspond to rows and numbers 1-10 to columns. The rules are fixed: 1 four-cell ship, 2 three-cell ships, 3 two-cell ships, 4 one-cell ships; ships must not touch each other, even at corners.

Implementing the Field Class

Initialization creates an empty grid:

Google AdInline article slot
class Field:
    def __init__(self):
        height = 10
        width = 10
        self.grid = [[' ' for i in range(width)] for i in range(height)]

The display() method outputs the field to the console with coordinates. The first version contained an error—the method line.insert(0, letter) mutated the original grid by adding letters on repeated calls. The fix: print the letter separately without altering the structure.

def display(self):
    print('  1 2 3 4 5 6 7 8 9 10')
    letters = 'ABCDEFGHIJ'
    count_letters = 0
    for line in self.grid:
        print(letters[count_letters], *line)
        count_letters += 1

Coordinate validation is extracted into the private method _validation_coordinate(). It checks the string length (2-3 characters), presence of a letter in A-J, and a number in 1-10. The method find_coordinate() determines the cell state: clear, hit, destroyed, undamaged, missed.

Dictionaries for coordinate conversion are stored as attributes self.rows and self.columns:

Google AdInline article slot
  • rows: {'a': 0, 'b': 1, ..., 'j': 9}
  • columns: {'1': 0, '2': 1, ..., '10': 9}

The Ship Class and Ship Placement

The Ship class stores parameters in a dictionary:

class Ship:
    def __init__(self):
        self.parameters = {
            'alive coordinates': [],
            'hit coordinates': [],
            'alive': True
        }
        self.field = None
    def set_field(self, field):
        self.field = field

The method add_ship(ship, coordinate_line, length) in Field places a ship based on a string like 'a1-a2-a3'. First, line validation _ship_line_validation(): parsing into coordinates, checking linearity (horizontal/vertical), and matching length. Then, for each coordinate, call ship.set_coordinate_in_ship(coordinate), setting '1' in the grid.

def add_ship(self, ship, coordinate_line, length):   
    ship.set_field(self)
    if not self._ship_line_validation(coordinate_line):
        return False
    coordinates = coordinate_line.split('-')   
    if length != len(coordinates):
        return False
    for coordinate in coordinates:
        result = ship.set_coordinate_in_ship(coordinate)
        if result:
            rows = self.rows[coordinate[0]]
            columns = self.columns[coordinate[1:]]
            self.grid[rows][columns] = '1'
    return True

The Field-Ship relationship is bidirectional: Field calls ship.set_field(self), and Ship uses self.field for validation. This violates the encapsulation principle of base layers but simplifies the API.

Google AdInline article slot

Refactoring Insights

The project revealed key beginner pitfalls:

  • Method Overloading: find_coordinate() combined validation, search, and output—split into _validation_coordinate() and get_cell_state().
  • Data Mutation: Avoid insert() in display(); work with copies.
  • Hardcoded Dictionaries: Extract into class constants or use ord() for conversions.
  • Return Values: Standardize: True/False for success, strings for errors.

For optimal gameplay, integrate probabilistic search algorithms: upon a hit, scan adjacent cells; upon destruction, exclude decks from probabilities.

Key Takeaways

  • Field as a matrix of lists with symbols for states; coordinate validation via dictionaries self.rows/self.columns.
  • Ship stores alive/hit coordinates; bidirectional link with Field via set_field().
  • Placement via coordinate_line ('a1-a2'); check linearity and buffer zones.
  • Avoid mutating grid in display(); extract common checks into private methods.
  • For advanced algorithms: store a probability map of shots separately from the grid.

— Editorial Team

Advertisement 728x90

Read Next