Game of Life in Python?

2 Antworten

Bezüglich der Logikimplementation würde ich dir empfehlen, den Wikipedia-Artikel zu nutzen, denn in dem werden die Spielregeln erklärt. Das Spielfeld kannst du darstellen, indem du Sequenzen ineinander schachtelst, wobei je Feld der Zustand mit einem boolschen Wert ausgedrückt wird. Für die Zustandsänderungen musst du je Feld dessen Nachbarfelder auswerten.

Letzten Endes ist es also eine Übung von Kontrollstrukturen und Sequenzen.

Wenn die Aufgabe eine grafische Darstellung des Spielfeldes verlangt und ihr von eurem Auftraggeber kein entsprechendes Tool vorgelegt bekommen habt, würde ich dir empfehlen, Processing.py oder eben doch Pygame zu nutzen.

Hicksder1 
Fragesteller
 06.10.2023, 19:12

Wie werte ich die Nachbarfelder aus?

0
regex9  06.10.2023, 19:21
@Hicksder1

Denk an ein Koordinatensystem.

(0,0) (1,0) (2,0)
(0,1) (1,1) (2,1)
(0,2) (1,2) (2,2)

Anhand der Koordinate eines Feldes kannst du die Koordinaten der Nachbarfelder ermitteln und kommst so also auch an deren Werte.

1
ralphdieter  07.10.2023, 02:12
@Hicksder1
Wie werte ich die Nachbarfelder aus?
def neighbours ( x, y ):
    return ( (x-1,y-1), (x-1,y), (x-1,y+1)
           , (x  ,y-1),          (x  ,y+1)
           , (x+1,y-1), (x+1,y), (x+1,y+1)
           )
0

Du brauchst einen Plan, wie das Spielfeld gespeichert wird und die passenden Algorithmen dafür.

Das Spielfeld wird am naheliegendsten als boolesches 2D-Array, also eine Liste von Listen, implementiert. Der Haken dabei ist, dass so ein Feld begrenzt ist. Entweder legt man seine Größe fest und entscheidet, wie man am Rand vorgeht, oder man erweitert das Feld bei Bedarf, was aber recht aufwändig ist. Und so ein Feld braucht sehr viel Speicher. Viel größer als 10000x10000 wird es kaum werden können.

Alternativ kann man das Spielfeld als Liste (besser noch: als Menge) aller lebenden Zellen abbilden. Das ist wesentlich effizienter. Der Haken ist, dass man hier die relevanten toten Zellen aus den toten Nachbarn aller lebenden Zellen berechnen muss. Als Entschädigung bekommt man eine recht kurze Liste, die nur die toten Zellen mit mindestens einem lebenden Nachbarn enthält.

Wenn Du wirklich etwas lernen willst, empfehle ich Dir, beide Varianten zu implementieren und miteinander zu vergleichen. Wenn es Dir nur darum geht, die Aufgabe irgendwie schnell zu erledigen, dürfte ein offenes 2D-Array fester Größe am geignetesten sein. ( „Offen“ heißt hier, dass alle Zellen um das Spielfeld tot sind und sonst nicht weiter beachtet werden).

Der Kernalgorithmus berechnet ein neues Feld aus dem alten:

def step ( game ):
    # leeres Feld erzeugen und darin 
    # folgende Zellen aus game als lebend markieren:
    # - alle lebenden mit 2 oder 3 lebenden Nachbarn
    # - alle toten mit genau 3 Nachbarn

Eine Zelle wird durch ihre Koordinaten (x, y) adressiert – ich wüsste nicht, wie man das sinnvoll anders machen kann.

Die Methode step() braucht also folgende Zugriffe auf das Spielfeld:

  • game.is_alive(x, y): Teste, ob eine Zelle lebt. Das wird sicher an mehreren Stellen (auch zur Ausgabe des Spielfelds) gebraucht.
  • game.iter_living(): Iteriere über alle lebenden Zellen
  • game.iter_dead(): Iteriere über alle relevanten toten Zellen. Davon gibt es theoretisch unendlich viele, aber nur die mit genau 3 lebenden Nachbarn sind interessant. Es wäre gut, wenn man zumindest keine großen toten Flächen untersuchen müsste.
  • Alternativ zu 1. und 2. kannst Du (bei der 2D-Array-Variante) über alle Zellen iterieren und jeweils deren Zustand abfragen.
  • game.count_neighbours(x, y): Zähle die lebenden Nachbarn einer Zelle.
  • game.min() und max(): Die Zellen links oben und rechts unten im umgebenden Rechteck.

Je nach Implementierung des Spielfelds (2D-Array oder Menge) werden einige dieser Methoden trivial sein und andere aufwändiger.

Obendrauf brauchst Du jetzt noch:

  • game.__init__(...): Anfangs-Spielfeld irgendwie initialisieren. Entweder mit hart codierten Daten oder ganz komfortabel aus einer Textdatei (mit "X" und " ").
  • paint(game): Spielfeld auf der Konsole ausgeben.
  • main(): Initialisierung und Schleife über mehrere step() mit Ausgabe und ggf. Pause oder Tastaturabfrage für den Einzelschritt-Modus.

Diese Funktionen sind unabhängig von der Implementierung des Spielfelds. Bei paint() kommt es allerdings darauf an, ob das Programm in einem xterm oder einer Dos-Box laufen soll.

Ich schlage vor, dass Du erst einmal game.__init__(...) und paint() implementierst. Damit siehst Du schon mal etwas und hast ein wenig Übung. Und dann kommen die Algorithmen dran.

Wenn dann Probleme auftreten, wirst Du Dich sicher wieder melden ;-) Nur eine Bitte im Voraus: Quelltext und Fehlermeldungen immer mit dem </> aus der Formatierleiste formatieren – nicht als Fließtext, und wenn ich einen Screenshot sehe, bin ich raus.