Chess AI not evaluating more than 1 move ahead despite having depth of 3

61 Views Asked by At

I've been working on a chess bot, and I've finished up a draft for the minimax function, but it seems to be entirely disregarding any minimising or maximising past the 1st step. In order to make it decide the move that leads to the best evaluation, I'm making use of a structure which I have called "move", which has a member string called "algebraicMove" (which stores the actual move) and a member double called "eval" which stores the move's evaluation. Here's the function:

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;
    }

}

To test my situation, currently, I've given the bot this starting position:

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

If you're not too familiar with chess, the main idea is that the black queen (denoted with a lowercase q) on the 4th row down has as very clear objective: get away from the knight (aka "horse") on the 6th row which is targetting it. However, when I give the minimax thsi board, it calculates that the best move for black is to take the white pawn 3 squares in front of the black queen (denoted with an uppercase P), even though that leaves the black queen vulnerable to being captured 3 different ways on the next move. I have absolutely no idea why this is happening, and I have run through my code a million times with breakpoints and the debugger, but I just can't seem to track down the error and I'm convinced something in my logic is wrong.

1

There are 1 best solutions below

1
bforbiggy On

There's only one reason there isn't recursion which is that the code never does the recursion. The only way this is possible if either for loop doesn't trigger, so I'd check piecesRemaining or legalMoves to ensure they have the result you actually expect.

(As a side note, the code is factored poorly so you should move some code to a separate function and pass all the variables in)