Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 检查/绘图的Negamax实现_C#_Artificial Intelligence_Minimax_Negamax - Fatal编程技术网

C# 检查/绘图的Negamax实现

C# 检查/绘图的Negamax实现,c#,artificial-intelligence,minimax,negamax,C#,Artificial Intelligence,Minimax,Negamax,我一直在尝试为Unity3D制作的跳棋游戏实现一个好的人工智能,通过在线搜索,我发现最好的选择是MiniMax/Negamax, 所以我创建了这个类: public static class NegaMax { public static IMiniMaxNode FindBestChoice(IEnumerable<IMiniMaxNode> choices, int depth, int sign) { // if I simply use -Ca

我一直在尝试为Unity3D制作的跳棋游戏实现一个好的人工智能,通过在线搜索,我发现最好的选择是MiniMax/Negamax, 所以我创建了这个类:

public static class NegaMax
{
    public static IMiniMaxNode FindBestChoice(IEnumerable<IMiniMaxNode> choices, int depth, int sign)
    {
        // if I simply use -Calculate(...) I'm obtaining different results based on whether the depth is even or odd
        // I suspect this is wrong nonetheless
        int inverter = (depth % 2 == 0) ? 1 : -1;

        IMiniMaxNode bestNode = null;
        float alpha = float.NegativeInfinity;
        foreach (var choice in choices)
        {
            var score = inverter * Calculate(choice, depth - 1, float.NegativeInfinity, -alpha, -sign);
            if (score > alpha)
            {
                alpha = score;
                bestNode = choice;
            }
        }

        return bestNode;
    }

    private static float Calculate(IMiniMaxNode node, int depth, float alpha, float beta, int sign)
    {
        if (depth == 0)
        {
            return node.CalculateValue() * sign;
        }

        node.CreateChildren();

        if (node.Children.Length == 0) // if the opponent has no possible move
        {
            return sign / 0f; // (sign == 1) ? positive infinity : negative infinity
        }

        // standard negamax
        var bestValue = float.NegativeInfinity;
        for (int i = 0; i < node.Children.Length; i++)
        {
            var value = -Calculate(node.Children[i], depth - 1, -beta, -alpha, -sign);

            bestValue = Math.Max(bestValue, value);
            alpha = Math.Max(alpha, bestValue);

            if (alpha >= beta)
            {
                return bestValue;
            }
        }

        return bestValue;
    }
}
实际的实施是这样的:

public class CheckersMove : IMiniMaxNode
{
    public int Team { get; private set; }
    public IMiniMaxNode Parent { get; private set; }
    public IMiniMaxNode[] Children { get; private set; }

    public float CalculateValue()
    {
        // data.state is the current board array after this move has been applied
        // the board array is an int[8,8]:
        //     empty = 0, black pawn = -1, black king = -2, white pawn = 1, white king = 2
        //
        // and GetAbsoluteValue() simply returns a Sum of each element

        return data.state.GetAbsoluteValue() * Team;
    }

    public void CreateChildren()
    {
        // calculate every possible move for the opponent and assign them to the Children array
        // every child has Team = -Parent.Team
        // also, if a move has multiple jumps, they all get included in the same node
    }

    // ... other methods and properties to calculate the possible moves, 
    // and to store movement data (starting position, movement deltas, jump[s], promotion)
}
然后我在我的CheckersAI类中使用它:

private static MovementData CalculateMoveInternal(State state)
{
    // - state.team represents the team that has to make a move:
    //       black = -1, white = +1
    // - state.input is the current board state, represented an int[8,8] array: 
    //       empty = 0, black pawn = -1, black king = -2, white pawn = 1, white king = 2
    // - state.depth is simply the depth argument for the negamax function

    // create an empty root node, corresponding to the opponent's last move (hence -state.team)
    var move = CheckersMove.CreateRoot(state.input, -state.team);

    // calculate all possible moves for the current team
    move.CreateChildren();

    // find the best node amongst all of the root node children (shuffled to obtain different results if the best moves have the same value)
    var result = NegaMax.FindBestChoice(move.Children.Shuffle(), state.depth, state.team) as CheckersMove;

    // cache some values for debugging
    _lastRootMove = move;
    _lastChosenMove = result;

    // convert the result node into a valid move for the engine
    return CreateMovementData(result);
}
AI似乎在比赛的大部分时间都能正常工作,但有时会做出明显错误的决定(例如,无明显原因牺牲2个棋子),有时它会颠倒“不可能移动”情况的值,因为它赋予它正无穷大(胜利),而它本应是负无穷大(失败)

我90%确信问题出在某个地方的错误符号上,但我一直在尝试每种方法中符号的每种可能组合,人工智能总是做出意外的决定,我以黑人团队(-1)和白人团队(+1)的身份对其进行测试

有谁能帮我指出我可能做错了什么?
我试着把所有相关的代码都写进去,并对每一段重要的文章都发表评论。谢谢大家!

这是非常广泛的。我建议玩游戏直到出现错误的移动,记住电路板的情况,回到那个情况,开始一步一步地调试AI是如何得出错误结论的。我已经这样做了,虽然当有数千个节点需要评估时,一步一步地调试它很困难,但主要问题是损失(-无穷大)通常被认为是赢(+无穷大),所以我只想知道实际的Negamax实现是否正确。为什么有符号和反相器?反相器不是从符号推导出来的?正如我在评论中写到的,反相器存在只是因为我得到了不同的结果,如果深度是偶数,与奇数时相比,我应该用符号乘以吗?或者完全移除它?我做了很多尝试,但不知道哪个标志会出现在哪里。。。
private static MovementData CalculateMoveInternal(State state)
{
    // - state.team represents the team that has to make a move:
    //       black = -1, white = +1
    // - state.input is the current board state, represented an int[8,8] array: 
    //       empty = 0, black pawn = -1, black king = -2, white pawn = 1, white king = 2
    // - state.depth is simply the depth argument for the negamax function

    // create an empty root node, corresponding to the opponent's last move (hence -state.team)
    var move = CheckersMove.CreateRoot(state.input, -state.team);

    // calculate all possible moves for the current team
    move.CreateChildren();

    // find the best node amongst all of the root node children (shuffled to obtain different results if the best moves have the same value)
    var result = NegaMax.FindBestChoice(move.Children.Shuffle(), state.depth, state.team) as CheckersMove;

    // cache some values for debugging
    _lastRootMove = move;
    _lastChosenMove = result;

    // convert the result node into a valid move for the engine
    return CreateMovementData(result);
}