imshow animation stellt nur ein state dar

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Lenny48
User
Beiträge: 2
Registriert: Samstag 4. Juni 2022, 19:46

Hi, ich habe folgendes Problem:
Ich möchte eine state machine programmieren und dessen states bzw. verschiedenen Generationen dann per imshow animation darstellen. Die state machine funktioniert soweit ohne Probleme per Ausgabe in der Console, jedoch benötige ich eine Hilfestellung zu imshow. Ich habe versucht die verschiedenen states per animate() Fkt zi visualisieren, jedoch bekomme ich nur den ersten State ausgegeben. Hat jemand evtl. einen Tipp, was ich versuchen könnte oder wo mein Logikfehler liegt.

Vielen Dank im Vorraus

Code: Alles auswählen

from array import array

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.pyplot import imshow, show 
import matplotlib.animation as animation



    
cells=np.zeros((10,10),int)
cells[2][2]=1
cells[5][6]=1


    

def size_x():
    size_x=20
    return size_x

def size_y():
    size_y=20
    return size_y

def time_set():
    time=10
    return time

def get_states(cells):

     
    next_gen=np.zeros((10,10),int)
    image=next_gen
    for i in range(1,cells.shape[0]-1):
                    for j in range(1,cells.shape[1]-1):
                            left =       cells[(i-1),j]
                            right=       cells[(i+1),j]
                            top=         cells[i,(j+1)]
                            bottom=      cells[i,(j-1)]
                            top_left=    cells[(i-1),(j-1)]
                            top_right=   cells[(i+1),(j-1)]
                            bottom_left= cells[(i-1),(j+1)]
                            bottom_right=cells[(i+1),(j+1)]
                            curr=cells[i][j]
                
                            if left==1 or right==1 or bottom ==1 or  top==1 or top_left==1 or top_right==1 or bottom_left==1 or bottom_right==1 or curr==1:
                                next_gen[i][j]=1
    cells=next_gen 
    
    return cells
  

fig, ax = plt.subplots()
image=ax.imshow(get_states(cells))
def animate(i):
    get_states(cells)
    image.set_data(get_states(cells))
    return image
myAnimation= animation.FuncAnimation(fig,animate,interval=25)  
plt.show()`#main
fig, ax = plt.subplots()
image=ax.imshow(get_states(cells))
def animate(i):
    get_states(cells)
    image.set_data(get_states(cells))
    return image
myAnimation= animation.FuncAnimation(fig,animate,interval=25)  
plt.show()
Benutzeravatar
Dennis89
User
Beiträge: 1562
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

es macht auf mich den Eindruck, dass du denkst ein Programm läuft von oben nach unten durch und wenn eine Funktion erscheint, dass die dann automatisch ausgeführt wird.
Das ist nicht so. Funktionen muss man aufrufen, wenn man will das die ausgeführt werden.

Das geht zum Beispiel so:

Code: Alles auswählen

def print_something():
    print('something')
    
print_something()
Dann bekommt eine Funktion alles was sie braucht, außer Konstanten, als Argumente übergeben. Globale Variablen darf es nicht geben.
Zum Beispiel übergebe ich der folgenden Funktion eine Liste mit zwei Zahlen und die Funktion gibt per 'return' wieder ein Objekt zurück:

Code: Alles auswählen

def calculate_numbers(numbers):
    return numbers[0] + numbers[1]

result = calculate_numbers([1, 2])
print(result)
So jetzt noch etwas, damit du gleich die "richtige" Struktur verwendest: Auf Modulebene, der Code ohne Einrückungen, darf kein ausführbarer Code stehen, außer der Einstiegspunkt in die 'main'-Funktion. Dass ist auch die Funktion, aus der das Hauptprogramm gesteuert wird. Mein Beispiel von oben würde man alo so schreiben:

Code: Alles auswählen

def calculate_numbers(numbers):
    return numbers[0] + numbers[1]


def main():
    result = calculate_numbers([1, 2])
    print(result)


if __name__ == "__main__":
    main()
Ich hoffe ich habe an der richtigen Stelle angesetzt und du kommst der Problemlösung so ein Stück näher.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Lenny48: Irgendwie ist da Code am Ende doppelt.

Eingerückt wird per Konvention vier Leerzeichen pro Ebene.

Der `array`-Datentyp aus dem `array`-Modul aus der Standardbibliothek wird importiert, aber nicht verwendet. Der macht auch keinen Sinn hier, weil das nur eindimensionale Arrays sind.

Aus `matplotlib.pyplot` werden `imshow()` und `show()` importiert, aber ebenfalls nirgends verwendet.

``as`` bei Importen ist zum umbenennen, aber das `animation`-Modul wird gar nicht umbenannt.

Die ersten drei Funktionen werden nirgends verwendet. Die machen auch nicht wirklich Sinn. Zudem muss man den Rückgabewert nicht vor dem ``return`` an einen Namen binden, man kann den auch gleich hinter dem ``return`` angeben. Lokale Namen sollten auch nicht gleich dem Funktionsnamen sein, dass ist verwirrend.

Das Problem bei dem Code ist, dass die `get_cells()`-Funktion immer mit dem gleichen `cells`-Arrays als Ausgangswert arbeitet. Du musst dieses `cells`-Array verändern, also den neuen Zustand in das Array zurückkopieren, damit der nächste Aufruf auf der nächsten Generation arbeitet. Da ist der Funktionsname dann nicht mehr passend und die Funktion gibt dann nichts mehr zurück.

In der Funktion wird ein `image` definiert, was nirgends verwendet wird.

Das Array für die nächste Generation sollte nicht mit hart kodierten Werten für die Form und den Datentyp erstellt werden, sondern mit `numpy.zeros_like()` und `cells`. Dann kann man die Grösse und den Elementtyp von `cells` ändern und das Array für die nächste Generation hat automatisch die gleiche Grösse und den gleichen Elementtyp.

Die Zellwerte sind 0 und 1, was als Wahrheitswerte `False` und `True` gewertet wird, dass heisst die ganzen Vergleich auf ``== 1`` sind überflüssig, weil die 1 selbst schon wie `True` gewertet wird.

Umgekehrt verhalten sich `True` und `False` auch wie die Zahlen 1 und 0, das heisst das ``if`` ist überflüssig, weil man der neuen Zelle gleich das Ergeignis der Bedingung zuweisen kann.

Code: Alles auswählen

            if (
                left
                or right
                or bottom
                or top
                or top_left
                or top_right
                or bottom_left
                or bottom_right
                or center
            ):
                next_generation[i][j] = 1

            # =>

            next_generation[i][j] = (
                left
                or right
                or bottom
                or top
                or top_left
                or top_right
                or bottom_left
                or bottom_right
                or center
            )
Letztlich ist das aber auch viel unnötige Tipparbeit gewesen diesen ganzen Namen einen Wert zuzuweisen, denn das hätte man mit zwei Schleifen einfacher und kürzer haben können.

Code: Alles auswählen

            left = cells[i - 1, j]
            right = cells[i + 1, j]
            top = cells[i, j + 1]
            bottom = cells[i, j - 1]
            top_left = cells[i - 1, j - 1]
            top_right = cells[i + 1, j - 1]
            bottom_left = cells[i - 1, j + 1]
            bottom_right = cells[i + 1, j + 1]
            center = cells[i][j]

            next_generation[i][j] = (
                left
                or right
                or bottom
                or top
                or top_left
                or top_right
                or bottom_left
                or bottom_right
                or center
            )
            
            # =>
            
            for d_i in range(-1, 2):
                for d_j in range(-1, 2):
                    if cells[i + d_i, j + d_j]:
                        next_generation[i, j] = 1
Das nutzt Numpy jetzt aber so gar nicht aus. Man sollte da mindestens mal ein Subarray ausschneiden und mit `any()` schauen ob irgendeine Zelle da drin ungleich 0 ist.

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation


def calculate_next_state(cells):
    next_generation = np.zeros_like(cells)
    for i in range(1, cells.shape[0] - 1):
        for j in range(1, cells.shape[1] - 1):
            next_generation[i, j] = cells[i - 1 : i + 2, j - 1 : j + 2].any()

    return next_generation


def animate(_frame, cells, image):
    cells[:] = calculate_next_state(cells)
    image.set_data(cells)


def main():
    cells = np.zeros((10, 10), int)
    cells[2][2] = 1
    cells[5][6] = 1

    figure, ax = plt.subplots()
    image = ax.imshow(cells)
    _animation = FuncAnimation(
        figure, animate, fargs=(cells, image), interval=1000
    )
    plt.show()


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Lenny48
User
Beiträge: 2
Registriert: Samstag 4. Juni 2022, 19:46

Vielen Dank für die schnelle und ausführliche Erklärung, tut mir leid, dass vieles in meinem Code kein Sinn ergibt XD, bin erst relativ kurz beim Programmieren dabei.
Antworten