使用 Alpha-Beta 修剪的迭代加深 Negamax

Posted

技术标签:

【中文标题】使用 Alpha-Beta 修剪的迭代加深 Negamax【英文标题】:Iterative Deepening Negamax with Alpha-Beta Pruning 【发布时间】:2012-11-13 00:42:17 【问题描述】:

我的程序中有一个有效的 negamax 算法。但是,我需要该程序在kMaxTimePerMove 时间内找到可能的最佳移动。我做了一些研究,似乎在我的 negamax 算法中使用迭代深化是最好的方法。现在,我开始搜索的函数如下所示:

// this is a global in the same scope as the alpha-beta functions, so they can check the elapsed time
clock_t tStart;

int IterativeDeepening(Board current_state)

    bool overtime = false;
    int depth = 0;
    tStart = clock();

    MoveHolder best_move(-1, kWorstEvaluation);

    while ((static_cast<double> (clock() - tStart)/CLOCKS_PER_SEC) < kMaxTimePerMove)
    
        MoveHolder temp_move = AlphaBetaRoot(kWorstEvaluation, -best_move.evaluation_,++depth, current_state, overtime);          
        if (!overtime)
            best_move = temp_move;
    

    return best_move.column_;

我认为我还应该将以前的最佳移动重新排序到子列表的前面,但是,我正在等待实现它,直到我得到基本版本的工作。实际的 Alpha-Beta 函数如下所示:

MoveHolder AlphaBetaRoot(int alpha, int beta, int remaining_depth, Board current_state, bool &overtime)

    MoveHolder best(-1, -1);
    if (overtime)
        return MoveHolder(0,0);

    std::vector<Board> current_children;
    current_state.GetBoardChildren(current_children);

    for (auto i : current_children)
    
        best.evaluation_ = -AlphaBeta(-beta, -alpha, remaining_depth - 1, i, overtime);
        if ((static_cast<double> (clock() - tStart)/CLOCKS_PER_SEC) > kMaxTimePerMove)
        
            overtime = true;
            return MoveHolder(0,0);
         
        if (best.evaluation_ >= beta)
            return best;
        if (best.evaluation_ > alpha)
        
            alpha = best.evaluation_;
            best.column_ = i.GetLastMoveColumn();
        
    
    return best;


int AlphaBeta(int alpha, int beta, int remaining_depth, Board2 current_state, bool &overtime)

    if (overtime)
        return 0;
    if ((static_cast<double> (clock() - tStart)/CLOCKS_PER_SEC) > kMaxTimePerMove)
    
        overtime = true;
        return 0;
    

    if (remaining_depth == 0 || current_state.GetCurrentResult() != kNoResult)
    
        return current_state.GetToMove() * current_state.GetCurrentEvaluation();
    


    std::vector<Board> current_children;
    current_state.GetBoardChildren(current_children);
    for (auto i : current_children)
    
        int score = -AlphaBeta(-beta, -alpha, remaining_depth - 1, i, overtime);
        if (score >= beta)
        
            return beta;
        
        if (score > alpha)
        
            alpha = score;
        
    
    return alpha;

当我尝试调试时,一切似乎都按预期工作。但是,当我将迭代深化版本与常规的 alpha-beta 实现进行对比时,它总是会失败。有时它似乎被“卡住”,并返回一个可怕的举动。

举个例子,如果这个程序被“强迫”在下一回合移动,否则对手会获胜,它不会阻止胜利。在那次移动中,它报告说它正在搜索深度为 38。我发现该算法极难调试,因为如果我中断执行,它会破坏时间。

我不确定我是否错误地实现了算法,或者只是这里有一个棘手的错误。如果有人能指出我正确的方向,我将不胜感激。

【问题讨论】:

【参考方案1】:

您使用-best_move.evaluation_ 作为搜索的beta 值,其中best_move 是前一个深度的最佳移动。这是不正确的:假设一个动作在 depth=2 时看起来不错,但在更深的时候结果却很糟糕。这种方法将继续认为它是好的,并导致在其他移动中不应该发生的 beta 截止。

您应该在 (-infinity, infinity) 上搜索每次迭代以解决此问题。您也可以使用aspiration windows 来限制 alpha-beta 范围。

请注意,由于您没有使用上一次迭代来改进下一次的移动顺序,因此迭代深化会导致结果稍差。理想情况下,您希望移动排序从转置表和/或前一次迭代的主要变体中选择最佳移动。

【讨论】:

@Kyryx 至少在根部做一些移动排序,否则你实际上会丢失在下一次迭代开始时找到的好移动。将找到的最佳着法(更大的alpha)移到前面就足够了。 非常感谢,我误会了如何使用先前的深度提前切断。我想我现在明白了

以上是关于使用 Alpha-Beta 修剪的迭代加深 Negamax的主要内容,如果未能解决你的问题,请参考以下文章

使用带有 Alpha-Beta 修剪的 MinMax 找到最佳移动

我对 Connect Four 的评估函数和 Alpha-beta 修剪的实现不够智能

迭代加深搜索[codevs1004 四子连棋]

uva 11212 - Editing a Book(迭代加深搜索 IDA*) 迭代加深搜索

没初看上去那么慢的迭代加深(Iterative Deepening)搜索

迭代加深搜索(IDDFS)