Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Wator - torusförmiges Spielfeld


galagher - So 13.07.14 13:01
Titel: Wator - torusförmiges Spielfeld
Hallo!

Schone wieder habe ich irgendwo einen Denkfehler bei einem torusförmigen Spielfeld! Ich möchte "Wator" programmieren: http://de.wikipedia.org/wiki/Wator
Erstmal nur mit Fischen, aber schon das klappt nicht. Fische, die über den Rand "schwimmen", tauchen an der gegenüberliegenden Seite nicht wieder auf. Wo ist da der Fehler?

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
var
  x, y, i: Integer;
  b: Boolean;
begin
(*
  Regeln für Fische
    - Jeder Fisch schwimmt zufällig auf eines der vier angrenzenden Felder,
      sofern es leer ist.
    - Jeder Fisch hat ein Alter; überschreitet dieses die Fisch-Lebenszeit,
      so wird auf einem leeren, angrenzenden Feld ein neuer Fisch geboren.
*)

  {Temporäres Array zunächst "mit Wasser füllen"}
  for x := iBoardMin-1 to iBoardMax+1 do
    for y := iBoardMin-1 to iBoardMax+1 do
      atmpBoard[x, y].clCell := clWater;

  for x := iBoardMin to iBoardMax do
    for y := iBoardMin to iBoardMax do
    begin
      if (aBoard[x, y].clCell = clFish) then
      begin
        case Random(4of
          0if aBoard[x+1, y].clCell = clWater then
             begin
               atmpBoard[x+1, y].clCell := clFish;
             end;
          1if aBoard[x-1, y].clCell = clWater then
             begin
               atmpBoard[x-1, y].clCell := clFish;
             end;
          2if aBoard[x, y+1].clCell = clWater then
             begin
               atmpBoard[x, y+1].clCell := clFish;
             end;
          3if aBoard[x, y-1].clCell = clWater then
             begin
               atmpBoard[x, y-1].clCell := clFish;
             end;
        end;
      end;
    end;

  {Vom temporären Array zurückschreiben}
  for x := iBoardMin-1 to iBoardMax+1 do
    for y := iBoardMin-1 to iBoardMax+1 do
    begin
      aBoard[x, y].clCell :=
        atmpBoard[x mod iBoardMax, y mod iBoardMax].clCell;
    end;

 {Zuletzt zeichnen}
 DrawBoard;
end;


Xion - So 13.07.14 14:55

Zum einen sind deine Arraygrößen irgendwie verwirrend. Vermutlich liegt dort der Fehler.
Zum anderen ist -1 mod 5 = -1 in Delphi, weil mod nunmal den Rest bei Ganzzahldivision bestimmt, und -1/5 ist nunmal 0 Rest -1. Da in deinem Codeabschntt iBoardMin und iBoardMax nicht angegeben sind, ist dies aber eventuell kein Problem :nixweiss:

Ich würde folgendermaßen vorgehen und den ganzen Torus-Quatsch in einer Klasse verstecken:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
type TDoubleBufferedTorusMap = class
  private
    vMap: array [0..1of array of array of TCell;
    vActiveMap: integer;
    vWidth, vHeight: integer;
  public
    constructor Create( width,height: integer );
    function GetActiveCell(x,y: integer): ^TCell; 
    function GetBackupCell(x,y: integer): ^TCell; 
    procedure SwapBuffers(); 
end;

constructor TDoubleBufferedTorusMap.Create( width,height: integer );
begin
  SetLength( vMap[0], width, height );
  SetLength( vMap[1], width, height );
  vActiveMap := 0
  vWidth := width;
  vHeight := height;
end

function TDoubleBufferedTorusMap.GetActiveCell(x,y: integer): ^TCell; 
var posX,posY: integer
begin
  posX := (x + vWidth) mod vWidth;
  posY := (y + vHeight) mod vHeight;
  Result := @vMap[vActiveMap][posX,posY];
end;

function TDoubleBufferedTorusMap.GetBackupCell(x,y: integer): ^TCell; 
var posX,posY: integer
begin
  posX := (x + vWidth) mod vWidth;
  posY := (y + vHeight) mod vHeight;
  Result := @vMap[(vActiveMap+1)mod 2][posX,posY];
end;

procedure TDoubleBufferedTorusMap.SwapBuffers(); 
begin
  vActiveMap := (vActiveMap+1mod 2;
end;


Nun kannst du ganz einfach darauf zugreifen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
//--- initialisierung
map := TDoubleBufferedTorusMap.create(100,100);
for X:= 0 to 99 do
  for Y:= 0 to 99 do
    map.GetActiveCell(X,Y).clCell := clWater;

//--- Simulations-Beispiel: rotiere Torus nach unten
while true do
  begin
    for X:= 0 to 99 do
      for Y:= 0 to 99 do
        map.GetActiveCell(X,Y+1).clCell := map.GetBackupCell(X,Y).clCell;
    map.SwapBuffers();
  end;


Mit Potential für schönere Objektorientierung :)

Noch eine weitere Anmerkung zu deiner Simulation: Es genügt nicht zu gucken, ob auf der alten Karte an dieser Stelle kein Fisch war. Sonst kann es passieren, dass danach mehrere Fische auf ein (vorher leeres) Feld zusammenfallen und es weniger Fische sind, als vorher. Ich nehme mal an, das soll so nicht sein bei der Wator-Simulation.


Horst_H - So 13.07.14 17:24

Hallo,

wobei

Delphi-Quelltext
1:
  posX := (x + vWidth) mod vWidth;                    

auch nur für x > -vWidth funktioniert.
Im Paranoia-Modus wäre eine Verschiebung auf Maxint/2/vWidth optimaler ;-)

Delphi-Quelltext
1:
2:
3:
4:
5:
..in create ..
 cWidth := (maxint div 2+1DIV vWidth;

und dann 
 posX := (x + cWidth) mod vWidth;

aber das rechnet noch etwas langsamer.

Ohne Objektorientierung und ohne modulo, weil man darauf achtet, das man es nicht braucht.
Dort ist das Originalspielfeld von 1..n und tmp von 0..n+1. Das weitere wird dort beschrieben.
Siehe Game of Life - wie torusförmiges Spielfeld erstellen [http://www.entwickler-ecke.de/viewtopic.php?t=113147&postorder=asc&start=6] und im Programm [http://www.entwickler-ecke.de/viewtopic.php?t=113147&start=29&postorder=asc]

Gruß Horst


Mathematiker - So 13.07.14 19:37

Hallo,
hattest Du das Thema nicht schon einmal, irgendwie?
siehe http://www.entwickler-ecke.de/viewtopic.php?t=111219&highlight=wator

Ich verweise noch einmal auf mein kleines Beispielprogramm unter http://www.entwickler-ecke.de/viewtopic.php?p=674090#674090. Vielleicht hilft es ja doch etwas.

Beste Grüße
Mathematiker


galagher - Mo 14.07.14 19:13

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Hallo,
hattest Du das Thema nicht schon einmal, irgendwie?
siehe http://www.entwickler-ecke.de/viewtopic.php?t=111219&highlight=wator

Irgendwie, ja! :mrgreen:

Habe jetzt keine Lust mehr, mich - unter den geänderten Bedingungen - da nochmal durchzubeissen, jetzt bleibt alles innerhalb des Spielfeldes und gut ist's.

//Edit: Damit's keine Missverständnisse gibt nach dem Motto: "Da fragt er was, dann sagt man ihm was dazu, und dann sagt der, er will das nicht":
Ich wollte das mit meinem Code machen, so, wie ich mir das denke. Ist ja von euch gut gemeint, doch wenn ich es mit meinem Ansatz nicht schaffe, dann möchte ich auch nichts anderes machen!