Chess negamax algorithm moves pieces back and forth. What's wrong?

Okay, so I'll admit up front this one's going to be a bit long. I'm writing a chess engine for C#, with the eventual goal including UCI implementation. I've got it to the point where, given a board, the engine will generate a list of all valid moves; however, my evaluation code seems to be struggling, because when playing a game against itself, the engine will move two pawns on either side, and then just move a piece back and forth on either side. I'm going to outline the crucial parts of the program below in order to best allow you to understand under what conditions my code is called and used, in the hope that it will help you answer my question.

This is just the main method called by my interface, nothing exciting here.

class BreezeEngine
{
    // Declares Piece Values
    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;
    }

This method is working just fine. It returns a list of moves with format x1, y1, x2, y2, weight The weight this method generates is the value of whatever piece is killed. If you have questions, let me know.

    private static List<int[]> CalcFutures(Board chessBoard)
    {
        // Move generation stuff.
    }

This method isn't complete yet (since it doesn't handle castling or en passant), but it's basically just to generate a new board object from any given move.

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

This code is probably not needed, but I'm including it on the off chance that a typo in here is causing the bug. This is just a very basic user interface to allow me to play a game against my engine, or have the engine play itself.

    private static void PlayGame(Board chessBoard, bool demo)
    {
        int[] move = new int[5];
        if (!(chessBoard.IsTurn() || demo))
        {
            Console.WriteLine("Type in your move one integer at a time: 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("Calculating Move..." + chessBoard.IsTurn());
            move = Evaluate(CalcFutures(chessBoard), chessBoard);
        }
        if (Math.Abs(chessBoard.Pieces()[move[3], move[2]]) == king)
        {
            if (chessBoard.IsTurn()) Console.Write("White Wins");
            else Console.Write("Black Wins");
            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);
    }
}

Now, I'm going on a brief tangent before presenting the evaluation algorithm itself. This is the board object you've seen referenced many other times throughout the code. It contains an array for the chess board, as well as other variables necessary for defining the game's current state.

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

Now that I've outlined the rest of my program, here is the evaluation method itself. The code takes in a list of moves, and if it is to the furthest depth that is supposed to be searched, it simply returns the one with the greatest absolute value. If it's not to the bottom, it simply generates a list of futures, for each move in the list of futures it receives, and calls itself again. The returned value is then added to the original move's weight, and compared to the best move it has found so far. However, I've been having issues with this approach, and I'm guessing it's either because I've misunderstood how negamax is supposed to work, or I've made a typo somewhere along the way. Any idea what's going on?

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

You're doing the evaluation wrong. You have to make sure that either side is picking its own best future. Honestly, minimax is easy to implement given your data structure for moves. Here's the fixed evaluation function.

    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())
            {
                // Maximum
                //bestMove[4] = -1000000;
                foreach (int[] move in futures)
                {
                    if (move[4] > bestMove[4])
                    {
                        Array.Copy(move, bestMove, 5);
                    }
                }
            }
            else
            {
                // Minimum
                //bestMove[4] = 1000000;
                foreach (int[] move in futures)
                {
                    if (move[4] < bestMove[4])
                    {
                        Array.Copy(move, bestMove, 5);
                    }
                }

            }
        }
        else
        {

            if (chessBoard.IsTurn())
            {
                // Maximum
                //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
            {
                // Minimum
                //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;
    }
链接地址: http://www.djcxy.com/p/14822.html

上一篇: 国际象棋:Alpha中的错误

下一篇: 国际象棋negamax算法来回移动棋子。 怎么了?