我正在尝试使用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;
}
我的初次通话如下:
[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的建议,但我仍然无法让它工作。我在深度= 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&lt;最大化时的alpha。否则,它将在扫描第一个根移动后始终修剪。
这是我启动递归的方式:
[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;
}
答案 0 :(得分:-1)
这个错误似乎在你的修剪可能部分(这是一个negamax-alpha beta的实现,而你正在使用minimax-alphabeta)。
要解决此问题,只需添加一个if,目前正在最大化或最小化(正如您在更改alpha或beta时所做的那样)。
当你最小化的时候,只要alpha&gt; = beta就会修剪,反之则最大化。
之后,代码应该可以正常工作(如果没有其他错误;)。)。