Wie die davon gebildete Population sich schrittweise entwickelt, ist durch folgende Regeln festgelegt:
- Eine Zelle stirbt (an Vereinsamung), wenn sie nur 0 oder 1 Nachbarn hat; in unserem Beispiel ist das für D1 und C3 der Fall.
- Eine Zelle stirbt (an Überpopulation), wenn sie 4 oder mehr Nachbarn hat; in unserem Beispiel ist das für B2 der Fall.
- Eine Zelle bleibt am Leben, wenn sie 3 oder 4 Nachbarn hat; in unserem Beispiel ist das für A1, B1 und A2 der Fall.
- Eine (unbelebte) Zelle wird belebt, wenn sie von genau drei lebenden Zellen umgeben ist; in unserem Beispiel ist das für C1 und B3 der Fall.
- Es bildet sich eine stabile Population aus.
- Es bildet sich eine oszillierende Population aus, d. h. ab einem bestimmten Punkt verläuft die Entwicklung zyklisch.
- Die gesamte Population stirbt aus.
- Die Population entwickelt sich (ohne Periode) weiter; dabei kann die Anzahl der lebenden Zellen unbegrenzt ansteigen.
- Interessant sind die sogenannten Spaceships; hierbei handelt es sich um oszillierende Populationen, wobei sich die Population nach einer Periode aber an einer anderen Position befinden. Sie gleitet quasi durch den Raum.
Mit Hilfe unseres TTGO-Moduls können wir solche Entwicklungsprozesse studieren. Hierbei stellen wir eine Population durch eine Matrix dar: Das Matrix-Element einer lebenden Zelle wird durch den Wert 1 gekennzeichnet, andernfalls ist der Wert 0. (Wie man bei Micropython mit Matrizen umgeht, habe ich in dem Beitrag https://www.forum.g-heinrichs.de/viewto ... f=18&t=159 ausgeführt.)
Mit Hilfe der oben angegebenen Regeln wird aus der "alten" Matrix eine "neue Matrix" bestimmt und anschließend auf dem Display dargestellt; dabei werden nur lebende Zellen angezeigt, und zwar durch ein kleines Quadrat.
Diese "neue" Matrix wird nun in die "alte" Matrix kopiert und aus dieser wird wieder eine "neue" Matrix bestimmt...
In unserem Programm werden verschiedene Startpopulationen zur Verfügung gestellt:
Code: Alles auswählen
# zur Auswahl:
# startmatrix_r()
# startmatrix_block()
# startmatrix_glider()
# startmatrix_glider_blinker1()
# startmatrix_glider_blinker2()
# startmatrix_glider_blinker3()
# startmatrix_glider_blinker4()
# startmatrix_glider_blinker5()
# startmatrix_diamant()
# startmatrix_balken_am_rand()
# startmatrix_balken_im_zentrum()
Code: Alles auswählen
# game_of_life_0_4.py
# Version 0.4 (Experimental-Version)
# etwa 1 Dutzend Start-Populationen (Startmatrizen)
# Abbrechen der Simulation mit Taster A (GPIO0)
# Quellen:
# https://playgameoflife.com/
# https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
from time import sleep
from machine import Pin, SPI
import st7789
tasterA = Pin(0, Pin.IN, Pin.PULL_UP)
spi = SPI(2, baudrate=20000000, polarity=1, sck=Pin(18), mosi=Pin(19))
display = st7789.ST7789(spi, 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=3)
# Landscape
display.init()
display.fill(0)
def zeige_matrix(m):
size = 4
display.fill(0)
display.rect(0,0,size*w,size*h,st7789.GREEN)
for i in range(h):
for j in range(w):
if m[i][j] == 1:
display.rect(size*j, size*i, size, size, st7789.WHITE)
def kopiere_neu_zu_alt(): # neue_matrix = alte_matrix funktioniert nicht, weil hier nur Pointer neu zugeordnet werden
for i in range(h):
for j in range(w):
alte_matrix[i][j] = neue_matrix[i][j]
def erzeuge_naechste_generation():
for i in range(h):
for j in range(w):
if alte_matrix[i][j] == 1: # Zelle besetzt
n = anzahl_der_nachbarn(alte_matrix, i, j)
if n <= 1: # 0; 1, vgl. anzahl_der_nachbarn
neue_matrix[i][j] = 0
else:
if n <= 3: # 2; 3
neue_matrix[i][j] = 1
else: # 4; 5; ...; 8
neue_matrix[i][j] = 0
else: # Zelle nicht besetzt
n = anzahl_der_nachbarn(alte_matrix, i, j)
if n == 3 :
neue_matrix[i][j] = 1
def anzahl_der_nachbarn(m, i, j):
n = 0 # Zellen am Rand werden sterben
if i > 0 and j > 0 and i < (h-1) and j < (w-1): # Zellen nicht am Rand
n = m[i][j-1] + m[i-1][j-1] + m[i-1][j] + m[i-1][j+1] + m[i][j+1] + m[i+1][j+1] + m[i+1][j] + m[i+1][j-1]
return n
################## verschiedene Startmatrizen #############################
def startmatrix_r():
# Startmatrix ('r'):
alte_matrix[19][25] = 1
alte_matrix[19][26] = 1
alte_matrix[20][25] = 1
alte_matrix[20][24] = 1
alte_matrix[21][25] = 1
def startmatrix_block():
alte_matrix[15][30] = 1
alte_matrix[15][31] = 1
alte_matrix[16][30] = 1
alte_matrix[16][31] = 1
def startmatrix_glider():
alte_matrix[2][2] = 1
alte_matrix[3][3] = 1
alte_matrix[3][4] = 1
alte_matrix[4][2] = 1
alte_matrix[4][3] = 1
def startmatrix_blinker():
alte_matrix[15][29] = 1
alte_matrix[15][30] = 1
alte_matrix[15][31] = 1
def startmatrix_glider_blinker1():
alte_matrix[2][22] = 1
alte_matrix[3][23] = 1
alte_matrix[3][24] = 1
alte_matrix[4][22] = 1
alte_matrix[4][23] = 1
alte_matrix[15][29] = 1
alte_matrix[15][30] = 1
alte_matrix[15][31] = 1
def startmatrix_glider_blinker2():
alte_matrix[2][20] = 1
alte_matrix[3][21] = 1
alte_matrix[3][22] = 1
alte_matrix[4][20] = 1
alte_matrix[4][21] = 1
alte_matrix[15][29] = 1
alte_matrix[15][30] = 1
alte_matrix[15][31] = 1
def startmatrix_glider_blinker3():
alte_matrix[2][18] = 1
alte_matrix[3][19] = 1
alte_matrix[3][20] = 1
alte_matrix[4][18] = 1
alte_matrix[4][19] = 1
alte_matrix[15][29] = 1
alte_matrix[15][30] = 1
alte_matrix[15][31] = 1
def startmatrix_glider_blinker4():
alte_matrix[2][16] = 1
alte_matrix[3][17] = 1
alte_matrix[3][18] = 1
alte_matrix[4][16] = 1
alte_matrix[4][17] = 1
alte_matrix[15][29] = 1
alte_matrix[15][30] = 1
alte_matrix[15][31] = 1
def startmatrix_glider_blinker5():
alte_matrix[2][13] = 1
alte_matrix[3][14] = 1
alte_matrix[3][15] = 1
alte_matrix[4][13] = 1
alte_matrix[4][14] = 1
alte_matrix[15][29] = 1
alte_matrix[15][30] = 1
alte_matrix[15][31] = 1
def startmatrix_diamant():
for i in range(4):
alte_matrix[11][i+28] = 1
for i in range(8):
alte_matrix[13][i+26] = 1
for i in range(12):
alte_matrix[15][i+24] = 1
for i in range(8):
alte_matrix[17][i+26] = 1
for i in range(4):
alte_matrix[19][i+28] = 1
def startmatrix_balken_am_rand():
for i in range(1, 30):
alte_matrix[15][i] = 1
alte_matrix[16][i] = 1
def startmatrix_balken_im_zentrum():
for i in range(1, 30):
alte_matrix[15][i+15] = 1
alte_matrix[16][i+15] = 1
################################# Hauptprogramm #################################
zeitintervall = 0.3
# Erzeuge 2 Matrizen
w, h = 60, 33
alte_matrix = [[0 for x in range(w)] for y in range(h)]
# [0 for x in range(w)] erzeugt Liste mit w 0-Elementen (1 Zeile mit w Elementen)
# [[...] for y in range(h)] erzeugt Liste mit h [...]-Elementen
# beachte: alte_matrix[i][j] bezieht sich auf das Matrixelement in Zeile i | Spalte j
neue_matrix = [[0 for x in range(w)] for y in range(h)]
# zur Auswahl:
# startmatrix_r()
# startmatrix_block()
# startmatrix_glider()
# startmatrix_glider_blinker1()
# startmatrix_glider_blinker2()
# startmatrix_glider_blinker3()
# startmatrix_glider_blinker4()
# startmatrix_glider_blinker5()
# startmatrix_diamant()
# startmatrix_balken_am_rand()
startmatrix_balken_im_zentrum()
# Start-Generation:
zeige_matrix(alte_matrix)
sleep(3) # Startgeneration 3 s anzeigen
while tasterA.value(): # zum Beenden der Simulation tasterA mindestens zeitinterval Sekunden gedrückt halten
sleep(zeitintervall)
erzeuge_naechste_generation()
display.fill(0)
zeige_matrix(neue_matrix) # nächste Generation
kopiere_neu_zu_alt()
Es gibt auch ein entsprechendes Programm mit einer tastengesteuerter Menüführung:
Das Programm mitsamt gof.mp4-Video finden Sie im Anhang.
.