Artificial intelligence 使用alpha-beta修剪的Minimax产生错误的结果

Artificial intelligence 使用alpha-beta修剪的Minimax产生错误的结果,artificial-intelligence,minimax,alpha-beta-pruning,Artificial Intelligence,Minimax,Alpha Beta Pruning,我正在尝试用alpha-beta剪枝实现一个抽象的minimax算法。minimax部分工作得很好,但只要我添加alpha-beta修剪,IA就开始表现得非常愚蠢,甚至跳过了明显的动作。我不知道发生了什么事 这就是我的递归函数的样子: - (id<MMGameMove>)getBestMove:(id<MMGame>)game player:(MMPlayerSeed)player depth:(NSInteger)depth alpha:(NSInteger)alpha

我正在尝试用alpha-beta剪枝实现一个抽象的minimax算法。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;
}
据我所知,对于相同的游戏状态,alpha-beta剪枝应该给我与minimax一样的动作,但在这个实现中,情况显然不是这样

编辑1

在建议的修改之后,还有另一个错误,那就是我正在修剪根节点。我编辑了代码以反映正确的答案。在这样做并运行了minimax和alpha-beta修剪之后,我现在可以看到两者都产生了相同的结果,并且我能够检查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;
}
编辑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;
}
-(NSInteger)alphabeta:(id)游戏玩家:(MMPlayerSeed)玩家深度:(NSInteger)深度alpha:(NSInteger)alpha beta:(NSInteger)beta
{
如果(深度==0 | | game.isOver)
{
return[玩家的游戏分数:self.playerId深度:深度];
}
MMPlayerSeed对手;
NSInteger最佳成绩;
if(player==self.playerId)
{
对手=self.opponentId;
最佳分数=-无穷大;
}
其他的
{
对手=self.playerId;
最佳分数=无穷大;
}
for(游戏中id移动。所有移动)
{
id gameCopy=[游戏克隆];
move.player=玩家;
[游戏副本保存移动:移动];
self.count++;
NSInteger得分=[自我αβ:游戏副本玩家:对手深度:深度-1α:αβ:β];
if(player==self.playerId)
{
最佳分数=最大值(最佳分数,分数);
alpha=最大值(alpha,最佳分数);
if(depth==self.maxDepth)
{
move.rank=@(分数);
[self.rootMoves addObject:move];
}
如果(β<α)
{
打破
}
}
其他的
{
最佳得分=最小值(最佳得分,得分);
贝塔=分钟(贝塔,最佳分数);
如果(β<α)
{
打破
}
}
}
返回最佳分数;
}

错误似乎出现在修剪可能的部分(这是negamax alpha beta的实现,而您使用的是minimax alpha beta)

要解决这个问题,只需添加一个if,无论您当前正在最大化还是最小化(就像您在更改alpha或beta时所做的那样)

当最小化时,每当alpha>=beta时,就进行修剪,反之亦然


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

感谢您的帮助。我编辑了代码以包含您的建议。它似乎在做同样的事情。我想还有别的事。我试图找出原因,因为除了你提到的问题之外,根据我所读到的以及算法的其他实现,实现对我来说很好。好吧,至少我们知道错误一定在极大极小部分的某个地方。我会看一看……我已经研究这个算法两周了,我发誓我不能掌握它。我真的不明白为什么它会失败,所以感谢您再次查看它。如果我按照你的建议在最大化时进行修剪&&alpha忘记那个评论,我是在午餐时写的,并没有真正思考--我今天晚上会将它与我自己的算法进行比较。这一定是它!尝试在最小化截止值决策时,而不是alpha>beta score=beta。非常感谢您查看它。它仍然有错误。有时它忽略了,有时它报告了错误的号码。我猜这可能与我的代码中的其他内容有关。我几乎在晚上梦见所有不同的minimax alpha-beta剪枝实现,我自己一直在检查和实现,对于所有这些实现,似乎都有一个错误。奇怪的是,我的代码看起来与其他声称工作正常的实现几乎相同。谢谢你试一试。真的很感激你花了一些时间去看它。
[self alphabeta:game player:self.playerId depth:self.maxDepth alpha:-INFINITY beta:INFINITY];
- (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;
}