Hier werden die Unterschiede zwischen zwei Versionen gezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
ws2425:lebewesenverhalten [2024/12/19 17:18] R.Arndt |
ws2425:lebewesenverhalten [2025/05/05 19:41] (aktuell) stefanborn [Unser Code:] |
||
---|---|---|---|
Zeile 6: | Zeile 6: | ||
Richard Arndt | Richard Arndt | ||
- | ====Beschreibung der Projektidee:==== | + | ====Beschreibung der Projektidee (Stand: Projektbeginn):==== |
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. | 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. | ||
====Quellen und Literatur:==== | ====Quellen und Literatur:==== | ||
+ | - 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) | ||
- | ====Projektplanung:==== | + | |
+ | - https://docs.python.org/3/library/math.html (zuletzt besucht: 05.03.2025; 11:30 Uhr) | ||
+ | |||
+ | |||
+ | ====Projektplanung (Stand: Projektbeginn):==== | ||
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. | 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. | ||
- | (Hier muss noch Link zur Cloud hin) | ||
Zeile 58: | Zeile 63: | ||
===4. Projekttag (12.12.2024)=== | ===4. Projekttag (12.12.2024)=== | ||
- | 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 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. |
===5. Projekttag (19.12.2024)=== | ===5. Projekttag (19.12.2024)=== | ||
- | 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 gut. Wir sind nur nicht vollständig zufrieden damit, wie das aussieht. | + | 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. |
+ | |||
+ | |||
+ | ===6. Projekttag (09.01.2025)=== | ||
+ | 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. | ||
+ | |||
+ | |||
+ | ===7. Projekttag (16.01.2025)=== | ||
+ | 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". | ||
+ | |||
+ | ===8. Projekttag (23.01.2025)=== | ||
+ | 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. | ||
+ | |||
+ | ===9. Projekttag (30.01.2025)=== | ||
+ | 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. | ||
+ | |||
+ | ===10. Projekttag (06.02.2025)=== | ||
+ | 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). | ||
+ | |||
+ | |||
+ | ===11. Projekttag (13.02.2025)=== | ||
+ | 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. | ||
+ | |||
+ | |||
+ | ===12. Projekttag - Zusatztag 1 (04.03.2025)=== | ||
+ | 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. | ||
+ | |||
+ | ===13. Projekttag - Zusatztage 2 (05.03.2025)=== | ||
+ | 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. | ||
+ | |||
+ | ===14. Projekttag - Zusatztage 3 (06.03.2025)=== | ||
+ | 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. | ||
+ | |||
====Fazit zum Projekt==== | ====Fazit zum Projekt==== | ||
+ | ==Was macht unser Programm nun?== | ||
+ | |||
+ | |||
+ | 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. | ||
+ | |||
+ | |||
+ | |||
+ | ==Vorstellung vs. Realität== | ||
+ | |||
+ | |||
+ | 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. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==Was würden wir nächstes mal anders machen?== | ||
+ | |||
+ | |||
+ | 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. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ====Unser Code:==== | ||
+ | |||
+ | <code python>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() | ||
+ | </code> | ||
+ | |||
+ | |||
+ | |||
+ |