Inhaltsverzeichnis

Lebewesenverhalten

lebewesenverhalten

Namen:

Bennet Stieball

Richard Arndt

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.

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)

- 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.

Sollten wir außerhalb der Stunden kommunizieren, tuen wir dies über WhatsApp.

Dokumentation:

1. Projekttag (21.11.2024)

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

2. Projekttag (28.11.2024)

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.

3. Projekttag (05.12.2024)

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.

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.

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. 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

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:

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()