Autor Beitrag
GTA-Place
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
EE-Regisseur
Beiträge: 5248
Erhaltene Danke: 2

WIN XP, IE 7, FF 2.0
Delphi 7, Lazarus
BeitragVerfasst: Sa 01.09.07 11:56 
ausblenden Delphi-Quelltext
1:
2:
3:
4:
    if c[i]>9999 then  
      c[i] := 9000 + c[i] mod 1000  
    else if c[i]<-9999 then  
      c[i] := -9000 + c[i] mod 1000;

@BenBe: Die while-Schleife ist schneller als Mod.

10.000 Durchgänge (c[i] = High(Integer)):
while: 0 ms
mod: 33266 ms

_________________
"Wer Ego-Shooter Killerspiele nennt, muss konsequenterweise jeden Horrorstreifen als Killerfilm bezeichnen." (Zeit.de)
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 01.09.07 16:14 
user profile iconGTA-Place hat folgendes geschrieben:
aber bitte nicht StrToInt(FindComponent()) verwenden. Das kostet enorm Zeit.
StrToInt(TLabel(FindComponent('L'+IntToStr(i))).Caption) wird am Beginn jedes neuen Spiels aufgerufen und nicht ständig! Wie viele Millisekunden könnte ich da gleich sparen? 3 oder mehr? :mrgreen: Das bringt doch nichts! Anders wäre es natürlich, wenn der Aufruf ständig hintereinander erfolgt, ok, das ist ein Punkt, den man dann verbessern könnte. Aber wenn das alle 60 Sekunden bei niedrigen Levels oder, was weiss ich, alle 5 Stunden bei hohen Levels geschieht - bringt doch nix!

user profile iconBenBE hat folgendes geschrieben:
Dein Source greift zu häufig auf die VCL in einer Hauptschleife zurück.
Ja, eben wieder nur am Beginn jedes neuen Spiels! Shape1 setzt einfach einen Rahmen um ein label mit einer Zahl, damit man sieht, welche Zahl gerade dran ist.

user profile iconBenBE hat folgendes geschrieben:
Ferner solltest Du vermeiden, Progressbars in jedem Schleifendurchlauf neu zu setzen, da dann ein Neuzeichnen dieser erzwungen wird, was unnötig Zeit frisst.
Und wieder nur am Beginn jedes neuen Spiels, nicht dauernd. Ist zwar nur eine optische Spielerei, aber man sieht dadurch, dass das Programm etwas tut.

user profile iconBenBE hat folgendes geschrieben:
Bau deinen Source mal so um, dass z.B. nur noch alle 1000 Durchläufe (hier fragt man am Besten auf If Count and $3FF = 0 Then ab) die Progressbars neuzeichnet. Ähnliches gilt für die anderen VCL-Zugriffe. Alles, was über die VCL geleitet wird (und sei's das Setzen einer Caption) verschwendet Zeit.
Und wieder: alle VCL-Zugriffe erfolgen jeweils am Anfang eines neuen Spiels. Klar, bei niedrigen Levels erfolgen die Zugriffe schneller hintereinander, 30-60 Sekunden oder so, bei hohen Levels werden die Abstände schon länger.

Positive/Negative Zahlen: Sieht besser aus, so wie du das machst, ok, danke!

user profile iconBenBE hat folgendes geschrieben:
Ferner fehlt hier die untere Grenze???
ausblenden Delphi-Quelltext
1:
2:
    while c[i]>9999 do
      c[i] := c[i] - 1000;

Geht so hier:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
    if c[i]>9999 then
      c[i] := 9000 + c[i] mod 1000
    else if c[i]<-9999 then
      c[i] := -9000 + c[i] mod 1000;
Fehlt doch gar nicht:
Solange c[i] grösser als 9999 ist, ziehe 1000 von c[i] ab. Sobald c[i] kleiner als 9999 ist, bricht die Schleife doch ab, oder habe ich da einen Denkfehler drin? Und wenn c[i] < 9999, dann wird die Schleife gar nicht ausgeführt.

user profile iconBenBE hat folgendes geschrieben:
VCL in nem zeitkritischen Abschnitt, speziell FindComponent geht mal gar nicht!!!
ausblenden Delphi-Quelltext
1:
    if c[i] = StrToInt(TLabel(FindComponent('L'+IntToStr(i))).Caption) then					

Mal abgesehen davon, dass hier jegliche Fehlerprüfung fehlt!
Zum zeitkritischen Abschnitt: Eben nur am Beginn eines neuen Spiels. Zur Fehlerprüfung: Hat bisher immer funktoniert, muss ich mir mal ansehen! i war bisher immer 0 - 19.

user profile iconBenBE hat folgendes geschrieben:
GUI-Interaktion ohne Ende ...
... immer am Beginn eines neuen Spiels.

Mit "schneller" meinte ich soetwas wie "aus 4 Stunden mach 3" oder so.

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
GTA-Place
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
EE-Regisseur
Beiträge: 5248
Erhaltene Danke: 2

WIN XP, IE 7, FF 2.0
Delphi 7, Lazarus
BeitragVerfasst: Sa 01.09.07 16:23 
Da mod ja so ewig langsam ist, könnte das hier helfen:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
X := cnt;

while X > 500 do
  X := X - 500;

if X = 500 then

statt
ausblenden Delphi-Quelltext
1:
if cnt mod 500 = 0 then					

_________________
"Wer Ego-Shooter Killerspiele nennt, muss konsequenterweise jeden Horrorstreifen als Killerfilm bezeichnen." (Zeit.de)
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 01.09.07 16:27 
user profile iconGTA-Place hat folgendes geschrieben:
Da mod ja so ewig langsam ist, könnte das hier helfen:
Das passiert alle 500 Spiele! Da bringen mir ein paar ms nichts! Jedes 500. Mal wird mit den alten Werten, jedes 50. Mal mit den Originalwerten verglichen, das bedeutet, 49x bzw. 499x wird der Code nicht ausgeführt.

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

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Sa 01.09.07 16:43 
user profile icongalagher hat folgendes geschrieben:
user profile iconBenBE hat folgendes geschrieben:
Ferner fehlt hier die untere Grenze???
ausblenden Delphi-Quelltext
1:
2:
    while c[i]>9999 do
      c[i] := c[i] - 1000;

Geht so hier:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
    if c[i]>9999 then
      c[i] := 9000 + c[i] mod 1000
    else if c[i]<-9999 then
      c[i] := -9000 + c[i] mod 1000;
Fehlt doch gar nicht:
Solange c[i] grösser als 9999 ist, ziehe 1000 von c[i] ab. Sobald c[i] kleiner als 9999 ist, bricht die Schleife doch ab, oder habe ich da einen Denkfehler drin? Und wenn c[i] < 9999, dann wird die Schleife gar nicht ausgeführt.

Für den Fall, dass deine Variable aber am Anfang <= -10000 wird diese aber nicht in ihrem Wertebereich beschränkt. Ich wollte nur wissen, ob das beabsichtig ist (und hab für den Fall, dass dies nicht der Fall ist) eine mögliche Lösung angegeben.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
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 01.09.07 16:57 
user profile iconBenBE hat folgendes geschrieben:
Für den Fall, dass deine Variable aber am Anfang <= -10000 wird diese aber nicht in ihrem Wertebereich beschränkt.

Stimmt! Danke für den Tipp! Lieber auf Nummer sicher gehen!

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

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Sa 01.09.07 17:27 
Hi galagher,

da der Code von mir ist, werd ich mal meinen Senf dazugeben: Du willst ihn schneller machen, und da hilft nur eins: Du musst an das Herz des Spielealgorithmus ran: Ich habe einen Minimax/Negmax-Algorithmus verwendet. Das Bauernspiel, um das es hier vermutlich noch geht, benötigt zum starken Spiel nicht nur eine hohe Zugtiefe, sondern auch eine ausgefeilte Bewertungsfunktion. Ich hatte Dir bereits vorgeschlagen, mit Mustererkennung zu arbeiten und ggf den Horizonteffekt zu umgehen. Der Horizonteffekt tritt dann ein, wenn das Programm bei einer festen Zugtiefe die Vorausberechnung abbricht und in diesem Stadium scheinbar im Vorteil ist. Das es im darauffolgenden Zug verliert, wird übersehen (=Horizonteffekt). Wenn man Gewinnstellungen als Muster sucht, kann man diesen Effekt vermeiden.

Der Evolutionsalgorithmus, den Du hier gezeigt hast, ist ja eigentlich nicht Bestandteil des Spiels, sondern nur ein Bonmot, um zu zeigen, wie einfach man einem PC das 'Lernen' beibringen kann.

Also: Es bringt (noch) nichts, an irgendwelchen einzelnen Statements rumzubasteln: Zunächst muss das Verfahren verbessert werden. Negamax ist schon ein sehr guter Algorithmus, hier ist also nicht viel zu holen. Stelle die Bewertungsfunktion auf den Prüfstand und lasse sie hier im Forum verbessern. Diese Funktion wird pro Zug (je nach Tiefe) eben einige Millionen mal aufgerufen und hier bringt eine Optimierung am meisten.

_________________
Na denn, dann. Bis dann, denn.
GTA-Place
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
EE-Regisseur
Beiträge: 5248
Erhaltene Danke: 2

WIN XP, IE 7, FF 2.0
Delphi 7, Lazarus
BeitragVerfasst: Sa 01.09.07 17:47 
user profile icongalagher hat folgendes geschrieben:
user profile iconGTA-Place hat folgendes geschrieben:
Da mod ja so ewig langsam ist, könnte das hier helfen:
Das passiert alle 500 Spiele! Da bringen mir ein paar ms nichts! Jedes 500. Mal wird mit den alten Werten, jedes 50. Mal mit den Originalwerten verglichen, das bedeutet, 49x bzw. 499x wird der Code nicht ausgeführt.

Das ist doch in einer repeat-Schleife?!

_________________
"Wer Ego-Shooter Killerspiele nennt, muss konsequenterweise jeden Horrorstreifen als Killerfilm bezeichnen." (Zeit.de)
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Sa 01.09.07 18:01 
Der Evolutionsalgorithmus geht so (vorgegeben ist eine Reihe von Koeffizienten K0, die die Bewertungsfunktion definiert):
ausblenden Quelltext
1:
2:
3:
4:
5:
K := K0
Repeat
  K1 := ErzeugeZufälligeMutation (K)
  Lass K gegen K1 spielen (Der Gewinner ist K)
Until Weihnachten = Ostern.

Wobei die zufällige Mutation von K eben ab und an entweder K0 oder ein älteres K liefert. Auf diese Weise wird K (also die Bewertungsfunktion) immer besser werden. Natürlich darf man keine Wunder erwarten, es kommt immer noch in erster Linie auf die Funktion selbst an, und nicht auf die Koeffizienten. Sie definieren aber z.B. wie stark geschlagene Steine und bestimmte Konstellationen gewichtet werden.

So, jetzt klinke ich mich aber wieder aus und überlasse galagher das Ruder.

_________________
Na denn, dann. Bis dann, denn.
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 01.09.07 18:08 
user profile iconalzaimar hat folgendes geschrieben:
da der Code von mir ist, werd ich mal meinen Senf dazugeben: Du willst ihn schneller machen, und da hilft nur eins: Du musst an das Herz des Spielealgorithmus ran:
der Code ist ja deshalb von dir, weil soetwas meine Kenntnisse übersteigt! Am Anfang habe ich mir das viel einfacher vorgestellt. Sagen wir so: Ich habe die Karosserie, die Räder und die Bedienelemente gemacht, der Motor ist von dir. Nun will ich den Motor tunen!

user profile iconalzaimar hat folgendes geschrieben:
Ich hatte Dir bereits vorgeschlagen, mit Mustererkennung zu arbeiten und ggf den Horizonteffekt zu umgehen.
Ich habe aber keinen Ansatz, was eine Mustererkennung sein könnte, nicht, weil ich nicht will, sondern weil ich es nicht kann! Deshalb habe ich auch an mehreren Stellen im Quellcode vermerkt, dass dieser von dir stammt und "alzaimar" scheint auch im Info-Fenster auf. Und deshalb konnte ich den Code, den du mir zur Verfügung gestellt hast, im Wesentlichen nur kopieren und einfügen, dann anpassen und erweitern. Ganz einfach deshalb, weil ich ihn nicht ganz verstehe. Ich steige da wirklich nicht durch!

user profile iconalzaimar hat folgendes geschrieben:
Der Evolutionsalgorithmus, den Du hier gezeigt hast, ist ja eigentlich nicht Bestandteil des Spiels, sondern nur ein Bonmot, um zu zeigen, wie einfach man einem PC das 'Lernen' beibringen kann.
Ja, aber der braucht am Längsten, weil er ja beim "Lernen" beide Spieler ist.

user profile iconalzaimar hat folgendes geschrieben:
Stelle die Bewertungsfunktion auf den Prüfstand und lasse sie hier im Forum verbessern. Diese Funktion wird pro Zug (je nach Tiefe) eben einige Millionen mal aufgerufen und hier bringt eine Optimierung am meisten.
Naja, das ist sie - nochmal: sie stammt (bis auf einige Änderungen und Erweiterungen) von user profile iconalzaimar:
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:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
function Score(const aBoard: TBoard; aPlayer, aOpponent: TPlayer): Integer;
(******************************************************************************
 * Das ist das Herz der KI: Hier wird die Stellung bewertet.                  *
 * >0 bedeutet, gut für aPlayer, <0 bedeutet, schlecht für aPlayer            *
 *                                                                            *
 * Je besser und genauer diese Funktion die Stellung wiederspiegelt, desto    *
 * stärker spielt das Programm.                                               *
 ******************************************************************************)

  function _Score(aPlayer, aOpponent: TPlayer): Integer;
  var
    z, i, j, r, r1: Integer;
    w, b, b2, m, p, s, d, e, o, a, v, l, c, _y, y, t, no, np, ne, _f, _g, f, g: Integer;
    OpponentRow: array[0..7of Boolean;
    BlockedCols: array[0..8of Boolean;

  begin
    {Alten, bisherigen Wert sichern, vergleichen zu können (s. Unit1)}
    iOldBewertung := iBewertung;

    r := iRows[aPlayer, 7];
    for i := 0 to 7 do
      if aBoard[r, i] = aPlayer then begin
        Result := sWinScore;
        Exit;
      end;

    e := 0;  {Leeres Feld}
    p := 0;  {Anzahl der Steine}
    m := 0;  {Anzahl der Zugmöglichkeiten ohne Schlagen}
    s := 0;  {Anzahl der Zugmöglichkeiten mit Schlagen}
    d := 0;  {Anzahl der gedeckten Steine}
    w := 0;  {Abstand eines Gewinnsteins zur Grundlinie}
    o := 0;  {Nächster Zug wird Gegner bedrohen}
    a := 0;  {Nächster Zug wird eigenen Stein decken}
    v := 0;  {2 Felder vor Grundlinie bei freiem, ungefährdetem Zug}
    l := 0;  {blockierte Steine}
    c := 0;  {3 Felder links bzw. rechts sind frei, wenn dann eigener Stein folgt}
    y := 0;  {Feststellen, ob aPlayer mehr als 1 Stein in der Reihe hat}
    t := 0;  {Schlagabtausch}
    no := 0{Nachbarstein(e) ist/sind Gegner}
    np := 0{Nachbarstein(e) ist/sind eigene}
    ne := 0{Nachbarfeld(er) ist/sind leer}
    f := 0;  {Freie Reihe (nur leere Felder)}
    g := 0;  {Freie Zeile (nur leere Felder)}

    _f := 0{Hilfszähler für f}
    _g := 0{Hilfszähler für g}
    _y := 0{Hilfszähler für y}

    Fillchar(BlockedCols, Sizeof(BlockedCols), 0);
    Fillchar(OpponentRow, Sizeof(OpponentRow), 0);

    for i := 6 downto 0 do begin
      r := irows[aPlayer, i];
      r1 := irows[aPlayer, i + 1];

      for j := 0 to 7 do begin
        if aBoard[r1, j] = aOpponent then
          OpponentRow[j] := True;
        if aBoard[r, j] = aPlayer then begin
          BlockedCols[j] := True;
          {Wenn kein Gegner zwischen diesem Stein und irows [aplayer,7] ist,}
          {und zwar in den Spalten j-1, j und j+1, dann haben wir schon gewonnen}
          if not (opponentRow[j - 1or opponentRow[j] or opponentRow[j + 1])
            then
            inc(w, i);
          inc(p);

          if aBoard[r1, j] = plEmpty then inc(m);
          if aBoard[r1, j - 1] = aOpponent then inc(s);
          if aBoard[r1, j + 1] = aOpponent then inc(s);
          if aBoard[r1, j - 1] = aPlayer then inc(d);
          if aBoard[r1, j + 1] = aPlayer then inc(d);
          if aBoard[r1, j - 1] = plEmpty then inc(e);
          if aBoard[r1, j + 1] = plEmpty then inc(e);

{Bei Nicht-funktionieren Coeff[8] - Coeff[19] ändern, oder völlig entfernen}
{und das Coeff-Array von TCoeffArray = array[0..19] of Integer zu}
{TCoeffArray = array[0..7] of Integer zurückstellen - diesfalls aber auch}
{L8, L9, L10, L11, L12, L13, L14, L15, L16, L17, L18, L19 in Form1 entfernen und
{in Bauernspiel.ini den Abschnitt [Coeffs] um die letzen 11 Koeffizienten kürzen}

{oder entfernen. Die Variablen}
{o, a, v, l, c, _y, y, t, no, np, ne, _f, _g, f, g}
{in dieser Funktion sind dann ebenfalls zu entfernen!}
{------------------------------------------------------------------------------}
{Coeff[8]}
          {Nächster Zug wird Gegner bedrohen}
          if (aBoard[r1-1, j + 1] = aPlayer) or    {übernächstes Feld schräg oben}
             (aBoard[r1-1, j - 1] = aOpponent) and {übernächstes Feld schräg oben}
             (aBoard[r, j] = aPlayer) and          {aktuelles Feld}
             (aBoard[r1, j] = plEmpty) then        {Feld gerade voraus}
           inc(o);

          if (aBoard[r1-1, j + 1] = aOpponent) or  {übernächstes Feld schräg unten}
             (aBoard[r1-1, j - 1] = aPlayer) and   {übernächstes Feld schräg unten}
             (aBoard[r, j] = aOpponent) and        {aktuelles Feld}
             (aBoard[r1, j] = plEmpty) then        {Feld gerade voraus}
           inc(o);


{Coeff[9]}
         {Nächster Zug wird eigenen Stein decken bzw. Stein ist gedeckt}
          if (aBoard[r1-1, j + 1] = aPlayer) or    {übernächstes Feld schräg oben}
             (aBoard[r1-1, j - 1] = aPlayer) and   {übernächstes Feld schräg oben}
             (aBoard[r, j] = aPlayer) and          {aktuelles Feld}
             (aBoard[r1, j] = plEmpty) then        {Feld gerade voraus}
           inc(a);

          if (aBoard[r1-1, j + 1] = aOpponent) or  {übernächstes Feld schräg unten}
             (aBoard[r1-1, j - 1] = aPlayer) and   {übernächstes Feld schräg unten}
             (aBoard[r, j] = aOpponent) and        {aktuelles Feld}
             (aBoard[r1, j] = plEmpty) then        {Feld gerade voraus}
           inc(o);


{Coeff[10]}
          {2 Felder vor Grundlinie bei freiem, ungefährdetem Zug}
          if (aBoard[6, j] = plEmpty) and not
             (aBoard[6, j+1] = aOpponent) and not
             (aBoard[6, j-1] = aOpponent) and
             (aBoard[5, j] = aPlayer) then
           inc(v);

          if (aBoard[1, j] = plEmpty) and not
             (aBoard[1, j+1] = aOpponent) and not
             (aBoard[1, j-1] = aOpponent) and
             (aBoard[2, j] = aPlayer) then
           inc(v);


{Coeff[11]}
          {Blockierte Linien}
          if not (aBoard[r, j] = plEmpty) and not
                 (aBoard[r1, j] = plEmpty) then
           inc(l);


{Coeff[12]}
          {Lücken in der Stellung erkennen}

          {Die nächsten 3 Felder links sind frei, dann folgt eine eigener Stein}
          if (aBoard[r, j-4] = aPlayer) then
           if (aBoard[r, j-1] = plEmpty) and
              (aBoard[r, j-2] = plEmpty) and
              (aBoard[r, j-3] = plEmpty) then
           inc(c);

          {Die nächsten 3 Felder rechts sind frei, dann folgt eine eigener Stein}
          if (aBoard[r, j+4] = aPlayer) then
           if (aBoard[r, j+1] = plEmpty) and
              (aBoard[r, j+2] = plEmpty) and
              (aBoard[r, j+3] = plEmpty) then
           inc(c);


{Coeff[13]}
          {Feststellen, ob aPlayer mehr als 1 Stein in der Reihe hat, ...}
          for z := 0 to 7 do
          begin
           if (aBoard[z, j] = aPlayer) then
            inc(_y);

{Coeff[18]}
          {Feststellen, ob die ganze Reihe (senkrecht) frei ist, ...}
           if (aBoard[z, j] = plEmpty) then
            inc(_f);

{Coeff[19]}
           if (aBoard[r, z] = plEmpty) then
            inc(_g);
          end;

          if _y > 1 then inc(y);  {... wenn ja, Zähler y erhöhen -> Coeff[13]}

          {Wenn ganze Reihe frei, f erhöhen -> Coeff[18]}
          if _f = 6 then inc(f);

          {Wenn ganze Zeile frei, g erhöhen -> -> Coeff[19]}
          if _g = 7 then inc(g);


{Coeff[14]}
          {Schlagabtausch - etwa diese Stellung:}
{          X X X
           - O -
           O - O
}

          if (aBoard[r-2, j] = aPlayer) and
             (aBoard[r-2, j+1] = aplayer) and
//           (aBoard[r-2, j+2] = aplayer) and   {Klappt nicht (??)}

             (aBoard[r-1, j] = plEmpty) and
             (aBoard[r-1, j+1] = aOpponent) and
             (aBoard[r-1, j+2] = plEmpty) and

             {Mit weiteren if's klappt's nicht (??)}
             (aBoard[r, j-1] = aOpponent) then
              inc(t);

          {Schlagabtausch - etwa diese Stellung:}
{          O - O
           - X -
           X - X
}

          if (aBoard[r-2, j] = aOpponent) and
             (aBoard[r-2, j+1] = plEmpty) and
             (aBoard[r-2, j+2] = aOpponent) and

             (aBoard[r-1, j] = plEmpty) and
             (aBoard[r-1, j+1] = aPlayer) and
             (aBoard[r-1, j+2] = plEmpty) and

             {Mit weiteren if's klappt's nicht (??)}
             (aBoard[r, j] = aPlayer) then
              inc(t);


{Coeff[15]}
          {Nachbarstein(e) ist/sind Gegner}
          if (aBoard[r, j+1] = aOpponent) or
             (aBoard[r, j-1] = aOpponent) then
              inc(no);


{Coeff[16]}
          {Nachbarstein(e) ist/sind eigene}
          if (aBoard[r, j+1] = aPlayer) or
             (aBoard[r, j-1] = aPlayer) then
              inc(np);


{Coeff[17]}
          {Nachbarfeld(er) ist/sind leer}
          if (aBoard[r, j+1] = plEmpty) or
             (aBoard[r, j-1] = plEmpty) then
              inc(ne);

{------------------------------------------------------------------------------}
        end;
      end;
    end;
    b := 0;
    b2 := 0;
    for i := 0 to 7 do
      if not BlockedCols[i] then begin
        inc(b);
        if not BlockedCols[i + 1then
          inc(b2)
      end;
    Result := p * coeffs[0]
      + m * coeffs[1]
      + s * coeffs[2]
      + d * coeffs[3]
      + e * coeffs[4]
      + b * coeffs[5]
      + b2* coeffs[6]
      + w * coeffs[7]
      + o * coeffs[8]
      + a * coeffs[9]
      + v * coeffs[10]
      + l * coeffs[11]
      + c * coeffs[12]
      + y * coeffs[13]
      + t * coeffs[14]
      + no* coeffs[15]
      + np* coeffs[16]
      + ne* coeffs[17]
      + f * coeffs[18]
      + g * coeffs[19];
  end;

begin
  Result := _Score(aPlayer, aOpponent);
  iBewertung := Result;
  if Result >= sWinScore then Exit;
  Result := Result - _Score(aOpponent, aPlayer);
  iBewertung := Result;
end;

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
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 02.09.07 09:23 
Das Problem an der ganzen Sache ist, dass das Lernen umso langsamer wird, je genauer ich die Spielsituation darstelle, da, wie alzaimar schon gesagt hat, der Code millionenfach ausgeführt wird. Das summiert sich! Das dauert Stunden, mitunter sogar Tage, bis auch nur ein Koeffizient verbessert (und damit übernommen) wird. Ein Dilemma: Genauere Spielsituation = langsamer, aber besser, weniger genau = schneller, aber schlechter.

Und da das Programm alle 50 Durchgänge gegen die alten und alle 500 Durchgänge gegen die Original-Koeffizienten spielt - bis da auch nur 50 Spiele erreicht sind, kann man inzwischen im Park spazieren gehen, was heisst, das reicht glatt für einen Urlaub!

Wenn eine Mustererkennung oder ein effizienterer Code eine Lösung wäre, und jemand kann das, dann bitte ich ihn, mir zu helfen! :D

Schon jetzt: Danke an alle für euer Interesse!

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
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: Mi 05.09.07 17:40 
Hallo!

Sorry, dass ich mich zum 3. Mal in Serie melde, aber die Anzahl der Zugriffe zeigt mir, dass das Interesse ja da ist, aber keiner weiss, wie es nun weitergeht! Und, ja, so weit bin ich auch!

Das Programm braucht im höchsten Level wahrscheinlich Tage, bis auch nur ein Spiel beim "Lernen" zu Ende ist. Es ist aber sinnvoll, zumindest 50 Durchgänge zu machen, denn da spielt es mit den neuen Werten gegen die alten. Wohlgemerkt, ohne wesentliche GUI-Zugriffe.

Also versuche ich jetzt, das Berechnen jedes Zuges nach einer einstellbaren Maximalzeit zu beenden (klappt aber noch nicht). Das ist zwar nicht optimal, aber ich habe leider keinen zweiten Rechner, der so nebenbei mal ein paar Monate läuft und das Programm lernen lässt. Ich habe die Stellungsanalyse nach alzaimar's Vorbild ausgebaut, sodass das Programm jetzt 20 Koeffizienten hat. Aber das macht es langsamer, je genauer, desto lahmer, aber dafür desto besser.

Es spielt ja recht gut, alzaimar's KI ist wirklich ok (hätte ich nie hinbekommen!), aber - konkret:
Wer kann mir einen Ansatz geben, wie im Code eine Mustererkennung, ohne den von alzaimar angesprochenen Horizonteffekt funktionieren könnte, und das in einer akzeptablen Zeit, sodass ein Spiel im "Lernmodus" in, sagen wir mal, maximal 10 Stunden vorbei ist?

Das Bauernspiel ist nicht allein mein Programm, aber es ist mir das wichtigste von allen! :flehan: Bitte Hilfe, jedenfalls aber an dieser Stelle nochmal Dank!

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

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Do 06.09.07 11:11 
Hi galagher

nachdem ich den Thread nun schon 197,3 mal gelesen habe, ist mir endlich aufgefallen, was Du willst....

Du bist in dem Glauben, das das Lernprogramm Superkoeffizienten herausbringt, wenn man die 'Spieler' gegeinander im höchsten Level antreten lässt. Das stimmt aber nicht. Es reicht imho, die Spieler mit einem relativ geringen Level gegeneinander antreten zu lassen. Eigentlich müsste Level-0 ausreichen. Du willst ja nur zwei 'DNS-Stränge' (also die Koeffizienten) gegeneinander antreten lassen, und nicht, was irgendwelche Spielealgorithmen daraus machen.

Hier ist es auch so, das die Bewertungsfunktion unabhängig vom Suchalgorithmus agiert, Du kannst also getrost den Level beim Lernen runtersetzen. Probier doch mal, was für unterschiede in den Koeffizienten es gibt, wenn man beide Spieler mit Level 0 und mit 3 gegeneinander spielen lässt.

Die Bewertungsfunktion, die Du optimieren willst, soll den aktuellen Spielstand möglichst genau wiedergeben. Optimalerweise sollte die Funktion in der Lage sein, einen strategischen Vorteil zu erkennen. Dazu kann die Mustererkennung dienen. Die hast Du ja schon implementiert (oder war ich das?)
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
...
 {Schlagabtausch - etwa diese Stellung:}
{          X X X
           - O -
           O - O
}

          if (aBoard[r-2, j] = aPlayer) and
             (aBoard[r-2, j+1] = aplayer) and
//           (aBoard[r-2, j+2] = aplayer) and   {Klappt nicht (??)}

Es kann durchaus sein, das so eine Spielstellung nicht sonderlich von solchen Mustern abhängt. Dann würde ein lernenes Programm das relativ schnell erkennen, indem es den Koeffizienten hierfür gegen 0 tendieren, oder wahllos in der Gegend rumhüpfen lässt.

Vielleicht solltest Du eher schauen, das das Programm im Einzelfall über die maximale Zugtiefe hinaus weitersucht. Das kann z.B. bei einem Schlagabtausch nötig sein. Hier ist es ja eher so, das das Schlagen eines Steines nur einen strategischen Vorteil bringt (man schafft sich vielleicht eine leere Spalte zum durchmarschieren).

Welche Stellungen/Muster sind Winner? Welche sind Loser? Gibt es Stellungsmuster, die auf ein Patt hindeuten? Wie sollten die bewertet werden (ach, dafür ist ja der Lern-O-Mat da)...

In diese Richtung solltest Du weiter forschen.

_________________
Na denn, dann. Bis dann, denn.
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Do 06.09.07 14:57 
Mir ist auch noch ne Kleinigkeit aufgefallen: Du solltest versuchen, bei Index-Angaben so wenig wie möglich berechnungen durchzuführen.

Wenn Du also mehrfach auf arr[a-1, b+2] zugreifen musst, dann berechnet Delphi jedes Mal auf's Neue, wo sich diese Variable befindet. Diese BErechnung kannst Du etwas verkürzen, indem Du:
1. die Adressen häufig gebrauchter Zellen über Pointer ansprichst
2. Die Index-Attribute zwischenspeicherst.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
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: Do 06.09.07 17:27 
user profile iconalzaimar hat folgendes geschrieben:
Du bist in dem Glauben, das das Lernprogramm Superkoeffizienten herausbringt, wenn man die 'Spieler' gegeinander im höchsten Level antreten lässt.
Ja, dachte ich! Es lernt nun schon seit gestern seit 21:15 ununterbrochen in Level 4 (von 8 ), hat bis jetzt 1 Koeffizienten verändert und ist erst im 8. Spiel! Mit Level 1 (0 hab ich aus dem Programm genommen) geht das Lernen ja ruck-zuck, im Nu sind da 50 Spiele beisammen. Aha, also dann, wenn ich das recht verstehe, ist es genauso gut, das Programm in Level 1 5000 Spiele spielen zu lassen, auch, wenn dann pro Zug die Rechentiefe sehr gering ist? Muss man ja erst mal wissen, DANKE!

user profile iconalzaimar hat folgendes geschrieben:
Du willst ja nur zwei 'DNS-Stränge' (also die Koeffizienten) gegeneinander antreten lassen, und nicht, was irgendwelche Spielealgorithmen daraus machen.
Ich dachte immer, was sie daraus machen sei entscheidend, weil es ja in höheren Levels (=mehr Rechenzeit pro Zug) besser spielt...

user profile iconalzaimar hat folgendes geschrieben:
Hier ist es auch so, das die Bewertungsfunktion unabhängig vom Suchalgorithmus agiert, Du kannst also getrost den Level beim Lernen runtersetzen.
Dann schmeiss' ich die Level-Einstellmöglichkeit beim Lernen raus und setze gleich Level 1 (oder 0)! Geht ja dann gleich viel schneller!

user profile iconalzaimar hat folgendes geschrieben:
Probier doch mal, was für unterschiede in den Koeffizienten es gibt, wenn man beide Spieler mit Level 0 und mit 3 gegeneinander spielen lässt.
Aber wenn's doch egal ist, wozu? Level 3 ist ja wieder langsamer als 0.

user profile iconalzaimar hat folgendes geschrieben:
Die Bewertungsfunktion, [...] Dazu kann die Mustererkennung dienen. Die hast Du ja schon implementiert (oder war ich das?)
Du warst das, ich habe sie aber auf 20 Koeffizienten ausgebaut. Mal sehen, könnten ja auch mehr sein.

user profile iconalzaimar hat folgendes geschrieben:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
...
 {Schlagabtausch - etwa diese Stellung:}
{          X X X
           - O -
           O - O
}

          if (aBoard[r-2, j] = aPlayer) and
             (aBoard[r-2, j+1] = aplayer) and
//           (aBoard[r-2, j+2] = aplayer) and   {Klappt nicht (??)}
Ja, so in etwa sieht das aus. Ein solcher Code IST also schon die Mustererkennung? Dann ist sie ja schon eingebaut (natürlich mehr davon, für alle 20 Koeffizienten).

user profile iconalzaimar hat folgendes geschrieben:
Vielleicht solltest Du eher schauen, das das Programm im Einzelfall über die maximale Zugtiefe hinaus weitersucht.
Macht es bei Level 7 und 8.

Also, zusammenfassend: Level 0 beim Lernen genügt, aber es sollten so viele Spiele wie möglich sein, oder? Und was die Mustererkennung angeht, das ist obiger Code, der die Stellung analysiert und bewertet, richtig? Und den kann man immer weiter ausbauen, sodass möglichst viele Stellungen berücksichtigt werden, auch korrekt?

Vielen Dank, hast mir sehr weitergeholfen, das ist schon was!

user profile iconBenBE hat folgendes geschrieben:
Mir ist auch noch ne Kleinigkeit aufgefallen: Du solltest versuchen, bei Index-Angaben so wenig wie möglich berechnungen durchzuführen.
Werde ich mir auch ansehen, danke!

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
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: Mo 10.09.07 15:58 
Hallo zusammen!

Nun fehlt noch eine wesentliche Sache: Das Spiel soll Züge, die als einzige verhindern können, dass der Spieler gewinnt, sofort ausführen, und nicht auch noch andere, die den Spieler gewinnen lassen würde, berechnen. Letztlich führt es in jedem Level den einzig sinnvollen Zug ja doch aus. Ich möchte einfach, dass es ihn schneller ausführt, habe aber keine Ahnung, wie ich das programmieren soll.

Ich meine so in der Art: "Wenn du erkennst, dass der Spieler gewinnt, wenn du seinen Stein nicht schlägst (oder was auch immer), dann höre auf zu rechnen und schlage den Stein sofort!"

Am Besten seht ihr euch das folgende Beispielbild an. Das Programm spielt blau, der Mensch rot. Das Programm kann jetzt noch nicht gewinnen, und wenn es mit seinem bedrohten blauen Stein nicht schlägt, dann schlägt rot und gewinnt im darauffolgenden Zug:
Einloggen, um Attachments anzusehen!
_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Mo 10.09.07 16:52 
Du könntest auch den Ansatz etwas anders wählen:

Bewerte diese.
Führe zuerst alle möglichen Züge 1. Ebene aus.
Bewerte alle Züge 2. Ebene
Entscheide dich für die weiter zu verfolgenden
Stelle alle anderen zurück
Führe die bevorzugten Züge aus (der reihe nach) und verfahre genauso.

Auf diese Weise hast Du eine gewichtete Baum-Traversierung, wodurch stark zu bevorzugende Züge schnell gefunden werden sollten. Nachteil ist allerdings der etwas erhöhte Back-Tracking-Aufwand, da Du erstmal alle Möglichkeiten ausführen musst, bevor Du die Bewertung ermitteln kannst UND sich die Bewertung ggf. nachträglich noch mal ändert.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
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: Mo 10.09.07 17:17 
user profile iconBenBE hat folgendes geschrieben:
Auf diese Weise hast Du eine gewichtete Baum-Traversierung, wodurch stark zu bevorzugende Züge schnell gefunden werden sollten.

Ja, und da ist es wieder, das Problem: theoretisch verstehe ich das ja, aber die Praxis... nicht vergessen, die KI hat user profile iconalzaimar geschrieben, nicht ich! Zur Zeit gibt es keine solche Baum-Traversierung, denn wenn man das Programm "zwingt", sofort zu ziehen (ich hab' einen Button dafür eingebaut!), macht es einfach den aktuell berechneten Zug, auch wenn der geradezu hirnrissig ist.
Solche Züge müsste man irgendwie wegbekommen...

Habe nicht einmal ansatzweise eine Idee, wie ich das realisieren soll. Man müsste die Züge nach besseren und schlechteren sortieren, abhängig von der Spielsituation, ja, gut. Aber wie? Das Programm denkt ja nicht und sieht das Naheliegende nicht wie wir, und seine Berechnungen dauern nun schon teilweise recht lange. Es arbeitet mittlerweile mit 27 Koeffizienten, die ich alle nach alzaimar's Vorbild eingebaut habe, und das kostet eben Zeit.

Ok, das mit dem "Lernen per Evolution" klappt hervorragend mit Level 0 - über 75.000 Spiele in 12 Stunden ist akzeptabel. Aber das Sortieren per "gewichtete Baum-Traversierung" bleibt für mich graue Theorie. Ich apelliere hier an alzaimar: Nur noch diese eine Hilfe, dann ist es fertig!

Ich denke auch an eine einfache Eröffnungsbibliothek, falls sinnvoll. Das werde ich auch hinbekommen, aber was die KI angeht, die ist mir immer noch zu hoch... :(

//Edit: Bei Level 0 macht es den einzig sinnvollen Zug sofort, ab Level 1 rechnet es, um ihn schliesslich doch zu machen, nur eben später. :?

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
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: Mi 12.09.07 07:46 
Habe es hinbekommen, dass der einzig sinnvolle Zug gleich ausgeführt wird. Ist zwar eine Abfrage, wo der Spieler und der Computer in den entscheidenden Reihen stehen, und ob der Computer gewinnen kann, aber erfüllt seinen Zweck.

Nun möchte ich, dass sich das Spiel eine Art Eröffnungsbibliothek erspielt. Ich denke mir das so, wenn der Spieler den Zug macht, den das Programm an seiner Stelle auch gemacht hätte, speichert es beide Züge ab. Das macht es so bis zum 6. Zug. Wenn bei einem späteren Spiel genau diese Züge kommen, spielt es aus der Bibliothek und damit schneller. Wird sicher klappen.

Nur das Sortieren der errechenten Züge, sodass der beste an erster Stelle kommt, kriege ich nicht hin. Ich meine das so: Wenn der Spieler die Zugberechnung des Spiels abbricht und es zum Ziehen zwingt, soll es nicht den aktuell berechneten Zug machen, sondern den, den es bis dahin als den besten ermittelt hat. Das mag zwar nicht der beste Zug insgesamt sein, aber so sielt es ja doch ein wenig stärker.

In diesem Zusammenhang könnte man auch gleichwertige Züge ermitteln, das wäre dann wiederum sinnvoll für die Eröffnungsbibliothek, die damit flexibler würde. Ich weiss nur nicht, wie!

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
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 15.09.07 21:16 
* Schieb * :wave:

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