Entwickler-Ecke

Algorithmen, Optimierung und Assembler - Schachprogramm: Logik-Part


Hidden - Do 10.04.08 22:26
Titel: Schachprogramm: Logik-Part
Hi,

Ich habe eine Unit geschrieben, die den logischen Teil eines Schachprogramms übernehmen soll. Erstmal sollen nur Züge validitätsgeprüft werden, Stellungsbewertungen kommen eventuell später hinzu. Wenn sie fertig ist, soll die Unit in die Open-Source-Units-Sparte, daher wäre ich dankbar für eine generelle Bewertung von Konzept und(wenn dort etwas nicht stimmen sollte) auch Programmierstil.

Zur Indexierung: Ich habe mich entschlossen, mit Indizes, bezogen auf Arrays(Figuren, Felder), zu arbeiten; ich hätte zwischendurch auch(implizite) Pointer verwenden können, wodurch der Code teilweise leichter zu verstehen gewesen wäre(

Delphi-Quelltext
1:
qFiguren[eineFigur].doSomeThing or eineFigur.doSomeThing                    

). Allerdings wollte ich das Indizekonzept wenn, dann durchgängig, verwenden und Pointer auf Figuren in lokalen Array-Variablen(->Stellungsanalyse) wären nach der Methode invalid gewesen. Mit Indizes kann ich auch später noch arbeiten und z.B. dieselbe Figur zu einem anderen Zeitpunkt betrachten.


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:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332:
333:
334:
unit logik_Schach;

interface

const
  Bauer = 0;
  Springer = 1;
  Laeufer = 2;
  Turm = 3;
  Dame = 4;
  Koenig = 5;
  atNil = 32;  //Indexrepresentative für nil
  Weiss = 0;
  Schwarz = 1;

type
  FigurIndex = 0..atNil;  //Nummerierung der Figuren
  TypIndex = Bauer..atNil;  //Bauer, Springer, etc.
  FeldIndex = -7..atNil;  //Nummerierung der Felder

type
  TFeld = record
    X, Y: FeldIndex;
  end;
  TFarbe = Weiss..Schwarz;

function cFeld(const X, Y: FeldIndex): TFeld;

type
  TFigur = record
    Typ: TypIndex;  //Bauer, Springer, etc.
    Farbe: TFarbe;
    Feld: TFeld;
  end;

type
  TFigurArray = array[0..31of TFigur;
  TFeldArray = array[0..70..7of FigurIndex;
  TStellung = record  //nur Rückgabewert für Stellungsanalysen
    Brett: TFeldArray;
    Figuren: TFigurArray;
  end;

procedure NeuePartie(var qFiguren: TFigurArray; var qFelder: TFeldArray);
function Zug(const qFelder: TFeldArray; const qFiguren: TFigurArray;
             const startFeld, zielFeld: TFeld): TStellung;
procedure Ziehe(var qFelder: TFeldArray; var qFiguren: TFigurArray;
                const startFeld, zielFeld: TFeld);
function VersucheZug(var qFelder: TFeldArray; var qFiguren: TFigurArray;
                     var qFigur: FigurIndex; zielFeld: TFeld): Boolean;
function gibKoenig(qFarbe: TFarbe): FigurIndex;

function ZugFeld(const qFigur: TFigur; const qFeld: TFeld;
           const qFiguren: TFigurArray; const qFelder: TFeldArray): Boolean;
function Bedroht(const qFelder: TFeldArray; const qFiguren: TFigurArray;
                const qFigur: TFigur): Boolean;
function AbzugsSchach(const qFiguren: TFigurArray; const qFelder: TFeldArray;
                      const qAbgzogeneFigur: TFigur): Boolean;

implementation

uses
  Types, Math;
                                                                   
function cFeld(const X, Y: FeldIndex): TFeld;
begin
  result.X := X;
  result.Y := Y;
end;

procedure NeuePartie(var qFiguren: TFigurArray; var qFelder: TFeldArray);
  procedure FigurAufFeld(const qFigur: FigurIndex; const qTyp: TypIndex;
                         const qFarbe: TFarbe; const qFeld: TFeld);
  begin
    with qFiguren[qFigur] do begin
      Typ := qTyp;
      Farbe := qFarbe;
      Feld := qFeld;
    end;
    qFelder[qFeld.X, qFeld.Y] := qFigur;
  end;

var
  i, j: FeldIndex;
const
  Kompl: Array[0..7of TypIndex =
    (Turm, Springer, Laeufer, Dame, Koenig, Laeufer, Springer, Turm);
begin
  for i := 0 to 7 do begin
    for j := 2 to 5 do
      qFelder[i][j] := atNil;  // leeres Feld
    FigurAufFeld(i,      Bauer,    Weiss,   cFeld(i, 6));
    FigurAufFeld(i + 16, Bauer,    Schwarz, cFeld(i, 1));
    FigurAufFeld(i + 8,  Kompl[i], Weiss,   cFeld(i, 7));
    FigurAufFeld(i + 24, Kompl[i], Schwarz, cFeld(i, 0));
  end;
end;

function Zug(const qFelder: TFeldArray; const qFiguren: TFigurArray;
             const startFeld, zielFeld: TFeld): TStellung;
var
  i, j: FeldIndex;
begin
  with result do begin
    for i := 0 to 31 do
      Figuren[i] := qFiguren[i];  //Figuren kopieren
    for i := 0 to 7 do
      for j := 0 to 7 do
        Brett[i][j] := qFelder[i][j];  //Stellung kopieren
    Figuren[Brett[startFeld.X, startFeld.Y]].Feld := zielFeld;
    if Brett[zielFeld.X, zielFeld.Y] <> atNil then
      Figuren[Brett[zielFeld.X, zielFeld.Y]].Typ := atNil;  //geschlagen
    Brett[zielFeld.X, zielFeld.Y] := Brett[startFeld.X, startFeld.Y];
    Brett[startFeld.X, startFeld.Y] := atNil;
  end;
end;

procedure Ziehe(var qFelder: TFeldArray; var qFiguren: TFigurArray;
                const startFeld, zielFeld: TFeld);
begin
  qFiguren[qFelder[startFeld.X, startFeld.Y]].Feld := zielFeld;
  if qFelder[zielFeld.X, zielFeld.Y] <> atNil then
    qFiguren[qFelder[zielFeld.X, zielFeld.Y]].Typ := atNil;  //geschlagen
  qFelder[zielFeld.X, zielFeld.Y] := qFelder[startFeld.X, startFeld.Y];
  qFelder[startFeld.X, startFeld.Y] := atNil;
end;

function VersucheZug(var qFelder: TFeldArray; var qFiguren: TFigurArray;
                     var qFigur: FigurIndex; zielFeld: TFeld): Boolean;
begin
  result := qFigur <> atNil;
  if result then begin
    result := ZugFeld(qFiguren[qFigur], zielFeld, qFiguren, qFelder);
    if result then
      Ziehe(qFelder, qFiguren, qFiguren[qFigur].Feld, zielFeld);
  end;
end;

function gibKoenig(qFarbe: TFarbe): FigurIndex;
begin
  if qFarbe = Weiss then
    result := 12
  else result := 28;
end;

function ZugFeld(const qFigur: TFigur; const qFeld: TFeld;
           const qFiguren: TFigurArray; const qFelder: TFeldArray): Boolean;
var
  tempFigur: FigurIndex;
begin
  result := false;
  tempFigur := qFelder[qFeld.X, qFeld.Y];  //eventuell geschlagene Figur
  with qFigur do begin
    if tempFigur <> atNil{nil} then
      if qFiguren[tempFigur].Farbe = Farbe then
        Exit;  //man kann keine eigenen Figuren schlagen
    case Typ of  //Liegt das Zielfeld im Zugmuster der Figur?
      {$REGION 'Bauer:'}
      Bauer: begin
        if Abs(Feld.X - qFeld.X) < 2 then begin  //Startfeld.X - Zielfeld.X
          //Ansonsten weicht Zielfeld.X um mehr als 1 von Startfeld.X ab!
          if qFeld.Y = Feld.Y + IfThen(Farbe = Weiss, -11then begin
            if Feld.X = qFeld.X then  //normaler Zug geradeaus
              result := tempFigur = atNil  //Feld ist leer
            else result := tempFigur <> atNil;
          end else begin  //Wenn Zug um mehr als 1 Feld
            result := (Feld.X = qFeld.X) and (tempFigur = atNil);
            if result then
              if Farbe = Weiss then
                result := (Feld.Y = 6and (qFeld.Y = 4)  //Doppelzug am Anfang
              else result := (Feld.Y = 1and (qFeld.Y = 3);
          end;
        end;
      end;
      {$ENDREGION}
      Springer: result := Abs((Feld.X - qFeld.X) * (Feld.Y - qFeld.Y)) = 2;
      Laeufer:  //        DeltaY        =         DeltaX
        result := Abs(qFeld.Y - Feld.Y) = Abs(qFeld.X - Feld.X);  
      Turm: result := (qFeld.X = Feld.X) or (qFeld.Y = Feld.Y);
      Dame: result := (qFeld.X = Feld.X) or (qFeld.Y = Feld.Y) or  //Turm
                      (Abs(qFeld.Y - Feld.Y) = Abs(qFeld.X - Feld.X));  //Läufer
      Koenig: result := (Abs(qFeld.X - Feld.X) < 2and
                        (Abs(qFeld.Y - Feld.Y) < 2);
    end;
  end;
  //result := result and not Bedroht(qFelder, qFiguren,
  //                           qFiguren[IfThen(qFigur.Farbe = Weiss, 12, 28)]);
  //result := result and not AbzugsSchach(qFiguren, qFelder, qFigur);
end;

function Bedroht(const qFelder: TFeldArray; const qFiguren: TFigurArray;
                const qFigur: TFigur): Boolean;
var
  i: Byte;
  j, k: FeldIndex;  //j = X; k = Y
  LaufVar: ^FeldIndex;
  incJ, incK: Boolean;
  tempFigur: FigurIndex;
const
  Linear = [Turm, Dame];  //Gefahrenquellen
  Diagonal = [Laeufer, Dame];
  SpringerFeld: Array[0..9of FeldIndex =  //i = 0..7 [i, i + 2] erfasst
    ( 2,  1, -1, -2, -2, -11221);   //alle Springerfelder
begin
  result := false;
  with qFigur do begin
    {$REGION 'Springer'}
      for i := 0 to 7 do begin
        j := Feld.X + SpringerFeld[i];
        k := Feld.Y + SpringerFeld[i + 2];
        if (j in [0..7]) and (k in [0..7]) then begin
          tempFigur := qFelder[j][k];
          if tempFigur <> atNil then begin
            if (qFiguren[tempFigur].Typ = Springer) and
               (qFiguren[tempFigur].Farbe <> Farbe) then
            begin
              result := true;  //Schach durch Springer
              Exit;
            end;
          end;
        end;
      end;
    {$ENDREGION}
    {$REGION 'Bauer'}
      k := Feld.Y + 2 * Farbe - 1;  //Y eines pot. schachgebenden Bauers
      if k in [0..7then begin
        tempFigur := qFelder[Feld.X, k];
        if tempFigur <> atNil then
          if qFiguren[tempFigur].Typ = Bauer then begin
            result := true;  //Schach durch Bauern
            Exit;
          end;
      end;
    {$ENDREGION}
    {$REGION 'König'}
      tempFigur := 12 * (2 - Farbe);  //gegnerischer König
      if Abs((Feld.X - qFiguren[tempFigur].Feld.X) *
             (Feld.Y - qFiguren[tempFigur].Feld.Y)) = 1 then
      begin
        result := true;  //Schach durch gegnerischen König
        Exit;
      end;
    {$ENDREGION}
    //In alle Richtungen nach Schachgebern suchen
    {$REGION 'Strahlenförmige Suche vom König weg'}
      for i := 0 to 3 do begin
      {$REGION 'Lineare Richtungen prüfen'}
        j := Feld.X;
        k := Feld.Y;
        if i < 2 then LaufVar := @j  //Die ersten zwei zählen X,
        else LaufVar := @k;  //Die letzten zwei Y-Richtung

        incJ := i mod 2 = 0;  //Jede zweite zählt nach unten
        if incJ then Inc(LaufVar^) else Dec(LaufVar^);
        while LaufVar^ in [0..7do begin
          tempFigur := qFelder[j, k];
          if tempFigur <> atNil then begin
            if (qFiguren[tempFigur].Farbe <> Farbe) and
               (qFiguren[tempFigur].Typ in Linear) then
            begin
              result := true;  //Schach durch Dame oder Turm
              Exit;
            end;
            Break;  //Figur blockiert Reihe, keine weitere Schachmöglichkeit
          end;
          if incJ then Inc(LaufVar^) else Dec(LaufVar^);
        end;
      {$ENDREGION}
      {$REGION 'Diagonale Richtungen prüfen'}
        incJ := i < 2;  //Die ersten zwei zählen X aufwärts
        incK := (i mod 2) = 0;  //jede zweite zählt Y abwärts
        j := Feld.X + IfThen(incJ, 1, -1);
        k := Feld.Y + IfThen(incK, 1, -1);
        while (j in [0..7]) and (k in [0..7]) do begin  //doppelte for(diagonal)
          tempFigur := qFelder[j, k];
          if tempFigur <> atNil then begin
            if (qFiguren[tempFigur].Farbe <> Farbe) and
               (qFiguren[tempFigur].Typ in Diagonal) then
            begin
              result := true;  //Schach
              Exit;
            end;
            Break;  //Figur blockiert Reihe, keine weitere Schachmöglichkeit
          end;
          if incJ then Inc(j) else Dec(j);
          if incK then Inc(k) else Dec(k);
        end;
      {$ENDREGION}
      end;
    {$ENDREGION}
  end;
end;

function AbzugsSchach(const qFiguren: TFigurArray; const qFelder: TFeldArray;
                      const qAbgzogeneFigur: TFigur): Boolean;
var
  Richtung: TFeld;  //Differenzvektor aus Ortsverktoren ZugFigur - König
  tempFigur: TFigur;
  tempTyp: TypIndex;
  MoveLinear, MoveDiagonal: Boolean;
  i, j: FeldIndex;  //Laufvariablen
const
  Linear = [Turm, Dame];  //Gefahrenquellen
  Diagonal = [Laeufer, Dame];
begin
  result := false;
  if qAbgzogeneFigur.Typ = Koenig then
    result := not Bedroht(qFelder, qFiguren, qAbgzogeneFigur)
  else begin
    tempFigur := qFiguren[gibKoenig(qAbgzogeneFigur.Farbe)];  //eigener König
    Richtung := cFeld(tempFigur.Feld.X - qAbgzogeneFigur.Feld.X,
                      tempFigur.Feld.Y - qAbgzogeneFigur.Feld.Y);
    MoveLinear := Richtung.X * Richtung.Y = 0;
    MoveDiagonal := Abs(Richtung.X) = Abs(Richtung.Y);
    if MoveLinear or MoveDiagonal then begin  //auf Abzugsschach prüfen
      Richtung.X := Sign(Richtung.X);
      Richtung.Y := Sign(Richtung.Y);
      i := tempFigur.Feld.X + Richtung.X;
      j := tempFigur.Feld.Y + Richtung.Y;
      while (i in [0..7]) and (j in [0..7]) do begin
        if qFelder[i, j] <> atNil then begin
          tempTyp := qFiguren[qFelder[i][j]].Typ;
          result := (MoveLinear  and  (tempTyp in Linear)) or
                    (MoveDiagonal and (tempTyp in Diagonal));
          Exit;
        end;
        Inc(i, Richtung.X);
        Inc(j, Richtung.Y);
      end;
    end;
  end;
end;

end.


Edit: Quelltext aktualisiert.

mfG,


Xion - Fr 11.04.08 14:56

hmm, also da bei Schach jetzt nicht so auf Rechenleistung zu achten ist (außer wenn der NPC einen zug macht) würde ich das ganze mehr auf Übersicht machen:


Delphi-Quelltext
1:
2:
3:
type TPlayer=record
   Attacks: array [0..7,0..7of integer; 
end;


in dieses Array schreibst du nach jedem Zug, wo überall angegriffen wird (und wie viele). Dadurch könnte es dir auch später einfacher sein, einen Zug für den PC zu berechnen, denn man sieht sofort, welches Feld wie oft angegriffen ist. Wenn du das für jeden Spieler machst, siehst du z.B. sofort, wann eine Figur ungedeckt ist usw.

Delphi-Quelltext
1:
2:
3:
if (Player[0].Feld[X,Y]=0)and(Brett[X,Y]<>FIG_NONE) then 
  ShowMessage('ungedeckte Figur von Spieler 1 auf Feld +'+inttostr(X)+'/'+inttostr(Y)
  +'. Sie wird '+ inttostr(Player[1].Feld[X,Y])+'x bedroht');


Zudem würde ich nicht die gleichen Figuren durch verschiedene Nummern kennzeichnen:

Delphi-Quelltext
1:
2:
3:
const
 FIG_NONE=0;
 FIG_BAUER=1;

ggf eine Ausnahme würde da vielleicht der schwarze/weiße Läufer machen, aber das ist eher ein Stellungsvorteil.


Hidden - Fr 11.04.08 16:35

Hi,

user profile iconXion hat folgendes geschrieben:
hmm, also da bei Schach jetzt nicht so auf Rechenleistung zu achten ist (außer wenn der NPC einen zug macht) würde ich das ganze mehr auf Übersicht machen:


Delphi-Quelltext
1:
2:
3:
type TPlayer=record
   Attacks: array [0..7,0..7of integer; 
end;


in dieses Array schreibst du nach jedem Zug, wo überall angegriffen wird (und wie viele). Dadurch könnte es dir auch später einfacher sein, einen Zug für den PC zu berechnen, denn man sieht sofort, welches Feld wie oft angegriffen ist. Wenn du das für jeden Spieler machst, siehst du z.B. sofort, wann eine Figur ungedeckt ist usw.

Delphi-Quelltext
1:
2:
3:
if (Player[0].Feld[X,Y]=0)and(Brett[X,Y]<>FIG_NONE) then 
  ShowMessage('ungedeckte Figur von Spieler 1 auf Feld +'+inttostr(X)+'/'+inttostr(Y)
  +'. Sie wird '+ inttostr(Player[1].Feld[X,Y])+'x bedroht');

eine nett Idee, aber kompliziert und daher wahrscheinlich leider unbrauchbar: Einerseits muss bei Figuren wie z.B. Dame dann vor einem Zug in jede Richtung dec(Attacks[dame.x, dame.y] und nach einem Zug in jede Richtung inc mit den neuen Koordinaten. Diese strahlenförmige Suche wird dann abgebrochen, wenn eine blockierende Figur gefunden wurde, wie gehabt.

Andererseits musst du bei der Gelegenheit auch auf Abzugsschach prüfen, d.h. schauen, in welchen Richtungen ein Zugstrahl(Dame, Turm, Läufer) blockiert wurde und diesen fortsetzen.

Entscheidend ist aber die bereits von dir genannte Problematik: sobald eine KI verwendet wird, muss dies für jeden simulierten Zug auch mitsimmuliert werden. Die Unit soll, wie gesagt, beliebig ausbaubar sein und dieser Ansatz würde einen Ausbau zur KI blockieren.

Zitat:
Zudem würde ich nicht die gleichen Figuren durch verschiedene Nummern kennzeichnen:

Delphi-Quelltext
1:
2:
3:
const
 FIG_NONE=0;
 FIG_BAUER=1;

ggf eine Ausnahme würde da vielleicht der schwarze/weiße Läufer machen, aber das ist eher ein Stellungsvorteil.

Ich nehme mal an, du beziehst dich auf den Wert 32, der hier als Synonym für nil verwendet wird. Eventuell zuerst zur Kompatiblität des Umstiegs auf Zeiger belassen. Sauberer ist es natürlich ohne: "Keine Figur" ist strenggenommen kein Figurentyp.

Das mit den Läufern verstehe ich nicht ganz...

mfG,


Xion - Sa 12.04.08 11:23

user profile iconHidden hat folgendes geschrieben:

eine nett Idee, aber kompliziert und daher wahrscheinlich leider unbrauchbar: Einerseits muss bei Figuren wie z.B. Dame dann vor einem Zug in jede Richtung dec(Attacks[dame.x, dame.y] und nach einem Zug in jede Richtung inc mit den neuen Koordinaten. Diese strahlenförmige Suche wird dann abgebrochen, wenn eine blockierende Figur gefunden wurde, wie gehabt.

stelle ich mir eigentlich machbar vor.

user profile iconHidden hat folgendes geschrieben:

Andererseits musst du bei der Gelegenheit auch auf Abzugsschach prüfen, d.h. schauen, in welchen Richtungen ein Zugstrahl(Dame, Turm, Läufer) blockiert wurde und diesen fortsetzen.

ggf. das ganze array nach jedem Zug platt machen und komplett neu befüllen, das würde auch das erste problem vereinfachen (kein dec)

user profile iconHidden hat folgendes geschrieben:

Entscheidend ist aber die bereits von dir genannte Problematik: sobald eine KI verwendet wird, muss dies für jeden simulierten Zug auch mitsimmuliert werden. Die Unit soll, wie gesagt, beliebig ausbaubar sein und dieser Ansatz würde einen Ausbau zur KI blockieren.

hab mir die Idee nur so ausm stegreif überlegt...8x8 felder sind aber jetzt eigentlich nicht soo kompliziert zu berechnen.

user profile iconHidden hat folgendes geschrieben:

Ich nehme mal an, du beziehst dich auf den Wert 32, der hier als Synonym für nil verwendet wird. Eventuell zuerst zur Kompatiblität des Umstiegs auf Zeiger belassen. Sauberer ist es natürlich ohne: "Keine Figur" ist strenggenommen kein Figurentyp.

ich meine eigentlich noch zusätzlich, dass jeder Bauer eigentlich gleich ist (da du aber bis 32 zählst nimmst du für jeden bauer eine andre zahl). Das mit dem schwarzen/weißen Läufer bezieht sich darauf, dass die eigentlich nicht gleich sind (wie die Bauern), aber ich sie trotzdem mit der gleichen Nummer kennzeichnen würde und dafür auf Stellungsvorteil achten würde.

Man könnte jedem Bauer dann einen "Informations-Record" zuweisen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
Figur=array of TFigur;
PlayerColor[0]='black';
[...]
Figur[FIG_BAUER].Pic:='bauer.png';
//ggf. auch mit zugmöglichkeiten
[...]
Brett[x,y].Pic.LoadfromFile( PlayerColor[ Brett[x,y].Color ] + Figur[ Brett[x,y].Fig ].Pic)


Hidden - Sa 12.04.08 11:33

Hi,

user profile iconXion hat folgendes geschrieben:

Man könnte jedem Bauer dann einen "Informations-Record" zuweisen:

Delphi-Quelltext
1:
2:
3:
4:
5:
Figuren=array of TFigur;
const PlayerColor='black';
[...]
Figur[FIG_BAUER].Pic=PlayerColor+'bauer.png';
//ggf. auch mit zugmöglichkeiten


Hi,

Es gibt ein Array FBmps[Schachbrett..Koenig] of TBitmap, das genau diesem Vorschlag entspricht, allerdings dann im Program, das diese Unit einbindet. :wink: .

Zu deinem Edit: Ich zeichne die Figuren am Ende mit Draw(x, y, FBmps[meineFigur.Typ]], bzw. strechdraw, auf die Paintbox.

Zitat:
(da du aber bis 32 zählst nimmst du für jeden bauer eine andre zahl)

0..31 sind die Referenzen zu den von dir erwähnten Informationsrecords. Sie enthalten Typ, Farbe, Feld.

Edit: Wobei Farbe und Typ sich im Prinzip auch aus der Referenznummer ergeben. Typ setze ich auf 32, wenn die Figur geschlagen wurde. 32 war ursprünglich einfach die erste Zahl außerhalb des Arrays der Figuren und ist hiermit eigentlich zu einem Synonym für nil geworden. Wie o.e., kann ich keine Zeiger verwenden, da bei vorausberechneten Stellungen diese auf ein beliebiges Figurenarray(Infomationsrecords) anwendbar sein sollen.

Edit2: die Farben sind übrigens mit Boolean realisiert:

Delphi-Quelltext
1:
2:
3:
4:
5:
type
  TFarbe = Boolean;
const
  Weiss = true;
  Schwarz = false;


mfG,


Xion - Sa 12.04.08 11:46

user profile iconHidden hat folgendes geschrieben:

Edit: Wobei Farbe und Typ sich im Prinzip auch aus der Referenznummer ergeben. Typ setze ich auf 32, wenn die Figur geschlagen wurde. 32 war ursprünglich einfach die erste Zahl außerhalb des Arrays der Figuren und ist hiermit eigentlich zu einem Synonym für nil geworden. Wie o.e., kann ich keine Zeiger verwenden, da bei vorausberechneten Stellungen diese auf ein beliebiges Figurenarray(Infomationsrecords) anwendbar sein sollen.

hmm, also ich würde statt 32 eine -1 nehmen...

Edit: ich mag für sowas keine booleans, da man diese nicht einfach aus einem array auslesen kann:

Delphi-Quelltext
1:
2:
 Color:=C_BlackColor;
 LoadPicture(Pic[Color]);

bei einem boolean braucht man dann immer if abfragen usw.


Hidden - Sa 12.04.08 11:57

user profile iconXion hat folgendes geschrieben:
user profile iconHidden hat folgendes geschrieben:

Edit: Wobei Farbe und Typ sich im Prinzip auch aus der Referenznummer ergeben. Typ setze ich auf 32, wenn die Figur geschlagen wurde. 32 war ursprünglich einfach die erste Zahl außerhalb des Arrays der Figuren und ist hiermit eigentlich zu einem Synonym für nil geworden. Wie o.e., kann ich keine Zeiger verwenden, da bei vorausberechneten Stellungen diese auf ein beliebiges Figurenarray(Infomationsrecords) anwendbar sein sollen.

hmm, also ich würde statt 32 eine -1 nehmen...

Gute Idee. Leider verwende ich Byte; sollte ich generell auf Shortint umsteigen?
Zitat:
Edit: ich mag für sowas keine booleans, da man diese nicht einfach aus einem array auslesen kann:

Delphi-Quelltext
1:
2:
 Color:=C_BlackColor;
 LoadPictures(Pic[Color]);

bei einem boolean braucht man dann immer if abfragen usw.

Das stimmt für Arrays.. Wenn ich allerdings(was vergleichsweise ständig vorkommt) im logik-Teil auf die Farbe prüen muss, ist das if Farbe = C_BlackColor statt if Farbe. Andererseits wird der code dadurch unleserlich und ich brauche häufiger einen kommentar //Weiss entspricht true.

Edit: Mal was anderes: Wenn das System am Ende auf KI umgerüstet wird, ist es dann nicht günstiger, wenn ich statt dieser methematischen Zugmusterprüfung vordefinierte Arrays [-7..7, -7..7] of Boolean für jede Figur definiere?


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:
case Typ of  //Liegt das Zielfeld im Zugmuster der Figur?
  {$REGION 'Bauer:'}
  Bauer: begin
    if Abs(Feld.X - qFeld.X) < 2 then begin  //Startfeld.X - Zielfeld.X
      //Ansonsten weicht Zielfeld.X um mehr als 1 von Startfeld.X ab!
      if qFeld.Y = Feld.Y + IfThen(Farbe, -11then begin
        if Feld.X = qFeld.X then  //normaler Zug geradeaus
          result := tempFigur = 32  //Feld ist leer
        else result := tempFigur <> 32
      end else //Wenn Zug um mehr als 1 Feld
        if Farbe {= Weiss} then  //Weiss entspricht true
          result := (Feld.Y = 6and (qFeld.Y = 4)  //Doppelzug am Anfang
        else result := (Feld.Y = 1and (qFeld.Y = 3);
    end;
  end;
  {$ENDREGION}
  Springer: result := Abs((Feld.X - qFeld.X) * (Feld.Y - qFeld.Y)) = 2;
  Laeufer:  //        DeltaY        =         DeltaX
    result := Abs(qFeld.Y - Feld.Y) = Abs(qFeld.X - Feld.X);  
  Turm: result := (qFeld.X = Feld.X) or (qFeld.Y = Feld.Y);
  Dame: result := (qFeld.X = Feld.X) or (qFeld.Y = Feld.Y) or  //Turm
                  (Abs(qFeld.Y - Feld.Y) = Abs(qFeld.X - Feld.X));  //Läufer
  Koenig: result := Abs((qFeld.X - Feld.X) * (qFeld.Y - Feld.Y)) = 1;
end;


mfG,


Xion - Sa 12.04.08 12:03

user profile iconHidden hat folgendes geschrieben:

Gute Idee. Leider verwende ich Byte; sollte ich generell auf Shortint umsteigen?

wie wäre es mit var I: [-1..31](irgendwie so geht das ;) ggf. mit runden klammern)

user profile iconHidden hat folgendes geschrieben:

Wenn ich allerdings(was vergleichsweise ständig vorkommt) im logik-Teil auf die Farbe prüen muss, ist das if Farbe = C_BlackColor statt if Farbe. Andererseits wird der code dadurch unleserlich und ich brauche häufiger einen kommentar //Weiss entspricht true.

das versteh ich nicht...du hast doch für deine möglichkeit weiss=True definiert (unter const) somit ist es doch leserlich. Und statt True kannst du auch 0 zuweisen. :gruebel:

//Edit: du solltest natürlich NICHT if Farbe then schreiben :lol: sondern if Farbe=weiss then


Xion - Sa 12.04.08 13:03

user profile iconHidden hat folgendes geschrieben:

Edit: Mal was anderes: Wenn das System am Ende auf KI umgerüstet wird, ist es dann nicht günstiger, wenn ich statt dieser methematischen Zugmusterprüfung vordefinierte Arrays [-7..7, -7..7] of Boolean für jede Figur definiere?


hmmm, jo, könnte durchaus sinnvoll sein. Beim Bauer ist das nur etwas schwierig...Hmmm, ne, ich denke mal, die allgemeinen Regeln sind besser. Man müsste sich erstmal n Konzept überlegen, wie die KI vorgehen soll.


Hidden - Sa 12.04.08 14:19

user profile iconXion hat folgendes geschrieben:
user profile iconHidden hat folgendes geschrieben:

Edit: Mal was anderes: Wenn das System am Ende auf KI umgerüstet wird, ist es dann nicht günstiger, wenn ich statt dieser methematischen Zugmusterprüfung vordefinierte Arrays [-7..7, -7..7] of Boolean für jede Figur definiere?


hmmm, jo, könnte durchaus sinnvoll sein. Beim Bauer ist das nur etwas schwierig...Hmmm, ne, ich denke mal, die allgemeinen Regeln sind besser. Man müsste sich erstmal n Konzept überlegen, wie die KI vorgehen soll.


Hi und erstmal danke für dein reges Engagement :wink: ,

Ich dachte an eine function ZugMusterCheck(Figur, Zielfeld - Startfeld): Boolean. Zielfeld - Startfeld ist dabei die Projektion auf das Muster. Diese Funktion könnte ja ohne weiteres für andere Figuren ein vordefiniertes Array nehmen und einen Bauern seperat prüfen...

Die Frage, die ich mir stelle, ist diese: Wie sollte ich das Verhältnis zwischen Rechenleistung und Musterspeicher gestalten? Ich könnte z.B. bei Symmetrischen Figuren(also alles bis auf Bauer) die Arrays als [0..70..7] definieren und Abs(Zielfeld - Startfeld) nehmen, IMHO durchaus praktikabel. Abs() hätte ja sogar Laufzeit O(0), da ja nichts gerechnet werden muss, sondern nur das Vorzeichen nicht mit ausgelesen, oder?

Eigentlich geht es hier ja nicht hauptsächlich um eine KI, also hier [OT]: Eine Schach-KI stelle ich mir so vor, dass sie für jeden möglichen Zug wiederum jeden möglichen Zug im voraus berechnet. Die sich daraus ergebenden Zugketten hätten verschiedene Abbruchbedingungen. Nun werden so lange Zugketten fortgesetzt, bis entweder nurnoch ein Zug verbleibt oder n Züge erreicht sind. Nach n Zügen Auswertung wird dann der Zug gewählt, der, vorausgesetzt, dass beide Seiten jeweils nach diesem Modell spielen, nach n Zügen die beste Stellungsbewertung hat. Verbleiben mehrere Möglichkeiten, liefert rnd() Entscheidungshilfe :mrgreen:

Zusammenfassend gesagt, dachte ich also an eine rekursive Lösung. Gehen wir es noch einmal einfach durch: angenommen n = 1, so wird der Zug gewählt, der die Stellungsbewertung am positivsten verändert. ist n = 2, so wird der gewählt, der bei optimaler Reaktion des Gegners mit optimaler Bewertungsänderung ausgeht. Usw...

Eine Stellungsbewertung kann für große n einfach als der Quotient der Summen der Figurenwerte angesehen werden. Dabei bekommt der König einen Wert, der das Programm ein sicheres Schlagen des Königs(Matt) stets bevorzugen lässt.[/OT]

mfG,


Xion - Sa 12.04.08 14:30

diese Methode ist zwar die normale Methode, allerdings braucht die 32^n Berechnungsschritte, was durchaus problematisch wird.

Deshalb verwenden Schachprogramme meist eine Datenbank, und rechnen nur im Notfall. Dies ist z.B. bei der Eröffnung praktisch.

Ich fände ja ein Schachprogramm lustig, das eine solche Datenbank erst im Laufe der Zeit erstellt => am Anfang ist er erstmal sehr dumm. Aber nach kurzer Zeit funktionieren schon einige Tricks nicht mehr. Ich bin auch der Meinung, man sollte dem Rechner das vorgehen geben, welches auch ein echter Spieler hat. Ich z.B. berechne nie alle Zugmöglichkeiten im Kopf vorher. ;) z.B. sind Züge gut, die eine gegnerische Figur vor dem König fesseln usw.. Ich würde mehr mit solchen Dingen arbeiten, als zu rechnen bis der PC platzt, aber dafür weiß ich nicht, ob meine Methode auf Dauer funktioniert.

//Edit: unter "Logik-Part" verstehe ich auch KI ;)


Hidden - Sa 12.04.08 14:51

user profile iconXion hat folgendes geschrieben:
unter "Logik-Part" verstehe ich auch KI ;)
Könntest du mit durchkommen :lol:

Was den "Menschenähnlich-Part" angeht, die Idee hatte ich auch schon und dachte mir man könnte das vielleicht als Spielstufe möglich machen, eine KI, die wie ein Mensch spielt!

@(Dynamische Buchzüge und Gedächnis)^: Man könnte sogar eine KI programmieren, die explizit nach deinen typischen Fehlern spielt, die würde, glaube ich, in kürzester Zeit dein Schachspiel verbessern :!: leider ziemlich kompliziert...

mfG,


Xion - Sa 12.04.08 15:10

da liegt halt das Problem, KI(=künstliche Intelligenz) gibt es einfach nicht (bis heute zumindest).


Hidden - Sa 12.04.08 15:16

Ich hab den Quelltext oben mal auf den neusten Stand gebracht, sind aber eigentlich nur ein paar kleine Änderungen.

Edit: Kennt sich jemand mit GNU-Lizenzen aus? Dürfte ich z.B. diese Figur aus den Wikipedia-Comments einfach in mein Programm einbinden, wenn ich es hier gezippt hochladen würde: http://de.wikipedia.org/wiki/Bild:Chess_rdl44.png ?

Edit2: Ja, ist erlaubt.


AXMD - Sa 12.04.08 19:16

user profile iconHidden hat folgendes geschrieben:
Edit: Kennt sich jemand mit GNU-Lizenzen aus? Dürfte ich z.B. diese Figur aus den Wikipedia-Comments einfach in mein Programm einbinden, wenn ich es hier gezippt hochladen würde: http://de.wikipedia.org/wiki/Bild:Chess_rdl44.png ?


Bitte erstelle für eine neue Frage ein neues Topic (vgl. Foren-Richtlinien)

Danke
AXMD


Xion - So 13.04.08 10:51

auch wenns nicht zum Logik-Part des Schachprogramms gehört:
ich hab mal ein "Schachbrett" programmiert (völlig ohne jede Logik). Wenn du die Bilder verwenden willst, kannst die gerne haben (bitte vorher bescheidsagen). Das gilt auch für andre Leute, bei interesse könnt ihr euch diese Bilder nehmen, aber schreibt mir pls. vorher eine PN, damit ich bescheid weiß.


Delete - So 13.04.08 11:01

Was mich 'n biserl stört, ist die verwenndung von nummern...

user profile iconHidden hat folgendes geschrieben:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
begin
  qFiguren[qFelder[startFeld.X, startFeld.Y]].Feld := zielFeld;
  if qFelder[zielFeld.X, zielFeld.Y] <> 32 then
    qFiguren[qFelder[zielFeld.X, zielFeld.Y]].Typ := 32;  //geschlagen
  qFelder[zielFeld.X, zielFeld.Y] := qFelder[startFeld.X, startFeld.Y];
  qFelder[startFeld.X, startFeld.Y] := 32;
end;



dabei hast du sie eigentlich schon definiert:
user profile iconHidden hat folgendes geschrieben:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
  Bauer = 0;
  Springer = 1;
  Laeufer = 2;
  Turm = 3;
  Dame = 4;
  Koenig = 5;
  atNil = 32;  //Indexrepresentative für nil
  Weiss = 0;
  Schwarz = 1;

type
  FigurIndex = Byte;  //Nummerierung der Figuren
  TypIndex = Byte;  //Bauer, Springer, etc.
  FeldIndex = ShortInt;  //Nummerierung der Felder



fänd es gut, wenn du die zahlen gegen die konstanten austauschen würdest, dann kannst du dein programm wohl auch noch in ein paar wochen lesen.

BTW: man könnte sie ja auch anderweitig schreiben z.b.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
  type 
   tFigur = (figBauer, figLaeufer, figTurm, figSpringer, figDame, figKoenig);
   tColor = (clWhite, clBlack);

   tFigurs = Set of tFigur;
   tColors = Set of tColor;


dann könntest du über mengenoperatoren auch einfach darauf zugreifen.

BTW2: beim überfliegen deines codes, hatte ich den eindruck, dass hier noch keine logik für die züge vorhanden ist, sondern dass diese unit eher das regelwerk abbildet... das regelwerk ist natürlich elementar, ohne einem funktionierenden macht eine logik auch keinen sinn. von daher eine design frage, willst du für das regelwerk und die logik eine unit verwenden oder diese auf zwei units aufteilen?

das Regelwerk hat sich seit jahrtausenden praktisch nicht verändert (wenn man von der einführung einer zeitbeschränkung (z. b. blitzschach)) absieht.

während du wohl noch lange an der logik zur optimierung herumtüfteln kannst...


Hidden - So 13.04.08 11:30

Hi,

user profile iconGrenzgaenger hat folgendes geschrieben:
Was mich 'n biserl stört, ist die verwenndung von nummern...

Umgesetzt und neue Version hochgeladen.

user profile iconGrenzgaenger hat folgendes geschrieben:
BTW2: beim überfliegen deines codes, hatte ich den eindruck, dass hier noch keine logik für die züge vorhanden ist, sondern dass diese unit eher das regelwerk abbildet... das regelwerk ist natürlich elementar, ohne einem funktionierenden macht eine logik auch keinen sinn. von daher eine design frage, willst du für das regelwerk und die logik eine unit verwenden oder diese auf zwei units aufteilen?

das Regelwerk hat sich seit jahrtausenden praktisch nicht verändert (wenn man von der einführung einer zeitbeschränkung (z. b. blitzschach)) absieht.

während du wohl noch lange an der logik zur optimierung herumtüfteln kannst...


Ja, das kommt auf jeden Fall in eine getrennte Unit. Insofern war vielleicht der Titel des Treads nicht ganz richtig gewählt(werde ihn trotzdem mal lassen, da hier ja schon über logik generell gesprochen wurde).

Edit: Ich habe mich mal mit der praktischen Umsetzung meiner o.e. Idee befasst, den Zugmustercheck auf Arrays zurückzuführen. Hat das hier ncoh etwas mit sauberem Porogrammierstil zu tun, oder sollte ich die Arrays zur Laufzeit erstellen und nach meinen Formeln befüllen?

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:
const
  § = true;
  ² = false;
  ZugMuster: Array[Bauer..Koenig] of
             Array[ValidFeld] of
             Array[ValidFeld] of Boolean =
    (((², ², ², ², ², ², ², ²),  //Bauer
      (², ², ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²),
      (², §, ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²)),
     ((², ², ², ², ², ², ², ²),  //Springer
      (², ², ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²),
      (², §, ², ², ², ², ², ²),
      (², ², §, ², ², ², ², ²),
      (², ², ², ², ², ², ², ²)),
     ((², ², ², ², ², ², ², §),  //Laeufer
      (², ², ², ², ², ², §, ²),
      (², ², ², ², ², §, ², ²),
      (², ², ², ², §, ², ², ²),
      (², ², ², §, ², ², ², ²),
      (², ², §, ², ², ², ², ²),
      (², §, ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²)),
     ((§, ², ², ², ², ², ², ²),  //Turm
      (§, ², ², ², ², ², ², ²),
      (§, ², ², ², ², ², ², ²),
      (§, ², ², ², ², ², ², ²),
      (§, ², ², ², ², ², ², ²),
      (§, ², ², ², ², ², ², ²),
      (§, ², ², ², ², ², ², ²),
      (², §, §, §, §, §, §, §)),
     ((§, ², ², ², ², ², ², §),  //Dame
      (§, ², ², ², ², ², §, ²),
      (§, ², ², ², ², §, ², ²),
      (§, ², ², ², §, ², ², ²),
      (§, ², ², §, ², ², ², ²),
      (§, ², §, ², ², ², ², ²),
      (§, §, ², ², ², ², ², ²),
      (§, §, §, §, §, §, §, §)),
     ((², ², ², ², ², ², ², ²),  //Koenig
      (², ², ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²),
      (², ², ², ², ², ², ², ²),
      (§, §, ², ², ², ², ², ²),
      (², §, ², ², ², ², ², ²)));


mfG,


Xion - So 13.04.08 12:47

sollte die Figur nicht in der Mitte des Arrays stehen? Weil dein Läufer-Array ist so ja völlig unbrauchbar ^^

Springer:

- - - - - - - - - - - - - -
- - - - - - - - - - - - - -
- - - - - - - - - - - - - -
- - - - - - - - - - - - - -
- - - - - - x - x - - - - -
- - - - - x - - - x - - - -
- - - - - - - P - - - - - -
- - - - - x - - - x - - - -
- - - - - - x - x - - - - -
- - - - - - - - - - - - - -
- - - - - - - - - - - - - -
- - - - - - - - - - - - - -
- - - - - - - - - - - - - -

=> projeziert auf das Feld hast du dann alle Felder, auf die der Springer ziehen kann.

//Edit: naja, nach deinen arrays musst du sie dann noch nach unten/links spiegeln


Hidden - So 13.04.08 12:56

user profile iconXion hat folgendes geschrieben:
//Edit: naja, nach deinen arrays musst du sie dann noch nach unten/links spiegeln

Ups, danke. die Arrays haben ja oben links ihren Ursprung.

Zu der Sache mit der Mitte: Alle Figuren außer Bauer sind Symmetrisch, d.h., ich bilde Abs(StartFeld.X - Zielfeld.X) und dasgleiche mit Y und greife damit aufs Array zu.

Is es denn besser, sie als konstante drin zu haben oder sollte ich sie zur Laufzeit erzeugen?

mfG,


Xion - So 13.04.08 13:01

user profile iconHidden hat folgendes geschrieben:

Is es denn besser, sie als konstante drin zu haben oder sollte ich sie zur Laufzeit erzeugen?

:nixweiss: das ist eigentlich egal. Ich würde ja alle Werte usw. in eine extra Unit packen.


Delete - So 13.04.08 13:03

weshalb machst du denn nicht aus jeder figur ein objekt, welches weiss, wie es ziehen kann?

ausserdem solltest du auch an die sonderlocken denken, wie z.b. dass ein bauer beim ersten zug, zwei felder ziehen kann, und nachträglich kann er dann auch geschlagen werden (en passant).

für den könig gilt dann auch, kurze und lange rochade, unter der prämisse, dass er noch nicht bewegt wurde, der teilnehmende turm ebenfalls nicht, keine figur im weg steht und er sich nicht dem schach entzieht oder sich ins schach setzt. ausserdem darf der könig kein bedrohtes feld dabei überqueren.

dies wäre aber ein logikburch zu deinem bisherigen code. musst selbst entscheiden, ob du so besser zurande kämmst, oder du das procedureal abbilden möchtest, wie du bereits begonnen hast.

<HTH>

Edit: Rochade konkretisiert.


Delete - So 13.04.08 13:07

user profile iconHidden hat folgendes geschrieben:
Is es denn besser, sie als konstante drin zu haben oder sollte ich sie zur Laufzeit erzeugen?


wenn sich die konstanten nicht ändern, was spricht dagegen sie im code als konstanten zu deklarieren? dann kann der compiler auch besser optimieren...


Hidden - So 13.04.08 13:16

user profile iconGrenzgaenger hat folgendes geschrieben:
weshalb machst du denn nicht aus jeder figur ein objekt, welches weiss, wie es ziehen kann?

Da sind wir wieder beim selben Problem: Wie prüft das Objekt denn dann sein Zielfeld, per Array oder per Formel?

[qupte]ausserdem solltest du auch an die sonderlocken denken, wie z.b. dass ein bauer beim ersten zug, zwei felder ziehen kann, und nachträglich kann er dann auch geschlagen werden (en passant).

für den könig gilt dann auch, kurze und lange rochade, unter der prämisse, dass er noch nicht bewegt wurde, der teilnehmende turm ebenfalls nicht, keine figur im weg steht und er sich nicht dem schach entzieht oder sich ins schach setzt.[/quote]
Sonderfälle werden getrennt behandelt.

Zu deinem Edit: genau das meinte ich, danke!


Delete - So 13.04.08 13:30

user profile iconHidden hat folgendes geschrieben:
user profile iconGrenzgaenger hat folgendes geschrieben:
weshalb machst du denn nicht aus jeder figur ein objekt, welches weiss, wie es ziehen kann?

Da sind wir wieder beim selben Problem: Wie prüft das Objekt denn dann sein Zielfeld, per Array oder per Formel?


hier würde ich ein objekt erstellen, TBrett, bei dem sich jede Figur registriert und bei verlassen sein feld als frei kennzeichnet. da sich je nach feld, unterschiedliche zugmöglichkeiten ergeben, auch im bezug auf die belegten felder, gäbe es für jedes tFigur objekt eine methode GetMoveableFields, welches ein Array oder eine Liste der Feldkoordinaten zurückliefert, auf welches gezogen werden kann. Diese Analyse würde ich algorithmisch implementieren. ausserdem weiss ja jede figur (type tSpringer = class (tFigure)) wie es ziehen kann.

neben der klasse tBrett, gäbe es auch noch eine klasse TList, in welcher alle Figuren aufgeführt wären. So bräuchtest du nicht jedes mal das tBrett durchgehen und alle felder abzufragen um die figuren aufzufordern zu ziehen, seine felder zu ziehen oder zu bewerten. hierfür kannst abstracte methoden in tFigure implementieren, welche für alle abgeleiteten figuren gelten.

<HTH>

BTW: der objektorientierte ansatz ist zwar langsamer, denke zum schluss aber auch übersichtlicher in der implementierung.


Hidden - So 13.04.08 14:12

user profile iconGrenzgaenger hat folgendes geschrieben:
user profile iconHidden hat folgendes geschrieben:
user profile iconGrenzgaenger hat folgendes geschrieben:
weshalb machst du denn nicht aus jeder figur ein objekt, welches weiss, wie es ziehen kann?

Da sind wir wieder beim selben Problem: Wie prüft das Objekt denn dann sein Zielfeld, per Array oder per Formel?


hier würde ich ein objekt erstellen, TBrett, bei dem sich jede Figur registriert und bei verlassen sein feld als frei kennzeichnet.

Ist alles bereits so implementiert, nur einen Grund für den Umstieg auf Objekte sehe ich ehrlichgesagt nicht.


Delphi-Quelltext
1:
TFeldArray = array[0..70..7of FigurIndex;                    

Erfüllt z.B. die Funktion deines Schachbrett-Objektes wunderbar.

Edit: Sollte ich dieses const Array dann global oder lokal deklarieren, wie setzt der Compiler das um? Deklariere ich es z.B. am anfang einer Methode, könnte es ja sein, dass es jedes Mal neu erzeugt wird. Mir geht es dabei explizit um Laufzeit zu ungunsten von Speicher, die KI soll ja schnell sein!

Edit:
user profile iconXion hat folgendes geschrieben:
user profile iconHidden hat folgendes geschrieben:

Is es denn besser, sie als konstante drin zu haben oder sollte ich sie zur Laufzeit erzeugen?

:nixweiss: das ist eigentlich egal. Ich würde ja alle Werte usw. in eine extra Unit packen.

Also den Kopf der Unit mit den Deklarationen oder wie?

mfG,


Hidden - Mo 14.04.08 19:45

Hi,

Mir ist heute klargeworden, wie kompliziert man/ich eigentlich schachspielt: Wir haben ein Array "RelevantFigures", das wir für unsere Züge durchgehen und, das wir nach bestimmten Mustern, bzw., wenn der Gegner mit der Figur zieht, aktualisieren. Grundsätzlich setzt sich dieses Array aus den Figuren in unserem Blickfeld zusammen, aber das wäre für einen Computer ziemlich kompliziert...

mfG,


Fabian E. - Mo 14.04.08 20:21

user profile iconHidden hat folgendes geschrieben:

Ist alles bereits so implementiert, nur einen Grund für den Umstieg auf Objekte sehe ich ehrlichgesagt nicht.


Delphi-Quelltext
1:
TFeldArray = array[0..70..7of FigurIndex;                    

Erfüllt z.B. die Funktion deines Schachbrett-Objektes wunderbar.


naja der grund ist ganz einfach. dir fällt ziemlich viel verwaltungsaufwand weg. wenn du schön in OOP ein paar klassen bildest und diese dann noch sauber ableitest muss man sich nach dem implementieren der klassen um fast gar nichts mehr kümmern. deine objekte wüssten selber wohin sie dürfen und wo nicht. sie könnten sich selbständig vom spielfeld entfernen, etc...

gruß


Hidden - Mo 14.04.08 21:52

Hi,

Der erste Unterschied deines Konzeptes wäre, dass sich ein Array[Bauer..Koenig, 0..7, 0..7] in ein Array [0..7, 0..7] verwandeln würde, das für jede Figur anders ist. Der restliche Part deiner

Delphi-Quelltext
1:
function TFigur.ZugFeld(qFeld: TFeld): Boolean;                    
(gegenüber

Delphi-Quelltext
1:
function ZugFeld(qFigur: TFigur, qFeld: TFeld): Boolean;                    

) wäre bei allen Figuren gleich: Prüfen, ob eigene Figur auf dem Feld, Prüfen, ob das Array an den Koordinaten den Wert true hat, auf Abzugsschach prüfen.

Beim, dann folgenden, seperaten Teil für die Sonderfälle werden zwei if-Abfragen gespaart: Prüfen auf König und Prüfen auf Bauern(die zwei Sonderfälle). Die Sonderfallbehandlung ist dann ja auch relativ kurz(13 Zeilen beim Bauern, 2 beim König), ansonsten wäre das ein klarer Übersichtsvorteil.

Die Geschwindigkeitsfrage kann ich nicht beurteilen, aber da du gesagt hast, dass das langsamer wäre, nehme ich mal an, dass diese zwei if-Abfragen da nicht den entscheidenden Trend geben werden. Wie gesagt kommt es bei allen Operationen, die die KI zum Vorrechnen benötigt, in erster Linie auf Schnelligkeit an.

mfG,