国际象棋负极大算法棋子来回移动。哪里出了问题?

好的,我承认这篇文章会有点长。我正在为C#编写一个国际象棋引擎,最终目标包括实现UCI。我已经做到了一定程度,给定一个棋盘,引擎会生成所有有效移动的列表;然而,我的评估代码似乎遇到了困难,因为在与自己对弈时,引擎会移动两边的两个兵,然后只是在两边来回移动一个棋子。我将在下面概述程序的关键部分,以便您最好地理解我的代码在什么条件下被调用和使用,希望这能帮助您回答我的问题。

这是由我的界面调用的主方法,这里没有什么令人兴奋的。

class BreezeEngine{    // 声明棋子值
    public const int pawn = 1, knight = 4, bishop = 6, rook = 8, queen = 16, king = 60;
    public const int toDepth = 4;
    public static void BoardGen(string mode)
    {
        Board chessBoard = new Board(new int[8, 8] {
            { 8, 4, 6,16,60, 6, 4, 8 },
            { 1, 1, 1, 1, 1, 1, 1, 1 },
            { 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 0 },
            {-1,-1,-1,-1,-1,-1,-1,-1 },
            {-8,-4,-6,-16,-60,-6,-4,-8 }
            }, 0, true, true, true);
        PlayGame(chessBoard, true);
        return;
    }

这个方法运行得很好。它返回一个移动列表,格式为x1, y1, x2, y2, weight。这个方法生成的权重是所杀棋子的值。如果您有问题,请告诉我。

    private static List<int[]> CalcFutures(Board chessBoard)
    {
        // 移动生成代码。
    }

这个方法尚未完成(因为它不处理王车易位或吃过路兵),但它基本上只是从任何给定移动生成一个新的棋盘对象。

    private static Board MoveToBoard(int[] move, Board board)
    {
        int[,] newBoard = new int[8, 8];
        Array.Copy(board.Pieces(), newBoard, 64);
        newBoard[move[3], move[2]] = newBoard[move[1], move[0]];
        newBoard[move[1], move[0]] = 0;
        if (newBoard[move[3], move[2]] == pawn && move[3] == 7) newBoard[move[3], move[2]] = queen;
        if (newBoard[move[3], move[2]] == -pawn && move[3] == 0) newBoard[move[3], move[2]] = -queen;
        return new Board(newBoard, board.Depth() + 1, !board.IsTurn(), true, true);
    }

这段代码可能不需要,但我将其包括在内,以防这里的某个打字错误导致了错误。这只是一个非常基本的用户界面,允许我与引擎对弈,或让引擎自己对弈。

    private static void PlayGame(Board chessBoard, bool demo)
    {
        int[] move = new int[5];
        if (!(chessBoard.IsTurn() || demo))
        {
            Console.WriteLine("请一次输入一个整数来输入您的移动:x1,y1,x2,y2");
            move[0] = Convert.ToInt32(Console.ReadLine());
            move[1] = Convert.ToInt32(Console.ReadLine());
            move[2] = Convert.ToInt32(Console.ReadLine());
            move[3] = Convert.ToInt32(Console.ReadLine());
        }
        else
        {
            Console.WriteLine("正在计算移动..." + chessBoard.IsTurn());
            move = Evaluate(CalcFutures(chessBoard), chessBoard);
        }
        if (Math.Abs(chessBoard.Pieces()[move[3], move[2]]) == king)
        {
            if (chessBoard.IsTurn()) Console.Write("白方获胜");
            else Console.Write("黑方获胜");
            return;
        }
        chessBoard = MoveToBoard(move, chessBoard);
        chessBoard.SetDepth(0);
        for (int i = 0; i < 8; i++)
        {
            for (int j = 0; j < 8; j++)
            {
                Console.Write(chessBoard.Pieces()[i, j].ToString().PadLeft(3, ' '));
            }
            Console.WriteLine();
        }
        PlayGame(chessBoard, demo);
    }

现在,在展示评估算法本身之前,我将简要地偏离一下。这是您在代码中多次看到引用的棋盘对象。它包含一个棋盘数组,以及定义当前游戏状态所需的其他变量。

class Board{
    bool isTurn;
    bool castling;
    bool enemyCastling;
    int[,] pieces = new int[8, 8];
    int weight = 0;
    int depth;
    public Board(int[,] inBoard, int inDepth, bool inIsTurn, bool inCastling, bool inEnemyCastling)
    {
        Array.Copy(inBoard, pieces, 64);
        isTurn = inIsTurn;
        castling = inCastling;
        enemyCastling = inEnemyCastling;
        depth = inDepth;
    }
    public int Weight()
    {
        int sum = 0;
        foreach (int i in pieces)
            sum += i;
        weight = sum;
        return weight;
    }
    public int[,] Pieces() { return pieces; }
    public bool IsTurn() { return isTurn; }
    public void ToggleTurn() { isTurn = !isTurn; return; }
    public int Depth() { return depth; }
    public void SetDepth(int inDepth)
    {
        depth = inDepth;
    }
}

现在我已经概述了我的程序的其余部分,这里是评估方法本身。代码接收一个移动列表,如果它到达了应该搜索的最远深度,它只返回绝对值最大的那个。如果没有到达底部,它只是生成一个未来的列表,对于接收到的未来列表中的每个移动,它再次调用自己。然后返回的值被添加到原始移动的权重中,并与它找到的最佳移动进行比较。然而,我在这种方法上遇到了问题,我猜是因为我误解了负极大算法应该如何工作,或者在某个地方打错了字。您知道这是怎么回事吗?

    private static int[] Evaluate(List<int[]> futures, Board chessBoard)
    {
        int[] bestMove = new int[5];
        bestMove[0] = 30;
        if (chessBoard.Depth() >= toDepth)
        {
            foreach (int[] move in futures)
            {
                if (Math.Abs(move[4]) > Math.Abs(bestMove[4]))
                {
                    Array.Copy(move, bestMove, 5);
                }
            }
        }
        else
        {
            foreach (int[] move in futures)
            {
                Board newBoard = MoveToBoard(move, chessBoard);
                int[] testMove = Evaluate(CalcFutures(newBoard), newBoard);
                move[4] += testMove[4];
                if (bestMove[0] == 30) bestMove = move;
                if (chessBoard.IsTurn())
                {
                    if (move[4] > bestMove[4])
                    {
                        Array.Copy(move, bestMove, 5);
                    }
                }
                else
                {
                    if (move[4] < bestMove[4])
                    {
                        Array.Copy(move, bestMove, 5);
                    }
                }
            }
        }
        return bestMove;
    }

回答:

您的评估方法有问题。您必须确保双方都选择自己的最佳未来。说实话,给定您的移动数据结构,极小极大算法很容易实现。这里是修正后的评估函数。

    public static int[] Evaluate(List<int[]> futures, Board chessBoard)
    {
        int[] bestMove = new int[5];
        Random rndMove = new Random();
        Array.Copy(futures[rndMove.Next(futures.Count)], bestMove, 5);
        if (chessBoard.Depth() == toDepth)
        {
            if (chessBoard.IsTurn())
            {
                // 最大化
                //bestMove[4] = -1000000;
                foreach (int[] move in futures)
                {
                    if (move[4] > bestMove[4])
                    {
                        Array.Copy(move, bestMove, 5);
                    }
                }
            }
            else
            {
                // 最小化
                //bestMove[4] = 1000000;
                foreach (int[] move in futures)
                {
                    if (move[4] < bestMove[4])
                    {
                        Array.Copy(move, bestMove, 5);
                    }
                }
            }
        }
        else
        {
            if (chessBoard.IsTurn())
            {
                // 最大化
                //bestMove[4] = -1000000;
                foreach (int[] move in futures)
                {
                    if (Math.Abs(chessBoard.Pieces()[move[3], move[2]]) == king) return move;
                    Board newBoard = MoveToBoard(move, chessBoard);
                    move[4] += Evaluate(CalcFutures(newBoard), newBoard)[4];
                    if (move[4] > bestMove[4])
                    {
                        Array.Copy(move, bestMove, 5);
                    }
                }
            }
            else
            {
                // 最小化
                //bestMove[4] = 1000000;
                foreach (int[] move in futures)
                {
                    if (Math.Abs(chessBoard.Pieces()[move[3], move[2]]) == king) return move;
                    Board newBoard = MoveToBoard(move, chessBoard);
                    move[4] += Evaluate(CalcFutures(newBoard), newBoard)[4];
                    if (move[4] < bestMove[4])
                    {
                        Array.Copy(move, bestMove, 5);
                    }
                }
            }
        }
        //Console.WriteLine(bestMove[4]);
        return bestMove;
    }

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注