我一直在研究国际象棋机器人,并且已经完成了极小极大函数的草稿,但它似乎完全忽略了第一步之后的任何最小化或最大化。为了让它决定导致最佳评估的移动,我使用了一个称为“move”的结构,它有一个名为“algebraicMove”的成员字符串(它存储实际的移动)和一个成员double 称为“eval”,它存储移动的评估。这是函数:
AI_Player::move AI_Player::chooseMoveHelper(int depth, std::string maximizingPlayer, Board currBoard, std::string moveMadeToGetHere /*null move*/) { //the "actual AI" algorithm
//initializing current node -- O(1)
move currentNode{moveMadeToGetHere, currBoard.evaluatePosition()};
//creting a copy of the board so we don't mess up the actual board
Board boardCpy = currBoard;
//determine remaining pieces based on color -- O(1)
std::vector<Board::square> piecesRemaining;
maximizingPlayer == "white" ? piecesRemaining = boardCpy.piecesLeft("white") : piecesRemaining = boardCpy.piecesLeft("black");
//base case -- O(1)
if (depth == 0 /*OR checkmate, which hasn't been coded in yet*/)
return currentNode;
if (maximizingPlayer == "white") {
move maxEvalMove{ "/////", -999999999 };
for (int piece = 0; piece < piecesRemaining.size(); piece++) {
std::string squareOfPiece = piecesRemaining[piece].file + std::to_string(piecesRemaining[piece].rank);
std::vector<std::string> moves = playerBoard->legalMoves(squareOfPiece);
for (int i = 0; i < moves.size(); i++) {
boardCpy.makeMove(squareOfPiece + ":" + moves[i]);
move nextEval{squareOfPiece + ":" + moves[i], chooseMoveHelper(depth - 1, "black", boardCpy, squareOfPiece + ":" + moves[i]).eval};
//call function to adjust evaluation based on piece placement
nextEval.eval += boardCpy.adjustEvalByPiecePlacement(boardCpy[moves[i]]->piece, moves[i]);
if (nextEval.eval > maxEvalMove.eval)
maxEvalMove = nextEval;
boardCpy.makeMove(moves[i] + ":" + squareOfPiece);
}
}
return maxEvalMove;
}
else {
move minEvalMove{ "/////", 999999999 };
for (int piece = 0; piece < piecesRemaining.size(); piece++) {
std::string squareOfPiece = piecesRemaining[piece].file + std::to_string(piecesRemaining[piece].rank);
std::vector<std::string> moves = playerBoard->legalMoves(squareOfPiece);
for (int i = 0; i < moves.size(); i++) {
//store current piece on square we'll move to:
char capturedPiece = boardCpy[moves[i]]->piece;
boardCpy.makeMove(squareOfPiece + ":" + moves[i]);
move nextEval{ squareOfPiece + ":" + moves[i], chooseMoveHelper(depth - 1, "white", boardCpy, squareOfPiece + ":" + moves[i]).eval };
//call function to adjust evaluation based on piece placement
nextEval.eval -= boardCpy.adjustEvalByPiecePlacement(boardCpy[moves[i]]->piece, moves[i]);
if (nextEval.eval < minEvalMove.eval)
minEvalMove = nextEval;
boardCpy.makeMove(moves[i] + ":" + squareOfPiece);
boardCpy.editSquare(boardCpy[moves[i]], capturedPiece);
}
}
return minEvalMove;
}
}
为了测试我的情况,目前,我已经给机器人这个起始位置:
r n b . k b n r
p p p . p p p p
. . . . . . . .
. . . q . . . .
. . . . . . . .
. . N . . . . .
P P P P . P P P
R . B Q K B N R
如果您不太熟悉国际象棋,主要思想是第四行的黑皇后(用小写 q 表示)有一个非常明确的目标:远离棋盘上的马(又名“马”)。第 6 行是针对它的。然而,当我给出 minimax thsi 棋盘时,它计算出黑棋的最佳走法是在黑皇后(用大写 P 表示)前面 3 个格子处拿走白棋,尽管这使得黑皇后很容易被捕捉下一步动作的 3 种不同方式。我完全不知道为什么会发生这种情况,并且我已经使用断点和调试器运行了我的代码一百万次,但我似乎无法追踪错误,并且我确信我的逻辑中的某些内容是错误的。
没有递归的原因只有一个,那就是代码从不执行递归。如果 for 循环没有触发,这是可能的唯一方法,所以我会检查pieceRemaining 或legalMoves 以确保它们具有您实际期望的结果。
(顺便说一句,代码的分解很糟糕,因此您应该将一些代码移动到单独的函数中并传递所有变量)