0

My problem is when applying Alpha/Beta Pruning to Minimax. It does very wierd and bad moves. When I use Minimax without Alpha/Beta, it works fine. The two functions look like this:

Minimax With Alpha/Beta Pruning:

public int minimaxAB(Piece[,] board, int depth, int a, int b, bool maximizingPlayer, bool WhiteToPlay)
{
    if (depth == 0)
    {
        return EvaluatePosition(board, WhiteToPlay);
    }

    var moves = GenerateMoves(board, WhiteToPlay);
    if (maximizingPlayer)
    {
        int value = int.MinValue;
        foreach (var move in moves)
        {
            int minmaxResult = minimaxAB(move, depth - 1, a, b, false, !WhiteToPlay);
            value = Math.Max(value, minmaxResult);

            a = Math.Max(a, value);

            if (a >= b)
                return a;

            if (depth == depthB)
            {
                moveScores.Add(move, minmaxResult);
            }
        }
        return value;
    }
    else
    {
        int value = int.MaxValue;
        foreach (var move in moves)
        {
            int minmaxResult = minimaxAB(move, depth - 1, a, b, true, !WhiteToPlay);
            value = Math.Min(value, minmaxResult);

            b = Math.Min(b, value);

            if (b <= a)
                return b;

            if (depth == depthB)
            {
                moveScores.Add(move, minmaxResult);
            }
        }
        return value;
    }
}

Minimax without A/B:

public int minimax(Piece[,] board, int depth, bool maximizingPlayer, bool WhiteToPlay)
{
    if (depth == 0)
    {
        int result = EvaluatePosition(board, WhiteToPlay);
        return result;
    }

    var moves = GenerateMoves(board, WhiteToPlay);
    if (maximizingPlayer)
    {
        int value = int.MinValue;
        foreach (var move in moves)
        {
            int minmaxResult = minimax(move, depth - 1, false, !WhiteToPlay);
            value = Math.Max(value, minmaxResult);
            if (depth == depthB)
            {
                moveScores.Add(move, minmaxResult);
            }
        }
        return value;
    }
    else
    {
        int value = int.MaxValue;
        foreach (var move in moves)
        {
            int minmaxResult = minimax(move, depth - 1, true, !WhiteToPlay);
            value = Math.Min(value, minmaxResult);
            if (depth == depthB)
            {
                moveScores.Add(move, minmaxResult);
            }
        }
        return value;
    }
}

My evaluation function:

public int EvaluatePosition(Piece[,] boardPos, bool ForWhite)
{
    int eval_W = 0;
    int eval_B = 0;
    int eval = 0;
    for (int i = 0; i < 8; i++)
    {
        for (int j = 0; j < 8; j++)
        {
            if (boardPos[i, j] != Piece.Empty)
            {
                if (IsPieceWhite(boardPos[i, j]))
                {
                    eval_W += GetPieceWorth(boardPos[i, j]) + DistanceToCenter(i, j);
                    eval += GetPieceWorth(boardPos[i, j]);
                }
                else if (IsPieceBlack(boardPos[i, j]))
                {
                    eval_B += GetPieceWorth(boardPos[i, j]) + DistanceToCenter(i, j);
                    eval -= GetPieceWorth(boardPos[i, j]);
                }
            }
        }
    }

    if (ForWhite)
        return eval_W - eval_B;
    else
        return eval_B - eval_W;
}

I call with: minimaxAB(CurrentBoard, depthB, int.MinValue, int.MaxValue, true, whiteToMove);

I am aware that Minimax with AB is suppose to produce exactly the same result, but in my case it does not. I hope someone is able to spot what I did wrong.

foRei
  • 9
  • 1
  • No, this is a new question. The other one was about minimax. This one is about alpha beta pruning. – foRei Feb 09 '21 at 20:45
  • Why are you still using the depth == depthB? It is nowhere in any pseudo code, and this is wrong as mentioned in your other question. Also, does it now make legal moves only and play decent chess with just the minimax function? – eligolf Feb 10 '21 at 05:47
  • 'depth == depthB' is to check if the current depth, is the starting depthB of 4. And then only add the moves if true. It is because minimax does not actually produce moves, it just produces an evaluation number, indicating how good the position is. That code does not interfere with minimax in any negativ way? Also yes, it plays decent chess with the minimax function only. Once I add alphabeta, it plays very bad chess. – foRei Feb 10 '21 at 12:50
  • Look at the pseudo code, you don't just add a move at a certain depth. You have a whole line of moves that goes from depth 0 to depth MaxDepth, which should be updated each iteration. This code will not produce a nice result, the minimax should also play very weird. – eligolf Feb 10 '21 at 12:57
  • I don't see anything in the pseudocode, about how to actually produce moves. The problem is that it just returns a value if I call minimax. This way I get a list of moves and values for the first step only. – foRei Feb 10 '21 at 13:07
  • Instead try to get the entire PV line: https://www.chessprogramming.org/Principal_Variation. this way you will also see how the engine "thinks" and therefore be able to find any bugs or strange behaviour. – eligolf Feb 10 '21 at 13:09

1 Answers1

-1

I figured it out, I needed an alpha and beta for both white and black. The reason for this is that I call the minimaxAB function for both white and black moves.

Working method:

public int minimaxAB(Piece[,] board, int depth, int alpha_White, int beta_White, int alpha_Black, int beta_Black, bool maximizingPlayer, bool WhiteToPlay)
{
    if (depth == 0 || !HasKings(board))
    {
        return EvaluatePosition(board, WhiteToPlay);
    }

    var moves = GenerateMoves(board, WhiteToPlay);
    if (maximizingPlayer)
    {
        int value = int.MinValue;
        foreach (var move in moves)
        {
            int minmaxResult = minimaxAB(move, depth - 1, alpha_White, beta_White, alpha_Black, beta_Black, false, !WhiteToPlay);
            value = Math.Max(value, minmaxResult);

            if (WhiteToPlay)
            {
                alpha_White = Math.Max(alpha_White, value);
                if (alpha_White >= beta_White)
                    return alpha_White;
            }
            else
            {
                alpha_Black = Math.Max(alpha_Black, value);
                if (alpha_Black >= beta_Black)
                    return alpha_Black;
            }

            if (depth == depthB)
            {
                moveScores.Add(move, minmaxResult);
            }
        }
        return value;
    }
    else
    {
        int value = int.MaxValue;
        foreach (var move in moves)
        {
            int minmaxResult = minimaxAB(move, depth - 1, alpha_White, beta_White, alpha_Black, beta_Black, true, !WhiteToPlay);
            value = Math.Min(value, minmaxResult);

            if (WhiteToPlay)
            {
                beta_White = Math.Min(beta_White, value);
                if (beta_White <= alpha_White)
                    return beta_White;
            }
            else
            {
                beta_Black = Math.Min(beta_Black, value);
                if (beta_Black <= alpha_Black)
                    return beta_Black;
            }
            if (depth == depthB)
            {
                moveScores.Add(move, minmaxResult);
            }
        }
        return value;
    }
}

Called with:

minimaxAB(CurrentBoard, depthB, int.MinValue, int.MaxValue, int.MinValue, int.MaxValue, true, whiteToMove);
foRei
  • 9
  • 1
  • No this is completely wrong... You are making this way more complicated than it is. – eligolf Feb 11 '21 at 05:21
  • What is wrong? I want to be able to call the method for both white and black. – foRei Feb 11 '21 at 13:02
  • You will be without this complex thing. Also the depth = depthB is not needed. Just use the pseudo code: https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning. When calling the function you then see if score is better, and if so update the best move., – eligolf Feb 11 '21 at 15:03
  • I don't get it. What kind of code do I need for calling? Do I call minimax once for every possible move ? – foRei Feb 11 '21 at 15:17
  • You could call minimax for each move in the current position to your desired depth: 'for move in possible_moves: .....'. Then if the score that minimax returns is better than your previous best score you update your best score and best move in that loop. That is one way of doing it. However, if you want to get the PV line as I commented in the original question, you need to read up on that link. – eligolf Feb 12 '21 at 05:26