/*
* C++ Implementation: aibrain
*
* Description:
*
*
* Author: Hendrik Hochstetter <hochsthk@studi.informatik.uni-stuttgart.de>, (C) 2008
*
* Copyright: See COPYING file that comes with this distribution
*
*/
#include <msgchat.h>
#include <msgconv.h>
#include <QtCore/QDebug>
#include <QtCore/QThread>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtCore/QMap>
#include <QtCore/QSet>
#include <QtCore/QObject>
#include "aibrain.h"
#include "conversions.h"
#include "globals.h"
#include <math.h>
#define TIME_CUTOFF
Position *tempPosition;
/** priorität der figurklassen für sortierung der züge für effektiveres pruning **/
inline int priorityOfPiece (Piece p)
{
switch (p)
{
case PAWN_W:
return 12;
case PAWN_B:
return 12;
case BISHOP_W:
return 6;
case BISHOP_B:
return 6;
case KNIGHT_W:
return 4;
case KNIGHT_B:
return 4;
case ROOK_W:
return 2;
case ROOK_B:
return 2;
case QUEEN_W:
return 0;
case QUEEN_B:
return 0;
case KING_W:
return 10;
case KING_B:
return 10;
};
return 0;
};
/** @brief Sortierfunktion, die die Folgezüge jedesmal sortieren soll bevor alpha-beta-pruning erfolgt.
* @details Je weiter oben erfolgsversprechende Züge einsortiert werden, desto effektiver arbeitet
* die alpha-beta-suche.
**/
inline bool lessThan (Move m1, Move m2)
{
Piece p1 = tempPosition -> value (m1.from);
Piece p2 = tempPosition -> value (m2.from);
// wenn Figur, die zieht bei m1 wertvoller ist als bei m2
if (priorityOfPiece (p1) < priorityOfPiece (p2))
{
return true;
}
else if (priorityOfPiece (p1) > priorityOfPiece (p2))
{
return false;
}
Field t1 = move (m1);
Field t2 = move (m2);
p1 = tempPosition -> value (t1);
p2 = tempPosition -> value (t2);
// wenn wertvollere Figur geschlagen wird
if (abs (valueOfPiece (p1)) < abs (valueOfPiece (p2)))
{
return true;
}
else if (abs (valueOfPiece (p1)) > abs (valueOfPiece (p2)))
{
return false;
}
if (t1 < t2)
{
return true;
}
return false;
};
QString localStringOfZug (zug z)
{
return QString ("(") + QString::number (z.get_erste_k ().get_dim1 ()) + QString (",") + QString::number (z.get_erste_k ().get_dim2 ()) + QString (",") + QString::number (z.get_erste_k ().get_dim3 ()) + QString ("),(") + QString::number (z.get_zweite_k ().get_dim1 ()) + QString (",") + QString::number (z.get_zweite_k ().get_dim2 ()) + QString (",") + QString::number (z.get_zweite_k ().get_dim3 ()) + QString ("),[") + QString (z.get_nach_farbe ()) + QString (",") + QString (z.get_nach_figurtyp ()) + QString ("]");
};
void AiBrain::lustig () {
8;
};
AiBrain::AiBrain (
#ifdef BRAINDEBUG
QObject *parent,
#else
QThread *parent,
#endif
Position position, int lastMove, Colour colour, AiSettings aiSettings)
:
#ifdef BRAINDEBUG
QObject (parent),
#else
QThread (parent),
#endif
colour (colour), timerInterval (0), aiSettings (aiSettings), traversedNodes (0), duplicateNodes (0)
{
timer = new QTimer (this);
connect (timer, SIGNAL (timeout ()), this, SLOT (wakeUp ()));
timer -> start (10000);
timeCutOff = false;
maxTime = aiSettings.maxtime;
startState = StateNode ();
startState.depth = 0;
startState.last = lastMove;
startState.position = position;
tempPosition = &position;
startState.whiteState = 32767;
startState.blackState = 32767;
if (colour == WHITE)
{
if (lastMove != -1)
{
startState.whiteState = startState.whiteState | lastMove;
}
// Wir gehen davon aus dass der gegner rochieren darf und wir nicht
//! \TODO daten vom server bekommen wer wo rochieren darf.
startState.blackState = startState.blackState | CASTLE_LONG;
startState.blackState = startState.blackState | CASTLE_SHORT;
}
else
{
if (lastMove != -1)
{
startState.blackState = startState.whiteState | lastMove;
}
startState.whiteState = startState.whiteState | CASTLE_LONG;
startState.whiteState = startState.whiteState | CASTLE_SHORT;
}
valueFunctions [EVAL_OPENING] = &AiBrain::valueStateNodeOpening;
valueFunctions [EVAL_MIDDLEGAME] = &AiBrain::valueStateNodeMiddlegame;
valueFunctions [EVAL_ENDGAME] = &AiBrain::valueStateNodeEndgame;
#ifdef DUPLICATEPRUNING
// platz für die hashmap mit bereits besuchten stellungen speichern damit kein rehashing
alreadyVisited.reserve (pow (10, aiSettings.maxdepth));
#endif
};
AiBrain::EvaluationType AiBrain::gameState (StateNode *state)
{
int pieces = state -> position.size ();
if (pieces > 26)
{
return EVAL_OPENING;
}
else if (pieces > 16)
{
return EVAL_MIDDLEGAME;
}
return EVAL_ENDGAME;
};
/**
* Regelmäßig Statusinformationen über die aktuellen berechnungen ausgeben.
**/
void AiBrain::wakeUp ()
{
/** Suchabbruch wegen Zeitüberschreitung? **/
timeCutOff = maxTime <= (stopWatch.elapsed () / 1000);
qDebug () << this << gamename << "alive." << traversedNodes << "nodes traversed. Pruned" << duplicateNodes << "duplicate nodes. After " << stopWatch.elapsed () / 1000 << "s.";
if (timerInterval > 4)
{
emit send (new CheppSay (aiSettings.username + " alive. " + QString::number (traversedNodes) + " nodes traversed. Pruned " + QString::number (duplicateNodes) + " duplicate nodes. After " + QString::number (stopWatch.elapsed () / 1000) + "s."));
timerInterval = -1;
}
timerInterval ++;
};
bool zugLessThan (zug y, zug z)
{
if (y.get_erste_k ().get_dim1 () < z.get_erste_k ().get_dim1 ())
{
return true;
}
else if (y.get_erste_k ().get_dim1 () == z.get_erste_k ().get_dim1 ())
{
if (y.get_erste_k ().get_dim2 () < z.get_erste_k ().get_dim2 ())
{
return true;
}
else if (y.get_erste_k ().get_dim2 () == z.get_erste_k ().get_dim2 ())
{
if (y.get_erste_k ().get_dim3 () < z.get_erste_k ().get_dim3 ())
{
return true;
}
else if (y.get_erste_k ().get_dim3 () > z.get_erste_k ().get_dim3 ())
{
return false;
}
}
else
{
return false;
}
}
else
{
return false;
}
if (y.get_zweite_k ().get_dim1 () < z.get_zweite_k ().get_dim1 ())
{
return true;
}
else if (y.get_zweite_k ().get_dim1 () == z.get_zweite_k ().get_dim1 ())
{
if (y.get_zweite_k ().get_dim2 () < z.get_zweite_k ().get_dim2 ())
{
return true;
}
else if (y.get_zweite_k ().get_dim2 () == z.get_zweite_k ().get_dim2 ())
{
if (y.get_zweite_k ().get_dim3 () < z.get_zweite_k ().get_dim3 ())
{
return true;
}
}
}
return false;
};
bool moveLessThan (Move m, Move n)
{
if (fieldOfCoordinates (zOfField (m.from), yOfField (m.from), xOfField (m.from)) < fieldOfCoordinates (zOfField (n.from), yOfField (n.from), xOfField (n.from)))
{
return true;
}
if (fieldOfCoordinates (zOfField (m.from), yOfField (m.from), xOfField (m.from)) == fieldOfCoordinates (zOfField (n.from), yOfField (n.from), xOfField (n.from)))
{
if (xOfField (move (m)) < xOfField (move (n)))
{
return true;
}
else if (xOfField (move (m)) == xOfField (move (n)))
{
if (yOfField (move (m)) < yOfField (move (n)))
{
return true;
}
else if (yOfField (move (m)) == yOfField (move (n)))
{
return zOfField (move (m)) < zOfField (move (n));
}
}
}
return false;
};
int AiBrain::minValueOpening (StateNode *state, int alpha, int beta, int sisters)
{
#ifdef TIME_CUTOFF
if (timeCutOff)
{
state -> value = alpha+1;
return alpha+1;
}
#endif
#ifdef DUPLICATEPRUNING
if (alreadyVisited.contains (state -> position))
{
duplicateNodes ++;
state -> value = alreadyVisited.value (state -> position);
return state -> value;
}
#endif
calculateStateAndMoves (state);
calculateSuccessors (state);
traversedNodes ++;
// cutOff oder keine Züge mehr möglich: stellung bewerten.
if (cutOff (state) || state -> next.size () == 0)
{
int value = (this->*valueStateNode) (state, sisters);
#ifdef DUPLICATEPRUNING
alreadyVisited.insert (state -> position, value);
#endif
state -> value = value;
return value;
}
int value = 2000000000;
//qDebug () << "minValue" << state << state -> depth << state -> next.size ();
tempPosition = (&(state -> position));
QList<Move> moves = state -> next.keys ();
qSort (moves.begin (), moves.end (), lessThan);
foreach (Move m, moves)
// foreach (Move m, state -> next.keys ())
{
StateNode *succ = state -> next.value (m);
int value2 = (this ->* maxValue) (succ, alpha, beta, state -> next.size ());
value = value < value2 ? value : value2;
if (value <= alpha)
{
if (state != &startState)
{
qDeleteAll (state -> next.values ());
state -> next.clear ();
}
state -> value = value;
return value;
}
beta = beta < value ? beta : value;
}
if (state != &startState)
{
qDeleteAll (state -> next.values ());
state -> next.clear ();
}
state -> value = value;
return value;
};
int AiBrain::maxValueOpening (StateNode *state, int alpha, int beta, int sisters)
{
#ifdef TIME_CUTOFF
if (timeCutOff)
{
state -> value = beta-1;
return beta-1;
}
#endif
#ifdef DUPLICATEPRUNING
if (alreadyVisited.contains (state -> position))
{
duplicateNodes ++;
state -> value = alreadyVisited.value (state -> position);
return state -> value;
}
#endif
calculateStateAndMoves (state);
calculateSuccessors (state);
traversedNodes ++;
// cutOff oder keine Züge mehr möglich: stellung bewerten.
if (cutOff (state) || state -> next.size () == 0)
{
int value = (this->*valueStateNode) (state, sisters);
#ifdef DUPLICATEPRUNING
alreadyVisited.insert (state -> position, value);
#endif
state -> value = value;
return value;
}
int value = -2000000000;
tempPosition = (&(state -> position));
QList<Move> moves = state -> next.keys ();
qSort (moves.begin (), moves.end (), lessThan);
foreach (Move m, moves)
// foreach (Move m, state -> next.keys ())
{
StateNode *succ = state -> next.value (m);
//qDebug () << "maxValue" << succ;
int value2 = (this ->* minValue) (succ, alpha, beta, state -> next.size ());
value = value > value2 ? value : value2;
if (value >= beta)
{
if (state != &startState)
{
qDeleteAll (state -> next.values ());
state -> next.clear ();
}
state -> value = value;
return value;
}
alpha = alpha > value ? alpha : value;
}
if (state != &startState)
{
qDeleteAll (state -> next.values ());
state -> next.clear ();
}
state -> value = value;
return value;
};
/**
* @brief start der alpha-beta-suche nach dem besten zug.
* @see maxValue (StateNode *state, int alpha, int beta)
* @see minValue (StateNode *state, int alpha, int beta)
**/
zug AiBrain::calculateMove ()
{
calculateStateAndMoves (&startState);
evaluationType = gameState (&startState);
valueStateNode = valueFunctions.value (evaluationType);
if (evaluationType == EVAL_OPENING)
{
// in der eröffnung werden die züge nicht sortiert
minValue = &AiBrain::minValueOpening;
maxValue = &AiBrain::maxValueOpening;
}
else
{
// erst ab mittelspiel
minValue = &AiBrain::minValueElse;
maxValue = &AiBrain::maxValueElse;
}
if (startState.next.size () < 22)
{
cutOffDepth = aiSettings.mindepth + 1;
}
else if (startState.next.size () < 17)
{
cutOffDepth = aiSettings.mindepth + 1;
}
else if (startState.next.size () < 10)
{
cutOffDepth = aiSettings.mindepth + 2;
}
else if (startState.next.size () < 5)
{
cutOffDepth = aiSettings.mindepth + 2;
}
else
{
cutOffDepth = aiSettings.mindepth;
}
quiescentCut = aiSettings.maxdepth;
if ((cutOffDepth & 1) != (quiescentCut & 1))
{
quiescentCut ++;
}
// lustig ();
QList<Move> moves = startState.next.keys ();
qSort (moves.begin (), moves.end (), moveLessThan);
foreach (Move m, startState.next.keys ())
{
//qDebug () << "Move: " << stringOfField (m.from) << stringOfMoveVector (m.by, m.scale);
}
movesFromServer;
QList<zug> calculatedMoves;
foreach (Move m, moves)
{
Move result = Move (m);
Piece changeTo = EMPTY;
Piece from = startState.position.value (result.from);
// zug ist bauerntausch:
if (uncolouredPieceOfPiece (from) == PAWN)
{
if (abs (result.scale) == 3)
{
if (colourOfPiece (from) == WHITE)
{
changeTo = KNIGHT_W;
}
else
{
changeTo = KNIGHT_B;
}
// information, dass es sich um einen bauerntausch handelt muss wieder aus streckung entfert werden...
if (result.scale > 0)
{
result.scale = 1;
}
else
{
result.scale = -1;
}
}
else if (abs (result.scale) == 4)
{
if (colourOfPiece (from) == WHITE)
{
changeTo = QUEEN_W;
}
else
{
changeTo = QUEEN_B;
}
if (result.scale > 0)
{
result.scale = 1;
}
else
{
result.scale = -1;
}
}
}
calculatedMoves << zugOfMoveAndPiece (result, changeTo);
}
qDebug() << "Moves from server:" << movesFromServer.size ();
qDebug() << "Calculated moves:" << calculatedMoves.size ();
qSort (movesFromServer.begin (), movesFromServer.end (), zugLessThan);
qSort (calculatedMoves.begin (), calculatedMoves.end (), zugLessThan);
emit send (new CheppSay ("Number of calculated moves: " + QString::number (calculatedMoves.size ()) + "."));
if (calculatedMoves != movesFromServer)
{
//emit send (new CheppHi ("hochsthk", gamename + ": Differences in number of moves: calc: " + QString::number (calculatedMoves.size ()) + ", from server: " + QString::number (movesFromServer.size ())));
QList<zug> calcDiff = (calculatedMoves.toSet () - (movesFromServer.toSet ())).toList ();
QList<zug> servDiff = (movesFromServer.toSet () - (calculatedMoves.toSet ())).toList ();
qDebug () << "Moves from server that have not been calculated:\n";
//emit send (new CheppHi ("hochsthk", "Moves from server that have not been calculated:"));
qSort (servDiff.begin (), servDiff.end (), zugLessThan);
foreach (zug z, servDiff)
{
z.ausgabe ();
printf ("\n");
//emit send (new CheppHi ("hochsthk", localStringOfZug (z)));
}
printf ("\n");
qDebug () << "Calculated moves that the server did not send:\n";
//emit send (new CheppHi ("hochsthk", "Calculated moves that the server did not send:"));
qSort (calcDiff.begin (), calcDiff.end (), zugLessThan);
foreach (zug z, calcDiff)
{
z.ausgabe ();
printf ("\n");
//emit send (new CheppHi ("hochsthk", localStringOfZug (z)));
}
// printf ("Abort? (y/n)\n");
// char *c;
// scanf ("%s", c);
// if (c == "y")
// {
// return zug ();
// }
// printf ("Continuing...\n");
}
int value = 0;
Move result;
qDeleteAll (startState.next.values ());
startState.next.clear ();
stopWatch.start ();
if (colour == WHITE)
{
value = (this ->* maxValue) (&startState, -2000000000, 2000000000, 0);
}
else
{
value = (this ->* minValue) (&startState, -2000000000, 2000000000, 0);
}
qDebug () << "value of result" << stringOfColour (colour) << value << QString::number (startState.next.size ());
int curr = 0;
foreach (StateNode *succ, startState.next.values ())
{
qDebug () << "current value" << curr++ << succ << QString::number (succ -> value);
if (succ -> value == value)
{
Piece changeTo = EMPTY;
Move result = startState.next.key (succ);
Piece from = startState.position.value (result.from);
// zug ist bauerntausch:
if (uncolouredPieceOfPiece (from) == PAWN)
{
if (abs (result.scale) == 3)
{
if (colourOfPiece (from) == WHITE)
{
changeTo = KNIGHT_W;
}
else
{
changeTo = KNIGHT_B;
}
// information, dass es sich um einen bauerntausch handelt muss wieder aus streckung entfert werden...
if (result.scale > 0)
{
result.scale = 1;
}
else
{
result.scale = -1;
}
}
else if (abs (result.scale) == 4)
{
if (colourOfPiece (from) == WHITE)
{
changeTo = QUEEN_W;
}
else
{
changeTo = QUEEN_B;
}
if (result.scale > 0)
{
result.scale = 1;
}
else
{
result.scale = -1;
}
}
}
qDebug () << result.scale << QString::number (result.by, 8);
zug z = zugOfMoveAndPiece (result, changeTo);
qDeleteAll (startState.next.values ());
startState.next.clear ();
emit send (new CheppSay ("Done. Calculation took " + QString::number (stopWatch.elapsed ()/1000) + "s. " + QString::number (traversedNodes) + " nodes traversed. Pruned " + QString::number (duplicateNodes) + " duplicate nodes."));
emit send (new CheppSay (localStringOfZug(z)));
return zug ();
return z;
}
}
qDeleteAll (startState.next.values ());
startState.next.clear ();
return zug ();
};
/**
* @brief Berechnung der Stellungsbewertung für Eröffnungen:
* @details Miteinbezogen in die Stellungsbewertung werden:
* <br><b>1.) Figurenanzahl</b>
* <br>Die Anzahl der Figuren der jeweiligen Farbe, gewichtet nach Figurtyp. Weiße Figuren
* haben positives Vorzeichen, schwarze negatives, wodurch sich die Bewertung
* über Figuren als Summe über alle einzelnen Figurenwerte realisieren lässt.
* <br><br><b>2.a) Bedrohungen gegnerischer Figuren</b>
* <br>Die Anzahl an Bedrohungen, die in der aktuellen Stellung den jeweils gegnerischen
* Figuren gegenüber bestehen, werden gewichtet über den bedrohten Figurtyp und mit wie
* unter 1.) vorzeichenbehafteten Gewichten aufsummiert.
* <br><br><b>2.b) Deckung der eigenen Figuren</b>
* <br>Mit den gleichen Gewichten wie Bedrohungen, allerdings wird die Anzahl der
* deckenden Figuren um 1 verringert, wenn die andere Farbe am Zug ist.
* <br>Allgemein gilt: Weiß ist "max", schwarz ist "min".
**/
int AiBrain::valueStateNodeOpening (StateNode *state, int sisters)
{
int v = 0;
// Gewichte für eigenschaftsklassen...
/**
* Bewertung der Stellung muss immer niedriger sein als Figurenwerte:
* daher muss pieceWeight immer mindestens um den Faktor 128 größer sein,
* als positionWeight, da eine dame von maximal 16 gegnerischen Figuren
* gleichzeitig angegriffen werden kann und der Figurenwert für Damen 8
* beträgt.
**/
const int pieceWeight = 491520;
const int positionWeight = 1;
Colour toMove = colour;
Colour notToMove = Colour (toMove * -1);
//
// weiß positiv, schwarz negativ.
//
// gewichtete figurenwerte:
foreach (Piece p, state -> position.values ())
{
v = v + (pieceWeight * valueOfPiece (p));
}
// gewichtete Zahl an Bedrohungen und Deckungen:
foreach (Field f, state -> position.keys ())
{
Piece p = state -> position.value (f);
if (uncolouredPieceOfPiece (p) != KING)
{
v = v + (valueOfPiece (p) * positionWeight * (
(threatenedBy (state, f, toMove)).size () -
(threatenedBy (state, f, notToMove)).size ())
);
}
}
// Schachmatt / Patt - test
if (toMove == BLACK)
{
int nextCount = state -> next.size ();
if (nextCount == 0)
{
checkTest (state);
if (state -> check)
{
v = 1999999999 - state -> depth;
}
else
{
v = 0;
}
}
}
else
{
int nextCount = state -> next.size ();
if (nextCount == 0)
{
checkTest (state);
if (state -> check)
{
v = -1999999999 + state -> depth;
}
else
{
v = 0;
}
}
}
//...
return v;
};
/**
* @brief Berechnung der Stellungsbewertung im Mittelspiel:
* @details Miteinbezogen in die Stellungsbewertung werden:
* <br>1.), 2.a) und 2.b) von valueStateNodeOpening.
* <br><b>3.) Freibauern</b>
* <br>Es wird die Anzahl von (Frei-)bauern gewertet, wobei jeder zusätzlich
* <br>mit der Nähe zum "Horizont" gewichtet ist.
* <br><b>4.) Schachmatt-test</b>
* <br>Es wird getestet ob die Partei die als nächstes am Zug wäre im Schach steht
* <br>und wieviele Züge sie noch zur Auswahl hätte. bei 0 Zügen und Schach ist
* <br>das Ziel der jeweilgen anderen Partei erreicht.
**/
int AiBrain::valueStateNodeMiddlegame (StateNode *state, int sisters)
{
int v = 0;
int vTemp = 0;
// Gewichte für eigenschaftsklassen...
const int pieceWeight = 491520;
const int positionWeight = 56;
const int nextWeight = 1;
const int pawnWeight = 1;
//
// weiß positiv, schwarz negativ.
//
Colour toMove = colourOfDepthAndColour (state -> depth, colour);
Colour notToMove = Colour (toMove * -1);
bool whiteKingOnly = state -> whiteKingOnly;
bool blackKingOnly = state -> blackKingOnly;
bool check = state -> check;
// gewichtete figurenwerte:
foreach (Piece p, state -> position.values ())
{
v = v + (pieceWeight * valueOfPiece (p));
}
//qDebug () << "Figurenwerte: " << v;
vTemp = v;
// gewichtete Zahl an Bedrohungen und Deckungen:
foreach (Field f, state -> position.keys ())
{
Piece p = state -> position.value (f);
if (uncolouredPieceOfPiece (p) != KING)
{
v = v + (valueOfPiece (p) * positionWeight * (
(threatenedBy (state, f, toMove)).size () -
(threatenedBy (state, f, notToMove)).size ())
);
}
}
// Bauern
foreach (Field f, state -> position.keys ())
{
Piece p = state -> position.value (f);
// Anzahl und Entwicklug der Freibauern
if (p == PAWN_W)
{
// maximaler (manhattan) abstand eines Bauern von 0-linie ist 7
int distance = abs (yOfField (f) - 3) + abs (zOfField (f));
bool threat = false;
// weißer bauer muss für bauerntausch in 0-linie (bezogen auf z-achse) ziehen
for (int i = zOfField (f); i >= 0; i --)
{
if (isThreatened (state, f - fieldOfCoordinates (0,0,i), BLACK))
{
threat = true;
break;
}
}
if (!threat)
{
v = v + (pawnWeight * (7 - distance));
}
}
else if (p == PAWN_B)
{
int distance = abs (yOfField (f) - 3) + abs (zOfField (f) - 7);
bool threat = false;
// schwarzer bauer auf 7-linie
for (int i = zOfField (f); i <= 7; i ++)
{
if (isThreatened (state, f + fieldOfCoordinates (0,0,i), WHITE))
{
threat = true;
break;
}
}
if (!threat)
{
v = v + (pawnWeight * (7 - distance));
}
}
}
// Schachmatt / Patt - test
if (toMove == BLACK)
{
int nextCount = state -> next.size ();
if (nextCount == 0)
{
checkTest (state);
if (state -> check)
{
v = 1999999999 - state -> depth;
}
else
{
v = 0;
}
}
}
else
{
int nextCount = state -> next.size ();
if (nextCount == 0)
{
checkTest (state);
if (state -> check)
{
v = -1999999999 + state -> depth;
}
else
{
v = 0;
}
}
}
//...
return v;
};
/**
* @brief Berechnung der Stellungsbewertung im Endspiel:
* @details Miteinbezogen in die Stellungsbewertung werden:
* <br>1.), - 4.) von valueStateNodeMiddlegame.
* <br><b>5.) Anzahl möglicher Züge</b>
* <br>
**/
int AiBrain::valueStateNodeEndgame (StateNode *state, int sisters)
{
int v = 0;
int vTemp = 0;
// Gewichte für eigenschaftsklassen...
const int pieceWeight = 491520;
const int positionWeight = 1;
const int nextWeight = 2;
const int pawnWeight = 56;
//
// weiß positiv, schwarz negativ.
//
Colour toMove = colourOfDepthAndColour (state -> depth, colour);
Colour notToMove = Colour (toMove * -1);
// gewichtete figurenwerte:
foreach (Piece p, state -> position.values ())
{
v = v + (pieceWeight * valueOfPiece (p));
}
// gewichtete Zahl an Bedrohungen und Deckungen:
// foreach (Field f, state -> position.keys ())
// {
// Piece p = state -> position.value (f);
//
// if (uncolouredPieceOfPiece (p) != KING)
// {
// v = v + (valueOfPiece (p) * positionWeight * (
// (threatenedBy (state, f, toMove)).size () -
// (threatenedBy (state, f, notToMove)).size ())
// );
// }
// }
// Bauern
foreach (Field f, state -> position.keys ())
{
Piece p = state -> position.value (f);
// Anzahl und Entwicklug der Freibauern
if (p == PAWN_W)
{
// maximaler (manhattan) abstand eines Bauern von 0-linie ist 7
int distance = abs (yOfField (f) - 3) + abs (zOfField (f));
bool threat = false;
// weißer bauer muss für bauerntausch in 0-linie (bezogen auf z-achse) ziehen
for (int i = zOfField (f); i >= 0; i --)
{
if (isThreatened (state, f - fieldOfCoordinates (0,0,i), BLACK))
{
threat = true;
break;
}
}
if (!threat)
{
v = v + (pawnWeight * (7 - distance));
}
}
else if (p == PAWN_B)
{
int distance = abs (yOfField (f) - 3) + abs (zOfField (f) - 7);
bool threat = false;
// schwarzer bauer auf 7-linie
for (int i = zOfField (f); i <= 7; i ++)
{
if (isThreatened (state, f + fieldOfCoordinates (0,0,i), WHITE))
{
threat = true;
break;
}
}
if (!threat)
{
v = v + (pawnWeight * (7 - distance));
}
}
}
// Anzahl möglicher Züge für die Farbe die am Zug ist im Vergleich zu der Anzahl
// der Züge der anderen Partei im letzten bzw. nächsten Zug:
if (toMove == BLACK)
{
int nextCount = state -> next.size ();
if (nextCount == 0)
{
checkTest (state);
if (state -> check)
{
v = 1999999999 - state -> depth;
}
else
{
v = 0;
}
}
else
{
}
}
else
{
int nextCount = state -> next.size ();
if (nextCount == 0)
{
checkTest (state);
if (state -> check)
{
v = -1999999999 + state -> depth;
}
else
{
v = 0;
}
}
else
{
}
}
//...
//qDebug () << stringOfColour (toMove) << "valueEndgame" << v;
return v;
};
/**
* @brief Liste mit Zügen von Figuren der Farbe by, die Feld f1 angreifen.
* ist Feld f1 leer liefert die Funktion alle Züge die auf das Feld ziehen
* bei Bauern also solche mit (mit ggT (x,y,z...) normalisierte) manhattan distanz = 1
* (evtl. also auch Doppelsprünge)
**/
//! \TODO schauen ob auch alle Zugvektoren*Skalierung in die richtige Richtung zeigen.
QList<Move> AiBrain::threatenedBy (StateNode *state, Field f1, Colour by)
{
QList<Move> moves;
bool threat = false;
//qDebug () << "AiBrain::threatenedBy () Target: " << QString::number (f1, 8);
// Springerzüge:
// springer kann feld genau dann bedrohen wenn er einen manhattan abstand von 3
// und in einer komponente einen abstand von 2 hat (nur manhattan == 3 schließt
// abstand (+-1, +-1, +-1) nicht aus. im weg stehen kann nix.
foreach (Field knight, state -> position.keys (pieceOfUncolouredPieceAndColour (KNIGHT, by)))
{
if (knight != f1)
{
if (manhattanDistance (f1, knight) == 3)
{
if ((abs (xOfField (knight) - xOfField (f1)) == 2) ||
(abs (yOfField (knight) - yOfField (f1)) == 2) ||
(abs (zOfField (knight) - zOfField (f1)) == 2))
{
//qDebug () << "AiBrain::threatenedBy () Knight: " << QString::number (knight, 8);
moves << Move (knight, f1-knight+0030303, 1);
}
}
}
}
//qDebug () << "AiBrain::threatenedBy () Knights done.";
// Läuferzüge:
// Einheits-Läuferzüge haben immer Manhattan Distanz 2.
foreach (Field bishop, state -> position.keys (pieceOfUncolouredPieceAndColour (BISHOP, by)))
{
if (bishop != f1)
{
MoveVector mv = moveVectorOfFieldAndField (f1, bishop);
if (manhattanDistance (mv) == 2)
{
threat = true;
int scale = 1;
Field temp = move (f1, mv, scale);
while (temp != bishop)
{
if (colourOfPiece (state -> position.value (temp)) != NONE)
{
threat = false;
break;
}
scale ++;
if (scale > 10)
{
lustig ();
}
temp = move (f1, mv, scale);
}
if (threat)
{
//qDebug () << "AiBrain::threatenedBy () Bishop: " << QString::number (bishop, 8);
moves << Move (bishop, mv, -scale);
}
}
}
}
//qDebug () << "AiBrain::threatenedBy () Bishops done.";
// Turmzüge:
// Einheits-Turmzüge haben immer Manhattan Distanz 1.
foreach (Field rook, state -> position.keys (pieceOfUncolouredPieceAndColour (ROOK, by)))
{
if (rook != f1)
{
MoveVector mv = moveVectorOfFieldAndField (f1, rook);
if (manhattanDistance (mv) == 1)
{
threat = true;
int scale = 1;
Field temp = move (f1, mv, scale);
while (temp != rook)
{
if (colourOfPiece (state -> position.value (temp)) != NONE)
{
threat = false;
break;
}
scale ++;
temp = move (f1, mv, scale);
}
if (threat)
{
//qDebug () << "AiBrain::threatenedBy () Rook: " << QString::number (rook, 8);
moves << Move (rook, mv, -scale);
}
}
}
}
//qDebug () << "AiBrain::threatenedBy () Rooks done.";
// Damenzüge:
foreach (Field queen, state -> position.keys (pieceOfUncolouredPieceAndColour (QUEEN, by)))
{
if (queen != f1)
{
//qDebug () << "Queen: " << QString::number (queen, 8) << "F1: " << QString::number (f1, 8);
// als Turm interpretiert
MoveVector mv = moveVectorOfFieldAndField (f1, queen);
if (manhattanDistance (mv) == 2)
{
threat = true;
int scale = 1;
Field temp = move (f1, mv, scale);
while (temp != queen)
{
if (colourOfPiece (state -> position.value (temp)) != NONE)
{
threat = false;
break;
}
scale ++;
temp = move (f1, mv, scale);
}
if (threat)
{
//qDebug () << "AiBrain::threatenedBy () Queen: " << QString::number (queen, 8);
moves << Move (queen, mv, -scale);
}
}
// als Läufer interpretiert
else if (manhattanDistance (mv) == 1)
{
threat = true;
int scale = 1;
Field temp = move (f1, mv, scale);
while (temp != queen)
{
if (colourOfPiece (state -> position.value (temp)) != NONE)
{
threat = false;
break;
}
scale ++;
temp = move (f1, mv, scale);
}
if (threat)
{
//qDebug () << "AiBrain::threatenedBy () Queen: " << QString::number (queen, 8);
moves << Move (queen, mv, -scale);
}
}
}
}
//qDebug () << "AiBrain::threatenedBy () Queens done.";
// Bauernzüge:
foreach (Field f2, state -> position.keys (pieceOfUncolouredPieceAndColour (PAWN, by)))
{
Colour c = colourOfPiece (state -> position.value (f1));
if (f1 != f2)
{
if (isThreatenedByPawn (state, f1, f2, c, by))
{
MoveVector mv = fieldOfCoordinates (xOfField (f1)-xOfField (f2),yOfField (f1)-yOfField (f2),zOfField (f1)-zOfField (f2)) + 0030303;
// bauerntausch
// schlagen en passant wird von calculateSuccessors berechnet....
if ((by == WHITE && zOfField (f1) == 0) ||
((by == BLACK && zOfField (f1) == 7)))
{
moves << Move (f2, mv, 3);
moves << Move (f2, mv, 4);
}
else
{
moves << Move (f2, mv, 1);
}
//qDebug () << "threatenedBy pawn" << stringOfField (f2);
}
}
}
//qDebug () << "AiBrain::threatenedBy () Pawns done.";
// Königszüge:
{
Field f2 = state -> position.key (pieceOfUncolouredPieceAndColour (KING, by));
if (f1 != f2)
{
// auch hier nur Züge der "Länge" 1, daher kann nichts im weg stehen.
int x = xOfField (f2) - xOfField (f1);
if (abs (x) < 2)
{
int z = zOfField (f2) - zOfField (f1);
if (abs (z) < 2)
{
int y = yOfField (f2) - yOfField (f1);
if (abs (y) < 2)
{
int md = abs (x) + abs (y) + abs (z);
if ((md == 2) || (md == 1))
{
// gegnerischer König kann nicht angegriffen werden...
if (uncolouredPieceOfPiece (state -> position.value (f1)) != KING)
{
if (!isThreatened (state, f1, Colour (-1 * by)))
{
moves << Move (f2, 0030303 + fieldOfCoordinates (x,y,z), 1);
}
}
}
}
}
}
}
}
//qDebug () << "AiBrain::threatenedBy () Kings done.";
return moves;
};
/**
* @brief Zugmöglichkeiten des Königs in seinen 18 Zugrichtungen berechnen
* @details Alle berechneten Zugmöglichkeiten werden sofort als keys zu leeren values in
* QHash state eingetragen.
**/
void AiBrain::calculateKingsMoves (StateNode *state, Field king, Colour notToMove)
{
// für jede der 18 Möglichkeiten schauen ob auf Brett und wenn ob sicher.
Field king2 [18] = {
king - 0010000, // zurück
king + 0010000, // vor
king - 00100, // runter
king + 00100, // hoch
king - 001, // links
king + 001, // rechts
king - 0010001, // links, nach hinten
king + 001 - 0010000, // rechts, nach hinten
king - 001 + 0010000, // links, nach vorne
king + 0010001, // rechts, nach vorne
king + 0010100, // hoch, nach vorne
king - 0010100, // runter, nach hinten
king + 00100 - 0010000, // nach hinten, hoch
king - 00100 + 0010000, // nach vorne, runter
king + 00101, // hoch, rechts
king - 00101, // runter, links
king + 001 - 00100, // runter, rechts
king - 001 + 00100 // hoch, links
};
// damit der König nicht in seiner alten position einer möglichen Bedrohung
// im weg stehen kann.
state -> position.remove (king);
for (int i = 0; i < 18; i++)
{
if (fieldOnBoard (king2 [i]))
{
// if (i == 13)
// lustig ();
Piece p = state -> position.value (king2 [i]);
// Zielfeld darf nicht bedroht sein und es darf keine eigene figur darauf stehen.
if (colourOfPiece (p) == notToMove || colourOfPiece (p) == NONE)
{
if (!isThreatened (state, king2 [i], notToMove))
{
Move m (king, king2 [i] - king + 0030303, 1);
state -> next.insert (m, 0);
//qDebug () << "AiBrain::calculateKingsMoves () added a king move for" << stringOfField (king) << stringOfMoveVector (m.by, 1) << i;
}
}
}
}
// Rochade für weiß
if (notToMove == BLACK)
{
// damenseite
if (state -> whiteState & CASTLE_LONG)
{
if (king == 0070304)
{
if (!isThreatened (state, 0070304, notToMove) &&
!isThreatened (state, 0070303, notToMove) &&
!isThreatened (state, 0070302, notToMove) &&
!isThreatened (state, 0070301, notToMove) &&
!isThreatened (state, 0070300, notToMove) &&
!state -> position.contains (0070303) &&
!state -> position.contains (0070302) &&
!state -> position.contains (0070301) &&
state -> position.value (0070300) == ROOK_W)
{
Move m (king, 0030301, 1);
state -> next.insert (m, 0);
//qDebug () << "AiBrain::calculateKingsMoves () added a king move for" << stringOfField (king) << stringOfMoveVector (m.by, 1);
}
}
}
// königsseite
if (state -> whiteState & CASTLE_SHORT)
{
if (king == 0070304)
{
if (!isThreatened (state, 0070304, notToMove) &&
!isThreatened (state, 0070305, notToMove) &&
!isThreatened (state, 0070306, notToMove) &&
!isThreatened (state, 0070307, notToMove) &&
!state -> position.contains (0070305) &&
!state -> position.contains (0070306) &&
state -> position.value (0070307) == ROOK_W)
{
Move m (king, 0030305, 1);
state -> next.insert (m, 0);
//qDebug () << "AiBrain::calculateKingsMoves () added a king move for" << stringOfField (king) << stringOfMoveVector (m.by, 1);
}
}
}
}
// schwarz
else
{
if (state -> blackState & CASTLE_LONG)
{
if (king == 0000304)
{
if (!isThreatened (state, 0000304, notToMove) &&
!isThreatened (state, 0000303, notToMove) &&
!isThreatened (state, 0000302, notToMove) &&
!isThreatened (state, 0000301, notToMove) &&
!isThreatened (state, 0000300, notToMove) &&
!state -> position.contains (0000303) &&
!state -> position.contains (0000302) &&
!state -> position.contains (0000301) &&
state -> position.value (0000300) == ROOK_B)
{
Move m (king, 0030301, 1);
state -> next.insert (m, 0);
//qDebug () << "AiBrain::calculateKingsMoves () added a king move for" << stringOfField (king) << stringOfMoveVector (m.by, 1);
}
}
}
if (state -> blackState & CASTLE_SHORT)
{
if (king == 0000304)
{
if (!isThreatened (state, 0000304, notToMove) &&
!isThreatened (state, 0000305, notToMove) &&
!isThreatened (state, 0000306, notToMove) &&
!isThreatened (state, 0000307, notToMove) &&
!state -> position.contains (0000305) &&
!state -> position.contains (0000306) &&
state -> position.value (0000307) == ROOK_B)
{
Move m (king, 0030305, 1);
state -> next.insert (m, 0);
//qDebug () << "AiBrain::calculateKingsMoves () added a king move for" << stringOfField (king) << stringOfMoveVector (m.by, 1);
}
}
}
}
// König nach der Berechnung wieder an seinen alten Platz setzen.
state -> position.insert (king, pieceOfUncolouredPieceAndColour (KING, Colour (notToMove * -1)));
return;
};
/**
* @brief Zugmöglichkeiten und Zustand der Stellung berechnen.
* @details Hier wird berechnet, ob der König im Schach steht und von wievielen
* und welchen gegnerischen Figuren er aus welchen Richtungen bedroht wird.
* Zusätzlich werden hier die Zugmöglichkeiten berechnet. Jeder mögliche Zug wird dann
* in der QMap next (mit leerem StateNode) angelegt.
* Zugmöglichkeiten ergeben sich je nach dem Zustand des Königs der Farbe, die am Zug ist:
* <br><br><b>1.) König nicht im Schach:</b>
* <br><b>1.a) Fesselungen:</b>
* <br>Eine Figur ist dann auf den Bereich zwischen König und Angreifer gefesselt, wenn diese
* alleine den direkten Weg versperrt. Ab 2 eigenen Figuren im Weg bestehen keine
* Einschränkungen der Zugmöglichkeiten, genausowenig, wie bei einer gegnerischen Figur
* im Weg.
* <br><br><b>2.) König im Schach:</b>
* <br><b>2.a) König einfach bedroht:</b>
* <br>Zugmöglichkeiten sind hier alle Züge, die den direkten Weg zwischen Angreifer und
* König versperren, Züge die den Angreifer schlagen und Königszüge auf unbedrohte Felder.
* <br><b>2.b) König mehrfach bedroht:</b>
* <br>Bei einer Mehrfachbedrohung kommen nur Königszüge auf unbedrohte Felder in Frage.
* <br><br>Alle berechneten Werte werden direkt im StateNode auf den durch state verwiesen
* wird aktualisiert. Der StateNode kann danach für die Stellungsbewertung und die
* Erzeugung der Nachfolgerknoten verwendet werden.
* @see calculateKingsMoves
* Berechnet alle möglichen Königszüge
* @see threatenedBy
* Liste der bedrohenden Züge einer Farbe auf ein Feld
* @see isThreatened
* Ist Feld durch Farbe bedroht?
* @see calculateMovesOfPinnedAndUnpinned
* Berechnet alle Zugmöglichkeiten, ohne die des Königs in Abhängigkeit davon ob König im
* Schach ist und welche Figuren gefesselt sind.
**/
void AiBrain::calculateStateAndMoves (StateNode *state)
{
tempPosition = &(state -> position);
bool check = false;
// wieviele figuren bedrohen den könig?
// bei mehr als einer hilft ein "in den weg springen" nicht mehr, also
// muss der könig ziehen
int checkCount = 0;
// koenig der farbe, die bei aktueller suchtiefe am zug ist
Colour toMove = colourOfDepthAndColour (state -> depth, colour);
Colour notToMove = Colour (toMove * -1);
Field king = state -> position.key (pieceOfUncolouredPieceAndColour (KING, toMove));
/** Feld auf der die Figur steht, durch die König bedroht wird (wird nur im Falle einer Einfachbedrohung gebraucht) **/
Field threat = -1;
/** Vektoren, an denen entlang sich die gefesselten Figuren noch bewegen können. **/
QHash<Field,MoveVector> pinMV;
//qDebug () << "AiBrain::calculateStateAndMoves () Target: " << QString::number (king, 8);
Field tempPin;
MoveVector tempPinMV;
int inWayCounter = 0;
// Läuferzüge:
// Einheits-Läuferzüge haben immer Manhattan Distanz 2.
foreach (Field bishop, state -> position.keys (pieceOfUncolouredPieceAndColour (BISHOP, notToMove)))
{
MoveVector mv = moveVectorOfFieldAndField (king, bishop);
if (manhattanDistance (mv) == 2)
{
check = true;
int scale = 1;
Field temp = move (king, mv, scale);
while (temp != bishop)
{
Colour inWay = colourOfPiece (state -> position.value (temp));
if (inWay != NONE)
{
check = false;
if (inWay == notToMove)
{
inWayCounter = 0;
break;
}
else
{
// Figur temp wird ge-pin-t wenn sie Farbe
tempPin = temp;
tempPinMV = mv;
inWayCounter ++;
}
}
scale ++;
temp = move (king, mv, scale);
}
if (check)
{
checkCount ++;
//qDebug () << "AiBrain::calculateStateAndMoves () Bishop: " << QString::number (bishop, 8);
threat = bishop;
}
if (inWayCounter == 1)
{
pinMV[tempPin] = tempPinMV;
}
}
}
//qDebug () << "AiBrain::calculateStateAndMoves () Bishops done." << checkCount;
/// \FIXME dicker fehler in berechnung von zügen gepinter türme...
// Turmzüge:
// Einheits-Turmzüge haben immer Manhattan Distanz 1.
foreach (Field rook, state -> position.keys (pieceOfUncolouredPieceAndColour (ROOK, notToMove)))
{
MoveVector mv = moveVectorOfFieldAndField (king, rook);
if (manhattanDistance (mv) == 1)
{
inWayCounter = 0;
check = true;
int scale = 1;
Field temp = move (king, mv, scale);
while (temp != rook)
{
Colour inWay = colourOfPiece (state -> position.value (temp));
if (inWay != NONE)
{
//qDebug () << "something in the rook's way" << QString::number (temp, 8);
check = false;
if (inWay == notToMove)
{
inWayCounter = 0;
break;
}
else
{
// Figur temp wird ge-pin-t wenn sie Farbe
tempPin = temp;
tempPinMV = mv;
inWayCounter ++;
}
}
scale ++;
temp = move (king, mv, scale);
}
if (check)
{
checkCount ++;
//qDebug () << "AiBrain::calculateStateAndMoves () Rook: " << QString::number (rook, 8);
threat = rook;
}
if (inWayCounter == 1)
{
pinMV[tempPin] = tempPinMV;
}
}
}
//qDebug () << "AiBrain::calculateStateAndMoves () Rooks done." << checkCount;
// mehr als einfache Bedrohung -> nur noch Königszüge möglch.
if (checkCount > 1)
{
// alle bisher berechneten Zugmöglichkeiten werden wieder gelöscht
state -> next.clear ();
calculateKingsMoves (state, king, notToMove);
// wenn nur noch königszüge möglich ist nach deren Berechnung auch nix mehr zu tun.
return;
}
// Damenzüge:
foreach (Field queen, state -> position.keys (pieceOfUncolouredPieceAndColour (QUEEN, notToMove)))
{
// als Turm interpretiert
MoveVector mv = moveVectorOfFieldAndField (king, queen);
if (manhattanDistance (mv) == 2)
{
inWayCounter = 0;
check = true;
int scale = 1;
Field temp = move (king, mv, scale);
while (temp != queen)
{
Colour inWay = colourOfPiece (state -> position.value (temp));
if (inWay != NONE)
{
//qDebug () << "something in the queen's way" << QString::number (temp, 8);
check = false;
if (inWay == notToMove)
{
inWayCounter = 0;
break;
}
else
{
// Figur temp wird ge-pin-t wenn sie Farbe
tempPin = temp;
tempPinMV = mv;
inWayCounter ++;
}
}
scale ++;
temp = move (king, mv, scale);
}
if (check)
{
checkCount ++;
//qDebug () << "AiBrain::calculateStateAndMoves () Queen: " << QString::number (queen, 8);
threat = queen;
}
if (inWayCounter == 1)
{
pinMV[tempPin] = tempPinMV;
}
}
// als Läufer interpretiert
else if (manhattanDistance (mv) == 1)
{
inWayCounter = 0;
check = true;
int scale = 1;
Field temp = move (king, mv, scale);
while (temp != queen)
{
Colour inWay = colourOfPiece (state -> position.value (temp));
if (inWay != NONE)
{
//qDebug () << "something in the queen's way" << QString::number (temp, 8);
check = false;
if (inWay == notToMove)
{
inWayCounter = 0;
break;
}
else
{
// Figur temp wird ge-pin-t wenn sie Farbe
tempPin = temp;
tempPinMV = mv;
inWayCounter ++;
}
}
scale ++;
temp = move (king, mv, scale);
}
if (check)
{
checkCount ++;
//qDebug () << "AiBrain::calculateStateAndMoves () Queen: " << QString::number (queen, 8);
threat = queen;
}
if (inWayCounter == 1)
{
pinMV[tempPin] = tempPinMV;
}
}
}
//qDebug () << "AiBrain::calculateStateAndMoves () Queens done." << checkCount;
// mehr als einfache Bedrohung -> nur noch Königszüge möglch.
if (checkCount > 1)
{
// alle bisher berechneten Zugmöglichkeiten werden wieder gelöscht
state -> next.clear ();
calculateKingsMoves (state, king, notToMove);
// wenn nur noch königszüge möglich ist nach deren Berechnung auch nix mehr zu tun.
return;
}
// !! zwischen bedrohenden Springer bzw. Bauern kann keine Figur springer.
// !! daher kann, wenn schlagende Figuren ge-pin-t sind nur der König ziehen.
// Springerzüge:
// springer kann könig genau dann bedrohen wenn er einen manhattan abstand von 3
// und in einer komponente einen abstand von 2 hat (nur manhattan == 3 schließt
// abstand (+-1, +-1, +-1) nicht aus. im weg stehen kann nix.
foreach (Field knight, state -> position.keys (pieceOfUncolouredPieceAndColour (KNIGHT, notToMove)))
{
if (manhattanDistance (king, knight) == 3)
{
if (abs (xOfField (knight) - xOfField (king)) == 2)
{
checkCount ++;
threat = knight;
}
else if (abs (zOfField (knight) - zOfField (king)) == 2)
{
checkCount ++;
threat = knight;
}
else if (abs (yOfField (knight) - yOfField (king)) == 2)
{
checkCount ++;
threat = knight;
}
}
}
// mehr als einfache Bedrohung -> nur noch Königszüge möglch.
if (checkCount > 1)
{
// alle bisher berechneten Zugmöglichkeiten werden wieder gelöscht
state -> next.clear ();
calculateKingsMoves (state, king, notToMove);
// wenn nur noch königszüge möglich ist nach deren Berechnung auch nix mehr zu tun.
return;
}
// Bauernzüge:
if (notToMove == WHITE)
{
foreach (Field f1, state -> position.keys (PAWN_W))
{
check = false;
// nur züge der "länge" 1 -> da kann nix im weg stehen.
// weiss spielt von 7 nach 0 in z richtung
if (zOfField (king) - zOfField (f1) == -1)
{
check = abs (xOfField (king) - xOfField (f1)) + abs (yOfField (king) - yOfField (f1)) == 1;
if (check)
{
checkCount ++;
//qDebug () << "AiBrain::calculateStateAndMoves () Pawn: " << QString::number (f1, 8);
threat = f1;
}
}
}
}
else
{
foreach (Field f1, state -> position.keys (PAWN_B))
{
check = false;
// nur züge der "länge" 1 -> da kann nix im weg stehen.
// schwarz spielt von 0 nach 7 in z richtung
if (zOfField (king) - zOfField (f1) == 1)
{
check = abs (xOfField (king) - xOfField (f1)) + abs (yOfField (king) - yOfField (f1)) == 1;
if (check)
{
checkCount ++;
//qDebug () << "AiBrain::calculateStateAndMoves () Pawn: " << QString::number (f1, 8);
threat = f1;
}
}
}
}
//qDebug () << "AiBrain::calculateStateAndMoves () Pawns done.";
//qDebug () << "AiBrain::calculateStateAndMoves () check count: " << checkCount << threat;
// mehr als einfache Bedrohung -> nur noch Königszüge möglch.
if (checkCount > 1)
{
// alle bisher berechneten Zugmöglichkeiten werden wieder gelöscht
state -> next.clear ();
calculateKingsMoves (state, king, notToMove);
// wenn nur noch königszüge möglich ist nach deren Berechnung auch nix mehr zu tun.
return;
}
//qDebug () << "calulStaMov" << checkCount;
// foreach (Field fp, pinMV.keys ())
// {
// qDebug () << stringOfField (fp);
// }
// Königszüge
calculateKingsMoves (state, king, notToMove);
// die anderen Züge...
calculateMovesOfPinnedAndUnpinned (state, king, toMove, threat, &pinMV);
};
/**
* @brief berechnet züge an den vektor threatMV zwischen könig und bedrohung gefesselten bauern auf f.
**/
void AiBrain::calculatePinnedPawnsMoves (StateNode *state, Field f, MoveVector threatMV, Colour toMove)
{
int x = xOfField (threatMV) - 3;
if (abs (x) > 2)
{
return;
}
int y = yOfField (threatMV) - 3;
if (abs (y) > 2)
{
return;
}
int z = zOfField (threatMV) - 3;
if (abs (z) > 2)
{
return;
}
int md = abs (x) + abs (y) + abs (z);
if (md > 2)
{
return;
}
if (abs (x) == 2 || abs (y) == 2 || abs (z) == 2)
{
// gekürzte manhattanDistanz, da auf möglichen doppelsprung mit md == 1 untersucht wird.
md == 1;
}
/// @TODO "pinned" schlagen en passant hoch und runter, schlagen in x-y-ebene nicht möglich!
Piece p = state -> position.value (f);
if (colourOfPiece (p) == WHITE)
{
// weiß spielt in negativer x-richtung
// fesselung durch läufer / dame
// bei manhattanDistanz = 2 muss nur geschaut werden
// ob der Bauer die Bedrohung schlagen kann. da Bauer ohne
// schlagen nur mit manhattandistanz von 1 ziehen kann.
if (md == 2)
{
// in x-y-ebene schlagen ist nicht möglich.
if ((z) == -1)
{
Field target = move (f,threatMV,1);
if (colourOfPiece ((state -> position).value (target)) == BLACK)
{
// wenn am rand in z-richtung: bauerntausch
if (zOfField (target) == 0)
{
// tausch nach springer
state -> next.insert (Move (f, threatMV, 3), 0);
// tausch nach dame
state -> next.insert (Move (f, threatMV, 4), 0);
}
else
{
state -> next.insert (Move (f, threatMV, 1), 0);
}
}
}
}
// fesselung durch turm / dame
// hier kommt kein schlagen in frage...
else if (md == 1)
{
// in x richtung kann nicht gezogen werden.
if ((x) == 0)
{
Field target = move (f, threatMV, 1);
if (fieldOnBoard (target) &&
colourOfPiece ((state -> position).value (target)) == NONE)
{
if (zOfField (target) == 0)
{
// tausch nach springer
state -> next.insert (Move (f, threatMV, 3), 0);
// tausch nach dame
state -> next.insert (Move (f, threatMV, 4), 0);
}
else if (zOfField (f) == 6)
{
// Doppelsprung
if (nodeStateOfXCoord (xOfField (target)) & state -> whiteState)
{
state -> next.insert (Move (f, threatMV, 2), 0);
}
state -> next.insert (Move (f, threatMV, 1), 0);
}
else
{
state -> next.insert (Move (f, threatMV, 1), 0);
}
}
}
}
}
else
{
// schwarz spielt in positiver z-richtung
// fesselung durch läufer / dame
// bei manhattanDistanz = 2 muss nur geschaut werden
// ob der Bauer die Bedrohung schlagen kann. da Bauer ohne
// schlagen nur mit manhattandistanz von 1 ziehen kann.
if (md == 2)
{
// in x-y-ebene schlagen nicht möglich
// z-komponente != 0
if ((z) == 1)
{
Field target = move (f,threatMV,1);
if (colourOfPiece ((state -> position).value (target)) == WHITE)
{
if ((zOfField (threatMV) - 3) == 1)
{
// wenn am rand in z-richtung: bauerntausch
if (zOfField (target) == 0)
{
// tausch nach springer
state -> next.insert (Move (f, threatMV, 3), 0);
// tausch nach dame
state -> next.insert (Move (f, threatMV, 4), 0);
}
else
{
state -> next.insert (Move (f, threatMV, 1), 0);
}
}
}
}
}
// fesselung durch turm / dame
// hier kommt kein schlagen in frage...
else if (md == 1)
{
// in x richtung kann nicht gezogen werden.
if ((x) == 0)
{
Field target = move (f, threatMV, 1);
if (fieldOnBoard (target) &&
colourOfPiece ((state -> position).value (target)) == NONE)
{
if (zOfField (target) == 7)
{
// tausch nach springer
state -> next.insert (Move (f, threatMV, 3), 0);
// tausch nach dame
state -> next.insert (Move (f, threatMV, 4), 0);
}
else if (zOfField (f) == 1)
{
// Doppelsprung
if (nodeStateOfXCoord (xOfField (target)) & state -> blackState)
{
state -> next.insert (Move (f, threatMV, 2), 0);
}
state -> next.insert (Move (f, threatMV, 1), 0);
}
else
{
state -> next.insert (Move (f, threatMV, 1), 0);
}
}
}
}
}
};
/**
* @brief berechnet die züge des Bauern auf Feld f, der nicht gefesselt ist.
**/
void AiBrain::calculateUnpinnedPawnsMoves (StateNode *state, Field f, Colour toMove)
{
Colour notToMove = Colour (-1 * toMove);
int z = zOfField (f);
// bei Bauern hängen Zugmöglichkeiten von der Farbe ab.
// (weiß spielt in negativer, schwarz in positiver z-richtung)
if (toMove == WHITE)
{
if (z == 3)
{
// letzter zug war Doppelsprung eines bauern:
// beim schlagen en passant kann nichts im weg stehen,
// da vorher doppelsprung eines gegnerischen bauern über
// das zielfeld möglich war.
if (state -> last != -1)
{
// schlagen en passant nach rechts / links
int diff = state -> last - xOfField (f);
if (abs (diff) == 1 && yOfField (f) == 3)
{
// als weiß wird in negative z richtung gespielt.
state -> next.insert (Move (f, 0020303 + diff, 1), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a pawn move for" << stringOfField (f) << stringOfMoveVector (0020303 + diff,1);
}
// schlagen en passant nach oben / unten (gleiche x-komponente, y-komponente mit differenz von +-1)
else if (diff == 0)
{
diff = 3 - yOfField (f);
if (abs (diff) == 1)
{
state -> next.insert (Move (f, 0020303 + diff, 1), 0);
}
}
}
}
else if (z == 6)
{
// Beide Felder vor dem Bauern sind frei.
if (!state -> position.contains (f - 010000) &&
!state -> position.contains (f - 020000))
{
// Doppelsprung geht nur wenn Bauer noch nicht bewegt worden ist.
// und falls keine daten darüber vom server erhältlich
// muss bauer in y = 3 ebene sein.
if ((nodeStateOfXCoord (xOfField (f)) & state -> whiteState) && yOfField (f) == 3)
{
state -> next.insert (Move (f, 0010303, 1), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a pawn move for" << stringOfField (f) << stringOfMoveVector (0010303,1);
}
}
}
// kein schlagen en passant, kein doppelsprung:
// wenn feld vor Bauer frei ist:
if (!state -> position.contains (f - 010000) &&
fieldOnBoard (f - 010000))
{
if (zOfField (f - 010000) == 0)
{
// bauerntausch
state -> next.insert (Move (f, 0020303, 3), 0);
state -> next.insert (Move (f, 0020303, 4), 0);
}
else
{
state -> next.insert (Move (f, 0020303, 1), 0);
}
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a pawn move for" << stringOfField (f) << stringOfMoveVector (0020303,1);
}
// feld über oder unter bauer frei?
if (!state -> position.contains (f - 000100) &&
fieldOnBoard (f - 000100))
{
state -> next.insert (Move (f, 0030203, 1), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a pawn move for" << stringOfField (f) << stringOfMoveVector (0030203,1);
}
if (!state -> position.contains (f + 000100) &&
fieldOnBoard (f + 000100))
{
state -> next.insert (Move (f, 0030403, 1), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a pawn move for" << stringOfField (f) << stringOfMoveVector (0030403,1);
}
// Schlagvektoren
for (int i = 0; i<4; i++)
{
MoveVector vec = WhitePawnCaptureVectors[i];
Field target = move (f,vec,1);
// wenn auf zielfeld ein gegner steht ist schlagen möglich
if (colourOfPiece (state -> position.value (target)) == BLACK)
{
if (zOfField (target) == 0)
{
// bauerntausch
state -> next.insert (Move (f, vec, 3), 0);
state -> next.insert (Move (f, vec, 4), 0);
}
else
{
state -> next.insert (Move (f, vec, 1), 0);
}
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a pawn move for" << stringOfField (f) << stringOfMoveVector (vec,1);
}
}
}
else
{
if (z == 4)
{
// letzter zug war Doppelsprung eines bauern:
if (state -> last != -1)
{
// schlagen en passant nach rechts / links
int diff = state -> last - xOfField (f);
if (abs (diff) == 1 && yOfField (f) == 3)
{
// als weiß wird in negative z richtung gespielt.
state -> next.insert (Move (f, 0040303 + diff, 1), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a pawn move for" << stringOfField (f) << stringOfMoveVector (0020303 + diff,1);
}
// schlagen en passant nach oben / unten (gleiche x-komponente, y-komponente mit differenz von +-1)
else if (diff == 0)
{
diff = 3 - yOfField (f);
if (abs (diff) == 1)
{
state -> next.insert (Move (f, 0040303 + diff, 1), 0);
}
}
}
}
else if (z == 1)
{
// Beide Felder vor dem Bauern sind frei.
if (!state -> position.contains (f + 010000) &&
!state -> position.contains (f + 020000))
{
// Doppelsprung geht nur wenn Bauer noch nicht bewegt worden ist.
if ((nodeStateOfXCoord (xOfField (f)) & state -> blackState) && yOfField (f) == 3)
{
state -> next.insert (Move (f, 0050303, 1), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a pawn move for" << stringOfField (f) << stringOfMoveVector (0050303,1);
}
}
}
// kein schlagen en passant, kein doppelsprung:
// wenn feld vor Bauer unbesetzt ist:
if (!state -> position.contains (f + 010000) &&
fieldOnBoard (f + 010000))
{
if (zOfField (f + 010000) == 7)
{
// bauerntausch
state -> next.insert (Move (f, 0040303, 3), 0);
state -> next.insert (Move (f, 0040303, 4), 0);
}
else
{
state -> next.insert (Move (f, 0040303, 1), 0);
}
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a pawn move for" << stringOfField (f) << stringOfMoveVector (0040303,1);
}
// feld über oder unter bauer frei?
if (!state -> position.contains (f - 000100) &&
fieldOnBoard (f - 000100))
{
state -> next.insert (Move (f, 0030203, 1), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a pawn move for" << stringOfField (f) << stringOfMoveVector (0030203,1);
}
if (!state -> position.contains (f + 000100) &&
fieldOnBoard (f + 000100))
{
state -> next.insert (Move (f, 0030403, 1), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a pawn move for" << stringOfField (f) << stringOfMoveVector (0030403,1);
}
// Schlagvektoren
for (int i = 0; i<4; i++)
{
MoveVector vec = BlackPawnCaptureVectors[i];
Field target = move (f,vec,1);
// wenn auf zielfeld ein gegner steht ist schlagen möglich
if (colourOfPiece (state -> position.value (target)) == WHITE)
{
if (zOfField (target) == 7)
{
// bauerntausch
state -> next.insert (Move (f, vec, 3), 0);
state -> next.insert (Move (f, vec, 4), 0);
}
else
{
state -> next.insert (Move (f, vec, 1), 0);
}
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a pawn move for" << stringOfField (f) << stringOfMoveVector (vec,1);
}
}
}
};
/**
* @brief Berechnet die Zugmöglichkeiten für Einfachschach und kein Schach.
* @details
* @param state
**/
void AiBrain::calculateMovesOfPinnedAndUnpinned (
StateNode *state,
Field king,
Colour toMove,
Field threat,
QHash<Field,MoveVector>* pinMV)
{
// Königszüge werden außerhalb der Funktion berechnet.
/** Figuren, die nicht in Bedrohungsweg stehen. **/
QList<Field> unpinned;
foreach (Field f, state -> position.keys ())
{
Piece p = (state -> position).value (f);
// Figur muss von Farbe des ziehenden sein, darf aber nicht König sein (weil
// Königszüge außerhalb berechnet werden) und auch nicht ge-pin-te Figur sein.
if (colourOfPiece (p) == toMove)
{
if (uncolouredPieceOfPiece (p) != KING &&
!pinMV -> contains (f))
{
unpinned << f;
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () unpinned:" << QString::number (f, 8);
}
}
}
// König nicht im Schach
if (threat == -1)
{
// Züge ohne Einschränkungen durch Fesselungen oder Schach:
foreach (Field f, unpinned)
{
Piece p = (state -> position).value (f);
switch (uncolouredPieceOfPiece (p))
{
case PAWN:
{
calculateUnpinnedPawnsMoves (state, f, colourOfPiece (p));
break;
}
case BISHOP:
{
// 6 mögliche richtungen in die Läufer ziehen können (mit positiven und negativen Streckungen).
for (int j = 0; j < 6; j ++)
{
MoveVector vec = BishopVectors [j];
// vektoren mit positiven streckungen durchprobieren
for (int i = 1; i < 8; i ++)
{
// entweder irgendwann ist das Brett zu ende oder wir treffen auf eine Figur im weg.
if (!fieldOnBoard (move (f, vec, i)))
{
break;
}
Piece toMoveTo = state -> position.value (move (f, vec, i));
if (colourOfPiece (toMoveTo) != NONE)
{
if (colourOfPiece (toMoveTo) != toMove)
{
// Figur ist gegnerisch und kann daher geschlagen werden.
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a bishop move for" << stringOfField (f) << stringOfMoveVector (vec, i) << QString::number (vec, 8) << i;
}
// wenn figur nicht geschlagen werden kann ist zug nicht möglich und innere schleife kann abgebrochen werden.
break;
}
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a bishop move for" << stringOfField (f) << stringOfMoveVector (vec, i) << QString::number (vec, 8) << i;
}
// dann mit negativen.
for (int i = -1; i > -8; i --)
{
// entweder irgendwann ist das Brett zu ende oder wir treffen auf eine Figur im weg.
if (!fieldOnBoard (move (f, vec, i)))
{
break;
}
Piece toMoveTo = state -> position.value (move (f, vec, i));
if (colourOfPiece (toMoveTo) != NONE)
{
if (colourOfPiece (toMoveTo) != toMove)
{
// Figur ist gegnerisch und kann daher geschlagen werden.
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a bishop move for" << stringOfField (f) << stringOfMoveVector (vec, i) << QString::number (vec, 8) << i;
}
// wenn figur nicht geschlagen werden kann ist zug nicht möglich und innere schleife kann abgebrochen werden.
break;
}
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a bishop move for" << stringOfField (f) << stringOfMoveVector (vec, i) << QString::number (vec, 8) << i;
}
}
break;
}
case KNIGHT:
{
// Springerzüge: hier kann nix im weg stehen.
for (int i = 0; i < 24; i ++)
{
MoveVector vec = KnightVectors [i];
Field target = move (f, vec, 1);
// nur das spielbrett kann zu klein sein.
if (fieldOnBoard (target))
{
Piece tp = state -> position.value (target);
if (colourOfPiece (tp) == NONE ||
colourOfPiece (tp) != toMove)
{
state -> next.insert (Move (f, vec, 1), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a knight move" << stringOfMoveVector (vec, 1);
}
}
}
break;
}
case ROOK:
{
for (int j = 0; j < 3; j ++)
{
MoveVector vec = RookVectors [j];
// vektoren mit positiven streckungen durchprobieren
for (int i = 1; i < 8; i ++)
{
// entweder irgendwann ist das Brett zu ende oder wir treffen auf eine Figur im weg.
if (!fieldOnBoard (move (f, vec, i)))
{
break;
}
Piece toMoveTo = state -> position.value (move (f, vec, i));
if (colourOfPiece (toMoveTo) != NONE)
{
if (colourOfPiece (toMoveTo) != toMove)
{
// Figur ist gegnerisch und kann daher geschlagen werden.
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a rook move for" << stringOfField (f) << stringOfMoveVector (vec, i);
}
// wenn figur nicht geschlagen werden kann ist zug nicht möglich und innere schleife kann abgebrochen werden.
break;
}
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a rook move for" << stringOfField (f) << stringOfMoveVector (vec, i);
}
// dann mit negativen.
for (int i = -1; i > -8; i --)
{
// entweder irgendwann ist das Brett zu ende oder wir treffen auf eine Figur im weg.
if (!fieldOnBoard (move (f, vec, i)))
{
break;
}
Piece toMoveTo = state -> position.value (move (f, vec, i));
if (colourOfPiece (toMoveTo) != NONE)
{
if (colourOfPiece (toMoveTo) != toMove)
{
// Figur ist gegnerisch und kann daher geschlagen werden.
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a rook move for" << stringOfField (f) << stringOfMoveVector (vec, i);
}
// wenn figur nicht geschlagen werden kann ist zug nicht möglich und innere schleife kann abgebrochen werden.
break;
}
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a rook move for" << stringOfField (f) << stringOfMoveVector (vec, i);
}
}
break;
}
case QUEEN:
{
// 3 mögliche Richtungen als Turm interpretiert
for (int j = 0; j < 3; j ++)
{
MoveVector vec = RookVectors [j];
// vektoren mit positiven streckungen durchprobieren
for (int i = 1; i < 8; i ++)
{
// entweder irgendwann ist das Brett zu ende oder wir treffen auf eine Figur im weg.
if (!fieldOnBoard (move (f, vec, i)))
{
break;
}
Piece toMoveTo = state -> position.value (move (f, vec, i));
if (colourOfPiece (toMoveTo) != NONE)
{
if (colourOfPiece (toMoveTo) != toMove)
{
// Figur ist gegnerisch und kann daher geschlagen werden.
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a queen move for" << stringOfField (f) << stringOfMoveVector (vec, i) << QString::number (vec, 8) << i;
}
// wenn figur nicht geschlagen werden kann ist zug nicht möglich und innere schleife kann abgebrochen werden.
break;
}
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a queen move for" << stringOfField (f) << stringOfMoveVector (vec, i) << QString::number (vec, 8) << i;
}
// dann mit negativen.
for (int i = -1; i > -8; i --)
{
// entweder irgendwann ist das Brett zu ende oder wir treffen auf eine Figur im weg.
if (!fieldOnBoard (move (f, vec, i)))
{
break;
}
Piece toMoveTo = state -> position.value (move (f, vec, i));
if (colourOfPiece (toMoveTo) != NONE)
{
if (colourOfPiece (toMoveTo) != toMove)
{
// Figur ist gegnerisch und kann daher geschlagen werden.
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a queen move for" << stringOfField (f) << stringOfMoveVector (vec, i) << QString::number (vec, 8) << i;
}
// wenn figur nicht geschlagen werden kann ist zug nicht möglich und innere schleife kann abgebrochen werden.
break;
}
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a queen move for" << stringOfField (f) << stringOfMoveVector (vec, i) << QString::number (vec, 8) << i;
}
}
// 6 mögliche richtungen in die Läufer ziehen können (mit positiven und negativen Streckungen).
for (int j = 0; j < 6; j ++)
{
MoveVector vec = BishopVectors [j];
// vektoren mit positiven streckungen durchprobieren
for (int i = 1; i < 8; i ++)
{
// entweder irgendwann ist das Brett zu ende oder wir treffen auf eine Figur im weg.
if (!fieldOnBoard (move (f, vec, i)))
{
break;
}
Piece toMoveTo = state -> position.value (move (f, vec, i));
if (colourOfPiece (toMoveTo) != NONE)
{
if (colourOfPiece (toMoveTo) != toMove)
{
// Figur ist gegnerisch und kann daher geschlagen werden.
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a queen move for" << stringOfField (f) << stringOfMoveVector (vec, i) << QString::number (vec, 8) << i;
}
// wenn figur nicht geschlagen werden kann ist zug nicht möglich und innere schleife kann abgebrochen werden.
break;
}
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a queen move for" << stringOfField (f) << stringOfMoveVector (vec, i) << QString::number (vec, 8) << i;
}
// dann mit negativen.
for (int i = -1; i > -8; i --)
{
// entweder irgendwann ist das Brett zu ende oder wir treffen auf eine Figur im weg.
if (!fieldOnBoard (move (f, vec, i)))
{
break;
}
Piece toMoveTo = state -> position.value (move (f, vec, i));
if (colourOfPiece (toMoveTo) != NONE)
{
if (colourOfPiece (toMoveTo) != toMove)
{
// Figur ist gegnerisch und kann daher geschlagen werden.
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a queen move for" << stringOfField (f) << stringOfMoveVector (vec, i) << QString::number (vec, 8) << i;
}
// wenn figur nicht geschlagen werden kann ist zug nicht möglich und innere schleife kann abgebrochen werden.
break;
}
state -> next.insert (Move (f, vec, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a queen move for" << stringOfField (f) << stringOfMoveVector (vec, i) << QString::number (vec, 8) << i;
}
}
break;
}
default:
{
// Königszüge werden getrennt berechnet.
break;
}
}
}
// gefesselte figuren:
// vektoren der fesselungen sind negativ orientiert (zeigen von könig aus
// zu bedrohender figur)
foreach (Field f, pinMV -> keys ())
{
Piece p = (state -> position).value (f);
switch (uncolouredPieceOfPiece (p))
{
case PAWN:
{
calculatePinnedPawnsMoves (state, f, pinMV -> value (f), toMove);
break;
}
case BISHOP:
{
MoveVector mv = pinMV -> value (f);
// Läufer zieht immer mit manhattan distanz 2.
if (abs (xOfField (mv) - 3) + abs (yOfField (mv) - 3) + abs (zOfField (mv) - 3) == 2)
{
// positive pinvektor richtung
for (int i = 1; i < 8; i++)
{
Field target = move (f, mv, i);
Piece p = state -> position.value (target);
if (colourOfPiece (p) == toMove)
{
break;
}
else if (colourOfPiece (p) != NONE)
{
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a bishop move for" << stringOfField (f) << stringOfMoveVector (mv, i);
break;
}
state -> next. insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a bishop move for" << stringOfField (f) << stringOfMoveVector (mv, i);
}
// negative pinvektor richtung
for (int i = -1; i > -8; i--)
{
Field target = move (f, mv, i);
Piece p = state -> position.value (target);
if (colourOfPiece (p) == toMove)
{
break;
}
else if (colourOfPiece (p) != NONE)
{
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a bishop move for" << stringOfField (f) << stringOfMoveVector (mv, i);
break;
}
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a bishop move for" << stringOfField (f) << stringOfMoveVector (mv, i);
}
}
break;
}
case KNIGHT:
{
// springer kann fesselung nicht umgehen, da nur figuren fesseln können,
// die andere zugvektoren haben: manhattandistanz 1 bzw. 2 gegenüber manhattan
// distanz von 3 bei springer.
break;
}
case ROOK:
{
MoveVector mv = pinMV -> value (f);
// Turm zieht immer mit manhattan distanz 1.
if (abs (xOfField (mv) - 3) + abs (yOfField (mv) - 3) + abs (zOfField (mv) - 3) == 1)
{
// positive pinvektor richtung
for (int i = 1; i < 8; i++)
{
Field target = move (f, mv, i);
Piece p = state -> position.value (target);
if (colourOfPiece (p) == toMove)
{
break;
}
else if (colourOfPiece (p) != NONE)
{
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a rook move for" << stringOfField (f) << stringOfMoveVector (mv, i);
break;
}
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a rook move for" << stringOfField (f) << stringOfMoveVector (mv, i);
}
// negative pinvektor richtung
for (int i = -1; i > -8; i--)
{
Field target = move (f, mv, i);
Piece p = state -> position.value (target);
if (colourOfPiece (p) == toMove)
{
break;
}
else if (colourOfPiece (p) != NONE)
{
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a rook move for" << stringOfField (f) << stringOfMoveVector (mv, i);
break;
}
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a rook move for" << stringOfField (f) << stringOfMoveVector (mv, i);
}
}
break;
}
case QUEEN:
{
// königin lässt sich wieder als turm und läufer interpretieren...
MoveVector mv = pinMV -> value (f);
// Turm zieht immer mit manhattan distanz 1.
if (abs (xOfField (mv) - 3) + abs (yOfField (mv) - 3) + abs (zOfField (mv) - 3) == 1)
{
// positive pinvektor richtung
for (int i = 1; i < 8; i++)
{
Field target = move (f, mv, i);
Piece p = state -> position.value (target);
if (colourOfPiece (p) == toMove)
{
break;
}
else if (colourOfPiece (p) != NONE)
{
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a rook move for" << stringOfField (f) << stringOfMoveVector (mv, i);
break;
}
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a rook move for" << stringOfField (f) << stringOfMoveVector (mv, i);
}
// negative pinvektor richtung
for (int i = -1; i > -8; i--)
{
Field target = move (f, mv, i);
Piece p = state -> position.value (target);
if (colourOfPiece (p) == toMove)
{
break;
}
else if (colourOfPiece (p) != NONE)
{
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a rook move for" << stringOfField (f) << stringOfMoveVector (mv, i);
break;
}
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a rook move for" << stringOfField (f) << stringOfMoveVector (mv, i);
}
}
else if (abs (xOfField (mv) - 3) + abs (yOfField (mv) - 3) + abs (zOfField (mv) - 3) == 2)
{
// positive pinvektor richtung
for (int i = 1; i < 8; i++)
{
Field target = move (f, mv, i);
Piece p = state -> position.value (target);
if (colourOfPiece (p) == toMove)
{
break;
}
else if (colourOfPiece (p) != NONE)
{
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a bishop move for" << stringOfField (f) << stringOfMoveVector (mv, i);
break;
}
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a bishop move for" << stringOfField (f) << stringOfMoveVector (mv, i);
}
// negative pinvektor richtung
for (int i = -1; i > -8; i--)
{
Field target = move (f, mv, i);
Piece p = state -> position.value (target);
if (colourOfPiece (p) == toMove)
{
break;
}
else if (colourOfPiece (p) != NONE)
{
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a bishop move for" << stringOfField (f) << stringOfMoveVector (mv, i);
break;
}
state -> next.insert (Move (f, mv, i), 0);
//qDebug () << "AiBrain::calculateMovesOfPinnedAndUnpinned () added a bishop move for" << stringOfField (f) << stringOfMoveVector (mv, i);
}
}
break;
}
default:
{
break;
}
}
}
}
// König steht im Schach. Nur Züge möglich, die Bedrohung durch Schlagen oder
// in den Weg springen aufheben.
else
{
// MoveVector von Bedrohung zu König
MoveVector threatMV = moveVectorOfFieldAndField (threat, king);// + 0030303;
//qDebug () << stringOfMoveVector (threatMV, 1);
// die felder, die angesprungen werden können um bedrohung zu entgehen
QList<Field> toHit;
// Angreifer selbst kann geschlagen werden
toHit << threat;
for (int i = 1; i < 8; i ++)
{
// oder man setzt sich zwischen angreifer und könig.
Field temp = move (threat, threatMV, i);
if (temp == king)
{
break;
}
toHit << temp;
}
foreach (Field f1, toHit)
{
foreach (Move m, threatenedBy (state, f1, toMove))
{
// Figur darf nicht gefesselt sein.
if (unpinned.contains (m.from))
{
state -> next.insert (m, 0);
}
}
}
}
};
/**
* @brief kann der bauer auf pawn auf das Feld f1 ziehen bzw. die Figur auf f1 bedrohen?
* sollen nur "echte" bedrohungen berechnet werden muss ofF1 auf die Gegnerische Farbe
* eingestellt werden... ist die farbe NONE werden alle Züge berücksichtigt, die ohne
* schlagen auf f1 ziehen...
* verwendet wird die Funktion von threatenedBy und isThreatened in ihren beiden
* unterschiedlichen verwendungsmöglichkeiten.
* ob Zug ein bauerntausch ist muss vom Aufrufer untersucht werden...
**/
bool AiBrain::isThreatenedByPawn (StateNode *state, Field f1, Field pawn, Colour ofF1, Colour toMove)
{
if (manhattanDistance (f1, pawn) > 2)
{
return false;
}
Colour notToMove = Colour (-1 * toMove);
MoveVector mv = moveVectorOfFieldAndField (pawn, f1);
// zielfeld ist leer...
if (ofF1 == NONE)
{
// sprünge in nur einer dimension...
// Zug für weiß
if (manhattanDistance (mv) == 1)
{
if (toMove == WHITE)
{
// normaler einfachsprung auf das leere feld f1
if (zOfField (f1) - zOfField (pawn) == -1)
{
return true;
}
// evtl. doppelsprung möglich?
else if ((zOfField (f1) - zOfField (pawn) == -2) &&
(yOfField (pawn) == 3) &&
(zOfField (pawn) == 6) &&
(nodeStateOfXCoord (xOfField (pawn)) & (state -> whiteState)))
{
// feld vor bauer muss auch leer sein...
if (state -> position.value (fieldOfCoordinates (xOfField (pawn), 3, 5)) == Piece (0))
{
return true;
}
}
// nach oben / unten springen
else if (abs (yOfField (f1) - yOfField (pawn)) == 1)
{
return true;
}
}
// Zug für schwarz
else
{
// normaler einfachsprung auf das leere feld f1
if (zOfField (f1) - zOfField (pawn) == 1)
{
return true;
}
// evtl. doppelsprung möglich?
else if ((zOfField (f1) - zOfField (pawn) == 2) &&
(yOfField (pawn) == 3) &&
(zOfField (pawn) == 1) &&
(nodeStateOfXCoord (xOfField (pawn)) & (state -> whiteState)))
{
// feld vor bauer muss auch leer sein...
if (state -> position.value (fieldOfCoordinates (xOfField (pawn), 3, 2)) == Piece (0))
{
return true;
}
}
// nach oben / unten springen
else if (abs (yOfField (f1) - yOfField (pawn)) == 1)
{
return true;
}
}
}
// schlagen en passant....
else if (manhattanDistance (mv) == 2)
{
int dx = xOfField (f1) - xOfField (pawn);
int dy = yOfField (f1) - yOfField (pawn);
int dz = zOfField (f1) - zOfField (pawn);
// x-koordinate von Zielfeld = last-Doppelsprung
if (state -> last == xOfField (f1))
{
if (toMove == WHITE)
{
if (zOfField (f1) == 2 && yOfField (f1) == 3)
{
// weiß spielt in negative z-richtung
if (dz == -1)
{
if (abs (dx) + abs (dy) == 1)
{
return true;
}
}
}
}
else
{
if (zOfField (f1) == 5 && yOfField (f1) == 3)
{
if (dz == 1)
{
if (abs (dx) + abs (dy) == 1)
{
return true;
}
}
}
}
}
}
return false;
}
// feld ist mit gegner besetzt... normales schlagen, nicht en passant
else if (ofF1 == notToMove)
{
if (manhattanDistance (mv) == 2)
{
int dx = xOfField (f1) - xOfField (pawn);
int dy = yOfField (f1) - yOfField (pawn);
int dz = zOfField (f1) - zOfField (pawn);
if (toMove == WHITE)
{
// schlagen muss in negative z-richtung gehen und in 2 komponenten anteil 1 haben
if (dz == -1 && abs (dz) + abs (dy) + abs (dx) == 2)
{
return true;
}
}
else
{
if (dz == 1 && abs (dz) + abs (dy) + abs (dx) == 2)
{
return true;
}
}
}
}
return false;
};
/**
* @brief Ist das Feld f1 durch die Farbe by bedroht?
* @details Berechnet Feldes f1 durch Farbe by bedroht ist. Die Funktion
* arbeitet prinzipiell wie ihr pendant threatenedBy (), in positiven Fällen
* allerdings schneller, da sobald eine Bedrohung gefunden wurde true
* zurückgeliefert wird.
**/
bool AiBrain::isThreatened (StateNode *state, Field f1, Colour by)
{
bool threat = false;
//qDebug () << "AiBrain::isThreatened () Target: " << QString::number (f1, 8);
// Königszüge:
{
Field f2 = state -> position.key (pieceOfUncolouredPieceAndColour (KING, by));
if (f1 != f2)
{
// auch hier nur Züge der "Länge" 1, daher kann nichts im weg stehen.
int x = abs (xOfField (f2) - xOfField (f1));
if (x < 2)
{
int z = abs (zOfField (f2) - zOfField (f1));
if (z < 2)
{
int y = abs (yOfField (f2) - yOfField (f1));
if (y < 2)
{
int md = x + y + z;
if ((md == 2) || (md == 1))
{
/// \TODO was kann hier passieren wenn könig gegen könig steht?...
return true;
}
}
}
}
}
}
//qDebug () << "AiBrain::isThreatened () Kings done.";
// Damenzüge:
foreach (Field queen, state -> position.keys (pieceOfUncolouredPieceAndColour (QUEEN, by)))
{
if (queen != f1)
{
//qDebug () << "Queen: " << QString::number (queen, 8) << "F1: " << QString::number (f1, 8);
// als Turm interpretiert
MoveVector mv = moveVectorOfFieldAndField (f1, queen);
if (manhattanDistance (mv) == 1)
{
threat = true;
int scale = 1;
Field temp = move (f1, mv, scale);
while (temp != queen)
{
if (colourOfPiece (state -> position.value (temp)) != NONE)
{
threat = false;
break;
}
scale ++;
temp = move (f1, mv, scale);
}
if (threat)
{
return true;
}
}
// als Läufer interpretiert
else if (manhattanDistance (mv) == 2)
{
threat = true;
int scale = 1;
Field temp = move (f1, mv, scale);
while (temp != queen)
{
if (colourOfPiece (state -> position.value (temp)) != NONE)
{
threat = false;
break;
}
scale ++;
temp = move (f1, mv, scale);
}
if (threat)
{
return true;
}
}
}
}
//qDebug () << "AiBrain::isThreatened () Queens done.";
// Bauernzüge:
foreach (Field f2, state -> position.keys (pieceOfUncolouredPieceAndColour (PAWN, by)))
{
Colour c = colourOfPiece (state -> position.value (f1));
if (f1 != f2)
{
/// \TODO was passiert wenn hier jemand en passant schlagen kann?!
if (isThreatenedByPawn (state, f1, f2, Colour (-1 * by), by))
{
return true;
}
}
}
//qDebug () << "AiBrain::isThreatened () Pawns done.";
// Springerzüge:
// springer kann feld genau dann bedrohen wenn er einen manhattan abstand von 3
// und in einer komponente einen abstand von 2 hat (nur manhattan == 3 schließt
// abstand (+-1, +-1, +-1) nicht aus. im weg stehen kann nix.
foreach (Field knight, state -> position.keys (pieceOfUncolouredPieceAndColour (KNIGHT, by)))
{
if (knight != f1)
{
if (manhattanDistance (f1, knight) == 3)
{
if (abs (xOfField (knight) - xOfField (f1)) == 2) { return true; }
if (abs (zOfField (knight) - zOfField (f1)) == 2) { return true; }
if (abs (yOfField (knight) - yOfField (f1)) == 2) { return true; }
}
}
}
//qDebug () << "AiBrain::isThreatened () Knights done.";
// Läuferzüge:
// Einheits-Läuferzüge haben immer Manhattan Distanz 2.
foreach (Field bishop, state -> position.keys (pieceOfUncolouredPieceAndColour (BISHOP, by)))
{
if (bishop != f1)
{
MoveVector mv = moveVectorOfFieldAndField (f1, bishop);
if (manhattanDistance (mv) == 2)
{
threat = true;
int scale = 1;
Field temp = move (f1, mv, scale);
while (temp != bishop)
{
if (colourOfPiece (state -> position.value (temp)) != NONE)
{
threat = false;
break;
}
scale ++;
temp = move (f1, mv, scale);
}
if (threat)
{
return true;
}
}
}
}
//qDebug () << "AiBrain::isThreatened () Bishops done.";
// Turmzüge:
// Einheits-Turmzüge haben immer Manhattan Distanz 1.
foreach (Field rook, state -> position.keys (pieceOfUncolouredPieceAndColour (ROOK, by)))
{
if (rook != f1)
{
MoveVector mv = moveVectorOfFieldAndField (f1, rook);
if (manhattanDistance (mv) == 1)
{
threat = true;
int scale = 1;
Field temp = move (f1, mv, scale);
while (temp != rook)
{
if (colourOfPiece (state -> position.value (temp)) != NONE)
{
threat = false;
break;
}
scale ++;
temp = move (f1, mv, scale);
}
if (threat)
{
return true;
}
}
}
}
//qDebug () << "AiBrain::isThreatened () Rooks done.";
return false;
};
bool AiBrain::canCapture (StateNode *state, Field f1, Field f2)
{
// keine figur auf f1 -> keine bedrohung
if (!state -> position.contains (f1))
return false;
Piece p = state -> position.value (f1);
switch (uncolouredPieceOfPiece (p))
{
// für alle figuren außer bauern ist für bedrohung
// egal ob sie weiß oder schwarz sind.
case KNIGHT:
// kein im weg stehen.
if (manhattanDistance (f1, f2) == 3)
{
if (abs (xOfField (f2) - xOfField (f1)) == 2)
{
return true;
}
if (abs (zOfField (f2) - zOfField (f1)) == 2)
{
return true;
}
if (abs (yOfField (f2) - yOfField (f1)) == 2)
{
return true;
}
}
break;
case BISHOP:
{
MoveVector mv = moveVectorOfFieldAndField (f2, f1);
if (manhattanDistance (mv) == 2)
{
int scale = 1;
Field temp = move (f2, mv, scale);
while (temp != f1)
{
if (colourOfPiece (state -> position.value (temp)) != NONE)
{
// figur steht im weg -> keine bedrohung von f2 durch f1
return false;
}
scale ++;
temp = move (f2, mv, scale);
}
return true;
}
return false;
}
break;
case ROOK:
{
MoveVector mv = moveVectorOfFieldAndField (f2, f1);
if (manhattanDistance (mv) == 1)
{
int scale = 1;
Field temp = move (f2, mv, scale);
while (temp != f1)
{
if (colourOfPiece (state -> position.value (temp)) != NONE)
{
// figur steht im weg -> keine bedrohung von f2 durch f1
return false;
}
scale ++;
temp = move (f2, mv, scale);
}
return true;
}
return false;
}
break;
case QUEEN:
{
int scale = 1;
MoveVector mv = moveVectorOfFieldAndField (f2, f1);
// dame als turm
if (manhattanDistance (mv) == 1)
{
Field temp = move (f2, mv, scale);
while (temp != f1)
{
if (colourOfPiece (state -> position.value (temp)) != NONE)
{
// figur steht im weg
return false;
}
scale ++;
temp = move (f2, mv, scale);
}
return true;
}
else
// dame als läufer
if (manhattanDistance (mv) == 2)
{
scale = 1;
Field temp = move (f2, mv, scale);
while (temp != f1)
{
if (colourOfPiece (state -> position.value (temp)) != NONE)
{
return false;
}
scale ++;
temp = move (f2, mv, scale);
}
return true;
}
return false;
}
break;
case KING:
// nur züge der "länge" 1 -> da kann nix im weg stehen.
{
int x = xOfField (f2) - xOfField (f1);
int y = yOfField (f2) - yOfField (f1);
int z = zOfField (f2) - zOfField (f1);
int md = abs (x) + abs (y) + abs (z);
return (md == 2) || (md == 1);
}
break;
case PAWN:
// nur züge der "länge" 1 -> da kann nix im weg stehen.
if (colourOfPiece (p) == WHITE)
{
// weiss spielt von 7 nach 0 in x richtung
int x = xOfField (f2) - xOfField (f1);
int y = yOfField (f2) - yOfField (f1);
int z = zOfField (f2) - zOfField (f1);
if (x > 0)
{
return false;
}
return abs (x) + abs (y) + abs (z) == 2;
}
else
{
// schwarz spielt von 0 nach 7 in x richtung
int x = xOfField (f2) - xOfField (f1);
int y = yOfField (f2) - yOfField (f1);
int z = zOfField (f2) - zOfField (f1);
if (x < 0)
{
return false;
}
return abs (x) + abs (y) + abs (z) == 2;
}
break;
};
return false;
};
/** @brief nachfolgerknoten eines Knotens erzeugen
* @details in next müssen dafür schon die Zugmöglichkeiten mit leeren StateNode
* objekten angelegt sein.
**/
void AiBrain::calculateSuccessors (StateNode *state)
{
tempPosition = &(state -> position);
//qDebug () << "AiBrain::calculateSuccessors ()" << state -> next.size ();
foreach (Move m, state -> next.keys ())
{
// if (!fieldOnBoard (move (m)) && move (m) == 0xc8 && traversedNodes > 21100)
// {
// lustig ();
// }
Piece p = state -> position.value (m.from);
// Neuen Knoten für Suchbaum mit größerer Tiefe anlegen.
StateNode *succ = new StateNode (*state);
succ -> depth = state -> depth + 1;
// Startposition der ziehenden Figur muss zuerst geleert werden,
succ -> position.remove (m.from);
// dann die Zielposition, falls dort vorher eine Figur gestanden hat.
succ -> position.remove (move (m));
// erst dann kann die Figur auf das Zielfeld gestellt werden.
succ -> position.insert (move (m), p);
succ -> next.clear ();
// dummy zug ogne folgezustand, der in calculateStateAndMoves erzeugt wurde aus Liste entfernen
state -> next.remove (m);
// und den echten Zug mit Folgezustand einfügen.
state -> next.insert (m, succ);
Field target = move (m);
switch (uncolouredPieceOfPiece (p))
{
// Wenn Bauer sich bewegt hat darf er nicht mehr Doppelspringen.
// falls Doppelsprung muss das im Status vermerkt werden für
// "en passant" analyse.
case PAWN:
{
if (colourOfPiece (p) == WHITE)
{
succ -> whiteState = succ -> whiteState & (~nodeStateOfXCoord (xOfField (target)));
// schlagen en passant
if (state -> last == xOfField (target))
{
if (colourOfPiece (state -> position.value (target)) == NONE)
{
if (zOfField (target) == 4)
{
if (manhattanDistance (target) == 2)
{
succ -> position.remove (target - 0010000);
}
}
}
}
// doppelsprung
if (abs (zOfField (m.by)-3) == 2)
{
succ -> last = xOfField (target);
}
//bauerntausch
if (abs (m.scale) == 3)
{
succ -> position.remove (move (m));
if (m.scale > 0)
{
succ -> position.insert (move (m.from, m.by, 1), KNIGHT_W);
}
else
{
succ -> position.insert (move (m.from, m.by, -1), KNIGHT_W);
}
}
else if (abs (m.scale) == 4)
{
succ -> position.remove (move (m));
if (m.scale > 0)
{
succ -> position.insert (move (m.from, m.by, 1), QUEEN_W);
}
else
{
succ -> position.insert (move (m.from, m.by, -1), QUEEN_W);
}
}
}
else
{
succ -> blackState = succ -> blackState & (~nodeStateOfXCoord (xOfField (target)));
// schlagen en passant
if (state -> last == xOfField (target))
{
if (colourOfPiece (state -> position.value (target)) == NONE)
{
if (zOfField (target) == 5)
{
if (manhattanDistance (target) == 2)
{
succ -> position.remove (target + 0010000);
}
}
}
}
// doppelsprung
if (abs (zOfField (m.by)-3) == 2)
{
succ -> last = xOfField (target);
}
//bauerntausch
if (abs (m.scale) == 3)
{
succ -> position.remove (move (m));
if (m.scale > 0)
{
succ -> position.insert (move (m.from, m.by, 1), KNIGHT_B);
}
else
{
succ -> position.insert (move (m.from, m.by, -1), KNIGHT_B);
}
}
else if (abs (m.scale) == 4)
{
succ -> position.remove (move (m));
if (m.scale > 0)
{
succ -> position.insert (move (m.from, m.by, 1), QUEEN_B);
}
else
{
succ -> position.insert (move (m.from, m.by, -1), QUEEN_B);
}
}
}
break;
}
// Rochade wird als Königszug gewertet: wenn Rochiert wurde muss
// der status des rochierenden geändert werden.
case KING:
{
if (colourOfPiece (p) == WHITE)
{
succ -> whiteState = succ -> whiteState & (~CASTLE_LONG);
succ -> whiteState = succ -> whiteState & (~CASTLE_SHORT);
}
else
{
succ -> blackState = succ -> blackState & (~CASTLE_LONG);
succ -> blackState = succ -> blackState & (~CASTLE_SHORT);
}
// Wenn sich der König 2 Felder Felder weit bewegt dann
// rochiert er.
if ((xOfField (m.by)-3)*m.scale == -2)
// König geht nach links -> Turm kommt von links.
{
Piece p2 = succ -> position.value (fieldOfCoordinates (0, 3, zOfField (m.from)));
succ -> position.remove (fieldOfCoordinates (0, 3, zOfField (m.from)));
// Turm springt auf ein Feld links von alter Königsposition
succ -> position.insert (fieldOfCoordinates (xOfField (m.from) - 1, 3, zOfField (m.from)), p2);
}
if ((xOfField (m.by)-3)*m.scale == 2)
{
Piece p2 = succ -> position.value (fieldOfCoordinates (7, 3, zOfField (m.from)));
succ -> position.remove (fieldOfCoordinates (7, 3, zOfField (m.from)));
// Turm springt auf ein Feld rechts von alter Königsposition
succ -> position.insert (fieldOfCoordinates (xOfField (m.from) + 1, 3, zOfField (m.from)), p2);
}
break;
}
// wenn Turm sich bewegt dann darf nichtmehr rochiert werden.
case ROOK:
{
if (xOfField (m.from) == 0)
{
if (colourOfPiece (p) == WHITE)
{
succ -> whiteState = succ -> whiteState & (~CASTLE_LONG);
}
else
{
succ -> blackState = succ -> blackState & (~CASTLE_LONG);
}
}
if (xOfField (m.from) == 7)
{
if (colourOfPiece (p) == WHITE)
{
succ -> whiteState = succ -> whiteState & (~CASTLE_SHORT);
}
else
{
succ -> blackState = succ -> blackState & (~CASTLE_SHORT);
}
}
break;
}
}
}
};
/**
* kann eine der Parteien womöglich im nächsten Zug einen Bauerntausch machen?
**/
bool AiBrain::canQueenPawn (StateNode *state)
{
/// \TODO Für bauern, die nicht in y=3 ebene stehen berechnen ob unbedrohter weg zu bauerntausch möglich....
Colour can = colourOfDepthAndColour (state -> depth, colour);
if (can == WHITE)
{
for (int i = 0; i < 8; i ++)
{
if (state -> position.value (fieldOfCoordinates (i, 3, 1)) == pieceOfUncolouredPieceAndColour (PAWN, can))
{
return true;
}
}
}
else
{
for (int i = 0; i < 8; i ++)
{
if (state -> position.value (fieldOfCoordinates (i, 3, 6)) == pieceOfUncolouredPieceAndColour (PAWN, can))
{
return true;
}
}
}
return false;
};
/**
* ist könig im schach? sind noch figuren außer könig da?
**/
void AiBrain::checkTest (StateNode *state)
{
bool whiteKingOnly = true;
bool blackKingOnly = true;
bool check = false;
// könig im schach? könig alleine übrig? gewichte anpassen...
foreach (Field f, state -> position.keys ())
{
Piece p = state -> position.value (f);
if (colourOfPiece (p) == WHITE)
{
if (uncolouredPieceOfPiece (p) != KING)
{
whiteKingOnly = false;
}
else
{
check = check || isThreatened (state, f, BLACK);
}
}
else
{
if (uncolouredPieceOfPiece (p) != KING)
{
blackKingOnly = false;
}
else
{
check = check || isThreatened (state, f, WHITE);
}
}
}
state -> whiteKingOnly = whiteKingOnly;
state -> blackKingOnly = blackKingOnly;
state -> check = check;
};
/** wenn stellung ruhig ist muss nur mit mindepth suchtiefe gerechnet
* werden. ansonsten bis maxdepth oder bis ruhige stellung erreicht wird. **/
bool AiBrain::quiescent (StateNode *state)
{
// if (state -> depth & 1)
// {
// return false;
// };
bool quiescentTemp = true;
// erst wenn mittel- oder endspiel schauen ob schach...
checkTest (state);
if (state -> check)
{
quiescentTemp = false;
}
if (state -> next.size () == 1)
{
quiescentTemp = false;
}
// if (state -> position.kingOnly ())
// {
// quiescentTemp = false;
// }
if (canQueenPawn (state))
{
quiescentTemp = false;
}
// bei jedem cutoff muss die gleiche farbe am zug sein
return quiescentTemp || (state -> depth > quiescentCut);
};
int AiBrain::minValueElse (StateNode *state, int alpha, int beta, int sisters)
{
#ifdef TIME_CUTOFF
if (timeCutOff)
{
state -> value = alpha+1;
return alpha+1;
}
#endif
#ifdef DUPLICATEPRUNING
if (alreadyVisited.contains (state -> position))
{
duplicateNodes ++;
state -> value = alreadyVisited.value (state -> position);
return state -> value;
}
#endif
calculateStateAndMoves (state);
calculateSuccessors (state);
traversedNodes ++;
// cutOff oder keine Züge mehr möglich: stellung bewerten.
if (cutOff (state) || state -> next.size () == 0)
{
int value = (this->*valueStateNode) (state, sisters);
#ifdef DUPLICATEPRUNING
alreadyVisited.insert (state -> position, value);
#endif
state -> value = value;
return value;
}
int value = 2000000000;
//qDebug () << "minValue" << state << state -> depth << state -> next.size ();
tempPosition = (&(state -> position));
QList<Move> moves = state -> next.keys ();
qSort (moves.begin (), moves.end (), lessThan);
foreach (Move m, moves)
// foreach (Move m, state -> next.keys ())
{
StateNode *succ = state -> next.value (m);
if (m.from == 0020102)
{
lustig ();
}
int value2 = (this ->* maxValue) (succ, alpha, beta, state -> next.size ());
value = value < value2 ? value : value2;
if (value <= alpha)
{
if (state != &startState)
{
qDeleteAll (state -> next.values ());
state -> next.clear ();
}
state -> value = value;
return value;
}
beta = beta < value ? beta : value;
}
if (state != &startState)
{
qDeleteAll (state -> next.values ());
state -> next.clear ();
}
state -> value = value;
return value;
};
int AiBrain::maxValueElse (StateNode *state, int alpha, int beta, int sisters)
{
#ifdef TIME_CUTOFF
if (timeCutOff)
{
state -> value = beta-1;
return beta-1;
}
#endif
#ifdef DUPLICATEPRUNING
if (alreadyVisited.contains (state -> position))
{
duplicateNodes ++;
state -> value = alreadyVisited.value (state -> position);
return state -> value;
}
#endif
calculateStateAndMoves (state);
calculateSuccessors (state);
traversedNodes ++;
// cutOff oder keine Züge mehr möglich: stellung bewerten.
if (cutOff (state) || state -> next.size () == 0)
{
int value = (this->*valueStateNode) (state, sisters);
#ifdef DUPLICATEPRUNING
alreadyVisited.insert (state -> position, value);
#endif
state -> value = value;
return value;
}
int value = -2000000000;
tempPosition = (&(state -> position));
QList<Move> moves = state -> next.keys ();
qSort (moves.begin (), moves.end (), lessThan);
foreach (Move m, moves)
// foreach (Move m, state -> next.keys ())
{
StateNode *succ = state -> next.value (m);
//qDebug () << "maxValue" << succ;
if (m.from == 0030402)
{
lustig ();
}
int value2 = (this ->* minValue) (succ, alpha, beta, state -> next.size ());
value = value > value2 ? value : value2;
if (value >= beta)
{
if (state != &startState)
{
qDeleteAll (state -> next.values ());
state -> next.clear ();
}
state -> value = value;
return value;
}
alpha = alpha > value ? alpha : value;
}
if (state != &startState)
{
qDeleteAll (state -> next.values ());
state -> next.clear ();
}
state -> value = value;
return value;
};