【问题标题】:Minimax with alpha-beta pruning yields wrong results带有 alpha-beta 修剪的 Minimax 会产生错误的结果
【发布时间】:2015-10-02 14:07:00
【问题描述】:

我正在尝试使用 alpha beta 修剪实现一个抽象的 minimax 算法。极小极大部分效果很好,但是一旦我添加了 alpha beta 剪枝,IA 就开始表现得非常愚蠢,甚至会跳过明显的动作。我不确定发生了什么。

这就是我的递归函数的样子:

- (id<MMGameMove>)getBestMove:(id<MMGame>)game player:(MMPlayerSeed)player depth:(NSInteger)depth alpha:(NSInteger)alpha beta:(NSInteger)beta
{
    id<MMGameMove> bestMove = nil;
    NSArray *allMoves = [game allMoves];

    for (id<MMGameMove> move in allMoves)
    {
        //Take the move and evaluate the game's score
        id<MMGame> gameBoard = [game clone];
        move.player = player;
        [gameBoard saveMove:move];
        self.count++;

        if (depth == 0 || gameBoard.isOver)
        {
            move.rank = [gameBoard scoreForPlayer:self.playerId depth:depth];
        }
        else
        {
            MMPlayerSeed opponent = (player == self.playerId) ? self.opponentId : self.playerId;
            move.rank = [self getBestMove:gameBoard player:opponent depth:depth-1 alpha:alpha beta:beta].rank;
        }

        //If the new move is better than our previous move, take it
        BOOL minMove = (player == self.opponentId && move.rank <= beta);
        BOOL maxMove = (player == self.playerId && move.rank >= alpha);

        if (minMove || maxMove)
        {
            BOOL shouldPrune = NO;
            if (minMove)
            {
                beta = move.rank;
                if (alpha >= beta) {
                    shouldPrune = YES;
                }
            }
            else if (maxMove)
            {
                alpha = move.rank;
                if (alpha <= beta) {
                    shouldPrune = YES;
                }
            }

            bestMove = move;

            if (shouldPrune && depth < self.maxDepth) {
                break;
            }
        }
    }

    return bestMove;
}

我的初始调用如下所示:

[self getBestMove:game player:self.playerId depth:self.maxDepth alpha:INT_MIN beta:INT_MAX];

据我了解,对于相同的游戏状态,alpha-beta 剪枝应该给我带来与没有它的 minimax 完全相同的动作,但在这个实现中显然不是这样。

编辑 1

在建议的修改之后还有另一个错误,那就是我正在修剪根节点。我编辑了代码以反映正确的答案。在执行此操作并运行带和不带 alpha-beta 剪枝的 minimax 之后,我现在可以看到两者都产生了相同的结果,而且我能够检查从 alpha beta 加法中获得的更好的性能。

编辑 2

上面发布的代码实际上并没有按预期工作。我遵循了 xXliolauXx 的建议,但仍然无法正常工作。我在 depth = 0 或游戏结束时得到了正确的值,但似乎它们没有递归地传递回相应的根移动。例如,我可以看到我的启发式方法对于第一个根移动的子节点返回 -3,而对于其余子节点返回 0。所以我希望第一个根移动报告 -3 而不是 0,因为这是计算机执行该移动时可能发现的最坏情况。

这是我的新代码:

- (NSInteger)alphabeta:(id<MMGame>)game player:(MMPlayerSeed)player depth:(NSInteger)depth alpha:(NSInteger)alpha beta:(NSInteger)beta
{
    if (depth == 0 || game.isOver)
    {
        return [game scoreForPlayer:self.playerId depth:depth];
    }

    MMPlayerSeed opponent = (player == self.playerId) ? self.opponentId : self.playerId;

    for (id<MMGameMove> move in game.allMoves)
    {
        id<MMGame> gameCopy = [game clone];
        move.player = player;
        [gameCopy saveMove:move];
        self.count++;

        NSInteger score = [self alphabeta:gameCopy player:opponent depth:depth-1 alpha:alpha beta:beta];

        if (player == self.playerId)
        {
            if (depth == self.maxDepth)
            {
                move.rank = @(score);
                [self.rootMoves addObject:move];
            }

            alpha = MAX(alpha, score);

            if (beta < alpha)
            {
                break;
            }
        }
        else
        {
            beta = MIN(beta, score);

            if (beta < alpha)
            {
                break;
            }
        }
    }

    return (player == self.playerId) ? alpha : beta;
}

请注意,当 beta

这就是我启动递归的方式:

[self alphabeta:game player:self.playerId depth:self.maxDepth alpha:-INFINITY beta:INFINITY];

编辑 3

我想我明白了。我没有返回 alpha 或 beta,而是返回最好(或最差)的分数。我需要清理我的代码以使其更具可读性,但现在看起来是这样的:

- (NSInteger)alphabeta:(id<MMGame>)game player:(MMPlayerSeed)player depth:(NSInteger)depth alpha:(NSInteger)alpha beta:(NSInteger)beta
{
    if (depth == 0 || game.isOver)
    {
        return [game scoreForPlayer:self.playerId depth:depth];
    }

    MMPlayerSeed opponent;
    NSInteger bestScore;

    if (player == self.playerId)
    {
        opponent = self.opponentId;
        bestScore = -INFINITY;
    }
    else
    {
        opponent = self.playerId;
        bestScore = INFINITY;
    }

    for (id<MMGameMove> move in game.allMoves)
    {
        id<MMGame> gameCopy = [game clone];
        move.player = player;
        [gameCopy saveMove:move];
        self.count++;

        NSInteger score = [self alphabeta:gameCopy player:opponent depth:depth-1 alpha:alpha beta:beta];

        if (player == self.playerId)
        {
            bestScore = MAX(bestScore, score);
            alpha = MAX(alpha, bestScore);

            if (depth == self.maxDepth)
            {
                move.rank = @(score);
                [self.rootMoves addObject:move];
            }

            if (beta < alpha)
            {
                break;
            }
        }
        else
        {
            bestScore = MIN(bestScore, score);
            beta = MIN(beta, bestScore);

            if (beta < alpha)
            {
                break;
            }
        }
    }

    return bestScore;
}

【问题讨论】:

  • 好吧,至少我们知道错误一定在极小部分的某个地方。我去看看……
  • 我已经研究这个算法两周了,我发誓我无法掌握它。我真的不明白为什么它会失败,所以感谢您再次查看它。如果我在最大化 && alpha
  • 忘掉那条评论吧,我是在午餐时写的,并没有真正考虑过-.- 今天晚上我会把它和我自己的算法进行比较。
  • 这一定是它!尝试在最大化时代替 alpha>beta score=beta 来进行截止决策。
  • 非常感谢您查看它。它仍然有错误。有时它会忽略,有时它会报告错误的数字。我猜这可能与我的代码中的其他内容有关。我几乎在晚上梦到我自己检查和实现的所有不同的极小极大 alpha-beta 修剪实现,对于所有这些实现似乎都存在错误。奇怪的是,我的代码看起来与其他声称运行良好的实现几乎相同。感谢您试一试。非常感谢您花一些时间来查看它。

标签: artificial-intelligence minimax alpha-beta-pruning


【解决方案1】:

错误似乎出在您的 pruning-possible-part 中(它是 negamax-alpha beta 的实现,而您使用的是 minimax-alphabeta)。

要修复它,只需添加一个 if,无论您当前是最大化还是最小化(就像您在更改 alpha 或 beta 时所做的那样)。

当您进行最小化时,只要 alpha >= beta,您就会进行修剪,反之亦然。

之后,代码应该可以正常工作(如果没有其他错误;))。

【讨论】:

  • 感谢您的帮助。我编辑了代码以包含您的建议。它似乎在做同样的事情。我想那还有别的。我试图弄清楚为什么,因为除了你提到的问题之外,根据我一直在阅读的内容以及算法的其他实现,该实现对我来说看起来不错。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多