Bennet Stieball
Richard Arndt
Wir fanden es spannend, eine Simulation bzw. Modellierung von Lebewesen zu erstellen. Diese Lebewesen sollten sich eigenständig in einer von uns erstellten Welt bzw. Umgebung bewegen und nach ihren Bedürfnissen handeln. Diese Bedürfnisse und Instinkte wollen wir aus der echten Natur implementieren und somit ein Geschehen modellieren, welches dem Verhalten der Tiere in der Realität so sehr wie es geht entspricht. Dafür ist es wichtig, dass wir früh die Simulationswelt erstellen.
- https://matplotlib.org/stable/gallery/index (zuletzt besucht: 05.03.2025; 11:00 Uhr)
- https://docs.python.org/3/library/tkinter.html (zuletzt besucht: 05.03.2025; 11:30 Uhr)
- https://docs.python.org/3/library/math.html (zuletzt besucht: 05.03.2025; 11:30 Uhr)
Da wir zu zweit arbeiten, ist es für uns am sinnvollsten die Dateien in einer gemeinsamen tubCloud zu sammeln. Dort laden wir die neuen Dateien hoch. Wir planen, jede Woche einen Ordner für den jeweiligen Donnerstagstermin zu erstellen und dort die neuesten Dateien hineinzufügen. Wären wir in einer größeren Gruppe wäre eine andere Herangehensweise, wie zum Beispiel über gitlab, sinnvoller. Für uns reicht aber vermutlich das Cloud System.
Sollten wir außerhalb der Stunden kommunizieren, tuen wir dies über WhatsApp.
Am ersten Projekttag haben wir uns zuerst in Gruppen zusammengefunden, weswegen für die Arbeit am Projekt nur wenig Zeit übrig blieb. Wir haben also nur eine sehr grobe Struktur für das Wiki erstellt und geplant, was wir alles programmieren müssen und wann wir diese Unterthemen bearbeiten. Daran wird sich natürlich noch sehr viel ändern.
Erste grobe Einschätzung der Aufgaben, die wir lösen müssen (Stand 21.11.2024):
Aufgaben | 21.11.2024 | 28.11.2024 | 05.12.2024 | 12.12.2024 | 19.12.2024 | 09.01.2025 |
---|---|---|---|---|---|---|
Untergrunderstellung (x und y Koordinaten) | xxx | |||||
Untergrunderstellung (Grenzen der Simulationsfläche) | xxx | |||||
Untergrunderstellung (Wasser, Berge, Wiese etc.) | xxx | |||||
Tierverhalten | ||||||
Tiereigenschaften (x und y Koordinaten) | xxx | |||||
Tiereigenschaften (Alter) | xxx | |||||
Tiereigenschaften (Schnelligkeit) | xxx | |||||
Tierinteraktionen (Sicht) | xxx | |||||
Am zweiten Tag haben wir das Wiki weiter strukturiert und es so gestaltet, dass wir ab nun jeden Arbeitsschritt in das Wiki nebenbei notieren könnten. Außerdem haben wir nach einem Tool gesucht, mit dem wir die Welt visualisieren können. Dort sind wir auf „tkinter“ gekommen und haben uns darin eingelesen. Wir haben dann anfänglich ein Canvas erstellt als Grundlage der Simulation.
Am 05.12.2024 haben wir uns um die Lebewesen kümmern wollen. Da war erstmal das Ziel für heute, Lebewesen zufällig auf dem Untergrund zu verteilen und sie sich random bewegen zu lassen. Dies wollten wir über das Importieren von Klassen tuen, da Klassen unsere spätere Grundlage für das Verbessern der Lebewesen seien werden.
Wird sind letzendlich heute soweit gekommen, dass wir beliebig große Kreise auf zufällige Orte in beliebigen Anzahlen auf unserer Grundfläche in eine Richtung bewegen lassen können.
Am 12.12.2024 haben wir versucht arbeitsteilig unterschiedliche Dinge zu implementieren. Wir wollten den Tieren eine „health“ geben und sie beim Erreichen von health = 0 sterben lassen. Das sterben lassen ging voran aber hat noch nicht final funktioniert. Außerdem haben wir die 360 Grad Bewegung hinzugefügt, sowie zufällige Startrichtungen. Temporär haben wir hinzugefügt, dass Tiere bei Kollision mit der „Wand“ oder einem anderen Tier sich umdrehen. Final haben wir entschlossen, dass sich Tiere nicht erst bei Zusammenstößen umdrehen, sondern vor und zum Teil um sich schauen können und bei Detektion sich bereits umdrehen, um Kollisionen zu vermeiden.
Am letzten Tag vor den Weihnachtsferien haben wir uns darum gekümmert, dass wir die Visualisierung im Vollbildmodus auf dem gesamten Bildschirm anzeigen lassen. So wollen wir später auch die finale Modellierung anzeigen lassen. Durch den Vollbildmodus konnte man die Visualisierung nurnoch extern schließen und nicht mehr über den klassischen Weg. Dafür haben wir hinzugefügt, dass man mit dem Drücken der Escape-Taste den Vollbildmodus beendet. Dies funktioniert. Wir haben die Tiere nun Pflanzenfresser genannt und hinzugefügt, dass ihre „Sichtchecks“ auf verschiedenen Distanzen durchgeführt werden. Wir haben verbessert, dass Tiere die sich nah kommen, die Richtung ihrer Bewegung stärker ändern. Anschließend an die letzte Woche, wo Pflanzenfresser eine „Lebensanzahl“ erhalten haben, haben wir heute implementiert, dass sie konstant Leben verlieren und bei weniger als 0 Leben aus der Liste gelöscht werden und damit sterben. Fleischfresser haben wir nun auch und diese laufen fürs erste zufällig durch den Canvas. Wir haben Funktionen, die für verschiedene Tierklassen benutzt werden in eine Elternklasse, namens Tiere, ausgelagert.
Wir haben am 6. Projekttag versucht auf unserer Spielfläche unbegehbare Orte zu erstellen und denen ein Design zu geben. Wir wollten zum Beispiel Seen und Berge umsetzen die für Dinos nicht betretbar sind. Diese Hindernisse zu erstellen und zu positionieren war möglich. So hatten wir es geschafft einen blauen Kreis auf der Bildfläche zu platzieren. Die Interaktionen der Tiere mit diesen Bereichen waren aber schwerer, da man diese Zonen als nicht begehbar und Hinderniss einstellen musste. Das hat nicht geklappt. Außerdem haben wir hinzugefügt, dasss Fleischfresser auf Pflanzenfresser instinktiv hinzu gehen und diese auffressen. Wenn Pflanzenfresser sterben werden diese ab nun nicht mehr aus der Liste entfernt, sondern sie werden lediglich versteckt. So bleibt die Länge der Liste immer gleich.
An diesem Tag wollten wir das Thema der letzten Woche mit den Seen lösen und haben überlegt, dass man die Seen zusammensetzen kann aus mehreren geometrischen Formen wie Kreisen oder anderen, die als Hindernisse auf andere Dinos wirken. Damit hatten wir aber immernoch Probleme. Außerdem hat jeder Dino einen Status, der sein Handeln direkt beeinflußt. Zum Beispiel „Hunting“, „Wandering“ und „Fleeing“.
Am 23.01. haben wir die Seen erstellt und das hat gut geklappt. Das Problem bei den Seen ist, dass ein normaler Kreis etwas unnatürlich und langweilig ist. Deshalb haben wir eine Kombination aus Rechtecken und Kreisen in der selben Farbe passend nebeneinander angeordnet, um eine rundere aber natürliche Form zu erhalten. Das hat gut funktioniert. Wir sind noch nicht so weit, dass die Lebewesen den See als Hindernis ansehen. Gegen Ende haben wir noch versucht eine Art von Strand um den See herum zu legen. Dies haben wir über die selbe Methode wie beim See gemacht. Außerdem wird nun die Sicht nichtmehr mit Stichprobeartigen Checks durchgeführt, sondern mithilfe von Winkeln. Ausweichen wird mit hilfe von Abstand gemacht.
An diesem Tag war Richard nur eine Stunde da wegen eines Arzttermins. In der Zeit haben wir vorallem geplant, wie wir die nächsten Wochen angehen und es wurde im Plenum das faire und neidfreie Teilen von Kuchen besprochen. Deswegen hatten wir heute weniger Zeit.
Heute war Bennet nicht da, was wir am 9. Projekttag schon wussten. Daher konnten wir schon bereden was ich heute machen kann. Ich habe versucht die Statistik, die parallel zur Modellierung laufen soll zu erstellen. Das Gute daran ist, dass das was komplett neues ist, was ich unabhänig von Bennet erstellen kann. Die Idee war es, mit Madplotlib zu arbeiten. Leider hat das nicht so gut geklappt und es machte mir Probleme zu implementieren, dass die Statistik in einem parallelen Fenster läuft, was die Modellierung nicht blockiert und ich hatte Probleme zu erstellen, dass die Statistik live Werte nimmt aus den Listen (Anzhl lebender Dinos von der jeweiligen Art).
Das war der letzte normale Donnerstagstermin von Mathesis und alle Gruppen haben ihre Ergebnisse präsentiert. Daher hatten wir nur wenig Zeit und sind an diesem Tag nicht wirklich voran gekommen.
Heute war der erste Tag vom Labor Block in den Ferien. Wir haben uns für das implementieren eines simpleren Sichtsystems entschieden. Dabei haben wir auf die Autonomie der Tiere und auf die Vermeidung von Kollisionen von Tieren der gleichen Art verzichtet. Mit Matplotlib haben wir auch weitergearbeitet. An diesem Tag sind wir etwas früher gegangen und hatten weniger Zeit.
Am zweiten Tag haben wir die begleitende Statistik in Form von zwei Graphen, die die Anzahl der lebenden Dinos zur jeweiligen Zeit notieren, finalisiert und zum Laufen gebracht. Das funktioniert jetzt ziemlich gut. Unser Ziel ist, dass wir damit am Ende ermitteln können wie sich das Geschehen der Tiere verändert, jenachdem wie viele Tiere von welcher Art man in die Modellierung hineinfügt. Außerdem haben wir den Code vereinfacht und die Basisaktionen der Tiere finalisiert. An diesem Tag sind wir sehr weit voran gekommen.
Am letzten Tag haben wir das Programm von Fehlern befreit. Außerdem haben wir die Essensmechanik hinzugefügt. Das ermöglicht später ein ökosystem-ähnliches Verhalten. Außerdem können wir nun jeden Graphen den wir erstellen als png-Datei in unserem Dateien speichern. Das ist die Basis für eine potentielle Zusammenfassung von Daten über das Verhalten unserer Dinosaurier. Des weiteren haben alle Gruppen ihren letzten Stand präsentiert und vorgestellt.
Wir haben nun ein Programm, was visuell die Interaktionen zwischen Jägern und Gejagten darstellt. Dabei können wir einige Parameter an „Stückzahlen“ der Dinos anpassen, um damit neue Szenarios zu erstellen. Außerdem können wir das statistisch begleiten und haben komplexe und größtenteils autonome Bewegungen im Spielfeld geschaffen.
Wir haben uns vorgestellt, dass wir ein Ökosystem erschaffen, in welchem verschiedene Arten von Dinos leben und interagieren. Wir wollten herausfinden, wie sich die verschiedenen Arten verhalten, jenachdem, wie viele man von welcher Sorte hineinfügt. Wir hätten uns gerne bei der Erstellung der Arten auf Eigenschaften der Dinos bezogen, die sie damals in ihrem echten Leben auch hatten. In Wirklichkeit kamen wir nicht dazu mehr als 2 Klassen an Dinos zu erstellen und konnten dabei nicht viele Eigenschaften hinzufügen. Dafür fehlte etwas die Zeit, da einige Dinge länger dauerten, als wir erwarteten. Trotzdem haben wir es geschafft, Tiere miteinander interagieren zu lassen. Außerdem haben wir es geschafft die Verhalten statistisch zu beobachten und können nun die Parameter der Anzahlen ändern und anpassen. Also haben wir trotzdem einiges geschafft.
Beim nächsten Mal würden wir uns von Anfang an etwas weniger vornehmen und versuchen auf die perfekten Bewegungsverhalten eher zu verzichten und zumindest zum Anfang eher auf gröbere Bewegung zu setzen. Die Arbeitsweise mit der Cloud und den wöchentlichen Dokumentationen war aber sehr gut und hat gut geklappt.
import tkinter as tk import random as r import math as m import numba import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg fenster = tk.Tk() fenster.title("Lebewesenverhalten") """ fenster.attributes("-fullscreen", True) def beenden(event=None): fenster.attributes("-fullscreen", False) exit() fenster.bind("<Escape>", beenden) width = fenster.winfo_screenwidth() hight = fenster.winfo_screenheight() """ fenster.geometry("1000x600") width = 1000 hight = 600 canvas = tk.Canvas(fenster, bg="pink") canvas.pack(fill="both",expand=True) def end_simulation(event): fenster.destroy() fenste = tk.Tk() fenste.title("Statistik") fenste.geometry("1000x600") fig, ax = plt.subplots(figsize=(6,4)) ax.plot(x_werte, plant_values) ax.plot(x_werte, meat_values) canvas = FigureCanvasTkAgg(fig, master=fenste) canvas.draw() canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) fenster.bind("<Return>", end_simulation) start_pflanzenfresser = 50 start_fleischfresser = 5 anzahl_seen = 1 lines = [] dis = 100 time = 0 pflanzenfresser = [] new_pflanzenfresser = [] fleischfresser = [] new_fleischfresser = [] seen = [] x_werte = [] plant_values = [] meat_values = [] def update(event = None): global time if time % 50 == 0: x_werte.append(time) plant_values.append(len(pflanzenfresser)) for tier in pflanzenfresser: if not tier.alive: plant_values[-1] -= 1 meat_values.append(len(fleischfresser)) for tier in fleischfresser: if not tier.alive: meat_values[-1] -= 1 #for z in range(len(lines)): # canvas.delete(lines[z]) global new_pflanzenfresser global new_fleischfresser for baby in new_pflanzenfresser: pflanzenfresser.append(baby) baby.show() new_pflanzenfresser = [] for baby in new_fleischfresser: fleischfresser.append(baby) baby.show() new_fleischfresser = [] for pflanzenfresse in pflanzenfresser: if pflanzenfresse.alive: if time % 5 == 2: pflanzenfresse.sight() pflanzenfresse.check_grazing() pflanzenfresse.change_food(-5*pflanzenfresse.speed) if pflanzenfresse.mating_cooldown > 0: pflanzenfresse.mating_cooldown -= 5 pflanzenfresse.move() for fleischfresse in fleischfresser: if fleischfresse.alive: if time % 5 == 0: fleischfresse.sight() fleischfresse.change_food(-5*fleischfresse.speed) if fleischfresse.mating_cooldown > 0: fleischfresse.mating_cooldown -= 5 fleischfresse.move() time += 1 #fenster.bind("<space>", update) canvas.after(20, update) def det(alpha, beta): return m.sin(m.radians(beta-alpha)) #Distanz zwischen zwei Punkten def distance(x1, x2, y1, y2): return m.sqrt((x1 - x2)**2 + (y1 - y2)**2) #Quadrat des Abstandes zweier Punkte (für schnelle Berechnungen) def squared_distance(x1, x2, y1, y2): return (x1 - x2)**2 + (y1 - y2)**2 class Objekt: def __init__(self, x, y): self.x = x self.y = y class Tiere(Objekt): def __init__(self, x, y , direction, health = 1000, food = 10000, a = 40, b = 180, color = "black", status = "wandering"): #Eigenschaften super().__init__(x, y) self.direction = direction self.direction_offset = 0 self.health = health self.food = food self.color = color self.mating_cooldown = 0 #Sicht self.sichtweite = a self.sichtkegelwinkel = b #Eigenschaften, von denen das Handeln abhängt self.alive = True self.status = "wandering" self.threat = None self.target = None self.avoiding = None def change_food(self, change): if self.food > 0 or change > 0: self.food += change elif self.food <= 0: self.change_health(-10) #tiere verlieren teile ihrer Gesundheit def change_health(self, change): self.health += change if self.health <= 0: self.die() #Änderung der Richtung def change_direction(self, type, change): if type == "set": self.direction = change elif type == "add": self.direction += change self.direction %= 360 #tiere werden wenn sie keine Lebenspunkte mehr haben aus der update-schleife genommen def die(self): self.alive = False if isinstance(self, Fleischfresser): print(self.id) self.hide() #bewegt ein Tier vorwärts abhängig von "speed" und "direction" def forward(self): self.x += self.speed*m.cos(m.radians(self.direction+self.direction_offset)) self.y += self.speed*m.sin(m.radians(self.direction+self.direction_offset)) self.hide() self.show() #beschreibt das Verhalten eines Tieres während seines sogenannten Zuges basierend auf dem Status def move(self): if self.status == "wandering": self.change_direction("add", r.randint(-5,5)) self.forward() elif self.status == "fleeing": if squared_distance(self.x, self.threat.x, self.y, self.threat.y) >= 90000 and self.threat.target != self: self.status = "wandering" self.threat = None self.speed = self.walking_speed self.forward() elif self.status == "hunting": self.change_direction("set", m.degrees(m.atan2((self.target.y-self.y),(self.target.x-self.x)))) if (self.x-self.target.x)**2+(self.y-self.target.y)**2 <= (self.radius+self.target.radius)**2: self.collision() self.forward() #Visualisierung der Tiere def show(self): self.id = canvas.create_oval(self.x-self.radius, self.y-self.radius, self.x+self.radius, self.y+self.radius, fill=self.color) def hide(self): canvas.delete(self.id) #def det(alpha, beta): # return m.sin(m.radians(beta-alpha)) #Überprüft ob die Richtung des Hindernisses im Wahrnehmungsbereich des Tieres liegt def in_sight(self, avoid): direction_left_edge = (self.direction + self.direction_offset) + (self.sichtkegelwinkel / 2) #in der Visualiesierung sind die Seiten vertauscht, weil y-Achse auch gespiegelt ist direction_right_edge = (self.direction + self.direction_offset) - (self.sichtkegelwinkel / 2) #in der Visualiesierung sind die Seiten vertauscht, weil y-Achse auch gespiegelt ist return det(direction_left_edge, avoid) < 0 and det(avoid, direction_right_edge) < 0 #Wahrnehmung der Umwelt mithilfe von Stichpunktartigen Zustandschecks entlang der sightrichtung ("direction") def sight(self): self.avoiding = None potential_targets = [] potential_threats = [] #debug: #lines.append(canvas.create_line(self.x, self.y, self.x+dis*m.cos(m.radians((self.direction + self.direction_offset) + (self.sichtkegelwinkel / 2))), self.y+dis*m.sin(m.radians((self.direction + self.direction_offset) + (self.sichtkegelwinkel / 2))), fill = "orange")) #lines.append(canvas.create_line(self.x, self.y, self.x+dis*m.cos(m.radians(self.direction+self.direction_offset)), self.y+dis*m.sin(m.radians(self.direction+self.direction_offset)), fill = "orange")) #lines.append(canvas.create_line(self.x, self.y, self.x+dis*m.cos(m.radians((self.direction + self.direction_offset) - (self.sichtkegelwinkel / 2))), self.y+dis*m.sin(m.radians((self.direction + self.direction_offset) - (self.sichtkegelwinkel / 2))), fill = "orange")) #Kollision mit der Ecke links oben verhindern if m.sin(m.radians(self.direction+self.direction_offset)) <= 0 and m.cos(m.radians(self.direction+self.direction_offset)) <= 0: if self.x > dis and self.y <= dis: self.avoiding = 270 elif self.x <= dis and self.y > dis: self.avoiding = 180 elif self.x <= dis and self.y <= dis: if self.avoiding == None: self.avoiding = 180 #Kollision mit der Ecke rechts oben verhindern elif m.sin(m.radians(self.direction+self.direction_offset)) <= 0 and m.cos(m.radians(self.direction+self.direction_offset)) > 0: if self.x <= width-dis and self.y <= dis: self.avoiding = 270 elif self.x > width-dis and self.y > dis: self.avoiding = 0 elif self.x > width-dis and self.y <= dis: if self.avoiding == None: self.avoiding = 0 #Kollision mit der Ecke links unten verhindern elif m.sin(m.radians(self.direction+self.direction_offset)) > 0 and m.cos(m.radians(self.direction+self.direction_offset)) <= 0: if self.x > dis and self.y > hight-dis: self.avoiding = 90 elif self.x <= dis and self.y <= hight-dis: self.avoiding = 180 elif self.x <= dis and self.y > hight-dis: if self.avoiding == None: self.avoiding = 180 #Kollision mit der Ecke rechts unten verhindern elif m.sin(m.radians(self.direction+self.direction_offset)) > 0 and m.cos(m.radians(self.direction+self.direction_offset)) > 0: if self.x <= width-dis and self.y > hight-dis: self.avoiding = 90 elif self.x > width-dis and self.y <= hight-dis: self.avoiding = 0 elif self.x > width-dis and self.y > hight-dis: if self.avoiding == None: self.avoiding = 0 if self.avoiding != None: self.avoid(self.avoiding) for k in range(len(pflanzenfresser)): if pflanzenfresser[k].alive and not ((isinstance(self, Pflanzenfresser) and k == self.number)): direction_animal = m.degrees(m.atan2((pflanzenfresser[k].y-self.y),(pflanzenfresser[k].x-self.x))) if self.in_sight(direction_animal): self.react_to_pflanzenfresser(k, potential_targets) for k in range(len(fleischfresser)): if fleischfresser[k].alive and not (isinstance(self, Fleischfresser) and k == self.number): direction_animal = m.degrees(m.atan2((fleischfresser[k].y-self.y),(fleischfresser[k].x-self.x))) if self.in_sight(direction_animal): self.react_to_fleischfresser(k, potential_threats) if len(potential_targets) > 0: self.set_target(self.find_animal(potential_targets)) if len(potential_threats) > 0: self.set_threat(self.find_animal(potential_threats)) if self.avoiding == None: self.change_direction("add", self.direction_offset) self.direction_offset = 0 #Ändert die Richtung, mithilfe einer Offset variable def avoid(self, direction_avoid): determinante = det(self.direction+self.direction_offset, direction_avoid) if determinante < 0: self.direction_offset += 10 else: self.direction_offset -= 10 def find_animal(self, animals): if len(animals) > 0: nearest_animal = animals[0] for i in range(1, len(animals)): if squared_distance(self.x, animals[i].x, self.y, animals[i].y) < squared_distance(self.x, nearest_animal.x, self.y, nearest_animal.y): nearstest_animal = animals[i] return nearest_animal #setzt den status auf "fleeing" def set_threat(self, threat): self.status = "fleeing" self.threat = threat self.speed = self.running_speed self.change_direction("set", m.degrees(m.atan2((self.threat.y-self.y),(self.threat.x-self.x))) + 180) #setzt den status auf "hunting" def set_target(self, target): self.status = "hunting" self.target = target self.color = "red" self.speed = self.running_speed #Wenn ein Fleischfresser einen Pflanzenfresser gefangen hat verliert der Pflanzenfresser leben bis er stirbt. Dann wird der Status des Fleischfressers wieder zurückgesetzt def collision(self): self.target.change_health(-100) if self.target.alive == False: self.change_food(500) self.status = "wandering" self.color = "blue" self.speed = self.walking_speed class Pflanzenfresser(Tiere): radius = 5 def __init__(self, i, x, y, direction): self.number = i self.grazing_speed = 0.1 self.walking_speed = 0.5 self.running_speed = 2.5 self.speed = self.walking_speed super().__init__(x, y, direction, 1000, 1000, 40, 180, "green") def check_grazing(self): if self.food < 900: self.speed = self.grazing_speed elif self.food >= 1000: self.speed = self.walking_speed if self.speed == self.grazing_speed: self.change_food(1) def react_to_pflanzenfresser(self, k, potential_threats): if self.status == "wandering" and pflanzenfresser[k].status == "wandering": if squared_distance(self.x, pflanzenfresser[k].x, self.y, pflanzenfresser[k].y) < 4*self.radius**2: if self.mating_cooldown == 0 and pflanzenfresser[k].mating_cooldown == 0: if self.food >= 500 and pflanzenfresser[k].food >= 500: if len(pflanzenfresser) <= 400: global new_pflanzenfresser random_direction = r.randint(0,359) new_pflanzenfresser.append(Pflanzenfresser(len(pflanzenfresser)+len(new_pflanzenfresser), self.x, self.y, random_direction)) self.mating_cooldown = 1000 pflanzenfresser[k].mating_cooldown = 1000 elif squared_distance(self.x, pflanzenfresser[k].x, self.y, pflanzenfresser[k].y) < 10000: if self.mating_cooldown == 0: self.change_direction("set", m.degrees(m.atan2((pflanzenfresser[k].y-self.y),(pflanzenfresser[k].x-self.x)))) def react_to_fleischfresser(self, k, potential_threats): quadratischeDistanz = squared_distance(self.x, fleischfresser[k].x, self.y, fleischfresser[k].y) if quadratischeDistanz <= dis**2: potential_threats.append(fleischfresser[k]) class Fleischfresser(Tiere): radius = 8 def __init__(self, i, x, y, direction): self.number = i self.walking_speed = 0.8 self.running_speed = 2 self.speed = self.walking_speed super().__init__(x, y, direction, 1000, 1000, 100, 120, "blue") def react_to_pflanzenfresser(self, k, potential_targets): quadratischeDistanz = squared_distance(self.x, pflanzenfresser[k].x, self.y, pflanzenfresser[k].y) if quadratischeDistanz <= 200**2: if self.food < 500: if self.status == "wandering": potential_targets.append(pflanzenfresser[k]) elif self.status == "hunting": if quadratischeDistanz < squared_distance(self.x, self.target.x, self.y, self.target.y): potential_targets.append(pflanzenfresser[k]) def react_to_fleischfresser(self, k, potential_targets): if self.status == "wandering" and fleischfresser[k].status == "wandering": if squared_distance(self.x, pflanzenfresser[k].x, self.y, pflanzenfresser[k].y) < 4*self.radius**2: if self.mating_cooldown == 0 and fleischfresser[k].mating_cooldown == 0: if self.food >= 500 and fleischfresser[k].food >= 500: global new_fleischfresser random_direction = r.randint(0,359) new_fleischfresser.append(Fleischfresser(len(fleischfresser)+len(new_fleischfresser), self.x, self.y, random_direction)) self.mating_cooldown = 1000 fleischfresser[k].mating_cooldown = 1000 elif squared_distance(self.x, fleischfresser[k].x, self.y, fleischfresser[k].y) < 40000: if self.mating_cooldown == 0: self.change_direction("set", m.degrees(m.atan2((fleischfresser[k].y-self.y),(fleischfresser[k].x-self.x)))) class See(Objekt): radius = 50 def __init__(self, number, x = 50, y = 50): self.number = number self.radius = radius self.x = x self.y = y self.color = "blue" super().__init__(x, y) #Simulationsaufbau for i in range(start_pflanzenfresser): random_x = r.randint(Pflanzenfresser.radius, width - Pflanzenfresser.radius) random_y = r.randint(Pflanzenfresser.radius, hight - Pflanzenfresser.radius) random_direction = r.randint(0,359) pflanzenfresser.append(Pflanzenfresser(i, random_x, random_y , random_direction)) pflanzenfresser[i].show() for i in range(start_fleischfresser): random_x = r.randint(Fleischfresser.radius, width - Fleischfresser.radius) random_y = r.randint(Fleischfresser.radius, hight - Fleischfresser.radius) random_direction = r.randint(0,359) fleischfresser.append(Fleischfresser(i, random_x, random_y, random_direction)) fleischfresser[i].show() for i in range(anzahl_seen): ###hiermit habe ich den See und Strand gezeichnet id = canvas.create_rectangle(0, 70, 100, 0, fill= "yellow", outline = "yellow") # Das ist die Füllung vom Loch oben links id = canvas.create_rectangle(0, 200, 100, 0, fill= "yellow", outline = "yellow") id = canvas.create_oval(0, 250, 100, 0, fill= "yellow", outline = "yellow") id = canvas.create_oval(0, 245, 50, 150, fill= "yellow", outline = "yellow") id = canvas.create_oval(0, 255, 70, 150, fill= "yellow", outline = "yellow") id = canvas.create_oval(-50, 255, 30, 100, fill= "yellow", outline = "yellow") id = canvas.create_oval(-100, 260, 100, 100, fill= "yellow", outline = "yellow") id = canvas.create_oval(-100, 260, 200, 100, fill= "yellow", outline = "yellow") id = canvas.create_oval(0, 150, 100, 0, fill= "blue", outline = "blue") id = canvas.create_oval(300, 150, 170, 0, fill= "blue", outline = "blue") id = canvas.create_oval(50, 100, 240, 200, fill= "blue", outline = "blue") id = canvas.create_rectangle(50, 0, 220, 150, fill= "blue", outline = "blue") id = canvas.create_oval(250, 140, 220, 157, fill= "blue", outline = "blue") id = canvas.create_oval(16, 50, 220, 190, fill= "blue", outline = "blue") update()