Zurück zur Startseite

Battleship-Bibliothek in Python: Field- und Ship-Klassen

Der Artikel zerlegt die Erstellung einer Battleship-Spiel-Logik-Bibliothek in Python auf. Beschreibt Field- und Ship-Klassen, Koordinatenvalidierungsmethoden, Schiffplatzierung und typische Fehler. Bietet Code-Beispiele und Refactoring-Empfehlungen für optimale Architektur.

Erstellen einer Battleship-Bibliothek: Field und Ship in Python
Advertisement 728x90

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:

Google AdInline article slot
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:

Google AdInline article slot
  • 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.

Google AdInline article slot

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

Advertisement 728x90

Weiterlesen