Beispielimplementierung Langtons Ameisen (Langton's Ants) (Delphi/Pascal)
{#############################################################################
# Unit zu Langton's Ants - im Programm unter Ameise #
#############################################################################}
unit Unit_Ameise;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Grids, StdCtrls, Buttons, ExtCtrls, RXSpin, ColorGrd, Menus;
type
TAmeise = Record
Col: integer;
Row: integer;
Sichtrichtung: integer; //1 bis 4 entsprechend der Himmelsrichtung
Farbe: TColor; //Darstellungsfarbe
end;
TMain = class(TForm)
Panel2: TPanel;
SG_Feld: TStringGrid;
Panel1: TPanel;
L_Pause: TLabel;
Panel3: TPanel;
BB_Reset: TBitBtn;
BB_Start: TBitBtn;
BB_Stop: TBitBtn;
Label1: TLabel;
Label2: TLabel;
L_Fs: TLabel;
Label3: TLabel;
L_Frame: TLabel;
Label5: TLabel;
L_Ameisen: TLabel;
Panel4: TPanel;
Label6: TLabel;
RxSpinEdit1: TRxSpinEdit;
Label7: TLabel;
RxSE_Ameisenzahl: TRxSpinEdit;
CB_Zufall: TCheckBox;
MainMenu1: TMainMenu;
Datei1: TMenuItem;
Ameise1: TMenuItem;
N1: TMenuItem;
Beenden1: TMenuItem;
Hilfe1: TMenuItem;
Info1: TMenuItem;
N2: TMenuItem;
Info2: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure BB_StartClick(Sender: TObject);
procedure BB_ResetClick(Sender: TObject);
procedure BB_StopClick(Sender: TObject);
procedure Ameise1Click(Sender: TObject);
procedure RxSE_AmeisenzahlChange(Sender: TObject);
procedure Info2Click(Sender: TObject);
procedure Info1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Beenden1Click(Sender: TObject);
//----
procedure Delay(Milliseconds: Integer);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var Main: TMain;
implementation
uses Unit_Wator;
var //globale Definition, um die unübersichtliche Parameterübergabe zu umgehen
Ameisen: Array [1..10] of TAmeise; //Ameisendaten
Ameisenzahl: integer;
Pause: integer; //Delaydauer
Stop: boolean; //boolean, um Simulation zu beenden
Frame, StopFrames: integer; //Daten zur Errechnung der F/s
{$R *.DFM}
procedure TMain.FormCreate(Sender: TObject);
begin
Randomize; //initialisiert Random
Ameisen[1].Farbe := clBlack; //weist unterschiedliche Ameisenfarben als Standard zu
Ameisen[2].Farbe := clMaroon;
Ameisen[3].Farbe := clYellow;
Ameisen[4].Farbe := clBlue;
Ameisen[5].Farbe := clLtGray;
Ameisen[6].Farbe := clTeal;
Ameisen[7].Farbe := clLime;
Ameisen[8].Farbe := clRed;
Ameisen[9].Farbe := clFuchsia;
Ameisen[10].Farbe := clPurple;
Stop := true; //initialisiert die Stopbedingung
end;
procedure TMain.BB_StartClick(Sender: TObject); //Start der Simulation
var Rect: TRect;
j: Integer;
Tick: integer;
begin
Stop := false; //Initialisierung/Buttons
BB_Stop.Enabled := true;
BB_Start.Enabled := false;
Tick := GetTickCount; //nimmt Zeit für F/s-Berechnung
repeat
Delay(Pause); //Unterbrechung
for j := 1 to Ameisenzahl do //pro Ameise bei jedem Zyklus ein Durchlauf
begin
//nimmt die Koorninaten der Zelle, um auf dem Formular (Grid) zu zeichnen
Rect := SG_Feld.CellRect(Ameisen[j].Col, Ameisen[j].Row);
//Daten ob Zelle 'schwarz' oder 'weiß' werden über Leerzeichen in Grid gespeichert
if SG_Feld.Cells[Ameisen[j].Col, Ameisen[j].Row] = ' ' then
begin //Zelle Schwarz, also:
SG_Feld.Canvas.Brush.Color := clWhite; //Zelle färben
SG_Feld.Cells[Ameisen[j].Col, Ameisen[j].Row] := ''; //ummarkieren
Ameisen[j].Sichtrichtung := Ameisen[j].Sichtrichtung - 1; //Ameise dreht sich nach links
end
else
begin //anderenfalls Zelle weiß, und entgegengesetzte Operationen
SG_Feld.Canvas.Brush.Color := Ameisen[j].Farbe;
SG_Feld.Cells[Ameisen[j].Col, Ameisen[j].Row] := ' ';
Ameisen[j].Sichtrichtung := Ameisen[j].Sichtrichtung + 1;
end;
SG_Feld.Canvas.FillRect(Rect); //Zelle im Stringgrid umfärben
case Ameisen[j].Sichtrichtung of //Prüfung ob die Sichtrichtung außerhalb
4: Ameisen[j].Sichtrichtung := 0; //der 4 Möglichkeiten liegt, wenn ja
-1: Ameisen[j].Sichtrichtung := 3; //wird sie auf den entsprechenden Wert
end; //zurückgesetzt, quasi als Loop von 1 bis 4
case Ameisen[j].Sichtrichtung of //neue Zelle wird aus Sichtrichtung ermittelt
0: Ameisen[j].Col := Ameisen[j].Col - 1;
1: Ameisen[j].Row := Ameisen[j].Row - 1;
2: Ameisen[j].Col := Ameisen[j].Col + 1;
3: Ameisen[j].Row := Ameisen[j].Row + 1;
end;
//wie Sichtrichtung, jedoch jetzt als Loop über das Gitter, die Ameise befindet sich auf einem Torus; einmal für vertikal und einmal für horizontal
case Ameisen[j].Col of
-1: Ameisen[j].Col := SG_Feld.ColCount - 1;
125: Ameisen[j].Col := 0;
end;
case Ameisen[j].Row of
-1: Ameisen[j].Row := SG_Feld.RowCount - 1;
125: Ameisen[j].Row := 0;
end;
end;
inc(Frame); //Erhöhung der Berechnungszahl um 1
L_Frame.Caption := IntToStr(Frame); //Ausgabe der Berechnungsanzahl
if Frame mod 100 = 0 then //Prüfung auf Vielfache von 100, um die
begin //Funktionen nicht ständig auszuführen
Application.ProcessMessages; //Abfangen von Nachrichten, recht Zeitintensiv
//nötig, um Eingaben zu verarbeiten (z.Bsp: BB_StopClick), alternative wäre eine Ausführung der Procedure als Tread
L_Fs.Caption := IntToStr (Round((Frame - StopFrames) / (((GetTickCount - Tick) + 1) / 1000))); //Ausgeben der F/s; +1 verhindert den teilweise auftretenden Fehler einer Division by Zero
end;
until Stop = true; //Durchläuft Zyklus, bis der Stop-Button gedrückt wurde
StopFrames := Frame; //speichert Framezahl, zur richtigen Errechnung von f/s, falls die Simulation fortgesetzt wird
end;
procedure TMain.BB_ResetClick(Sender: TObject);
var Col, Row, i: integer;
Rect: TRect;
begin //Initalisierungen
Frame := 0;
StopFrames := 0;
L_Fs.Caption := '';
L_Frame.Caption := '';
Pause := RxSpinEdit1.AsInteger;
L_Pause.Caption := IntToStr(Pause);
Ameisenzahl := RxSE_Ameisenzahl.AsInteger;
L_Ameisen.Caption := IntToStr(Ameisenzahl);
if (Ameisenzahl = 1) and (CB_Zufall.Checked = false) then
begin //setzt Ameise falls kein Zufallsfeld genutzt werden soll
SG_Feld.Cells[61, 61] := '';
Ameisen[1].Col := 61;
Ameisen[1].Row := 61;
Ameisen[1].Sichtrichtung := Random(4);
end
else //bestimmt Zufallsfelder für die eingestellte Ameisenzahl
begin
for i := 1 to Ameisenzahl do
begin
Ameisen[i].Col := Random(SG_Feld.ColCount - 1);
Ameisen[i].Row := Random(SG_Feld.RowCount - 1);
Ameisen[i].Sichtrichtung := Random(4);
SG_Feld.Cells[Ameisen[i].Col, Ameisen[i].Row] := '';
end;
end;
for Col := 0 to SG_Feld.ColCount - 1 do
for Row := 0 to SG_Feld.RowCount - 1 do
begin
Rect := SG_Feld.CellRect(Col, Row); //Canvas-Koordinaten der Gridzellen
SG_Feld.Canvas.Brush.Color := clgreen; //Standardfarbe wird zugewiesen,
for i := 1 to Ameisenzahl do
if (Ameisen[i].Col = Col) and (Ameisen[i].Row = Row) then
SG_Feld.Canvas.Brush.Color := Ameisen[i].Farbe //Farbgebung der Ameise
else
SG_Feld.Cells[Col, Row] := ''; //wenn keine Ameise, Feld als 'weiß' markieren
SG_Feld.Canvas.FillRect(Rect); //Feld färben
end;
BB_Start.Enabled := True;
end;
procedure TMain.BB_StopClick(Sender: TObject);
begin
Stop := true; //setzt Abbruchbedingung
BB_Stop.Enabled := false;
BB_Start.Enabled := true;
end;
procedure TMain.Ameise1Click(Sender: TObject);
begin
Stop := true; //wechselt zu Wator
Form2.Show;
Main.Hide;
end;
procedure TMain.RxSE_AmeisenzahlChange(Sender: TObject); //enabled Zufallsfeld
begin
if RxSE_Ameisenzahl.AsInteger > 1 then
CB_Zufall.Enabled := false
else
CB_Zufall.Enabled := true;
end;
procedure TMain.Info1Click(Sender: TObject); //Erklärung anzeigen
begin
ShowMessage ('Ameise (Turingmaschine)' + #13 + #13 +
'Chris Langtons Ameise ist eine Turingmaschine mit einem zweidimensionalen' + #13 +
'Speicher, mit sehr einfachen Regeln und sehr verblüffenden Ergebnissen.' + #13 +
'Die Ameise ist ein schönes Beispiel dafür, dass ein einfaches System' + #13 +
'mit einfachen Regeln sowohl komplexe chaotische, als auch komplexe' + #13 +
'geordnete Strukturen aufbauen kann, und das ganz ohne die Verwendung' + #13 +
'des Zufalls.' + #13 +
'Eine Ameise sitzt auf einem großen weißen, quadratisch Gitter' + #13 +
'und blickt in Richtung eines Nachbarfeldes. Wenn das Feld, auf' + #13 +
'dem sie sitzt, weiß ist, dann färbt sie es schwarz, dreht sich um 90°' + #13 +
'nach rechts, und geht auf das nächste Feld. Wenn das Feld, auf dem sie' + #13 +
'sitzt, schwarz ist, dann färbt sie es weiß, dreht sich um 90 Grad nach' + #13 +
'links, und geht auf das nächste Feld.' + #13 +
'In den ersten 10000 Schritten entsteht ein komplexes chaotisches Muster.' + #13 +
'Dann baut die Ameise eine regelmäßige Straße. Zu allem Überfluss hat' + #13 +
'die Ameise zwar symmetrische Verhaltensregeln, aber die Muster die sie damit' + #13 +
'erzeugt, sind asymmetrisch. Das Muster ist zwar nur dann vorhersagbar,' + #13 +
'wenn man nachmacht, was die Ameise tut, aber es ist streng deterministisch.' + #13 + #13 +
'aus Wikipedia, der freien Enzyklopädie');
end;
procedure TMain.Info2Click(Sender: TObject); //Info anzeigen
begin
ShowMessage ('Simulationen in Delphi' + #13 +
'Version: 0.6.2' + #13 +
'Enthalten:' + #13 +
' Langtons Ants' + #13 +
' WATOR' + #13 + #13 +
'(C) 2005-2006 Johannes Müller');
end;
procedure TMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
BB_Stop.Click; //beendet Berechnung, und anschließend das Programm
Application.Terminate; //über die Schaltfläche Schließen der Titlebar
end;
procedure TMain.Beenden1Click(Sender: TObject);
begin
BB_Stop.Click; //beendet Berechnung, und anschließend das Programm
Application.Terminate; //über Menü-Datei-Beenden
end;
//------------------------------------------------------------------------------
procedure TMain.Delay(Milliseconds: Integer);
{pausiert für die Zahl an Milisekunden die Anwendung, als Alternative zu Sleep()
entnohmen aus der Codelib der Delphipraxis www.delphipraxis.de}
var
Tick: DWord;
Event: THandle;
begin
Event := CreateEvent(nil, False, False, nil);
try
Tick := GetTickCount + DWord(Milliseconds);
while (Milliseconds > 0) and
(MsgWaitForMultipleObjects(1, Event, False, Milliseconds, QS_ALLINPUT) <> WAIT_TIMEOUT) do
begin
Application.ProcessMessages;
Milliseconds := Tick - GetTickcount;
end;
finally
CloseHandle(Event);
end;
end;
end.