Umsetzung
Die Umsetzung der beiden Simulationen erfolgte im Beispiel mit der Programmierumgebung Delphi (genauer Delphi 3). Für die Formulare wurden weitgehend die, bei D3 mitgelieferten, Standardkomponenten verwendet. Einzige Ausnahme bildet eine Komponente aus der RxLib1, welche zur Parametereingabe genutzt wurde. Insgesamt wurde der Quellcode so angelegt, das er verhältnismäßig übersichtlich erscheinen sollte, so wurden keine komplizierten Optimierungen und dergleichen vorgenommen, da in erster Linie das Programm auch als Beispiel, denn als reines Simulationsprogramm, um der Simulation Willen, angesehen wurde. Auch auf die Auslagerung der wiederholten Berechnungen in einen Tread wurde aus diesem Grunde verzichtet.Ameise
Alles in allem sind die Regeln von Langton's Ants mit etwas Erfahrung recht einfach umzusetzen. Bei der Simulation wurde dafür entschieden, nicht das gesamte, der Ameise verfügbare Gitter in einem zweidimensionalem Array zu speichern, sondern dieses Gitter wird direkt in Form eines Stringgrids ausgegeben, in welchem auch der Status der Felder (true/false) über den Inhalt gespeichert wird.Dies ist keine Umsetzung, welche man in der Praxis einsetzen würde, jedoch wird so eine etwas andere Verwendung der TStringGrid-Komponente gezeigt. Außerdem ist der Code so ebenfalls recht verständlich gehalten.
Bei der Darstellung mittels eines StringGrid ist zu beachten, das dessen Canvas2 nicht gespeichert wird. Dadurch kommt es, sobald das Fenster außerhalb des sichtbaren Bildschirmbereichs war (beispielsweise durch Überlagerung durch andere Fenster, Minimieren) zum Zurücksetzen aller Felder, welche verdeckt waren. Die interne Berechnung läuft dann zwar weiterhin korrekt, jedoch wird die Ausgabe dadurch letztlich unbrauchbar.
Die Ameisen werden mit ihrer aktuellen Sichtrichtung und denn Koordinaten ihres momentanen Feldes einzeln gespeichert, wodurch nicht bei jedem Durchlauf das gesamte Gitter durchgegangen werden muss, sondern die Ameisen direkt berechnet werden können. So sind pro Zyklus nur so viele Berechnungen nötig, wie Ameisen vorhanden sind.
Ein Problem, was die programmseitige Umsetzung betrifft, liegt in der Tatsache, das es ohne kompliziertere Verfahren nicht möglich ist, eine Programmunterbrechung für weniger als 20 ms zu erreichen. Dies liegt daran, dass der Rechner zur Zeitmessung und zum Aufruf der ProcessMessages und erneuten Zuteilung einer Zeitscheibe an den Anwendungstread diese Zeit benötigt. Dafür wurde leider keine, vom Umfang vertretbare, Lösung gefunden3.
Weiteres siehe den kommentierten Quelltext.
Wator
Die Regeln für Wator sind, im Vergleich zu denen der Ameise etwas aufwendiger zu implementieren.Das gesamte Gitter, auf welchem sich die Individuen bewegen, wurde in dieser Implementation als ein Zweidimensionales Array eines selbst definierten Records gespeichert. Dieses Array enthält pro Feld jeweils einen Wert für die vertretene Gattung (Hai/Fisch/unbesetzt) sowie weiterer Felder für Alter und Fastenzeit, welche jedoch erst Anwendung finden, wenn sich auf dem Feld auch ein entsprechendes Individuum befindet. Außerdem wird in diesem Array die erfolgte Bewegung mittels eines booleans vermerkt, um Mehrfachbewegungen innerhalb eines Zyklus zu verhindern. Dieses Array wird nun bei jeder Berechnung felderweise durchlaufen und die entsprechenden Berechnungen für jedes Feld einzeln durchgeführt. Die Berechnungen laufen wie folgt ab:
- Test, ob das Feld belegt ist, wenn ja:
- Prüfung, ob ein Nachbarfeld frei, bzw. ein vorhandener Hai einen Fisch erreichen kann, wenn Bewegung möglich:
- ermitteln eines Zielfeldes und übertragen der Daten auf dieses
Bedenkt man dabei, dass das in dieser Implementation verwendete Array aus 200 mal 200 Feldern besteht, so ergeben sich bereits bei dieser recht kleinen Weltgröße 40000 Felder, welche zumindest auf Belegung geprüft werden müssen.
Je nach Populationsdichte kann es durchaus vorkommen, das insgesamt deutlich über 30000 Felder neu berechnet werden müssen, wobei die hier aufgeführten Schritte nur eine äußert grobe Darstellung liefern. So gibt es noch etliche weitere Prüfungen. So muss zum Beispiel ebenfalls geprüft werden, ob ein Fisch nicht aus dem Gitter hinaus schwimmt, da er in diesem Fall ja auf der gegenüberliegenden Seite wieder auftauchen muss.
Ein Problem, bei der Umsetzung besteht also im enormen Rechenaufwand, welcher für einen Durchlauf inklusive der Ausgabe benötigt wird. Dies ließe sich allerdings mit entsprechender Optimierung und vor allem einer anderen Art der Anzeige, welche die meiste Zeit in Anspruch nimmt, optimieren. Jedoch wurde ja, wie bereits angesprochen, Wert auf die reine Umsetzung und weniger auf deren Performance gelegt. So wäre eine komfortable Grafikumsetzung, beispielsweise mittels OpenGL und somit ein direktes Ansprechen der Grafikkarte, ohne weiteres möglich, jedoch stünde das Verhältnis zwischen den eigentlichen Berechnungen und dem grafischen Aufwand für diese Anwendung wohl in keinem vernünftigen Verhältnis. Die aktuelle Ausgabe erfolgt in der Art, das intern im Speicher ein Bitmap generiert und dieses dann komplett ausgegeben wird. Die Daten das Arrays werden also intern zu einem Bild verarbeitet. Weiteres siehe den kommentierten Quelltext.
1RxLib 2.75 für Delphi 3 (heute in Jedi VCL integriert)
2Zeichenfläche der Komponente
3Alternative wäre die Auslagerung in einen Tread und eine Steuerung des Treaddurchlaufes pro Sekunde in Verbindung mit einer Erhöhung der Treadpriorität, sodass er vor den anderen Treads ausgeführt wird
