Projektdokumentation
Die grundsätzliche Idee unseres Projektes ist, ein Ökosystem zu simulieren und dieses am Laufen zu halten. Dieses sollte mit verschiedenen Arten mit leicht unterschiedlichen Individuen besiedelt werden, die intra- und interspezifisch interagieren, Räuber-Beute-Beziehungen haben und sich fortpflanzen. Auch eine Interaktion mit der Umwelt und den Pflanzen sollte stattfinden, indem an Wasserstellen getrunken wird und sie anders überquert werden als das Land und die Pflanzen als Nahrung dienen. Die Lebewesen müssen essen, trinken und sich fortpflanzen und sollen dabei nach einheitlichen Kriterien handeln und anhand von einem Nahrungs- und Wasserlevel Prioritäten setzen. Dann wollten wir die Populationsentwicklung und das Verhalten der Lebewesen betrachten. Erst wollten wir eine Evolution simulieren, doch merkten relativ schnell, dass die Verhaltensbeobachtung und die Evolution stark unterschiedliche Zeitskalen benötigen. Wir konzentrierten uns also auf die Beobachtung der Tiere auf einer kleinen Zeitspanne. Unser wissenschaftlicher Zusammenhang sollte ursprünglich sein, dass wir den Tieren reale Werte und Eigenschaften geben, sodass deren Interaktionen möglichst realitätsnah ablaufen. Mit heranschreitender Arbeitszeit merkten wir jedoch, dass die Funktionen und Variablen in unserer Simulation zu realitätsfern sind, dass das Verwenden von wissenschaftlich korrekten Werten keine zufriedenstellenden Ergebnisse liefern würde. Der wissenschaftlichen Zusammenhang ist bei unserem Projekt also eher die Reflexion der Simulation: Wir konnten untersuchen, ob wir trotz unserer inakkuraten Werte ökologische Gesetze und Prinzipien erkennen können, und uns überlegen, wie das Verhalten der Tiere in echt ablaufen würde, und wie unser Code diesen natürlichen Instinkt verändert. Den ausführlichen Verlauf unseres Projektes mit Daten kann man im Protokoll nachlesen, oder zusammengefasst in einer Tabelle in der Projektplanung. Generell kann man aber zu dem Verlauf unserer Projektarbeit sagen, dass er teilweise langsam, aber immer stetig war. Auch wenn wir manche Stunden nicht so wirklich viel geschafft haben, oder an Problemen hängen geblieben sind, gab es trotzdem immer einen kleinen Fortschritt. Eine feste Projektplanung mit Zwischenzielen und Zeitplänen hatten wir gar nicht so wirklich. Am Beginn der Mathesisstunden haben wir uns immer einmal besprochen, was wir diese Stunde überhaupt machen wollen oder müssen, und wer welche Aufgaben übernimmt, individuell gearbeitet, und am Ende noch kurz besprochen wieviel in dieser Stunde geschafft wurde und haben uns dann teilweise auch noch Hausaufgaben aufgegeben. Wir haben also eher immer spontan gearbeitet und haben geguckt wie weit wir damit kommen. Rückschauend kann man sagen, dass in den Stunden, wo nicht so viel geschafft wurde, meistens etwas Zeit durch die Anfangsbesprechungen verbraucht wurde. Es war zwar an sich sinnvoll, vor dem Arbeiten die nächsten Schritte zu besprechen, doch da wir uns eben nicht so wirklich klare Ziele gesetzt haben und ab dem 5.Block auch unsere Gruppenstruktur aufgelöst haben, gab es bei den Besprechungen immer ein wenig Planlosigkeit. Ein Strukturiertes Planen wäre definitiv sinnvoll gewesen. Jedoch hat auch unsere spontane Arbeitsmethode einige Vorteile mit sich gebracht: Es gab weniger Frustration, da wir nicht verbissen auf ein klares Ziel hingearbeitet haben, sondern auch bereit dazu waren, unsere groben Ziele flexibel an unseren Fortschritt und unsere Motivation anzupassen. Natürlich hatten wir immer das Ziel im Kopf, eine funktionierende Simulation eines Ökosystems zu programmieren, doch einen strikten Plan mit festgelegten Methoden gab es nie, weswegen wir immer offen für neue und verschiedene Arbeitsweisen waren.
Unsere Simulation besteht grob gesagt aus 4 Dateien:
(1) tiere_kolja_neu_.py: ist für die Simulation zuständig. In dieser Datei sind alle Tierklassen (Ameise, Fliege, Hase, Fuchs, Uhu) und alle Pflanzenklassen(Gras, Blaubeerstrauch, Kirschbaum) und alle ihre Funktionen und Beziehungen definiert.
(2) spawn_algorithmus.py: ist dafür zuständig, das die in (1) bestimmte Anzahl Lebewesen an einer zufälligen Stelle auf der Simulationsoberfläche erscheint
(3) write_file.py: ist dafür zuständig, dass die Daten aus (1) in ein Dokument geschrieben werden
(4) Visualisierung.py: verbindet die Lebewesenklassen mit den Icons, ermöglicht zoomen, verwendet die in (3) erstellte Datei zur Darstellung mit pyglet
(5) analysis.py: ermöglicht wichtige Daten einer laufenden Simulation in ein Dokument zu schreiben
tiere_kolja_neu_.py ist dabei unsere Hauptdatei und enthält folgende Funktionen:
Die variable Koordinatensystem ist eine dreidimensionale liste, welche ungefähr die simulierte Welt abbildet. Sie zeigt jedes Feld in der Welt mit X- und Y-Koordinate und auch noch welche Tiere sich auf dem Feld befinden.
act() ist die Hauptmethode im Programm, die erst alle Tiere „act_tier()“, dann alle Pflanzen „act_pflanze()“, dann noch das wasser „act_wasser()“ und als letztes setzt es noch die Zeitvariable „time_counter“ um eins hoch.
„act_tier()“ iteriert einfach einmal durch die gesamte Liste aller Tiere und lässt jedes Tier einmal „think_and_move()“ und einmal „end_of_action()“ ausführen
„think_and_move()“ ist mit der Hauptbestandteil des gesamten Handlungsprozesses. Die Methode ruft zuerst „line_of_sight()“ auf und bekommt aus dieser Methode alle Objekte, die in der Sichtweite des Tieres sind, geordnet nach den Feinden - „foe_in_range“, Partnern - „partner_in_range“, Nahrung - „food_in_range“ und noch der Rest - „in_range“. Dann wird geschaut, ob es einen Feind in der Sichtweite gibt und wenn ja flieht das Tier in die entgegengesetzte Richtung mit sogar noch einem Adrenalinschub! Sonst schaut das Tier mit der Funktion: „priorities()“, welche der Prioritäten: Trinken - „water_level“, Essen - „food_level“ und Paarung - „lust_level“ gerade am kleinsten und damit am wichtigsten ist, und versucht dieses Bedürfnis zu befriedigen. Daher versuchen sie im folgenden Schritt zu ihrer Priorität zu gelangen und führen dann, wenn sie auf ihrem Feld der Wahl angekommen sind die Objekt-Treff-Funktion - „meeting()“ aus. Wenn ihre Priorität nicht in ihrer Sichtweite ist, bewegen sie sich einfach in eine beliebige Richtung um ihre maximale Geschwindigkeit.
„meeting()“ wird immer ausgeführt, wenn sich zwei Objekte, von dem eines ein Tier ist, sich treffen. Dann wird das Verhältnis zwischen den Objekten bestimmt und danach gehandelt, sei es, dass ein neues Tier geboren oder eine Pflanze/Tier gegessen wird.
„end_of_action()“ setzt einfach den Durst - „water_level“ und den Hunger - „food_level“ um eins herunter. Der Paarungsdrang - „lustlevel“ wird, wenn er nicht schon unter 30 liegt, um den paarungshäufigkeitswert herunter gesetzt. Dadurch haben verschiedene Tiere, auch der gleichen Spezies, ein anderes Paarungsverhalten. Am Ende wird noch geschaut, ob der Hunger- oder Durst-Wert des Tiers unter 0 liegt und wenn ja verhungert/verdurstet das Tier.
„act_pflanze()“ ist das equivalent zu „act_tier()“ für Pflanzen. Da aber Pflanzen nicht so viele Aktionen in der Wirklichkeit haben, außer zu wachsen und sich zu vermehren. Dies geschieht in der „wachsen()“-Funktion
„wachsen()“ ist die Hauptfunktion der Pflanzen. Hier wird geschaut ob die Größe der Pflanze, die die Funktion ausführt, größergleich ihrer maximaler Größe ist und wenn ja, dann versucht sich die Pflanze mit der „nearby_check()“ Funktion zu vermehren. Wenn dies nicht der Fall ist, wächst die Pflanze um ihren Wachstumswert.
„nearby_check()“ schaut, ob zuerst neben der Pflanze horizontal platz ist und wenn ja, dann werden dort neue Pflanzen gespawnt. Wenn nicht, dann wird ebenfalls die Vertikale überprüft und dort werden pflanzen gespawnt.
„act_wasser()“ ist im eigentlichen Sinne keine richtige act-Methode. Denn nicht jedes Wasserfeld wird ausgeführt, sondern es werden einfach weitere Wasser- und Pflanzen in Abhängigkeit der Wasserfelder generiert. Dies geschieht mit der function „wassergrasspawn()“
„wassergrasspawn()“ erstellt entweder Pflanzen oder Wasser an einem älteren Wasserfeld
Code Implementierung der Kamerabewegung:
In unserem Visualisierungsskript haben wir mit Hilfe von OpenGL-Matrizen die Kamerabewegung gesteuert. Dazu haben wir zuallererst bestimmten Tastenanschläge definiert, die ein Signal zur Veränderung der Kamera geben. Wir haben uns für die Pfeiltasten entschieden um die Kamera in eine bestimmte Richtig zu bewegen und die Zeichen „+“ und „-“ um die Ansicht größer oder kleiner zu skalieren. Um diese Tastenanschläge zu überprüfen haben wir If-statements genutzt, die mit der pyglet.window.key Methode überprüfen, ob eine bestimmte Taste gedrückt wurde.
if symbol == pyglet.window.key.UP:
Daraufhin wurde eine Transforamtionsmatrix generiert, die die x-, y- und Skalierungswerte beinhaltet, um die die Kamera verschoben werden soll.
view_matrix = pyglet.math.Mat4.from_translation(vector=pyglet.math.Vec3(x=0, y=-100, z=0))
Weitergehend findet eine Matrix Multiplikation statt die die alte View-Matrix mit der Transformationsmatrix verrechnet. Heraus kommt die neue Matrix die auch eine neue Kamerabild darstellt.
window.view @ view_matrix
Um sicherzustellen das bestimmte Grenzen der Kamerabewegung eingehalten werden haben wir uns eine Funktion „valid_view“ erstellt. Diese Funktion soll die vollführte Kamerabewegung validieren und gegebenenfalls auch blockieren, falls sie nicht zulässig ist.
Um die maximal zulässige Breite und Höhe herauszufinden, berechnen wir mithilfe der Skalierung einen Wert, der die Grenze unserer Welt angibt
width_stop = -(scale*WIDTH-WIDTH) height_stop = -(scale*HEIGHT-HEIGHT)
Mit weiterfolgenden if-statements werden diese Weltgrenzen überprüft, ob diese überschritten werden. Falls dies eintritt wird der entsprechenden Wert auf 0 gesetzt.
Wichtig hierbei zu erwähnen ist, dass dieser Codeabschnitt aktuell nur für die Skalierung funktioniert. Bei der Implementierung der zentrierten Skalierung haben wir leider die Funktionalität des Bewegens verloren. Leider ist es uns in der zeit nicht mehr gelungen, dort noch eine geeignete Lösung zu finden.
Und Zuletzt werden die Werte noch in eine Matrix gesetzt und diese wird OpenGL als 4×4 Matrix wieder übergeben.
Zusammenfassend kann man sagen: Wir haben es geschafft, ein einigermaßen funktionierendes Ökosystem zu simulieren. Wie schon mehrmals beschrieben hatten wir keine festen Ziele, die erreicht werden konnten. Jeder hat so gut wie er konnte und auf seine Weise ohne viel Druck an unserem Projekt gearbeitet und zur „Vervollständigung“ beigetragen. Wir freuen uns, dass auf diese Weise etwas Funktionierendes und Vorzeigbares dabei entstanden ist. „Vervollständigung“ in Anführungszeichen, weil es jetzt gefühlt unendlich Möglichkeiten gibt, unsere Simulation noch zu verbessern oder zu erweitern. Wir könnten zum Beispiel noch einen Tag-Nachtrythmus und somit eine Art Uhr einführen, dann könnten wir einen Schlafrythmus für jedes Tier einführen, und ihre Tag- und Nachtaktivität beachten, indem sie sich während ihrem Schlaf verstecken. Die Lebensräume der Tiere könnten bestimmt werden, das lebensraumbezogene Konkurrenzverhalten könnte dann simuliert werden… So kann man immer weiter machen. Auch an der Visualisierung könnte man noch arbeiten: die Tiere „teleportieren“ sich in unserem jetztigen Stand jeden Simulationsschritt ein kleines Stück weiter, schöner wäre aber eine Vsualisierung der Bewegung dorthin. Auch das Laufen um Wasser herum könnte noch eingeführt werden, denn momentan laufen unsere Tiere durch das Wasser durch und werden langsamer, um das herumlaufen oder schwimmen zu simulieren. Ein weiterer Schritt, der zu Beginn auch noch ein Zeil von uns war, wäre das Einbinden einer weiteren Zeitskala, so dass man die Evolution der Lebewesen untersuchen könnte. Dann hätte man auch noch die Mutation einführen müssen…. Ein Ökosystem ist so kompliziert, dass eine Simulation dessen niemals vollendet sein kann. Wir konnten jedoch feststellen, dass ein Ökosystem, das aus den von uns verwendeten Individuen besteht, in der realen Welt so nicht funktionieren würde, da wir unsere erst realitätsgetreuen Parameter später ändern mussten, um das Ökosystem einigermaßen zum Laufen zu bekommen. Wir haben aber auch nie erwartet, dass eine auf unsere Art simuliertes Ökosystem irgendwie mit einem realen Ökosystem mithalten kann. Trotzdem sind wir aber mit unserem Ergebnis sehr zufrieden. Die Lebewesen haben zwar kein natürliches Verhalten, aber es ist hauptsächlich logisch. Wir konnten sogar einige ökologische Prozesse in unserer Simulation beobachten: das Räuber Beute Verhalten zwischen Hase und Fuchs: gab es viele Füchse, gab es weniger Hasen und anderseherum. Sogar ökologische Nischen konnten wir beobachten: zum Beispiel hatten in einem Simulationsdurchgang die Hasen eine Wasserstelle gefunden, umgeben mit genug Pflanzen und fern von ihren Feinden. Das Ökosystem ist nicht perfekt, aber es überlebt für einige Zeit, die gleichen Startbedingungen bringen wegen den leicht unterschiedlichen Parametern der Individuen unterscheidliche Ergebnisse bei jedem Durchgang und es ist spannend zu beobachten. Damit sind wir persönlich sehr zufrieden.