Dies ist eine alte Version des Dokuments!
Das Programm besteht aus zwei Dateien:
In der ersten sind das Game of life und seine Regeln implementiert (sie sollte GameOfLife.py heißen) und die zweite greift darauf zu und erstellt eine Animation mit der man interagieren kann.
GameOfLife.py:
import random import numpy as np import matplotlib.pyplot as plt from PIL import Image from math import floor, ceil import time #in dieser Datei sind die benoetigten Funktionen definiert, um das Game of Life zu starten #zum Verstaendnis sollte man mit dem Game Of Life vertraut sein aber die Grundlagen sind: 0=tot und 1=lebend #Zusammengefassung des Codes: #wenn Initialisiere() aufgerufen wird, legt der/die Benutzer/in die Feldgroesse (bis jetzt immer quadratisch) #und die Anfangskonfiguration fest. hierbei ist aus 5 moeglichen Modi zu waehlen(s.u.) #Um die GameOfLife Update Regel anzuwenden werden in Schritt() die Anzahl der Nachbarn einer jeden Zelle #und daraus der jeweils naechste Zustand(1 oder 0) berechnet #Zum Aufsummieren der Nachbarn multipliziert man die vorher mit Tripleeinheitsmatrix(z,s) erstellte Tridiagonalmatrix von beiden Seiten #an die Matrix, in der die Zustaende der zellen gespeichert sind (einmal um die horizontalen und einmal um die vertikalen Partner zu addieren) #als gleichung sieht das ungefaehr so aus:(tem steht hier fuer die tridiagonalmatrix) #anzahlnachbarn = tem*Zustand*tem #mit diesen zwei Matrixmultiplikationen kann man sehr recheneffektiv die 9 nachbarn(zelle selbst mitgezaehlt) #aller Zellen gleichzeitig aufsummieren und dabei haben wir auch noch direkt etwas ueber lineare Algebra gelernt! #der neue Zustand wird dann einfach abhaengig von der anzahl der nachbarn, entsprechend der regeln des game of life bestimmt #die 5 Konfigurationsmodi sind: #1.zufaellig: hier wird jede Zelle zufaellig einen wert 1 oder 0 bekommen #2.alternierend: hier wird das Feld in abwechselnd 1- und 0-Spalten eingeteilt, mit einem kleinen Fehler in der Mitte #3.Datei: hier kann direkt eine Konfiguration eingelesen werden. z.B. um eine gleiterkanone oder Aehnliches einzulesen #4.Bild: eine Bilddatei kann eingelesen und in eine annaehernd erkennbare anfangskonfiguration umgewandelt werden (da der kontrast zu schwarzweiss und die groesse geaendert wird) #5.Leer: hier wird das Feld mit Nullen gefuellt #da das Programm nur mit quadratischen Zustandsmatrizen funktioniert, wird bei Modi 3 und 4 die MachQuadrat(pixel) Funktion angewandt def Tripleeinheitsmatrix(zeilen,spalten): tem = np.zeros ((zeilen,spalten)) for i in range(0, zeilen): for j in range(0, spalten): if i==j: tem[(i,j)] = 1 else: tem[(i,j)] = 0 for i in range(0, zeilen): for j in range(0, spalten): if i==(j+1): tem[(i,j)] = 1 for i in range(0, zeilen): for j in range(0, spalten): if j==(i+1): tem[(i,j)] = 1 tem[0,tem.shape[1]-1] = 1 tem[tem.shape[1]-1,0] = 1 return tem def BerechneAnzahlNachbarn(zustand): z = np.dot(tem, zustand) k = np.dot(z, tem) anzahlNachbarn = k - zustand return anzahlNachbarn def BerechneNeuenZustand(zustand, anzahlNachbarn): #Ursprungszelle Tod + genau 3 lebende Nachbarn = lebend #Ursprungszelle Lebend + genau 1 lebenden Nachbarn = tot #Ursprungszelle Lebend + 2 o. 3 Nachbarn = lebend #Ursprungszelle lebend + 4 oder mehr Nachbarn = tot for i in range(0, zeilen): for j in range(0, spalten): if zustand[(i,j)] == 1 and (anzahlNachbarn[(i,j)] in range(2) or anzahlNachbarn[(i,j)] in range(4,9)): zustand[(i,j)]=0 elif zustand[(i,j)] == 0 and anzahlNachbarn[(i,j)]==3: zustand[(i,j)]=1 return zustand def Schritt(): global zustand anzahlNachbarn = BerechneAnzahlNachbarn(zustand) ergebnis = BerechneNeuenZustand(zustand, anzahlNachbarn) return ergebnis def Wiederhole(t): for i in range(t): Schritt() def MachQuadrat(pixel): breite = pixel.shape[0] hoehe = pixel.shape[1] size = max(breite, hoehe) links = floor((size - breite) / 2) rechts = ceil((size - breite) / 2) oben = floor((size - hoehe) / 2) unten = ceil((size - hoehe) / 2) pad_width = ((links, rechts), (oben, unten)) pixel = np.pad(pixel, pad_width, mode='constant', constant_values=0) return pixel def Initialisiere(): global zustand, tem, zeilen, spalten, cmap modus = int(input('Gib den Modus 1 (Zufall), 2 (alternierend), 3 (Datei), 4 (Bild) oder 5 (Leer) ein: ')) if modus==1 or modus==2 or modus==5: spalten = zeilen = int(input('Gib die Anzahl der Zeilen/Spalten ein: ')) if modus==1: zustand = np.round(np.random.random((zeilen,spalten))).astype(int) elif modus==2: zustand=np.zeros((zeilen,spalten)) for i in range(0,zeilen): for j in range (0,spalten): if j%2==0: zustand[(i,j)]=0 else: zustand[(i,j)]=1 if zustand[(int(zeilen/2),int(spalten/2))]==0: zustand[(int(zeilen/2),int(spalten/2))]=1 zustand[(int((zeilen/2)+1),int(spalten/2))]=1 else: zustand[(int(zeilen/2),int(spalten/2))]=0 zustand[(int((zeilen/2)+1),int(spalten/2))]=0 elif modus==3: DateiName = input('was willste oeffnen? ') Datei = open(DateiName) DateiAlsListe = [] for line in Datei: DateiAlsListe.append([int(c) for c in list(line.rstrip())]) DateiAlsMatrix = np.array(DateiAlsListe) zustand = MachQuadrat(DateiAlsMatrix) zeilen = spalten = zustand.shape[0] elif modus==4: dateiname = input('Gib den Bildpfad ein: ') bild = Image.open(dateiname) bild = bild.convert('1') bild.thumbnail((500,500)) pixel = np.array(bild) zustand = MachQuadrat(pixel) zeilen = spalten = zustand.shape[0] elif modus==5: zustand = np.zeros((zeilen,spalten)).astype(int) tem = Tripleeinheitsmatrix(zeilen, spalten)
Die Animationsdatei:
import GameOfLife as gol import numpy as np from matplotlib import pyplot as plt from matplotlib import animation # in dieser Datei, wird die Animation der 'Game of Life' - Schritte ausgeführt # die vorletzte Zeile mit Funcanimation bildet das Grundgeruest, der Rest des Codes ist groesstenteils Vorbereitung # und die Funktionen zum Interagieren # unterschiedliche colourmaps (weil ein buntes Leben, lebenswerter ist) # diese werden zufaellig aufgerufen colormaps = ['Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', 'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', 'GnBu', 'GnBu_r', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', 'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', 'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', 'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', 'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', 'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', 'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', 'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', 'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', 'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', 'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', 'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', 'coolwarm_r', 'copper', 'copper_r', 'cubehelix', 'cubehelix_r', 'flag', 'flag_r', 'gist_earth', 'gist_earth_r', 'gist_gray', 'gist_gray_r', 'gist_heat', 'gist_heat_r', 'gist_ncar', 'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', 'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gnuplot', 'gnuplot2', 'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'hot', 'hot_r', 'hsv', 'hsv_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', 'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', 'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', 'rainbow_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', 'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', 'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'twilight', 'twilight_r', 'twilight_shifted', 'twilight_shifted_r', 'viridis', 'viridis_r', 'winter', 'winter_r'] def animate(e): ''' die Funktion, die von Funcanimation ausgefuehrt wird wie gol.Schritt funktioniert ist in der Klassendatei beschrieben ''' gol.Schritt() draw(gol.zustand) return im, gol.Initialisiere() fig = plt.figure() def draw(zustand): ''' lebende Zellen aendern ihre Farbe, je laenger sie leben ''' global cmap #enthaelt Werte ueber die 'Lebensdauer' einer Zelle cmap = cmap * zustand + zustand im.set_array(np.ones(cmap.shape) - np.power(0.5, cmap)) fig.canvas.draw() zustand_neu = None anim_running = True drawing = False #die vier folgenden Funktionen sind zum Interagieren mit dem Geschehen def onPress(event): ''' zum Pausieren mit der Leertaste ''' global anim_running if event.key==' ': if anim_running: anim.event_source.stop() anim_running = False else: anim.event_source.start() anim_running = True def onClick(event): ''' mit dem Cursor kann man den Wert einer Zelle per Klick aendern von lebend zu tot, sowie andersherum ''' global cmap, zustand_neu, drawing if event.button == 1 and event.xdata != None and event.ydata != None: drawing = True anim.event_source.stop() zustand_neu = np.copy(gol.zustand) pos_x = int(np.round(event.xdata)) pos_y = int(np.round(event.ydata)) zustand_neu[pos_y, pos_x] = 1 - gol.zustand[pos_y, pos_x] cmap = np.zeros(zustand_neu.shape) draw(zustand_neu) def onMove(event): ''' mit dem Cursor kann man den Wert mehrerer Zellen per Klick aendern, indem man die Maustaste gedrueckt haelt die Animation wird hierfuer pausiert''' global zustand_neu, cmap if event.button == 1 and drawing and event.xdata != None and event.ydata != None: pos_x = int(np.round(event.xdata)) pos_y = int(np.round(event.ydata)) zustand_neu[pos_y, pos_x] = 1 - gol.zustand[pos_y, pos_x] cmap = np.zeros(zustand_neu.shape) draw(zustand_neu) def onClickEnd(event): ''' beim loslassen der Maustaste werden die geänderten Werte an die Animation gegeben und diese wird fortgesetzt ''' global zustand_neu, cmap, drawing if event.button == 1 and drawing: gol.zustand = zustand_neu cmap = np.zeros(zustand_neu.shape) draw(zustand_neu) drawing = False if anim_running: anim.event_source.start() plt.axis('off') #so wird bei jedem ausfuehren eine zufaellige colormap gewaehlt um ein angenehmeres, vielseitiges Langzeiterlebnis zu kreieren im = plt.imshow(gol.zustand, animated=True, cmap=plt.get_cmap(colormaps[int(np.random.random()*len(colormaps))])) cmap = np.zeros(gol.zustand.shape) draw(gol.zustand) #Verbinden, der interaktionsevents mit dem figure, in dem die animation laeuft fig.canvas.mpl_connect('key_press_event', onPress) fig.canvas.mpl_connect('button_press_event', onClick) fig.canvas.mpl_connect('button_release_event', onClickEnd) fig.canvas.mpl_connect('motion_notify_event', onMove) #die tatsaechliche Ausfuehrung: anim = animation.FuncAnimation(fig, animate, interval=200) plt.show()