Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Wator-Simulation - Werte in packed record nicht abfragbar


galagher - Fr 01.03.13 17:57
Titel: Wator-Simulation - Werte in packed record nicht abfragbar
Hallo!

Ich bastle mir gerade eine Wator-Simulation, ich habe folgendes deklariert:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
type
  TElement = (srtWater, srtShark, srtFish, srtBorder);

type
  TSort = packed record
    Element: TElement;
    X, Y, Age, Dir, Satt: Integer;
    aBoard: array [-1..66, -1..64of TElement;
  end;

const
  iMaxH   = 66;
  iMaxV   = 64;
//...

var
  Form1: TForm1;
  Waqua: TSort;
//...

aBoard ist, wie der Name schon sagt, das Spielbrett, damit kann ich auf einem TImage die zugehörigen Grafiken darstellen.
Aber ich kann nicht überprüfen, welches TElement das ist:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
procedure TForm1.FishTimerTimer(Sender: TObject);
var
  x, y: Integer;
begin
  for x := 0 to iMaxH-1 do
    for y := 0 to iMaxV-1 do
  begin
    if Waqua.aBoard[x, y] = srtFish then  //bis hier ok, aber ...
    begin
      if Waqua.Dir = 1 then  // ... hierher komme ich nie!
      begin
        //Das Programm kommt hier nie hin, weil ich offenbar Waqua.Dir nicht abfragen kann!
      end;
    end;
  end;
end;


Ich kann also Waqua.Dir und vermutlich auch X, Y, Age, Dir, Satt nicht abfragen.
Warum nicht? Was mache ich falsch?

Moderiert von user profile iconNarses: Doppelposting entfernt.


Mathematiker - Fr 01.03.13 18:22

Hallo,
ich habe Deinen Text 1:1 übernommen und in einer Methode

Delphi-Quelltext
1:
2:
    waqua.aBoard[2,4] := srtFish;
    waqua.dir:=1;

gesetzt. Die nachfolgende Prozedur

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure TForm1.Button2Click(Sender: TObject);
var x, y: Integer;
begin
  for x := 0 to iMaxH-1 do
    for y := 0 to iMaxV-1 do
  begin
    if Waqua.aBoard[x, y] = srtFish then  //bis hier ok, aber ...
    begin
      if Waqua.Dir = 1 then  // ... hierher komme ich nie!
      begin
        showmessage('Jetzt');
      end;
    end;
  end;
end;

stoppt genau dann, wenn Deine beiden Bedingungen erfüllt sind. Es funktioniert also.
Der Fehler liegt also wo anders. Du musst etwas mehr Quelltext zeigen, um den Fehler zu lokalisieren.
Nebenbei: srtfish, srtshark, ... sieht eher nach einer Wator-Simulation aus, weniger nach Game of Life.

Beste Grüße
Mathematiker


galagher - Fr 01.03.13 18:31

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
stoppt genau dann, wenn Deine beiden Bedingungen erfüllt sind. Es funktioniert also.
Der Fehler liegt also wo anders. Du musst etwas mehr Quelltext zeigen, um den Fehler zu lokalisieren.

Zunächst ist alles srtWater, dann befülle ich per Random mit srtShark und srtFish:

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:
{Board mit srtShark und srtFish befüllen, srtWater wird in ClearBoard gesetzt}
procedure TForm1.InitBoard;
var
  i, x, n: Integer;
begin
  ClearBoard;  {Erstmal alles leeren}

  for i := 0 to 500 do
  begin
    n := Random(12);
    case n of
      05 :
        begin
          Waqua.Element := srtWater;
          Waqua.Dir := dirWater;
        end;
      67 :
        begin
          Waqua.Element := srtShark;
         {Ausrichtung links oder rechts}
         if Random(2) = 0 then Waqua.Dir := dirSharkLeft
         else                  Waqua.Dir := dirSharkRight;
        end;
      8..11:
        begin
          Waqua.Element := srtFish;
          {Ausrichtung links oder rechts}
          if Random(2) = 0 then Waqua.Dir := dirFishLeft
          else                  Waqua.Dir := dirFishRight;
        end;
    end;

    Waqua.X := Random(iMaxH-1);
    Waqua.Y := Random(iMaxV-1);

    Waqua.aBoard[Waqua.X, Waqua.Y] := Waqua.Element;

//Zuweisungen fehlen noch:
//.Age
//.Satt

    DrawSort(Waqua);
  end;

//Zum Testen - klappt:
if Waqua.aBoard[00] = srtWater then label1.Caption := 'Water';
if Waqua.aBoard[00] = srtShark then label1.Caption := 'Shark';/////
if Waqua.aBoard[00] = srtFish then  label1.Caption := 'Fish';
end;


user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:

Nebenbei: srtfish, srtshark, ... sieht eher nach einer Wator-Simulation aus, weniger nach Game of Life.

Habe mich in der Eile geirrt :oops: und den Titel jetzt korrigiert!
//Edit: jetzt gibt's meine Eröffnung 2x, könnte das überflüssige 2. bitte jemand löschen?


Mathematiker - Fr 01.03.13 18:39

Hallo,
funktioniert perfekt. Nach der Initialisierung stoppt die Testroutine mehrfach, wenn dir=1 ist und das Feldelement srtFish. Der Fehler ist also an anderer Stelle.
Allerdings habe ich dirSharkLeft, dirSharkRight, usw... Zahlwerte von 0 bis 2 zugewiesen, da ich nicht weiß, welche Werte diese bei Dir haben sollen.

Beste Grüße
Mathematiker


galagher - Fr 01.03.13 18:50

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
funktioniert perfekt. Nach der Initialisierung stoppt die Testroutine mehrfach, wenn dir=1 ist und das Feldelement srtFish. Der Fehler ist also an anderer Stelle.

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Allerdings habe ich dirSharkLeft, dirSharkRight, usw... Zahlwerte von 0 bis 2 zugewiesen, da ich nicht weiß, welche Werte diese bei Dir haben sollen.


Ich habe das so gemacht:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
const
  dirWater  = 0;
  dirSharkLeft  = 1;
  dirSharkRight = 2;
  dirFishLeft   = 3;  // <--
  dirFishRight  = 4;

Ich habe einfach "1" angegeben, weil ich der einfacheren Lesbarkeit hier keine Konstantenbezeichnungen verwenden wollte! Diese Konstanten geben den Index der Grafik in einer TImageList an, zugleich verwende ich sie, um abzufragen, welches "Vieh" sich gerade wo befindet.

Wenn ich es testweise mit nur einem srtFish initialisiere, klappt es, aber noch bekomme ich es nicht hin, dass der "alte" Bereich mit srtWater überzeichnet wird:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
{Board mit srtShark und srtFish befüllen, srtWater wird in ClearBoard gesetzt}
procedure TForm1.InitBoard;
var
  i, x, n: Integer;
begin
  ClearBoard;  {Erstmal alles leeren}

  Waqua.Element := srtFish;

  {Ausrichtung links oder rechts}
  if Random(2) = 0 then Waqua.Dir := dirFishLeft
  else                  Waqua.Dir := dirFishRight;

   Waqua.X := Random(iMaxH-1);
   Waqua.Y := Random(iMaxV-1);

   Waqua.aBoard[Waqua.X, Waqua.Y] := Waqua.Element;

   DrawSort(Waqua);
end;


Mathematiker - Fr 01.03.13 18:56

Hallo,
da hast Du ja das Problem!

Delphi-Quelltext
1:
2:
3:
const
  dirFishLeft   = 3;  // <--
  dirFishRight  = 4;

Wenn Du das Feld mit srtFish füllst, gibst Du entweder dirFishLeft = 3 oder dirFishRight = 4 an, d.h. niemals eine 1.
Damit kann

Delphi-Quelltext
1:
2:
3:
4:
5:
    if Waqua.aBoard[x, y] = srtFish then  //bis hier ok, aber ...
    begin
      if Waqua.Dir = 1 then  // ... hierher komme ich nie!
      begin
    ...

nie erreicht werden.

Beste Grüße
Mathematiker

Nachtrag: Blödsinn. Jetzt zitiere ich mich schon selbst. Bitte nachfolgenden Eintrag entfernen.


galagher - Fr 01.03.13 19:06

Hallo!
user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Wenn Du das Feld mit srtFish füllst, gibst Du entweder dirFishLeft = 3 oder dirFishRight = 4 an, d.h. niemals eine 1.
Damit kann

Delphi-Quelltext
1:
2:
3:
4:
5:
    if Waqua.aBoard[x, y] = srtFish then  //bis hier ok, aber ...
    begin
      if Waqua.Dir = 1 then  // ... hierher komme ich nie!
      begin
    ...

nie erreicht werden.


Der Code sieht jetzt so aus, es klappt aber nur mit einem einzigen srtFish:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
  for x := -1 to iMaxH do
    for y := -1 to iMaxV do
  begin
    if Waqua.aBoard[x, y] = srtFish then
    begin
      if Waqua.Dir = dirFishLeft then // <-- !
      begin
        {Altes Feld übermalen und ...}
        Waqua.Dir := dirWater;
        Waqua.aBoard[x, y] := srtWater;
        DrawSort(Waqua);

        {... srtFish ein Feld nach links versetzen}
        Waqua.X := Waqua.X-1;
        Waqua.Dir := dirFishLeft;
        Waqua.aBoard[x, y] := srtFish;
        DrawSort(Waqua);
      end;
    end;
  end;

Wenn ich mit Random initialisiere, tut sich gar nichts.
Ausserdem habe ich an den "Rändern" mit -1, iMaxV und iMaxH srtBorder gesetzt, das sind unsichtbare Dinger, die ausserhalb des sichtbaren Bereichs liegen und die Viecher einfach dort die Richtung ändern lassen soll, soweit bin ich aber noch nicht.


Mathematiker - Fr 01.03.13 19:15

Hallo,
Du änderst nach dem Erkennen von srtfish die Variable dir auf dirwater:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
    if Waqua.aBoard[x, y] = srtFish then
    begin
      if Waqua.Dir = dirFishLeft then 
      begin
        {Altes Feld übermalen und ...}
        Waqua.Dir := dirWater;   //<----

Da Waqua.Dir für das ganze(!) Feld gilt, wird dirfishleft nicht mehr erreicht.
Ich vermute, Du möchtest für jedes Feld die Größe dir haben, d.h.

Delphi-Quelltext
1:
2:
3:
4:
    aBoard: array [-1..66, -1..64of record
         X, Y, Age, Dir, Satt: Integer;
         irgendetwas : TElement;
    end;

wäre besser, oder so ähnlich.

Beste Grüße
Mathematiker

Nachtrag: Ich melde mich erst einmal ab. Jetzt geht's zu Dieter Nuhr. :D


galagher - Fr 01.03.13 20:07

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Da Waqua.Dir für das ganze(!) Feld gilt, wird dirfishleft nicht mehr erreicht.
Ich vermute, Du möchtest für jedes Feld die Größe dir haben,
Genau das! Habe den Code aleso entsprechend umgebaut, sodass jedes einzelne (Teil-)Feld sowohl das Array als auch Dir, Age usw haben.
Allerdings funktioniert der Code im Timer jetzt nicht mehr...

//Edit: Hab's hinbekommen!

user profile iconMathematiker, vielen Dank!
Melde mich wieder, wenn ich nicht weiterkomme!


galagher - Sa 02.03.13 11:44

Hallo!

Jetzt schaffe ich es, dass srtFish waagrecht hin- und herlaufen, an einem "Hindernis" wie zB. srtShark die Richtung und zusätzlich die vertikale Position ändern.

Der Grundgedanke ist:
Bei links -> rechts: Setzt srtFish 1 Feld nach recht und überschreibe das Ursprungsfeld mit srtWater.
Bei rechts :> links: Setzt srtFish 1 Feld nach links und überschreibe das Ursprungsfeld mit srtWater.
Bei einem Hindernis wie zB. srtShark: Per Random ein Feld nach unten/oben.

Die Anzahl der srtFish müsste dabei doch eigentlich gleich bleiben, es werden aber immer weniger die Anzahl schwankt aber: wird zuerst weniger und schwankt dann!

Hier der Code - warum verringert sich die Anzahl an srtFish?

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:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
procedure TForm1.FishTimerTimer(Sender: TObject);
var
  x, y, i: Integer;
begin
  try
    {--------------------------- Von links nach rechts --------------------------}
    for x := -1 to iMaxH do
      for y := -1 to iMaxV do
    begin
      if Waqua.aBoard[x, y].Element = srtFish then
      begin
        if Waqua.aBoard[x, y].Dir = dirFishLeft then
        begin
          if Waqua.aBoard[x-1, y].Element <> srtWater then
          begin
            {Oberste Linie}
            if (y = 0then
            begin
              Waqua.aBoard[x, y+1].Dir := dirFishRight;
              Waqua.aBoard[x, y+1].Element := srtFish;

              Waqua.aBoard[x, y].Dir := dirWater;
              Waqua.aBoard[x, y].Element := srtWater;
            end;
            {Unterste Linie}
            if (y = iMaxV-1then
            begin
              Waqua.aBoard[x, y-1].Dir := dirFishRight;
              Waqua.aBoard[x, y-1].Element := srtFish;

              Waqua.aBoard[x, y].Dir := dirWater;
              Waqua.aBoard[x, y].Element := srtWater;
            end;


          {Alle anderen waagrechten Linien}
          if (x < iMaxH-1then//
            if (Random(2) = 0then//
            begin
              Waqua.aBoard[x, y-1].Dir := dirFishRight;
              Waqua.aBoard[x, y-1].Element := srtFish;

             {... das Ursprungsfeld mit srtWater überschreiben}
             Waqua.aBoard[x, y].Dir := dirWater;
             Waqua.aBoard[x, y].Element := srtWater;
            end
            else
            begin
              Waqua.aBoard[x, y+1].Dir := dirFishRight;
              Waqua.aBoard[x, y+1].Element := srtFish;

              {... das Ursprungsfeld mit srtWater überschreiben}
              Waqua.aBoard[x, y].Dir := dirWater;
              Waqua.aBoard[x, y].Element := srtWater;
            end;
          end
          else
          begin
            {srtFish ein Feld nach links versetzen und ...}
            Waqua.aBoard[x-1, y].Dir := dirFishLeft;
            Waqua.aBoard[x-1, y].Element := srtFish;

            {... das Ursprungsfeld mit srtWater überschreiben}
            Waqua.aBoard[x, y].Dir := dirWater;
            Waqua.aBoard[x, y].Element := srtWater;
          end;
        end;
      end;
    end;


    {--------------------------- Von rechts nach links --------------------------}
    for x := iMaxH downto -1 do
      for y := iMaxV downto -1 do
    begin
      if Waqua.aBoard[x, y].Element = srtFish then
      begin
        if Waqua.aBoard[x, y].Dir = dirFishRight then
        begin
          if Waqua.aBoard[x+1, y].Element <> srtWater then
          begin
            {Oberste Linie}
            if (y = 0then
            begin
              Waqua.aBoard[x, y+1].Dir := dirFishLeft;
              Waqua.aBoard[x, y+1].Element := srtFish;

              Waqua.aBoard[x, y].Dir := dirWater;
              Waqua.aBoard[x, y].Element := srtWater;
            end;
            {Unterste Linie}
            if (y = iMaxV-1then
            begin
              Waqua.aBoard[x, y-1].Dir := dirFishLeft;
              Waqua.aBoard[x, y-1].Element := srtFish;

              Waqua.aBoard[x, y].Dir := dirWater;
              Waqua.aBoard[x, y].Element := srtWater;
            end;


          {Alle anderen waagrechten Linien}
          if (x > 0then//
            if (Random(2) = 0then
            begin
              Waqua.aBoard[x-1, y-1].Dir := dirFishLeft;
              Waqua.aBoard[x-1, y-1].Element := srtFish;

             {... das Ursprungsfeld mit srtWater überschreiben}
             Waqua.aBoard[x, y].Dir := dirWater;
             Waqua.aBoard[x, y].Element := srtWater;
            end
            else
            begin
              Waqua.aBoard[x-1, y+1].Dir := dirFishLeft;
              Waqua.aBoard[x-1, y+1].Element := srtFish;

              {... das Ursprungsfeld mit srtWater überschreiben}
              Waqua.aBoard[x, y].Dir := dirWater;
              Waqua.aBoard[x, y].Element := srtWater;
            end;
          end
          else
          begin
            {srtFish ein Feld nach rechts versetzen und ...}
            Waqua.aBoard[x+1, y].Dir := dirFishRight;
            Waqua.aBoard[x+1, y].Element := srtFish;

            {... das Ursprungsfeld mit srtWater überschreiben}
            Waqua.aBoard[x, y].Dir := dirWater;
            Waqua.aBoard[x, y].Element := srtWater;
          end;
        end;
      end;
    end;

  finally
    {srtFish zählen}
    i := 0;
    //Die Anzahl schwankt!
    for x := -1 to iMaxH do
      for y := -1 to iMaxV do
    begin
      if Waqua.aBoard[x, y].Element = srtFish then
        Inc(i);
    end;
    Label2.Caption := inttostr(i);


    {Alles zeichnen}
    for x := -1 to iMaxH do
      for y := -1 to iMaxV do
        DrawSort(x, y, Waqua);
  end;
end;


Blup - Di 05.03.13 18:49

Der Grundgedanke sollte sein:
1. Ermittle das Feld zu dem sich der Fisch bewegen soll.
1.a Kann die bisherige Bewegungsrichtung beibehalten werden, bewege den Fisch dort hin.
1.b Wenn nicht ermittle für alle möglichen Bewegungsrichtungen die freien Felder.
1.b.a Wenn mindestens ein Feld frei ist, wähle eines der Felder aus, übernimm die Richtung und bewege den Fisch.
1.b.b Wenn kein Feld frei ist, tu nichts.
2. Markiere den Fisch als bereits in dieser Runde bewegt!


galagher - Di 05.03.13 20:02

user profile iconBlup hat folgendes geschrieben Zum zitierten Posting springen:
Der Grundgedanke sollte sein:
1. Ermittle das Feld zu dem sich der Fisch bewegen soll.
1.a Kann die bisherige Bewegungsrichtung beibehalten werden, bewege den Fisch dort hin.
1.b Wenn nicht ermittle für alle möglichen Bewegungsrichtungen die freien Felder.
1.b.a Wenn mindestens ein Feld frei ist, wähle eines der Felder aus, übernimm die Richtung und bewege den Fisch.
1.b.b Wenn kein Feld frei ist, tu nichts.
2. Markiere den Fisch als bereits in dieser Runde bewegt!

Ok, das habe ich so ähnlich umgesetzt: Die Fische und Haie bewegen sich im Wesentlichen waagrecht, können dabei aber jeweils 1 Feld nach oben oder unten wechseln. Habe das natürlich nicht in der letzten Stunde seit deinem Beitrag gemacht, das hat ein bisschen länger gedauert!

Jetzt läuft es korrekt, die Anzahl bleibt konstant, ich habe auch schon eingebaut, dass sich die Haie die Fische schnappen. Funktioniert alles.

Das mache ich mit zwei Timern, und ich dachte, ein optisch guter Effekt wäre, wenn SharkTimer.Interval etwas kürzer ist als FishTimer.Interval und sich die Haie dann schneller bewegen. Man merkt erst ab SharkTimer.Interval := FishTimer.Interval-10; einen Unterschied, aber dann ruckelt alles. Wenn Interval für beide Timer gleich ist, läuft es flüssig.

Kurz: Wie kann ich einen Timer schneller laufen lassen, ohne das die Grafik dann ruckelt? Und warum ist das so?


Tranx - Di 05.03.13 23:03

Möglicherweise ist das mit dem Ruckeln sowas wie ein Moirée-Effekt (nur in der Zeit, statt im Raum. Moirée-Muster entstehen, wenn zwei Muster transparent übereinander mit einer geringfügig anderen Wellenlänge/Frequenz liegen. Dann gibt es dunklere und hellere Stellen.

Das in die Zeit übertragen, wäre dann dein Ruckeln. Warum machst Du das nicht in einem Timer?


galagher - Di 05.03.13 23:17

user profile iconTranx hat folgendes geschrieben Zum zitierten Posting springen:
Warum machst Du das nicht in einem Timer?
Wollte ich zuerst, dann dachte ich, ich teile den Code besser auf zwei Timer auf, weil ich dann unterschiedliche Geschwindigkeiten einstellen kann, nur das klappt nicht so, wie ich will.
Habe Interval schon in den Timern selbst gesetzt, aber das Ruckeln bleibt. :nixweiss:


Mathematiker - Di 05.03.13 23:31

Hallo,
user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Habe Interval schon in den Timern selbst gesetzt, aber das Ruckeln bleibt. :nixweiss:

Da ich nicht weiß, wie schnell Dein Rechner ist, wie kurz die Intervalle des Timers sind und wie aufwendig das Erstellen der Grafik ist, rate ich einmal:
Die Intervalle sind zu kurz, d.h. der Computer kommt mit dem Zeichnen nicht nach. Dadurch siehst Du nicht jeden Zwischenschritt und somit ruckelt es.
Genaueres kann man aber nur sagen, wenn man von der Umsetzung mehr wüsste.

Wenn Du unter http://de.wikipedia.org/wiki/Wator nachliest, siehst Du, dass im Originalmodell Fische und Haie gleich schnell sind, d.h. ein Timer genügt. Die Effekte werden dann über die Lebensdauer der Haie ohne Futter gesteuert.
Mir ist aber klar, dass es reizvoll ist, auch unterschiedliche Geschwindigkeiten auszuprobieren.
Das kannst Du aber auch in einem Timer simulieren, in dem Du einer gewissen Anzahl von zufällig gewählten Haien z.B. zwei Bewegungsschritt gönnst. Im Mittel sind die Haie dann schneller.

Beste Grüße
Mathematiker


Blup - Mi 06.03.13 16:58

Speichere dir bei jedem Fisch bzw. Hai, wann er sich das nächste mal bewegen soll.
Dann gehst du alle Fische periodisch in kurzem Abstand durch.
Die Fische, die gerade bewegt werden sollen, werden bewegt und der Zeitpunkt für die nächste Bewegung je nach Flinkheit des Fisches aktualisiert. Wurden Fische bewegt, muss das Bild anschließend in den betreffenden Bereichen aktualisiert werden.


Horst_H - Mi 06.03.13 18:49

Hallo,

da würde ich aber lieber mit Listen/Stacks für Zeitscheiben arbeiten.
Ich stelle mir das dann so vor:
Du hast einen Taktgeber z.B alle 10 ms ein Takt einer inneren Uhr.
Fische bewegen sich maximal alle 73 Takte { ~random (44)+30 } und Haie maximal alle 31.
Die längste Zeit zwischen zwei Bewegungen ist also 73 also erstellst Du 73 Listen/Stacks.
Die Uhr läuft nun modulo maximaleTakteZwischenBewegungen
Du schiebst jeden Fisch/pointer darauf entsprechend seiner Zeit auf den passenden Stack
Mit jedem Takt werden alle Elemente des Stacks für diesen Takt verarbeitet/entfernt und entsprechend ihrer Geschwindigkeit auf den neuen passenden Stack mit der Nummer {Jetzige Zeit + Geschwindigkeit des Fisches) MOD maximaleTakteZwischenBewegungen} geschoben.
Damit braucht kein Fisch eine Zeit speichern, sondern nur seine Geschwindigkeit, und Du mußt nicht immer alle 3000 Fische abklappern.
Die Geschwingkeit kann dann nämlich extrem erhöht werden, indem Du völlig ohne Timer arbeitest, sondern jeden Durchlauf als einen Takt nimmst zudem werden immer nur Zeiger umgehängt und keine Daten verschoben, der Speicheraufwand bleibt konstant.

Gruß Horst
P.S:
Wieso überlege ich so lang, um etwas schneller hinzubekomen, was ich gar nicht brauche....


galagher - Mi 06.03.13 20:13

user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
da würde ich aber lieber mit Listen/Stacks für Zeitscheiben arbeiten.
Ich stelle mir das dann so vor:
Du hast einen Taktgeber z.B alle 10 ms ein Takt einer inneren Uhr.

Das wäre ja dann wie ein Timer, oder? Es geht doch darum, regelmässig und andauernd das gesamte Array zu durchlaufen, um alle darin enthaltenen Daten "zu verschieben", also irgendeine Veränderung darin zu erzeugen (ein zweidimensionales Array kann ich mir gut als ein reales Schachbrett vorstellen). Danach wird das Array grafisch umgesetzt.

user profile iconHorst_H hat folgendes geschrieben Zum zitierten Posting springen:
Wieso überlege ich so lang, um etwas schneller hinzubekomen, was ich gar nicht brauche....
Kannst es ja immer noch in eine Wator-Simulation umsetzen! :mrgreen:

Deinen Lösungsansatz kann ich aber, denke ich, nicht umsetzen...

Ich habe aber eine andere Lösung gefunden: Haie und Fische laufen zwar gleich schnell, aber die Haie wechseln im Gegensatz zu den Fischen per Random die horizontale Linie, das wirkt dann optisch so, also ob sie "jagen" würden, und zugleich ist die Einförmigkeit der Bewegungen aufgehoben!

Warum habe ich zwei Timer?
Zum Einen ist der Code jeweils doch unterschiedlich, zum anderen kann ich so den Code sauber trennen. Natürlich könnte ich die Sub-Prozeduren und -Funktionen mit Parametern versehen, sodass sie einmal für Fische, dann für Haie arbeiten, dann könnte ich noch mit if's Fisch- und Haie-Code trennen; klar geht das.
Aber ich denke, dann wird der Code zu unübersichtlich.

Mal sehen, wohin ich da noch komme, ich denke nämlich daran, dass das "Plankton" nicht wie im Original einfach da ist, sondern, dass man die Fische füttern muss, damit sie überleben.

Die Haie fressen ja die Fische, und im Moment ist es so, dass die Fische einfach weggefressen werden.


Mathematiker - Mi 06.03.13 23:08

Hallo,
ich habe mein Wator ausgekramt und noch ein paar kleine Änderungen vorgenommen.
Vielleicht hilft es ja etwas; wenn nicht, dann einfach ignorieren. :P

Beste Grüße
Mathematiker


Tranx - Do 07.03.13 05:30

Zum Plankton: Das ist auch in der Natur nicht einfach da. Wenn z.B. widrige Umweltbedingungen (Extrem kalt, Vergiftung des Wassers, Dunkelheit) vorherrschen, ist auch da Ebbe. Eine komplexe Umweltsituation ist schwer nachzusimulieren, weil sich viele Faktoren gegenseitig beeinflussen. Viele Fische, viel Fäkalien, viele Algen, wenig Fische, wenig Fäkalien, wenig Algen, Und bei den Räubern noch extremer, da sie zumeist größer sind. Dann noch äußere Einflusse (z.B. durch den Menschen) All dies zu berücksichtigen, ist eben nicht ganz so trivial. Aber im Kleine zu zeigen, dass das Drehen an einer Schraube eben nicht immer das gewünschte Ergebnis liefert, ist - bei aller Ungenauigkeit des Ergebnisses - schon die Anstrengung wert.

Steffen: Eine realistische Umsetzung des Themas. Bei einem Screensaver zu dem Thema ernährten häufig 7000 Fische 10000 Haie, total unrealistisch!


galagher - Do 07.03.13 19:42

user profile iconTranx hat folgendes geschrieben Zum zitierten Posting springen:
Zum Plankton: Das ist auch in der Natur nicht einfach da.
Ja, eben! Und ausserdem soll man das Programm nicht nur ansehen, sondern durch das Füttern der Fische auch eine Aufgabe haben!

user profile iconTranx hat folgendes geschrieben Zum zitierten Posting springen:
Aber im Kleine zu zeigen, dass das Drehen an einer Schraube eben nicht immer das gewünschte Ergebnis liefert, ist - bei aller Ungenauigkeit des Ergebnisses - schon die Anstrengung wert.
Ich werde noch einige Einstellungsmöglichkeiten einbauen.


galagher - Do 07.03.13 23:31

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Ich vermute, Du möchtest für jedes Feld die Größe dir haben, d.h.

Delphi-Quelltext
1:
2:
3:
4:
    aBoard: array [-1..66, -1..64of record
         X, Y, Age, Dir, Satt: Integer;
         irgendetwas : TElement;
    end;

wäre besser, oder so ähnlich.
Genau so habe ich es gemacht, ich will für jedes Feld des Arrays Integer-Werte setzen. Jetzt möchte auf die Integer-Variablen Age und Satt zugreifen. Dazu dachte ich mir, vor dem Durchlaufen des Array lese ich sie aus, sobald ich auf srtShark treffe, versetze dann srtShark auf das Zielfeld und schreibe die Werte dann für das neue Feld im Array:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
    if Waqua.aBoard[x, y].Sort = srtShark then
    begin
      tmpAge := Waqua.aBoard[x, y].Age;
      tmpSatt := Waqua.aBoard[x, y].Satt;
Caption:=inttostr(tmpsatt); //gibt immer den Wert 0 (für srtWater) zurück.

//...
      //Hier srtShark auf ein anderes Feld setzen und dann:
      Waqua.aBoard[x, y].Age := tmpAge;    //x, y gibt das Zielfeld an, kann auch x-1 oder y+1 usw. sein
      Waqua.aBoard[x, y].Satt := tmpSatt;  //x, y gibt das Zielfeld an, kann auch x-1 oder y+1 usw. sein
//...

Der Code ist vereinfacht, zeigt aber, was ich machen möchte.

Unmittelbar nach dem Befüllen des Arrays, wenn ich zum Testen die Timer nicht starte, haben alle Felder die korrekten Werte. Sobald sich im Array aber etwas ändert, stimmen die Variablen Age und Satt nicht mehr.
Was sehe ich da nicht, was ist falsch?


Mathematiker - Do 07.03.13 23:44

Hallo,
das sind wieder zu wenig Informationen um das Problem zu lokalisieren.
Es gibt da mehrere Möglichkeiten und ich kann nur raten:
1. Du durchläufst sicherlich das Feld mit Schleifen für x und y. Damit kann es sein, dass Du später noch einmal auf das vorher geänderte Feld zugreifst. Was dann? Wird der Status "Hai" gleich gesetzt? Dann bekommst Du die geplanten Maßnahme doppelt.
2. Was geschieht mit dem Feld, von dem sich der Hai wegbewegt. Auf welche Werte wird das gesetzt? Behält das den Status "Hai"?
3. Wie sieht Dein Record jetzt ganz konkret aus? Auch da könnte sich noch ein Fehler verstecken.
usw. usf.

Für eine genaue Fehlersuche wirst Du wohl mehr von den Methoden zeigen müssen.

Beste Grüße
Mathematiker


Horst_H - Fr 08.03.13 11:07

Hallo,

was user profile iconMathematiker mit
Zitat:
1. Du durchläufst sicherlich das Feld mit Schleifen für x und y. Damit kann es sein, dass Du später noch einmal auf das vorher geänderte Feld zugreifst. Was dann? Wird der Status "Hai" gleich gesetzt? Dann bekommst Du die geplanten Maßnahme doppelt.

beschreibt kannst Du leicht mit der Abspeicherung einer Kopie der jetzigen Zeile y vor der Bearbeitung, die Du anschliessend als vorherige Zeile y-1 nutzt und der rechtesten Spalte ( x= max )( x->0..max und y vonn 0-> max) ,die Du dann bei (y-1) und (x= -1== max) nutzt.
So braucht es keine vollständige Kopie bei einem sehr großen Feld.
Von der Verarbeitung her wäre es aber sicher viel einfacher, mit zwei kompletten Feldern zu arbeiten, bei denen man zur Verarbeitung von einem Feld in das andere schreibt.Wenn man bei der Verarbeitung Zeiger verwendet, kann man durch Zeigertausch Alt und Neu tauschen, ohne Speicher kopieren zu müssen, was ja bei der Verarbeitung ohnehin passiert.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
var
  Board_Alt,Board_neu: tBoard;
  p_BAlt,p_BNeu,p_temp : tpBoard;//^tBoard;

...
procedure Verarbeitung( p_Neu,pAlt:tpBoard); 
...
p_neu^[x,y] := f(p_Alt,x,y);
ausgabe(p_neu);
tausche(p_neu,p_alt);
end;


Gruß Horst


galagher - Fr 08.03.13 16:49

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
1. Du durchläufst sicherlich das Feld mit Schleifen für x und y.
Ja, genau, das passiert innerhalb der Timer!

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Damit kann es sein, dass Du später noch einmal auf das vorher geänderte Feld zugreifst. Was dann? Wird der Status "Hai" gleich gesetzt?
Ja, das passiert weiter unten in meinem Code, immer noch innerhalb der x-y-Schleife, nachdem ermittelt wurde, was im Zielfeld ist.

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
2. Was geschieht mit dem Feld, von dem sich der Hai wegbewegt. Auf welche Werte wird das gesetzt? Behält das den Status "Hai"?
Das Feld, von dem sich der Hai wegbewegt, wird mit srtWater besetzt. Ist das Zielfeld srtWater oder srtFish, dann Hai dorthin = dieses eine Feld ist dann mit dem Hai überschreiben.

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
3. Wie sieht Dein Record jetzt ganz konkret aus? Auch da könnte sich noch ein Fehler verstecken.
usw. usf.
Hier mein dafür relevanter Code:


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:
type
  TSort = (srtWater, srtShark, srtFish, srtBorder);

type
  TElement = packed record
      {aBoard hat sichtbare Felder waagr. und senkr. von 0..69
             Nicht sichtbare Randfelder
                      |   |   |   |   }

      aBoard: array [-1..70, -1..70of record  {Gilt individuell für jedes der Sort's}
      {  X-Koordinate
         |  Y-Koodinate
         |  |  Lebensalter
         |  |  |    Satt
         |  |  |    |     Ausrichtung (links/rechts)
         |  |  |    |     |
         |  |  |    |     |
         |  |  |    |     |           }

         X, Y, Age, Satt, Dir: Integer;
      {  Wasser, Hai, Fisch, Rand
         |                            }

         Sort: TSort;
    end;
    Plankton: Integer;  {Gilt für das gesamte Board}
  end;
//...
var
  Form1: TForm1;
  Waqua: TElement;
//...

Dann erfolgt das Befüllen des Arrays Waqua mit srtWater, danach werden mit Random srtShark und srtFish im Array "verteilt". Nun folgt SharkTimer.Enabled := True; und FishTimer.Enabled := True;. Der relevante Code in diesen Timers sieht so aus:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
  for x := 0 to iMaxH-1 do
    for y := 0 to iMaxV-1 do
      if not RunRightToLeft then break;

    for x := iMaxH-1 downto 0 do
      for y := iMaxV-1 downto 0 do
        if not RunLeftToRight then break;

Die beiden Funktionen sehen so aus (sind beide ähnlich, aber natürlich nicht identisch), zB RunRightToLeft:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
  {--------------------------- Von rechts nach links --------------------------}
  function RunRightToLeft: Boolean;
  begin
    Result := True;

    if Waqua.aBoard[x, y].Sort = srtShark then
    begin
      tmpAge := Waqua.aBoard[x, y].Age;
      tmpSatt := Waqua.aBoard[x, y].Satt;
Caption:=inttostr(tmpAge)+' '+inttostr(tmpSatt);  //Das stimmt nicht mehr, sobald der Timer srtShark auf ein anderes Feld bewegt hat!

Und zwar klappt es auch nicht mit nur einem einzigen srtShark. Die Variablen tmpAge und tmpSatt haben dann die Werte jenes Feldes, auf das srtShark gesetzt werden soll. Aber zu diesem Zeitpunkt ist dieses Zielfeld ja entweder srtWater oder srtFish, der Code KANN demnach gar nicht ausgeführt werden! Und noch dazu kapiere ich nicht, warum sich x, y hier offenbar nicht auf das Feld mit srtShark bezieht, dessen Werte ausgelesen werden sollen! Da steht doch klar if Waqua.aBoard[x, y].Sort = srtShark then, also muss doch x, y genau das Feld mit srtShark bezeichnen, und zwar, bevor srtShark im Array versetzt wird!

1) Lies Werte vom Ursprungsfeld in Variablen, 2) verändere das Array, 3) schreibe die Werte an das Zielfeld im Array. Obiger Code realisiert 1) und 2).

Ich hoffe, das Prinzip ist verständlich von mir dargelegt!


Mathematiker - Fr 08.03.13 19:16

Hallo,
ich vermute, dass die Funktionen RunRightToLeft und RunLeftToRight innerhalb der Timer-Routine stehen.
Selbst dann ist nicht garantiert, dass die aktuellen Werte x und y der Schleifen an beide Prozeduren korrekt übergeben werden. Mein Vorschlag, x und y übergeben:

Delphi-Quelltext
1:
  function RunRightToLeft(x,y :integer): Boolean;                    

Außerdem hast Du zwei Timer laufen. Die Variablen tmpAge und tmpSatt müssen dann lokal deklariert sein. Andernfalls beeinflussen sich beide Timer gegenseitig.

Beste Grüße
Mathematiker


galagher - Fr 08.03.13 19:57

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
ich vermute, dass die Funktionen RunRightToLeft und RunLeftToRight innerhalb der Timer-Routine stehen.
Da vermutest du völlig richtig!
user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Die Variablen tmpAge und tmpSatt müssen dann lokal deklariert sein. Andernfalls beeinflussen sich beide Timer gegenseitig.
tmpAge und tmpSatt als lokale Variablen werde ich noch machen. Solange ich teste, ist der ganze Code sowieso ziemlich "schmutzig". Jetzt habe ich an verschiedenen Stellen die Werte dieser beiden Variablen in das Zielfeld zurückgeschrieben. Bei einigen srtShark stimmen sie, bei anderen habe ich immer noch falsche Werte.
Irgendwo habe ich etwas vergessen oder übersehen. Zurückschreiben muss ich sie jedenfalls bei allen Richtungsänderungen, also immer dann, wenn sich das Array bezüglich der Hai-Felder ändert. Das wird schon.
Da es jetzt immerhin teilweise stimmt, bin ich wohl auf dem richtigen Weg, meine Überlegungen waren also grundsätzlich richtig.

Dann das Ganze nochmal bei den Fischen...

Weisst du eine Möglichkeit, wie man das automatisieren kann? Ich meine, "ändert sich im Array etwas, dann nimm alle Variablen mit, setze sie im neuen Feld ein und überschreibe sie im alten". Wäre doch viel eleganter!

Danke und Grüsse
galagher


Mathematiker - Fr 08.03.13 20:30

Hallo,
user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Weisst du eine Möglichkeit, wie man das automatisieren kann? Ich meine, "ändert sich im Array etwas, dann nimm alle Variablen mit, setze sie im neuen Feld ein und überschreibe sie im alten". Wäre doch viel eleganter!

Sehe ich im Moment leider nicht.
Wenn Du hier nicht alles veröffentlichen möchtest :wink: , kannst Du das Projekt mir auch ja per PN senden. Ich sehe dann besser, wie ich helfen kann.

Beste Grüße
Mathematiker


galagher - Sa 09.03.13 19:20

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Wenn Du hier nicht alles veröffentlichen möchtest :wink: , kannst Du das Projekt mir auch ja per PN senden. Ich sehe dann besser, wie ich helfen kann.
Ich komme ggf. darauf zurück! Leider kann ich derzeit nicht am PC arbeiten, weil vermutlich das Netzteil den Geist aufgegeben hat.
Ich hoffe, ab Montag läuft's wieder!
Zum Glück hab ich mein aktuelles Projekt auf USB-Stick gesichert. Gleich danach beim Testen schaltete sich plötzlich der PC ab und das war's!

Jedenfalls vielen Dank und Grüsse!