【问题标题】:MiniMax Algorithm with Chess in C# not working properlyC# 中国际象棋的 MiniMax 算法无法正常工作
【发布时间】:2021-02-06 22:29:19
【问题描述】:

我已经尝试在我的 C# 国际象棋引擎中实现 minimax 算法已经一个星期了,它做出了合法的移动,但没有有意义的移动。我找不到错误,希望有人能发现它。我试图通过测试每种方法来隔离问题,它们似乎都可以正常工作,除了 minimax。


public enum Piece
{
    Empty, Pawn_W, Pawn_B, Knight_W, Knight_B,
    Bishop_W, Bishop_B, Rook_W, Rook_B,
    Queen_W, Queen_B, King_W, King_B
}

public bool IsPieceWhite(Piece piece)
{
    if (piece == Piece.Pawn_W || piece == Piece.Knight_W ||
        piece == Piece.Bishop_W || piece == Piece.Rook_W ||
        piece == Piece.Queen_W || piece == Piece.King_W)
        return true;
    else return false;
}

public bool IsPieceBlack(Piece piece)
{
    if (piece == Piece.Pawn_B || piece == Piece.Knight_B ||
        piece == Piece.Bishop_B || piece == Piece.Rook_B ||
        piece == Piece.Queen_B || piece == Piece.King_B)
        return true;
    else return false;
}

public int GetPieceWorth(Piece piece)
{
    if (piece == Piece.Pawn_W || piece == Piece.Pawn_B)
        return 1;
    if (piece == Piece.Knight_W || piece == Piece.Knight_B)
        return 3;
    if (piece == Piece.Bishop_W || piece == Piece.Bishop_B)
        return 3;
    if (piece == Piece.Rook_W || piece == Piece.Rook_B)
        return 5;
    if (piece == Piece.Queen_W || piece == Piece.Queen_B)
        return 9;
    if (piece == Piece.King_W || piece == Piece.King_B)
        return 9999999;

    return 0;
}
Piece[,] CurrentBoard = GetStartingBoard();
Piece[,] bestMove;
public int depthB = 3;

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

    if (maximizingPlayer)
    {
        double best = Double.MinValue;
        double value = Double.MinValue;
        foreach (var move in GenerateMoves(board, maximizingPlayer))
        {
            Piece[,] clonedMove = CloneBoard(move);
            value = Math.Max(value, minimax(clonedMove, depth - 1, false));
            if (depth == depthB && value >= best)
            {
                best = value;
                bestMove = clonedMove;
            }
        }
        return value;
    }
    else
    {
        double best = Double.MaxValue;
        double value = Double.MaxValue;
        foreach (var move in GenerateMoves(board, maximizingPlayer))
        {
            Piece[,] clonedMove = CloneBoard(move);
            value = Math.Min(value, minimax(clonedMove, depth - 1, true));
            if (depth == depthB && value <= best)
            {
                best = value;
                bestMove = clonedMove;
            }
        }
        return value;
    }
}

public Piece[,] CloneBoard(Piece[,] boardPos)
{
    Piece[,] copy = boardPos.Clone() as Piece[,];
    return copy;
}

public double EvaluatePosition(Piece[,] boardPos, bool ForWhite)
{
    double 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 += GetPieceWorth(boardPos[i, j]);
                }
                else if (IsPieceBlack(boardPos[i, j]))
                {
                    eval -= GetPieceWorth(boardPos[i, j]);
                }
            }
        }
    }

    if (ForWhite)
        return eval;
    else
        return eval * -1;
}

//a-h,0-7
//Piece[,] board = new Piece[8, 8];

public static Piece[,] GetStartingBoard()
{
    Piece[,] board = new Piece[8, 8];
    for (int i = 0; i < 8; i++)
    {
        //initiate pawns
        board[1, i] = Piece.Pawn_W;
        board[6, i] = Piece.Pawn_B;
    }

    //white pieces
    board[0, 0] = Piece.Rook_W;
    board[0, 1] = Piece.Knight_W;
    board[0, 2] = Piece.Bishop_W;
    board[0, 3] = Piece.Queen_W;
    board[0, 4] = Piece.King_W;
    board[0, 5] = Piece.Bishop_W;
    board[0, 6] = Piece.Knight_W;
    board[0, 7] = Piece.Rook_W;

    //black pieces
    board[7, 0] = Piece.Rook_B;
    board[7, 1] = Piece.Knight_B;
    board[7, 2] = Piece.Bishop_B;
    board[7, 3] = Piece.Queen_B;
    board[7, 4] = Piece.King_B;
    board[7, 5] = Piece.Bishop_B;
    board[7, 6] = Piece.Knight_B;
    board[7, 7] = Piece.Rook_B;

    //test
    //board[1, 4] = Piece.Pawn_B;
    //board[6, 2] = Piece.Pawn_W;

    return board;
}

我上传了一段引擎与自己对战的短片,以展示奇怪的动作:https://www.youtube.com/watch?v=A0HVgXYSciY

【问题讨论】:

  • 国际象棋的评价函数不是微不足道的。除非depth 太大以至于片值存在显着的非瞬态差异,否则代码中的函数不会给出任何有意义的结果。在游戏开始时,这意味着 depth 需要很大,以至于使用 minimax 是不切实际的。
  • 但视频中黑方不与主教走卒,这难道不奇怪吗?
  • Minimax 因在评估函数有缺陷时会做出意想不到的事情而臭名昭著。最简单的例子是this tic tac toe position。计算机在它可以获胜的时候发挥着核心作用。为什么?因为 top-center 在 top-right 之前评估,所以深度是无限的,并且评估函数仅基于谁获胜。通过玩top-center,电脑一定会赢。没有比这更好的结果了,所以这就是计算机选择的移动。
  • 如果你想知道你的极小极大算法看到了什么,然后在你的GetStartingBoard函数中硬编码那个位置。然后打印出每个可能动作的分数。
  • 这不是合法的移动,在视频的中间它提升到皇后旁边的一个皇后,然后黑棋走车,即使唯一的动作是拿走皇后。

标签: c# chess minimax


【解决方案1】:

终于可以让minimax函数工作了。感谢大家的帮助!

工作方式:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多