Autor |
Beitrag |
GTA-Place
      

Beiträge: 5248
Erhaltene Danke: 2
WIN XP, IE 7, FF 2.0
Delphi 7, Lazarus
|
Verfasst: Sa 01.09.07 11:56
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 
      
Beiträge: 2556
Erhaltene Danke: 45
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Sa 01.09.07 16:14
GTA-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?  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!
BenBE 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.
BenBE 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.
BenBE 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!
BenBE hat folgendes geschrieben: | Ferner fehlt hier die untere Grenze???
Delphi-Quelltext 1: 2:
| while c[i]>9999 do c[i] := c[i] - 1000; |
Geht so hier:
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.
BenBE hat folgendes geschrieben: | VCL in nem zeitkritischen Abschnitt, speziell FindComponent geht mal gar nicht!!!
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.
BenBE 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
      

Beiträge: 5248
Erhaltene Danke: 2
WIN XP, IE 7, FF 2.0
Delphi 7, Lazarus
|
Verfasst: Sa 01.09.07 16:23
_________________ "Wer Ego-Shooter Killerspiele nennt, muss konsequenterweise jeden Horrorstreifen als Killerfilm bezeichnen." (Zeit.de)
|
|
galagher 
      
Beiträge: 2556
Erhaltene Danke: 45
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Sa 01.09.07 16:27
GTA-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
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Sa 01.09.07 16:43
_________________ 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 
      
Beiträge: 2556
Erhaltene Danke: 45
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Sa 01.09.07 16:57
BenBE 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
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: 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
      

Beiträge: 5248
Erhaltene Danke: 2
WIN XP, IE 7, FF 2.0
Delphi 7, Lazarus
|
Verfasst: Sa 01.09.07 17:47
galagher hat folgendes geschrieben: | GTA-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
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Sa 01.09.07 18:01
Der Evolutionsalgorithmus geht so (vorgegeben ist eine Reihe von Koeffizienten K0, die die Bewertungsfunktion definiert):
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 
      
Beiträge: 2556
Erhaltene Danke: 45
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Sa 01.09.07 18:08
alzaimar 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!
alzaimar 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!
alzaimar 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.
alzaimar 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 alzaimar:
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;
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..7] of Boolean; BlockedCols: array[0..8] of Boolean;
begin 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; p := 0; m := 0; s := 0; d := 0; w := 0; o := 0; a := 0; v := 0; l := 0; c := 0; y := 0; t := 0; no := 0; np := 0; ne := 0; f := 0; g := 0;
_f := 0; _g := 0; _y := 0;
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; if not (opponentRow[j - 1] or 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);
if (aBoard[r1-1, j + 1] = aPlayer) or (aBoard[r1-1, j - 1] = aOpponent) and (aBoard[r, j] = aPlayer) and (aBoard[r1, j] = plEmpty) then inc(o);
if (aBoard[r1-1, j + 1] = aOpponent) or (aBoard[r1-1, j - 1] = aPlayer) and (aBoard[r, j] = aOpponent) and (aBoard[r1, j] = plEmpty) then inc(o);
if (aBoard[r1-1, j + 1] = aPlayer) or (aBoard[r1-1, j - 1] = aPlayer) and (aBoard[r, j] = aPlayer) and (aBoard[r1, j] = plEmpty) then inc(a);
if (aBoard[r1-1, j + 1] = aOpponent) or (aBoard[r1-1, j - 1] = aPlayer) and (aBoard[r, j] = aOpponent) and (aBoard[r1, j] = plEmpty) then inc(o);
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);
if not (aBoard[r, j] = plEmpty) and not (aBoard[r1, j] = plEmpty) then inc(l);
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);
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);
for z := 0 to 7 do begin if (aBoard[z, j] = aPlayer) then inc(_y);
if (aBoard[z, j] = plEmpty) then inc(_f);
if (aBoard[r, z] = plEmpty) then inc(_g); end;
if _y > 1 then inc(y);
if _f = 6 then inc(f);
if _g = 7 then inc(g);
if (aBoard[r-2, j] = aPlayer) and (aBoard[r-2, j+1] = aplayer) and
(aBoard[r-1, j] = plEmpty) and (aBoard[r-1, j+1] = aOpponent) and (aBoard[r-1, j+2] = plEmpty) and
(aBoard[r, j-1] = aOpponent) then inc(t);
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
(aBoard[r, j] = aPlayer) then inc(t);
if (aBoard[r, j+1] = aOpponent) or (aBoard[r, j-1] = aOpponent) then inc(no);
if (aBoard[r, j+1] = aPlayer) or (aBoard[r, j-1] = aPlayer) then inc(np);
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 + 1] then 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 
      
Beiträge: 2556
Erhaltene Danke: 45
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: 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!
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 
      
Beiträge: 2556
Erhaltene Danke: 45
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: 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!  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
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: 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?)
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| ...
if (aBoard[r-2, j] = aPlayer) and (aBoard[r-2, j+1] = aplayer) and |
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
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: 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 
      
Beiträge: 2556
Erhaltene Danke: 45
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Do 06.09.07 17:27
alzaimar 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!
alzaimar 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...
alzaimar 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!
alzaimar 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.
alzaimar 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.
alzaimar hat folgendes geschrieben: |
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| ...
if (aBoard[r-2, j] = aPlayer) and (aBoard[r-2, j+1] = aplayer) and | |
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).
alzaimar 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!
BenBE 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 
      
Beiträge: 2556
Erhaltene Danke: 45
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: 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
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: 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 
      
Beiträge: 2556
Erhaltene Danke: 45
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Mo 10.09.07 17:17
BenBE 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 alzaimar 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 
      
Beiträge: 2556
Erhaltene Danke: 45
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: 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 
      
Beiträge: 2556
Erhaltene Danke: 45
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Sa 15.09.07 21:16
* Schieb * 
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
|