Autor Beitrag
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2556
Erhaltene Danke: 45

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Sa 07.06.14 11:07 
user profile iconJann1k hat folgendes geschrieben Zum zitierten Posting springen:
Also irgendwie hakt es ja ganz schön bei dir. Hidden möchte nur, dass du jedesmal aus atmpBoard[x, y] ein atmpBoard[x mod 97, y mod 97] machst. Durch die modulo Funktion bleiben die Indices immer im Bereich [0..96] und du kriegst deinen Torus. Die Konstruktion mit dem Rand voller toter Zellen kannst du dir dann sparen.

Und warum habe ich das nicht längst gemacht? Geht ja! :mrgreen:

user profile iconJann1k hat folgendes geschrieben Zum zitierten Posting springen:
Also irgendwie hakt es ja ganz schön bei dir.

Jetzt nicht mehr!
Sorry, aber ich konnte (kann) mir das einfach nicht vorstellen! Ein zweidimensionales Array ist ja kein Problem, aber mit so etwas habe ich mich noch nie beschäftigt.
Jetzt funktioniert es so, wie ich mir das wünsche: Eine "Kugel"-Welt ist einfach eleganter, denn da werden Objekte am Rand nicht einfach aufgelöst, sondern treten gegenüber wieder ein - eine Kugel eben. Danke!

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
catweasel
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 487
Erhaltene Danke: 1

Win 7 64bit
Delphi 7 Second Sedition V7.2
BeitragVerfasst: Sa 07.06.14 12:27 
Zitat:
user profile iconcatweasel hat folgendes geschrieben Zum zitierten Posting springen:
Der Quellcode ist nicht kommentiert, aber auch nicht sonderlich schwierig...

Jedenfalls ist er völlig anders als mein Code!


Ja, weil der Ansatz ein Anderer ist :)
Dein Ansatz ist : Array nehmen und analysieren und Ergennisse temporär speichern und dann die Ergebnisse in das Array zurückschreiben.
Und das macht es unnötig kompliziert. Vergegenwärtige dir das das "temp" array wenn es gefüllt ist, im Prinzip schon die nächste Generation darstellt. Wenn ich WorldA berechne dient WorldA quasi als temp array für WorldB. (Geschieht bei WorldA.Process(WorldB) )

Da pro Generation nr ein Array einmal glesen/geschrieben wird ist es auch schneller als Arry -> temp und dann temp -> array.

Die Sache mit den Koordinaten und dem Torus steckt in TWorldClass.Transform() die dann von TWorldClass.CountNeighbors() aufegrufen wird.
Da steckt der ganze Gag drin :)

Aber wenn du willst kann ich gerne den Rest des Codes kommentieren...
Wie du siehst ist wes wirklich simpler. Der ganze Quellcode ist weniger als deine TimerRoutine...

Evtl. könntest du auch die Timer Prozedur in kleinere Subroutinen zerlegen. Das würde die Übersichtlichkeit erhöhen.
Ich persönlich vermeide Methodenkörper mit mehr als 30 Zeilen.

Ist jetzt wirklich nur aus Neugier gefragt: Wie lange programmierst du schon mit Delphi?

Cheers,
Catweasel

_________________
Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2556
Erhaltene Danke: 45

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: Sa 07.06.14 13:57 
user profile iconcatweasel hat folgendes geschrieben Zum zitierten Posting springen:
Wenn ich WorldA berechne dient WorldA quasi als temp array für WorldB. (Geschieht bei WorldA.Process(WorldB) )

Da pro Generation nr ein Array einmal glesen/geschrieben wird ist es auch schneller als Arry -> temp und dann temp -> array.

Naja, jetzt läuft es und ich belasse es so!

user profile iconcatweasel hat folgendes geschrieben Zum zitierten Posting springen:
Evtl. könntest du auch die Timer Prozedur in kleinere Subroutinen zerlegen. Das würde die Übersichtlichkeit erhöhen.

Ist sicher sinnvoll und werde ich auch umsetzen.

user profile iconcatweasel hat folgendes geschrieben Zum zitierten Posting springen:
Ist jetzt wirklich nur aus Neugier gefragt: Wie lange programmierst du schon mit Delphi?

Schon etliche Jahre, aber ich habe nur wenige Spiele programmiert. Und über ein torusförmiges Array habe ich mir noch nie Gedanken gemacht, ich brauchte es schlicht nicht.

lg
galagher

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
catweasel
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 487
Erhaltene Danke: 1

Win 7 64bit
Delphi 7 Second Sedition V7.2
BeitragVerfasst: Sa 07.06.14 14:25 
Zitat:
Naja, jetzt läuft es und ich belasse es so!


Ja klar. Wenns einaml fertig ist :D
Ich wollte dir eigentlich nur aufzeigen wie der objektorientierte Ansatz hier die Aufgabenstellung radikal vereinfachen kann. :beer:
Mann müsste noch nichtmal Objekte nehmen. Das ganze ginge auch mit ein paar globalen Variablen....
Mal so gefragt: Wenn du das nochmal programmieren müsstest, würdest du wieder den gleichen Ansatz wählen?

Cheers,
Catweasel

_________________
Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2556
Erhaltene Danke: 45

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: So 08.06.14 09:41 
user profile iconcatweasel hat folgendes geschrieben Zum zitierten Posting springen:
Mal so gefragt: Wenn du das nochmal programmieren müsstest, würdest du wieder den gleichen Ansatz wählen?

Ja, würde ich. Es fällt mir ganz einfach leichter, ein quadratisches oder rechteckiges Spielfeld, das ich am Bildschirm ja sehen kann, im Kopf in ein zweidimensionales Array umzurechnen und umgekehrt.
Was ich mir nicht vorstellen konnte, war das Verbinden der Ränder. Aber dazu muss ich sagen, dass ich Mathematik seit der Schulzeit nicht mag. War immer der blanke Horror! :hair:

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: So 08.06.14 10:47 
Hallo,
user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Aber dazu muss ich sagen, dass ich Mathematik seit der Schulzeit nicht mag. War immer der blanke Horror! :hair:

Richtig! :zustimm:

Augustinus: "Der gute Christ soll sich hüten vor den Mathematikern und all denen, die leere Voraussagen zu machen pflegen, schon gar dann, wenn diese Vorhersagen zutreffen. Es besteht nämlich die Gefahr, dass die Mathematiker mit dem Teufel im Bunde den Geist trüben und in die Bande der Hölle verstricken."

Paul Epstein: "Die Mehrheit bringt der Mathematik Gefühle entgegen, wie sie nach Aristoteles durch die Tragödie geweckt werden sollen, nämlich Mitleid und Furcht. Mitleid mit denen, die sich mit der Mathematik plagen müssen, und Furcht, dass man selbst einmal in diese gefährliche Lage geraten könne."

Robert Niemann: "Mathematik ist nichts für den Wühltisch - Mathematik ist etwas für eine Handvoll Erleuchtete. Und das soll auch so bleiben."

In dem Sinne. Lass dich nicht von der Mathematik ärgern. Als Programmierer kann man darauf sicher verzichten. :mrgreen:

Beste Grüße
Mathematiker
catweasel
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 487
Erhaltene Danke: 1

Win 7 64bit
Delphi 7 Second Sedition V7.2
BeitragVerfasst: So 08.06.14 12:31 
Diese Männer habane natürlich völlig Recht :P
Mthematik ist natürlich nicht nur Teufelszeug (Zubersprüche des Herrn Binomi), sondern erfüllt sogar alle hinreichenden Bedingungen für total nerdige Freakscheisse :lol:
Dafür können Mthematiker ber sehr gut Löwen fangen...

(Bei meiner Version arbeit ich auch mit 2D arrays, ich meinte der Ansatz mit dem Double buffering)

Cheers,
Catweasel

_________________
Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: So 08.06.14 22:00 
Hallo,
user profile iconcatweasel hat folgendes geschrieben Zum zitierten Posting springen:
Mthematik ... erfüllt sogar alle hinreichenden Bedingungen für total nerdige Freakscheisse

Das ist nicht mehr "lustig"! :puke:

Mathematiker
catweasel
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 487
Erhaltene Danke: 1

Win 7 64bit
Delphi 7 Second Sedition V7.2
BeitragVerfasst: Mo 09.06.14 02:18 
Leider bekomme ich das immer zu hören wenn ich mit Mathematik anfange ... :cry:
:wink:

Catweasel

_________________
Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1654
Erhaltene Danke: 244

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Sa 14.06.14 09:35 
Hallo,

ich habe ein wenig an der "Berechnung" herumgeschraubt.
Meine Uralt Version mit 3 _Zeilen als Zwischenspeicher ist heutzutage nicht mehr schnell :-(
Ich habe user profile iconcatweasel Einwand, gegen das unnötige Kopieren beherzigt, aber schöner wird es durch Zeiger auf die Boards nicht.
Tatsächlich braucht es bei mir ohne Ausgabe pro Koordinatenpunkt nur 11 CPU-Takte.
Wie immer gilt, Entscheidungen fallen auch einer CPU schwer ;-) , also mittels cSurvives die möglichen Werte vorgeben.

ausblenden volle Höhe 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:
Program Gol;
// Game of life als gol.dpr speichern
{$IFDEF FPC}
  {$Mode delphi}
{$ELSE}
  {$Apptype Console}
{$ENDIF}
uses
  sysutils,crt;

const
  colMax = 79;
  rowMax = 23;
//Feld um eine Zeile,Spalte  davor und dahinter erweitern,
//das laesst sich einfeacher bearbeiten, ohne Sonderregeln für den Rand
type
  tFeld = byte;
  tRow = array[0..colMax+1of tFeld;//0..1 oder 0..3 /sum
  tprow = ^tRow;
  tBoard = array[0..rowMax+1of tRow;
  tpBoard = ^tBoard;
  tpBoards = array[0..1of tpBoard;
const
  dr = sizeOf(trow);
type
  tIntArr = array[0..2*dr+2of tFeld;
  tpIntArr = ^tIntArr;

var
  aBoard,bBoard : tBoard;
  pBoards :tpBoards;
  gblAktBoard : integer;

  gblGenCount : integer;

function Survive(p: tpIntArr):byte;
//p zeigt auf aktuelles Board adresse [row-1,col-1]
const
  cSurvives : array[boolean,0..8of integer=
              //0,1,2,3,4,5,6,7,8     Nachbarn
              ((0,0,0,1,0,0,0,0,0),   {alive =false}
               (0,0,1,1,0,0,0,0,0));  {alive =true}
var
  sum : integer;
begin
  // Zeile darueber
  // sum := byte(aBoard[row-1,col-1])+byte(aBoard[row-1,col])+byte(aBoard[row-1,col+1]);
  sum :=      integer(p[     0])+integer(p[    1]) +integer(p[     2]);
  sum := sum+ integer(p[  dr+0])                   +integer(p[  dr+2]);
  sum := sum +integer(p[2*dr+0])+integer(p[2*dr+1])+integer(p[2*dr+2]);

  survive := cSurvives[p[dr+1]<>0,sum];
end;

procedure Raender;
var
  p0 : tpIntArr;
  row: integer;
begin
  //Spalten ergaenzen
  p0 := @pBoards[gblAktBoard]^[1,0];
  For row := 1 to rowMax do
    begin
    p0^[0] := p0^[colMax];
    p0^[colmax+1] := p0^[1];
    p0 := Pointer(PtrUint(p0)+SizeOf(tRow));
    end;
  //oberste Zeile 1 nach unten rowMax+1 kopieren
  move(pBoards[gblAktBoard]^[1,0],pBoards[gblAktBoard]^[rowMax+1,0],sizeof(trow));
  //unterste Zeile rowMax nach oben 0 kopieren
  move(pBoards[gblAktBoard]^[rowMax,0],pBoards[gblAktBoard]^[0,0],sizeof(trow));
end;

procedure NextGen;
var
  p0,p1 : tpIntArr;
  col,row: integer;
begin
  Raender;
  For row := 1 to rowMax do
  begin
    p0 := @pBoards[gblAktBoard]^[row-1,0];
    p1 := @pBoards[1-gblAktBoard]^[row,0];
    For col := 1 to colMax do
      p1[col] := Survive(@p0[col-1]);
  end;
  //aBoard := bBoard;
  gblAktBoard :=1-gblAktBoard;
  inc(gblGenCount);
end;

procedure PrintGen;
const
  cChar: array[0..1of char = (' ','#');
var
  p0 : tpIntArr;
  col,row: integer;
  s : string;
begin
  setlength(s,colmax);
  gotoxy(1,1);
  writeln(gblGenCount:10);
  For row := 1 to rowMax do
  begin
    p0 := @pBoards[gblAktBoard]^[row,0];;
    For col := 1 to colMax do
      s[col] := cChar[p0[col]];
    writeln(s);
  end;
end;

procedure Init;
var
  col,row : integer;
begin
  randseed := 1;
  clrscr;
  fillchar(aBoard,SizeOf(aBoard),#0);
  pBoards[0] := @aBoard;
  pBoards[1] := @bBoard;
  For row := 1 to rowMax do
    For col := 1 to colMax do
      aBoard[row,col]:= Byte(random>0.9);
  gblAktBoard := 0;
end;

var
  cnt : integer;
  T1,T0 :TDateTime;
begin
  Init;
  cnt := 100000;
  T0 := Time;
  repeat
    PrintGen;
    NextGen;
    dec(cnt);
  until (cnt <= 0OR keypressed;
  // (Leerschleife also nur dec(cnt) etwa 24 ms ~ 840 CPU-Takte
  T1 := Time;
  Writeln((T1-t0)*86400*1000:0:3,' ms');
end.


Wie so oft ist die Ausgabe die Bremse schlechthin.
Es lohnt sich also bei grafischer Ausgabe, zwischen den beiden Boards zu vergleichen und nur die Unterschiede auszugeben.
Bei sehr großen Boards könnte sogar die Bestimmung von aktiven Lebenszonen etwas bringen.
Gruß Horst
catweasel
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 487
Erhaltene Danke: 1

Win 7 64bit
Delphi 7 Second Sedition V7.2
BeitragVerfasst: So 15.06.14 22:56 
Zitat:
Wie so oft ist die Ausgabe die Bremse schlechthin.
Es lohnt sich also bei grafischer Ausgabe, zwischen den beiden Boards zu vergleichen und nur die Unterschiede auszugeben.
Bei sehr großen Boards könnte sogar die Bestimmung von aktiven Lebenszonen etwas bringen.


Hmm bei einer Konsolenanwendung weiss ich nicht ob man da noch etwas optimieren kann, evtl Textausgabe per Assembler, anstelle von writeln. Müsste dann irgendwas mit MOV und dem guten alten INT 21h sein :)

Ich hatte es ja mit einer VCL Komponente gelöst.
Ich habe festgestellt das hier das OnPaint eines TDrawGrid schnell genug ist. Vorteil ist auch das sich die Zellengröße leicht anpassen kann, man einzele Zellen schön über SelectCell ansprchbar machen kann. Die Scrollbars für große Felder gibts auch gratis dazu; und dann werden auch nir die sichtbaren Zellen gezeichnet.

Das mit der aktiven Lebenszone kann schwierig werden. Ein Glider zum beispiel schleppt seine Zone immer mit sich rum. Einen Test in der Umgebung von zwei Zellen zu machen (Glider trifft auf Rand, die Zelle auf der anderen Seite der Randzelle muss auch gestet werden, deshalb zwei), ist aufwändiger als jede Zelle zu testen.

Wenn ich ein Array darstellen will ist das bei mir die Standardlösung, zumal ja die ACol und ARow Variablen sich gleich bequem als Arrayindizes einsetzen lassen (ausser man hat Fixed Rows/Cols).

Cheers,
Catweasel

_________________
Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
Fiete
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 617
Erhaltene Danke: 364

W7
Delphi 6 pro
BeitragVerfasst: Di 17.06.14 11:23 
Moin galagher,
vielleicht helfen diese Anregungen
www.entwickler-ecke....highlight=life+fiete

Gruß Fiete

_________________
Fietes Gesetz: use your brain (THINK)
catweasel
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 487
Erhaltene Danke: 1

Win 7 64bit
Delphi 7 Second Sedition V7.2
BeitragVerfasst: Di 17.06.14 12:22 
Jau. :)
Wenn man mit den Lebensregeln herumspielt kommen interessante Dinge raus.
Versuchmal : Bei ungeraden Nachbarn stirbt die Zelle, bei geraden Nachbarn lebt sie (nachbarn MOD 2 // 0 =leben; 1=tot)
Dann erhältst du die "Kopierwelt"... Zeichne ein Smiley oder sonstwas. Es werden dann Kopien darum erstellt, usw...

Cheers,
Ctweasel

_________________
Pommes werden schneller fertig wenn man sie vor dem Frittieren einige Minuten in siedendes Fett legt.
galagher Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2556
Erhaltene Danke: 45

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: So 29.06.14 20:21 
Hallo!

War auf Urlaub ohne Laptop (!) und antworte daher erst jetzt.
Danke für die Tipps, ich werde das eine oder andere eventuell noch einbauen!

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!