Autor |
Beitrag |
F34r0fTh3D4rk
      
Beiträge: 5284
Erhaltene Danke: 27
Win Vista (32), Win 7 (64)
Eclipse, SciTE, Lazarus
|
Verfasst: Mi 10.05.06 19:58
wenn du ein 3*3 oder 4*4 (nicht sooo viel größer) feld nimmst, dann solltest du mit einer einfachen unterscheidung von sieg und niederlage zum besten ziel gelangen, dann der suchbaum ist dann nicht ganz so groß, da jede figur nur eine bis 3 mögliche züge hat.(wenn überhaupt)
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Mi 10.05.06 20:49
Ok galagher,
Soweit bist du bestimmt schon:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| Const nPlayerCount = 8; Type TPlayer = (plHuman, plComputer); TBoard = Array [0..nPlayerCount -1, 0..nPlayerCount -1] of TPlayer; TChoord = Record cRow, cCol : Integer End; TMove = Record mPlayer : TPlayer; mFrom, mTo : TChoord; End; TMoveList = Array [0..2*nPlayerCount -1] Of TMove; |
Das war einfach. Wir können ja später eine Klasse basteln, aber für den Anfang ist mir das egal.
Wie ist die Anfangsstellung? Alle Bauern auf die Grundlinie? Oder in die 2.Reihe? Egal. Und bewegen und schlagen können sich die Bauern auch wie beim Schach? Gut.
Als Nächstes generieren wir eine Liste aller Züge, die ein Spieler 'aPlayer' auf einem Spieldfeld 'aBoard' ausführen kann.
Delphi-Quelltext 1: 2: 3: 4:
| Procedure CreateAllMoves (aPlayer : TPlayer; aBoard : TBoard; Var aMoves : TMoveList; Var aMoveCount : Integer); Begin ... End; |
So, nun noch eine Funktion, die die aktuelle Stellung bewertet. Hier benötigt man Experten für das jeweilige Spiel. Ich bin Keiner. Du? Na, mal sehen:
1.Erstmal ist es doch besser, wenn ich mehr Figuren habe, als Du.
2.Dann ist es besser, wenn ich mehr Figuren auf der Grundlinie habe, als Du (das ist schon mal die Gewinnbedingung).
3.Dann würde ich noch berücksichtigen, wie viele Züge ich brauchen würde, um alle meine Figuren auf die Grundlinie zu bekommen.
4.Schlecht ist es, wenn ich Figuren habe, die blockiert werden.
5.Dann gilt es noch, eigene Steine zu 'decken', speziell die, die angegriffen werden. Hier werden wieder diverse Koeffizienten, die die einzelnen Umstände bewerten, zum Einsatz kommen.
Ok: Sei N1 die Anzahl meiner Figuren, N2 die Anzahl Deiner Figuren.
Sei G1 die Anzahl meiner Figuren auf der gegnerischen Grundlinie, G2 entsprechende Deine.
Z1 ist die Gesamtanzahl der züge, die ich brauche, um alle meine Figuren auf die Grundlinie zu bekommen, Z2 wieder deine. Eine Figur, die blockiert ist, zählt X zusätzliche Züge (X ist ein KOEFFIZIENT, den wir später auch noch per Evolution optimieren können  )
B1 ist die Anzahl meiner Figuren, die blockiert sind.
D1 ist die Anzahl meiner gedeckten Figuren, A1 die der gedeckten Figuren, die angegriffen werden.
Eine Bewertungsfunktion FB könnte so aussehen:
FB = (Z1-Z2)*A + (G1-G2)*B + (Z1-Z2)*C + (B1-B2)*D + (D1-D2)*E + (A1-A2)*F
A-F sind wieder Koeffizienten, die wir erstmal willkürlich festlegen und später anpassen werden (Viva la Evolucion!). Da (B1-B2) die Gewinnbedingung ist (das primäre Ziel), wählen wir D ziemlich hoch. Den Rest nach Schnautze.
Das Resultat dieser Funktion ist umso höher, je BESSER der Spieler dasteht. Und, die Güte der Funktion ist entscheidend für die Spielstärke. Eine miserable Funktion lässt dein Programm spielen, wie einen rekonvaleszenten Rehpinscher. Die spielen bekanntlich nicht so gut.
Das wars. Beinahe. Wir müssen noch ein Patt und eine Gewinnsituation berücksichtigen (Keine Züge möglich). Das Spiel ist Zuende, wenn beide nicht mehr ziehen können, wenn also auf ein Patt von mir direkt ein Patt von Dir folgt.
Mal sehen:
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:
| FindeBestenZug (Spieler, Gegner, Brett, SpielTiefe, MaxTiefe, BesterZug, Patt) returns Integer; Begin MovePossible := False; CreateAllMoves (Sieler, Brett, M, N); For i:=0 to N-1 do Begin MovePossible := True; Board.DoMove(M[i]); If SpielTiefe = MaxTiefe Then S := Board.BewerteStellung (Spieler); Else S := -FindeBestenZug (Gegner, Spieler, Brett, SpielTiefe + 1, MaxTiefe, DummyZug, False); If S > MaxS Then MaxS := S BesterZug := M End; Board.UndoMove (M[i]); End
if Not MovePossible Then If Patt Then Result := Board.BewerteEndstellung (Spieler) else Result := -FindeBestenZug (Gegner, Spieler, Brett, SpielTiefe + 1, MaxTiefe, DummyZug, True); End |
(Die Variablen deklariere ich nicht, wozu auch, ist ja eh nur Pseudocode)
Fertig (wirklich!, bis auf meine Schussligkeits- und Denkfehler).
Fang mal so an:
1.Schreibe die Funktion, die alle Züge eines Spielers bei einer beliebigen Stellung erzeugt.
2.Schreibe die Funktion, die die Stellung strategisch bewertet.
3.Schreibe die Funktion, die die Stellung abschließend bewertet (Ich habe gewonnen: >+100000, Du hast gewonnen: <-100000).
4.Verwurste alles mit minimax.
5.Ändere Minimax in NegaScout (fast das Gleiche, siehe wikipedia)
6.Du bist ein Profi!
Fragen? Fragen!
_________________ 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: Mi 10.05.06 21:26
alzaimar hat folgendes geschrieben: | Fang mal so an:
1.Schreibe die Funktion, die alle Züge eines Spielers bei einer beliebigen Stellung erzeugt.
2.Schreibe die Funktion, die die Stellung strategisch bewertet.
3.Schreibe die Funktion, die die Stellung abschließend bewertet (Ich habe gewonnen: >+100000, Du hast gewonnen: <-100000).
4.Verwurste alles mit minimax.
5.Ändere Minimax in NegaScout (fast das Gleiche, siehe wikipedia)
6.Du bist ein Profi!
Fragen? Fragen! |
Zunächst einmal danke für deine Hilfe! Ich verstehe im Moment nur Bahnhof, aber ich werde ab morgen Schritt für Schritt anfangen, und, ja, Fragen werde ich sicher haben! Ich muss alles ausser die Oberfläche wegschmeissen, denn zB. die Zugregeln sind in der Prozedur, die die Züge durchführt. Ich stelle mir die Grundstellung mit je 2 Reihen "Figuren" vor, mit insgesamt 30 Feldern, alles Panels - geht das so oder soll ich eine andere Komponente verwenden?
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!
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Do 11.05.06 10:18
Ich würde für den Anfang ein blödes StringGrid nehmen, mit 'X' für deine und 'O' für die Steine des Gegners. Dann das Spiel programmieren, bis es richtig gut ist und zum Schluss eine schöne GUI drum herum basteln. Ich denke, du kannst das mit 4 Bitmaps erledigen (1x weisses Feld, 1x schwarzes Feld, 1x Bauer Weiss, 1x Bauer Schwarz). Das hübsch auf einen Canvas pinseln und fertig. Besser als 25 oder 64 panels jedenfalls.
_________________ 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: Do 11.05.06 19:28
Erst mal vielen Dank für deine Hilfe, aber ich sage gleich, es wird sicher mühsam werden, denn ein Spiel zu programmieren habe ich mir leichter vorgestellt! Aber ich hoffe dennoch, ich kriege es mit deiner Hilfe hin - dafür kommt dein Name dann auch in die About-Box!
alzaimar hat folgendes geschrieben: | Ok galagher,
Soweit bist du bestimmt schon:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| Const nPlayerCount = 8; Type TPlayer = (plHuman, plComputer); TBoard = Array [0..nPlayerCount -1, 0..nPlayerCount -1] of TPlayer; TChoord = Record cRow, cCol : Integer End; TMove = Record mPlayer : TPlayer; mFrom, mTo : TChoord; End; TMoveList = Array [0..2*nPlayerCount -1] Of TMove; | |
Ok, das hab' ich jetzt im Programm, aber es tut noch nichts und ich verstehe es nicht!
alzaimar hat folgendes geschrieben: |
Wie ist die Anfangsstellung? Alle Bauern auf die Grundlinie? Oder in die 2.Reihe? Egal. Und bewegen und schlagen können sich die Bauern auch wie beim Schach? Gut. |
Alle Bauern sind jeweils in der 1. und 2. Reihe, es gibt also 32 Bauern. Kann ich aber noch ändern. Es gibt jetzt wie beim Schach 64 Felder - kann ich aber natürlich auch noch ändern. Die Bauern ziehen wie beim Schach, nur auf die 2 Felder auf einmal beim 1. Zug verzichte ich - und man kann sich selbst schlagen! Die Züge funktionieren schon - ich habe das in einer Unit "Steuerung" in einer eigenen Prozedur "Zug(ACol, ARow: Integer)".
Aufgerufen wird Zug in StringGrid1SelectCell - man kann also schon mit sich selbst spielen.
alzaimar hat folgendes geschrieben: |
Als Nächstes generieren wir eine Liste aller Züge, die ein Spieler 'aPlayer' auf einem Spieldfeld 'aBoard' ausführen kann.
Delphi-Quelltext 1: 2: 3: 4:
| Procedure CreateAllMoves (aPlayer : TPlayer; aBoard : TBoard; Var aMoves : TMoveList; Var aMoveCount : Integer); Begin ... End; | |
Und schon wieder verstehe ich etwas nicht. Was ist TPlayer und TBoard, wenn es doch mit x und o auf einem StringGrid gemacht wird? Was soll ich da wie machen und wozu?
Weiter fragen kommt dann später! 
_________________ 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 11.05.06 21:42
Also:
Ein TPlayer ist der Wert, den ein Feld auf dem Brett annehmen kann. Dabei habe ich natürlich vergessen, das ein Brett auch leer sein kann.
Ich würde das StringGrid nur zur Darstellung verwenden, als interne Datenstruktur würde ich ein Array [0..7,0..7] of TPlayer oder ein Array [0..63] of TPlayer verwenden. Letzeres ist etwas schneller, aber nicht so trivial.
_________________ 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: Fr 12.05.06 18:09
alzaimar hat folgendes geschrieben: | Ich würde das StringGrid nur zur Darstellung verwenden, als interne Datenstruktur würde ich ein Array [0..7,0..7] of TPlayer oder ein Array [0..63] of TPlayer verwenden. Letzeres ist etwas schneller, aber nicht so trivial. |
Mir wäre es lieber, wenn wir einfach beim StringGrid bleiben könnten. Dann ist der Code zwar fix auf ein StringGrid festgelegt, aber ich glaube, so wird es einfacher.
Ich bin gerade dabei, eine Bewertungsfunktion zu schreiben; die Rückgabe ist ein Integerwert, und ich werde versuchen, die von dir aufgelisteten Kriterien zu berücksichtigen, aber:
alzaimar hat folgendes geschrieben: |
3.Dann würde ich noch berücksichtigen, wie viele Züge ich brauchen würde, um alle meine Figuren auf die Grundlinie zu bekommen.
4.Schlecht ist es, wenn ich Figuren habe, die blockiert werden.
5.Dann gilt es noch, eigene Steine zu 'decken', speziell die, die angegriffen werden. Hier werden wieder diverse Koeffizienten, die die einzelnen Umstände bewerten, zum Einsatz kommen.
Ok: Sei N1 die Anzahl meiner Figuren, N2 die Anzahl Deiner Figuren.
Sei G1 die Anzahl meiner Figuren auf der gegnerischen Grundlinie, G2 entsprechende Deine.
Z1 ist die Gesamtanzahl der züge, die ich brauche, um alle meine Figuren auf die Grundlinie zu bekommen, Z2 wieder deine. Eine Figur, die blockiert ist, zählt X zusätzliche Züge (X ist ein KOEFFIZIENT, den wir später auch noch per Evolution optimieren können )
B1 ist die Anzahl meiner Figuren, die blockiert sind.
D1 ist die Anzahl meiner gedeckten Figuren, A1 die der gedeckten Figuren, die angegriffen werden.
Eine Bewertungsfunktion FB könnte so aussehen:
FB = (Z1-Z2)*A + (G1-G2)*B + (Z1-Z2)*C + (B1-B2)*D + (D1-D2)*E + (A1-A2)*F
A-F sind wieder Koeffizienten, die wir erstmal willkürlich festlegen und später anpassen werden (Viva la Evolucion!). Da (B1-B2) die Gewinnbedingung ist (das primäre Ziel), wählen wir D ziemlich hoch. Den Rest nach Schnautze.
Das Resultat dieser Funktion ist umso höher, je BESSER der Spieler dasteht. Und, die Güte der Funktion ist entscheidend für die Spielstärke. Eine miserable Funktion lässt dein Programm spielen, wie einen rekonvaleszenten Rehpinscher. Die spielen bekanntlich nicht so gut. |
Das bekomme ich nicht hin. Ich kann Felder und deren "Figur" und die Felder rundherum bewerten, kann prüfen, ob und wieviele Figuren auf den Grundlinien stehen und ob Figuren blockiert werden. Aber zB. berechnen, wie viele Züge zum Sieg fehlen - das ist von mehreren Faktoren abhängig: Wie viele Figuren hat der Spieler, wie viele der Computer auf der Grundlinie. Wer hat die bessere Stellung, wie schnell (in wie vielen Zügen, und welche Züge!) können dies ändern, und ...
Das wird also ein rekonvaleszenter Rehpinscher, weil ich keine Ahnung habe, wie ich eine solche komplexe Funktion schreiben soll. Ich habe mir den Minimax-Algo einfacher vorgestellt - ich meine, man nimmt ihn, setzt ihn ein und das ist es dann! Naja, nicht ganz so naiv, aber eben einfacher. Etwa so, wie man lernt, eine CheckListBox, ein RichEdit oder SynEdit zu benutzen.
Ich meine, das mit den Zügen im StringGrid habe ich ja noch hinbekommen - mit dem StringGrid kann man jetzt ein Bauernspiel spielen. Die Zugregeln werden eingehalten, aber ein solches Spiel zu schreiben übersteigt insgesamt mein Können. Das ist kein "normales" Programm!
Wenn du meinst, es reicht, eine Bewertung zu schreiben, die das kann, was ich erwähnt habe, dann mache ich es. Wenn du meinst, dass das dann der rekonvaleszente Rehpinscher wird, dann lieber kein Programm als ein schlechtes!
Wenn du noch Chancen für das Programm siehst, und mir noch weiter helfen möchtest - aber mehr kann ich nicht! Ich habe noch nie ein "intelligentes" Spiel geschrieben.
_________________ 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: Fr 12.05.06 19:26
Die Bewertungsfunktion kann doch erstmal trivial sein. Mach einfach, soviel Du schaffst. Am aller Wichtigsten ist es doch, eine Gewinnstellung zu erkennen. Die Anzahl der Züge soll man natürlich nicht durchprobieren, sondern abschätzen. Eine andere Möglichkeit ist der Abstand der Figuren zum Ziel.
Eine Funktion, die eine Stellung bewertet, ist in der Tag etwas Schwierig. Bei einigen Spielen ist es einfach (4 gewinnt oder Reversi), bei anderen hingegen schwer (Dame, Backgammon, Schach, Go(!)). Wenn Du nur ein Spiel *haben* willst, dann lass es. Wenn Du aber ein Spiel *programmieren* willst, dann beiß dich durch. Muste ich doch auch machen. Außerdem ist noch kein Meister vom Himmel gefallen.
Wenn Du den Suchbaum komplett durchrechnen kannst (schneller Rechner, guter Algorithmus, nicht so komplexes Spiel), dann ist die Bewertungsfunktion sehr einfach: +1 für 'Spieler gewinnt', -1 für 'Gegner gewinnt'.
Wenn man nicht bis zum Schluss durchrechnen kann (z.B. Schach), dann benötigt man eine solche Funktion. Die muss verschiede Kriterien erfüllen:
1. Sie muss schnell sein.
2. Das Resultat muß die strategischen Vor- und Nachteile wiederspiegeln.
(1) vergessen wir mal. Bei (2) kommt der Appetit mit dem Essen. Wenn Du erstmal die einfachen Dinge ermittelt hast, dann kommen peu-a-peu weitere hinzu.
Zu deinem Stringgrid sag ich so viel: NEE! Nimm ein Array [0..7,0..7] Of Char, die Spieler sind 'X', 'O' und ' '. Du ziehst in dem Array und rufst nach jedem Zug einfach eine Routine auf, die dein Spielfeld visualisiert. Und DAFÜR nimmst Du das Stringgrid.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| Procedure TMyForm.Showboard (aBoard : TBoard); Var i,j : Integer;
Begin For i:=0 To 7 do For j:=0 to 7 do MyStringGrid.Cells[i,j] := aBoard[i,j]; End; |
Das ist doch nicht so schwer, oder?
Aber was Grundsätzliches zum Schluss. Ein Spiel zu programmieren, das nicht bescheuert spielt, ist natürlich nicht so einfach. Man muss abstrahieren können und Rekursivität beherrschen. Ohne das wirds Nichts. Meine ersten Spielprogramme (Dame und Reversi) waren unglaublich aufgebläht, verdammt langsam und - spielten schlecht. Na und? Ich habe weiter gemacht und weiter gemacht und irgendwann hatte ich ein Backgammon (Reversi wurde langweilig) Programm, das nicht schlecht spielte. Zumindest hatte es eine Eröffnungsbibliothek, hielt sich an die gängigen Tricks und schlug sich achtbar.
Wenn Du so ein Spiel mit Minimax, Stellungsfunktion etc. mal richtig programmiert hast, hast Du mehr gelernt als in 2 Semestern IT-Studium!
Aber wenn Du dir das nicht zutraust, dann über erstmal einfachere Programme und versuche es später einfach nochmal. Ich brauch manchmal auch ettliche Anläufe, bis ich ein für mich komplexes Programm richtig zum Laufen bekomme. Aber ohne erfolglose Versuche, Rückschläge, ausgerissene Haare, suizidales Kopf-auf-den-Schreibtisch-knallen, verzeifeltes "Maaamaaaaaa" schreien usw. hätte ich es nie geschafft.
Blöder Spruch zum Schluss: "Nimm dir immer eine Aufgabe vor, die dir etwas zu komplex erscheint"!
_________________ 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: Fr 12.05.06 21:25
Dann mache ich mich morgen mal an die Bewertung! Mal sehen, was das dann wird. Wenn möglich, versuche ich auch gleich, den Computer spielen zu lassen, wobei wird schon beim Array wären:
alzaimar hat folgendes geschrieben: |
Zu deinem Stringgrid sag ich so viel: NEE! Nimm ein Array [0..7,0..7] Of Char, die Spieler sind 'X', 'O' und ' '. Du ziehst in dem Array und rufst nach jedem Zug einfach eine Routine auf, die dein Spielfeld visualisiert. Und DAFÜR nimmst Du das Stringgrid.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| Procedure TMyForm.Showboard (aBoard : TBoard); Var i,j : Integer;
Begin For i:=0 To 7 do For j:=0 to 7 do MyStringGrid.Cells[i,j] := aBoard[i,j]; End; |
Das ist doch nicht so schwer, oder? |
Ich werde versuchen, das umzusetzen.
alzaimar hat folgendes geschrieben: |
Aber wenn Du dir das nicht zutraust, dann über erstmal einfachere Programme und versuche es später einfach nochmal. |
Zutrauen schon, nur fürchte ich, es wird eine halbe Sache - ein Programm, das nicht sinnvoll spielen kann. Das ist Mist. Schade um den Speicher, den es belegt!
Aber wie gesagt - mit deiner Hilfe versuche ich es schon!
//Edit: Verstehe ich das mit dem Array richtig: Der Spieler setzt seinen Zug direkt am StringGrid, das Array kennt die Stellung, berechnet wird mittels Array, der Computerzug erfolgt wie in deinem Beispiel wiederum im StringGrid?
_________________ 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 13.05.06 06:29
alzaimar hat folgendes geschrieben: |
Zu deinem Stringgrid sag ich so viel: NEE! Nimm ein Array [0..7,0..7] Of Char, die Spieler sind 'X', 'O' und ' '. Du ziehst in dem Array und rufst nach jedem Zug einfach eine Routine auf, die dein Spielfeld visualisiert. Und DAFÜR nimmst Du das Stringgrid. |
Ich kriege die Umsetzung des Arrays einfach nicht hin. Wenn ich im Array ziehen kann, dann ist es also ein Abbild des Spielfeldes, oder? Und warum also dann nicht gleich das Spielfeld, warum erst dieser Umweg und dann die Ausgabe auf dem StringGrid?
Wenn du mir da mal auf die Sprünge helfen könntest? Konkret: Man klickt auf das StringGrid, und dann passiert - was? Im Moment sieht das bei mir so aus:
Delphi-Quelltext 1: 2: 3: 4: 5:
| procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean); begin Zug(ACol, ARow); end; |
_________________ 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 13.05.06 08:31
Mojn,
Weil das StringGrid zu langsam ist, deshalb. Aber der Hauptgrund ist das 'Softwaredesign'. Man trennt nunmal die Visualisierung von der Funktion. Ok, wenn du mal eben ein kleines Progrämmchen hinfrickelst, dann natürlich nicht. Aber hier würde ich es schon machen. Wenn Du nachher ein schickes Schachbrett mit tollen Bauernfiguren verwenden willst, musst du dann nur ein paar Routinen ändern, und ziehst nicht dieses Pferdefuß von TStringGird mit.
_________________ Na denn, dann. Bis dann, denn.
|
|
F34r0fTh3D4rk
      
Beiträge: 5284
Erhaltene Danke: 27
Win Vista (32), Win 7 (64)
Eclipse, SciTE, Lazarus
|
Verfasst: Sa 13.05.06 08:58
über die visualisierung mache ich mir auch erst später gedanken, bzw entwickle das parallel, man sollte das möglichst trennen und dann eine zeichenfunktion bauen, die man dann ja anpassen kann.
|
|
galagher 
      
Beiträge: 2556
Erhaltene Danke: 45
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Sa 13.05.06 09:52
Also:
Ich hab das Ganze jetzt in einem Array und der Prozedur ist es jetzt egal, ob es von einem StringGrid oder sonstwas kommt:
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:
| var TBoard: Array [0..7, 0..7] of String[1]; [...]
procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean); var B, N: Byte; begin for B := 0 to StringGrid1.ColCount-1 do begin for N := 1 to StringGrid1.RowCount-1 do TBoard[B, N] := StringGrid1.Cells[B, N]; end;
Zug(ACol, ARow);
for B := 0 to 7 do begin for N := 1 to 7 do StringGrid1.Cells[B, N] := TBoard[B, N]; end; |
Es geht sicher eleganter, als immer alles neu zu zeichnen, aber erstmal ist es das, was du meinst, oder?
//Edit: geändert:
Delphi-Quelltext 1: 2: 3: 4: 5:
| const nPlayerCount = 8;
var TBoard: array [0..nPlayerCount-1, 0..nPlayerCount-1] of String[1]; |
_________________ 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 15.05.06 18:45
Hallo!
Ich habe jetzt eine Bewertungsfunktion
Delphi-Quelltext 1:
| function Bewertung(Player: TPlayer): Integer; |
die folgende Überprüfungen durchführt:
1. Zählen, wie viele eigene Figuren auf der gegnerischen Grundlinie sind - Inc(Result)
2. Zählen, wer mehr Figuren hat - eigene: Inc(Result), sonst Dec(Result)
3. Prüfen, ob sich der Spieler/Computer selbst blockiert;
das ist anfangs immer so (je 2 Reihen Figuren) - Dec(Result)
4. Prüfen, ob der Spieler/Computer eine oder zwei gegnerische Figuren schlagen könnte - je Inc(Result)
Ok, einfach, aber es ist eine Bewertung. Du hast ein Beispiel gebracht:
alzaimar hat folgendes geschrieben: | Eine Bewertungsfunktion FB könnte so aussehen:
FB = (Z1-Z2)*A + (G1-G2)*B + (Z1-Z2)*C + (B1-B2)*D + (D1-D2)*E + (A1-A2)*F
A-F sind wieder Koeffizienten, die wir erstmal willkürlich festlegen und später anpassen werden (Viva la Evolucion!). Da (B1-B2) die Gewinnbedingung ist (das primäre Ziel), wählen wir D ziemlich hoch. Den Rest nach Schnautze. |
Da sind Welten dazwischen...
Nun ja:
alzaimar hat folgendes geschrieben: | Fragen? Fragen! |
Das tue ich hiermit:
@alzaimar: Wenn ich nicht mit lauter IF arbeiten will (will ich definitiv nicht!), brauche ich jetzt einen sinnvollen Algorithmus, der die Bewertung verarbeitet und dann den Zug setzt. (Und eine bessere, genauere Bewertung). Nun, das kann ich einfach nicht (alleine).
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
F34r0fTh3D4rk
      
Beiträge: 5284
Erhaltene Danke: 27
Win Vista (32), Win 7 (64)
Eclipse, SciTE, Lazarus
|
Verfasst: Mo 15.05.06 18:50
minimax, davon sprechen wir doch die ganze zeit, der code steht hier doch auch schon mehrfach drin 
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Mo 15.05.06 22:51
galagher hat folgendes geschrieben: | Hallo! |
Hallo zurück
galagher hat folgendes geschrieben: | Ich habe jetzt eine Bewertungsfunktion
Delphi-Quelltext 1:
| function Bewertung(Player: TPlayer): Integer; |
die folgende Überprüfungen durchführt:
1. Zählen, wie viele eigene Figuren auf der gegnerischen Grundlinie sind - Inc(Result)
2. Zählen, wer mehr Figuren hat - eigene: Inc(Result), sonst Dec(Result)
3. Prüfen, ob sich der Spieler/Computer selbst blockiert;
das ist anfangs immer so (je 2 Reihen Figuren) - Dec(Result)
4. Prüfen, ob der Spieler/Computer eine oder zwei gegnerische Figuren schlagen könnte - je Inc(Result)
Ok, einfach, aber es ist eine Bewertung. Du hast ein Beispiel gebracht:
alzaimar hat folgendes geschrieben: | Eine Bewertungsfunktion FB könnte so aussehen:
FB = (Z1-Z2)*A + (G1-G2)*B + (Z1-Z2)*C + (B1-B2)*D + (D1-D2)*E + (A1-A2)*F
A-F sind wieder Koeffizienten, die wir erstmal willkürlich festlegen und später anpassen werden (Viva la Evolucion!). Da (B1-B2) die Gewinnbedingung ist (das primäre Ziel), wählen wir D ziemlich hoch. Den Rest nach Schnautze. |
Da sind Welten dazwischen...
|
Nein, nur ein paar Koeffizienten. Du machst fast genau das, was ich vorschlug. Aber: Die Anzahl der eigenen Figuren ist doch nicht so wichtig wie, sagen wir, die Anzahl der Steine, die in der 7.Reihe sind (denn die sind potentielle Gewinnsteine!). Ebenso ist es wichtiger, Steine zu haben die Andere schlagen können oder Steine, die gedeckt sind.
Du solltest nicht einfach Inc/Dec Result machen, denn dann ist alles gleich wichtig, isses aber nicht. Du solltest viel mehr die einzelnen Eigenschaften der Stellung (Anzahl der Steine, wie viele sind geblockt etc. ) in eigenen Variablen zunächst zählen und dann mit den unterschiedlichen Gewichten multiplizieren. Beim Schach ist es doch so, das ein Turm 3x so viel 'Wert' ist wie ein Bauer (oder?). Also ist die Stellungsfunktion schon mal so:
Quelltext
Du bist wirklich schon sehr weit mit deiner Bewertungsfunktion. Jetzt fehlen nur noch die Gewichtungen, also das, was ich 'Koeffizienten' nenne. Um wieviel wichtiger nun die Anzahl der nicht geblockten Steine gegenüber der Anzahl der Schlagmöglichkeiten ist, weiss ich auch nicht. Das ist ja gerade die Kunst! Aber du kannst erstmal mit groben Werten experimentieren, das ist jetzt auch nicht so wichtig, denn die Koeffizienten lässt man sich später per evolutionärem Verfahren einfach -schwupps- mal eben vom Programm selbst optimieren! Das ist total einfach und unglaublich verblüffend: Du bekommst ein lernendes System, das sich -geschickt programmiert- sogar dem jeweiligen Gegner anpasst und lernt! Das ist dann mal eine echte KI!
Also: Eigenschaften getrennt zählen, jeweils mit unterschiedlichen Faktoren multiplizieren und zum Schluss addieren
Denk immer dran, das die Bewertungsfunktion nur aus Sicht des übergebenen Players zählt. Wenn der "Player" besser dasteht, ist das Ergebnis positiv, sonst negativ.
_________________ 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: Di 16.05.06 19:19
alzaimar hat folgendes geschrieben: |
Nein, nur ein paar Koeffizienten. Du machst fast genau das, was ich vorschlug. Aber: Die Anzahl der eigenen Figuren ist doch nicht so wichtig wie, sagen wir, die Anzahl der Steine, die in der 7.Reihe sind (denn die sind potentielle Gewinnsteine!). Ebenso ist es wichtiger, Steine zu haben die Andere schlagen können oder Steine, die gedeckt sind. |
Ok, ist jetzt berücksichtigt.
alzaimar hat folgendes geschrieben: |
Du solltest nicht einfach Inc/Dec Result machen, denn dann ist alles gleich wichtig, isses aber nicht. Du solltest viel mehr die einzelnen Eigenschaften der Stellung (Anzahl der Steine, wie viele sind geblockt etc. ) in eigenen Variablen zunächst zählen und dann mit den unterschiedlichen Gewichten multiplizieren. Beim Schach ist es doch so, das ein Turm 3x so viel 'Wert' ist wie ein Bauer (oder?). Also ist die Stellungsfunktion schon mal so:
Quelltext |
Mit den Gewichten meinst du die Wertigkeit (höher oder geringer) einer Eigenschaft, zB. "Figur ist einfach gedeckt"? ZB. könnte man einer einfach gedeckten Figur die Wertigkeit 2 zuordnen, während einer Figur auf der gegnerischen Grundlinie ein viel höherer Wert zukommt, zB 10. Meinst du das so? Die Formel wäre demnach für 3 einfach gedeckte Figuren, 1 doppelt gedeckten Figur und 2 auf der Gegner-Grundlinie 3*2, 1*4, 2*10
Zum Schluss die einzelnen Ergebnisse addieren. Oder verstehe ich das falsch? Und wie bestimmt man die Werte richtig? Ist 2 relativ zu 10 wirklich korrekt, oder ist es nicht doch eher 3 ? Also, da raucht einem ja der Kopf!
alzaimar hat folgendes geschrieben: |
Du bist wirklich schon sehr weit mit deiner Bewertungsfunktion. Jetzt fehlen nur noch die Gewichtungen, also das, was ich 'Koeffizienten' nenne. Um wieviel wichtiger nun die Anzahl der nicht geblockten Steine gegenüber der Anzahl der Schlagmöglichkeiten ist, weiss ich auch nicht. Das ist ja gerade die Kunst! Aber du kannst erstmal mit groben Werten experimentieren, das ist jetzt auch nicht so wichtig, denn die Koeffizienten lässt man sich später per evolutionärem Verfahren einfach -schwupps- mal eben vom Programm selbst optimieren! Das ist total einfach und unglaublich verblüffend: Du bekommst ein lernendes System, das sich -geschickt programmiert- sogar dem jeweiligen Gegner anpasst und lernt! Das ist dann mal eine echte KI! |
Also hier steig ich erstmal aus und später wieder ein, wenn die Bewertung steht!
alzaimar hat folgendes geschrieben: |
Also: Eigenschaften getrennt zählen, jeweils mit unterschiedlichen Faktoren multiplizieren und zum Schluss addieren |
Stimmt demnach mein Beispiel oben mit 3*2, 1*4, 2*10 ?
_________________ 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: Di 16.05.06 20:08
Langer Beitrag, kurze Antwort: Ja, Alles richtig.
Und die Gewichtung ist wirklich eine Sache des Ausprobierens. Wir fangen erstmal mit linearen Gewichtungen an: Zwei gedeckte Figuren sind doppelt so gut, wie eine gedeckte Figur. Es kann aber sein, das das so nicht stimmt: Dann sind z.B. zwei gedeckte Figuren 3x so gut, wie Eine... Das sind aber alles Dinge, um die man sich später kümmern kann. Jetzt erstmal die Bewertungsfunktion sauber und einigermaßen schnell hinbekommen.
Danach bauen wir noch die Routine, die die Züge generiert und dann.... ja dann sind wir fertig
[edit] Und wenn Du dann noch Lust hast, lassen wir uns die Bewertungsfunktion vom Computer optimieren  [/edit]
_________________ 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: Di 16.05.06 21:11
_________________ 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 17.05.06 17:32
Hallo!
Ich habe jetzt soweit die Bewertung und die Werte der Eigenschaften fertig:
Wie viele Figuren sind insgesamt im Spiel: 1
Wie viele eigene Figuren sind im Spiel: 1
Wie viele eigene Figuren auf der Gegner-Grundlinie: 10
Wie viele eigene Figuren auf der Linie vor der Gegner-Grundlinie: 8
Wer hat mehr Figuren: 3
Ist eine eigene Figur einfach gedeckt: 2
Ist eine eigene Figur doppelt gedeckt: 4
Blockiert sich eine eigene Figur selbst: -4
Kann eine gegnerische Figur geschlagen werden: 5
Die Werte sind erstmal willkürlich und ich bin gespannt, wie man sie korrigiert und optimiert! Diese Bewertungen werden jeweils nur für den angegebenen Spieler vorgenommen - die Funktion erwartet plHuman oder plComputer; der Rückgabewert ist:
Delphi-Quelltext 1: 2: 3:
| Result := gesamt + eigene + auf_GegnerGrund + auf_vor_GegnerGrund + mehr_Spieler + mehr_Computer + einfach_gedeckt + doppelt_gedeckt + blockiert + kann_schlagen; |
//Edit: Nach den Multiplikationen (zB. gesamt := gesamt * gesamt_Value) natürlich!
Ich hoffe, ich habe nichts Wichtiges in der Bewertung vergessen! Jetzt brauche ich noch eine Prozedur, die auf Basis der Bewertung einen Zug macht.
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
|