Entwicklung einer Schiffe-Versenken-Spiel-Logik-Bibliothek in Python: Architektur und häufige Fallstricke
Die Bibliothek implementiert die Logik des klassischen Schiffe-Versenken-Spiels mit Fokus auf mathematisch optimalen Algorithmen. Die Hauptklassen sind Field für die Darstellung des Spielbretts und Ship für die Verwaltung der Schiffe. Das Feld wird als 10x10-Matrix mithilfe von Listen von Listen gespeichert, wobei Symbole Zustände bezeichnen: Leerzeichen für eine leere Zelle, '1' für einen intakten Schiffsteil, '.' für einen Fehlschuss, 'X' für eine zerstörte Zelle.
Die Field-Klasse übernimmt die Brett-Erstellung, Koordinaten-Validierung, Schiffsplatzierung und Schussverarbeitung. Koordinaten werden im Format 'a1' eingegeben, wobei Buchstaben A-J den Zeilen und Zahlen 1-10 den Spalten entsprechen. Die Regeln sind festgelegt: 1 Vier-Zellen-Schiff, 2 Drei-Zellen-Schiffe, 3 Zwei-Zellen-Schiffe, 4 Ein-Zellen-Schiffe; Schiffe dürfen sich nicht berühren, auch nicht an den Ecken.
Implementierung der Field-Klasse
Die Initialisierung erstellt ein leeres Raster:
class Field:
def __init__(self):
height = 10
weight = 10
self.grid = [[' ' for i in range(weight)] for i in range(height)]
Die display()-Methode gibt das Feld mit Koordinaten auf der Konsole aus. Die erste Version enthielt einen Fehler – die Methode line.insert(0, letter) veränderte das ursprüngliche Raster durch wiederholtes Hinzufügen von Buchstaben. Die Lösung: Den Buchstaben separat ausgeben, ohne die Struktur zu ändern.
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
Die Koordinatenvalidierung wird in die private Methode _validation_coordinate() ausgelagert. Sie prüft die Stringlänge (2-3 Zeichen), das Vorhandensein eines Buchstabens in A-J und einer Zahl in 1-10. Die Methode find_coordinate() bestimmt den Zellzustand: leer, getroffen, zerstört, nicht beschädigt, wurde beschossen.
Wörterbücher für die Koordinatenumwandlung werden als Attribute self.rows und self.columns gespeichert:
- rows: {'a': 0, 'b': 1, ..., 'j': 9}
- columns: {'1': 0, '2': 1, ..., '10': 9}
Die Ship-Klasse und Schiffsplatzierung
Die Ship-Klasse speichert Parameter in einem Wörterbuch:
class Ship:
def __init__(self):
self.parameters = {
'alive coordinates': [],
'hitted coordinates': [],
'alive': True
}
self.field = None
def set_field(self, field):
self.field = field
Die Methode add_ship(ship, coordinate_line, length) in Field platziert ein Schiff basierend auf einem String wie 'a1-a2-a3'. Zuerst erfolgt die Zeilenvalidierung _ship_line_validation(): Parsen in Koordinaten, Prüfung der Linearität (horizontal/vertikal) und Übereinstimmung der Länge. Dann wird für jede Koordinate ship.set_coordinate_in_ship(coordinate) aufgerufen, wodurch '1' im Raster gesetzt wird.
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
Die Field-Ship-Beziehung ist bidirektional: Field ruft ship.set_field(self) auf, und Ship verwendet self.field zur Validierung. Dies verletzt das Kapselungsprinzip der Basisebenen, vereinfacht aber die API.
Refactoring-Erkenntnisse
Das Projekt zeigte wichtige Anfänger-Fallstricke:
- Methodenüberladung: find_coordinate() kombinierte Validierung, Suche und Ausgabe – aufgeteilt in _validation_coordinate() und get_cell_state().
- Datenmutation: insert() in display() vermeiden; mit Kopien arbeiten.
- Festkodierte Wörterbücher: In Klassenkonstanten auslagern oder ord() für Umwandlungen verwenden.
- Rückgabewerte: Standardisieren: True/False für Erfolg, Strings für Fehler.
Für optimales Gameplay sollten probabilistische Suchalgorithmen integriert werden: Bei einem Treffer benachbarte Zellen scannen; bei Zerstörung Decks von Wahrscheinlichkeiten ausschließen.
Wichtige Erkenntnisse
- Field als Matrix von Listen mit Symbolen für Zustände; Koordinatenvalidierung über Wörterbücher self.rows/self.columns.
- Ship speichert intakte/getroffene Koordinaten; bidirektionale Verknüpfung mit Field über set_field().
- Platzierung über coordinate_line ('a1-a2'); Linearität und Pufferzonen prüfen.
- Rastermutation in display() vermeiden; gemeinsame Prüfungen in private Methoden auslagern.
- Für fortgeschrittene Algorithmen: Eine Wahrscheinlichkeitskarte für Schüsse separat vom Raster speichern.
— Editorial Team
Noch keine Kommentare.