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:
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:
- 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.
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
No comments yet.