Benutzer-Werkzeuge

Webseiten-Werkzeuge


ws1920:dokumentierter_code_finale_version

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen gezeigt.

Link zu dieser Vergleichsansicht

Nächste Überarbeitung
Vorhergehende Überarbeitung
ws1920:dokumentierter_code_finale_version [2020/03/18 14:52]
richard.wonneberger angelegt
ws1920:dokumentierter_code_finale_version [2020/03/26 22:50] (aktuell)
Labo neuste Version
Zeile 1: Zeile 1:
-Das Programm besteht aus zwei Dateien +Das Programm besteht aus zwei Dateien:
-1. die Klassendatei GameOfLife.py +
-2. die Ausführungsdatei+
  
-In der ersten sind das Game of life und seine Regeln implementiert und die zweite greift darauf zu und erstellt eine Animationmit der man interagieren kann.+  * 1. die GameOfLife.py Datei 
 +  * 2. die Ausführungsdatei 
 + 
 +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:​
  
 <​code>​ <​code>​
-pp+import random 
 +import numpy as np 
 +import matplotlib.pyplot as plt 
 +import time 
 + 
 +class GameOfLife(object):​ 
 +    """​  
 +    Diese Klasse beschreibt einen Zustand eines Game of Lifes und stellt Funktionen dazu bereit.  
 + 
 +    Attribute:​ 
 +        zustand: der aktuelle Zustand des Game of Life 
 +        groesse: die Seitenlänge des Spielfeldes 
 +        tem: die Tripleeinheitsmatrix,​ die zur Berechnung benötigt wird 
 +    """​ 
 + 
 +    def __init__(self,​ zustand): 
 +        """​ 
 +        Erzeugt ein neues GameOfLife-Objekt. 
 + 
 +        Parameter:​ 
 +            zustand: der initiale Zustand 
 +        """​ 
 +        self.zustand = zustand 
 +        self.groesse = zustand.shape[0] 
 +        self.generiereTripleeinheitsmatrix() 
 + 
 +    def generiereTripleeinheitsmatrix(self):​ 
 +        self.tem = np.zeros((self.groesse,​ self.groesse)) 
 +        for i in range(0, self.groesse):​ 
 +            for j in range(0, self.groesse):​ 
 +                if i == j: 
 +                    self.tem[(i,​j)] = 1 
 +                else: 
 +                    self.tem[(i,​j)] = 0 
 +                     
 +        for i in range(0, self.groesse):​ 
 +            for j in range(0, self.groesse):​ 
 +                if i == (j + 1): 
 +                    self.tem[(i,​j)] = 1 
 +        for i in range(0, self.groesse):​ 
 +            for j in range(0, self.groesse):​ 
 +                if j == (i + 1): 
 +                    self.tem[(i,​j)] = 1 
 +                     
 +        self.tem[0, self.tem.shape[1] - 1] = 1 
 +        self.tem[self.tem.shape[1] - 1, 0] = 1 
 +                     
 +    def berechneAnzahlNachbarn(self):​ 
 +        # 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! 
 +        z = np.dot(self.tem,​ self.zustand) ​       
 +        k = np.dot(z, self.tem) 
 +        anzahlNachbarn = k - self.zustand 
 +             
 +        return anzahlNachbarn 
 + 
 +    def schritt(self):​ 
 +        """​ Führt einen Berechnungsschritt aus und gibt den neuen Zustand zurück. """​ 
 +        anzahlNachbarn = self.berechneAnzahlNachbarn() 
 + 
 +        # berechne neuen Zustand 
 + 
 +        # 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, self.groesse):​ 
 +            for j in range(0, self.groesse):​ 
 +                if self.zustand[(i,​j)] == 1 and (anzahlNachbarn[(i,​j)] in range(2) or anzahlNachbarn[(i,​j)] in range(4,​9)):​ 
 +                    self.zustand[(i,​j)] = 0 
 +                elif self.zustand[(i,​j)] == 0 and anzahlNachbarn[(i,​j)] == 3: 
 +                    self.zustand[(i,​j)] = 1 
 +                         
 +        return self.zustand 
 +</​code>​ 
 + 
 + 
 +Die Animationsdatei (anzeige.py):​ 
 + 
 +<​code>​ 
 +from gameoflife import GameOfLife 
 +import numpy as np 
 +from matplotlib import pyplot as plt 
 +from matplotlib import animation 
 +import matplotlib.pyplot as plt 
 +from PIL import Image 
 +from math import floor, ceil 
 + 
 +class Anzeige(object):​ 
 +    """​ 
 +    Diese Klasse animiert ein GameOfLife-Objekt mithilfe der matplotlib. 
 + 
 +    Attribute:​ 
 +        gol: das GameOfLife-Objekt,​ das animiert wird 
 +        canvas: der matplotlib-Canvas,​ auf den gezeichnet wird 
 +        im: die aktuell angezeigte Bitmap 
 +        zellAlter: speichert für jede Zelle, für wie viele Generationen sie schon lebendig ist 
 +        animationLaeuft:​ gibt an, ob die Animation läuft (also nicht pausiert ist) 
 +        benutzerZeichnet:​ gibt an, ob der Benutzer gerade auf den Canvas zeichnet 
 +        anim: das matplotlib-Animationsobjekt 
 +    """​ 
 +     
 +    # 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 __init__(self,​ gol): 
 +        """​ 
 +        Erzeugt ein neues Anzeige-Objekt und beginnt die Animation. 
 + 
 +        Parameter:​ 
 +            gol: das GamoOfLife-Objekt 
 +        """​ 
 +        self.gol = gol 
 + 
 +        fig = plt.figure() 
 +        self.canvas = fig.canvas 
 +        plt.axis('​off'​) 
 + 
 +        #so wird bei jedem ausfuehren eine zufaellige colormap gewaehlt um ein angenehmeres,​ vielseitiges Langzeiterlebnis zu kreieren 
 +        self.im = plt.imshow(self.gol.zustand,​ animated=True,​ cmap=plt.get_cmap(Anzeige.colormaps[int(np.random.random()*len(Anzeige.colormaps))])) 
 +        self.zellAlter = np.zeros(self.gol.zustand.shape) 
 +        self.zeichne(self.gol.zustand) 
 + 
 +        #Verbinden, der interaktionsevents mit dem figure, in dem die animation laeuft 
 +        self.canvas.mpl_connect('​key_press_event',​ self.onPress) 
 +        self.canvas.mpl_connect('​button_press_event',​ self.onClick) 
 +        self.canvas.mpl_connect('​button_release_event',​ self.onClickEnd) 
 +        self.canvas.mpl_connect('​motion_notify_event',​ self.onMove) 
 + 
 +        self.animationLaeuft = True 
 +        self.benutzerZeichnet = False 
 + 
 +        #die tatsaechliche Ausfuehrung:​ 
 +        self.anim = animation.FuncAnimation(fig,​ self.animationsschritt,​ interval=200) 
 +        plt.show() 
 +     
 +    def animationsschritt(self,​ e): 
 +        '''​ Führt einen Animationsschritt durch. '''​ 
 +        self.gol.schritt() 
 +        self.zeichne(self.gol.zustand) 
 +        return self.im, 
 + 
 +    def zeichne(self,​ zustand): 
 +        '''​ Zeichnet den angegebenen Zustand auf den matplotlib-Canvas. '''​ 
 +        # enthaelt Werte ueber die '​Lebensdauer'​ einer Zelle 
 +        self.zellAlter = self.zellAlter * zustand + zustand 
 +        # lebende Zellen aendern ihre Farbe, je laenger sie leben 
 +        self.im.set_array(np.ones(self.zellAlter.shape) - np.power(0.5,​ self.zellAlter)) 
 +        self.canvas.draw() 
 + 
 +    #die vier folgenden Funktionen sind zum Interagieren mit dem Geschehen 
 +    def onPress(self,​ event): 
 +        # zum Pausieren mit der Leertaste 
 +        if event.key == ' ': 
 +            if self.animationLaeuft:​ 
 +                self.anim.event_source.stop() 
 +                self.animationLaeuft = False 
 +            else: 
 +                self.anim.event_source.start() 
 +                self.animationLaeuft = True 
 + 
 +    def onClick(self,​ event): 
 +        # mit dem Cursor kann man den Wert einer Zelle per Klick aendern von lebend zu tot, sowie andersherum 
 +        if event.button == 1 and event.xdata != None and event.ydata != None: 
 +            self.benutzerZeichnet = True 
 +            self.anim.event_source.stop() 
 +            self.zustandNeu = np.copy(self.gol.zustand) 
 +            pos_x = int(np.round(event.xdata)) 
 +            pos_y = int(np.round(event.ydata)) 
 +            self.zustandNeu[pos_y,​ pos_x] = 1 - self.gol.zustand[pos_y,​ pos_x] 
 +            self.zellAlter = np.zeros(self.zustandNeu.shape) 
 +            self.zeichne(self.zustandNeu) 
 +             
 +    def onMove(self,​ event): 
 +        # mit dem Cursor kann man den Wert mehrerer Zellen per Klick aendern, indem man die Maustaste gedrueckt haelt 
 +        # die Animation wird hierfuer pausiert 
 +        if event.button == 1 and self.benutzerZeichnet and event.xdata != None and event.ydata != None: 
 +            pos_x = int(np.round(event.xdata)) 
 +            pos_y = int(np.round(event.ydata)) 
 +            self.zustandNeu[pos_y,​ pos_x] = 1 - self.gol.zustand[pos_y,​ pos_x] 
 +            self.zellAlter = np.zeros(self.zustandNeu.shape) 
 +            self.zeichne(self.zustandNeu) 
 + 
 +    def onClickEnd(self,​ event): 
 +        # beim loslassen der Maustaste werden die geänderten Werte an die Animation gegeben und diese wird fortgesetzt 
 +        if event.button == 1 and self.benutzerZeichnet:​ 
 +            self.gol.zustand = self.zustandNeu 
 +            self.zellAlter = np.zeros(self.zustandNeu.shape) 
 +            self.zeichne(self.zustandNeu) 
 +            self.benutzerZeichnet = False 
 +            if self.animationLaeuft:​ 
 +                self.anim.event_source.start() 
 + 
 +###### Programmeinstieg ####### 
 + 
 +def main(): 
 +    # Erzeuge ein GameOfLife-Objekt anhand der Benutzereingaben 
 + 
 +    # 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-groesse 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 
 +    modus = int(input('​Gib den Modus 1 (Zufall), 2 (alternierend),​ 3 (Datei) oder 4 (Bild) ein:  ')) 
 + 
 +    if modus == 1 or modus == 2 or modus == 5: 
 +        groesse = int(input('​Gib die Anzahl der Zeilen/​Spalten ein: ')) 
 + 
 +    if modus == 1: 
 +        # Zufall 
 +        zustand = np.round(np.random.random((groesse,​ groesse))).astype(int) 
 +     
 +    elif modus == 2: 
 +        # Alternierend 
 +        zustand = np.zeros((groesse,​ groesse)) 
 +        for i in range(0, groesse): 
 +            for j in range (0, groesse): 
 +                if j%2==0: 
 +                    zustand[(i,​j)]=0 
 +                else: 
 +                    zustand[(i,​j)]=1 
 +     
 +        if zustand[(int(groesse/​2),​ int(groesse/​2))] == 0: 
 +            zustand[(int(groesse/​2),​ int(groesse/​2))] = 1 
 +            zustand[(int((groesse/​2)+1),​ int(groesse/​2))] = 1 
 +             
 +        else: 
 +            zustand[(int(groesse/​2),​ int(groesse/​2))] = 0 
 +            zustand[(int((groesse/​2)+1),​ int(groesse/​2))] = 0 
 +     
 +    elif modus == 3: 
 +        # Datei lesen 
 +        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) 
 +         
 +    elif modus == 4: 
 +        # Bild lesen 
 +        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) 
 +     
 +    # erzeuge GameOfLife-Objekt 
 +    gol = GameOfLife(zustand) 
 + 
 +    # initialisiere Anzeige 
 +    Anzeige(gol) 
 + 
 +def machQuadrat(pixel):​ 
 +    """​ Macht das angegebene zweidimensionale Numpy-Array quadratisch,​ indem es die Ränder mit Nullen auffüllt. """​ 
 +    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 
 + 
 +main()
 </​code>​ </​code>​
ws1920/dokumentierter_code_finale_version.1584539575.txt.gz · Zuletzt geändert: 2020/03/18 14:52 von richard.wonneberger