用Alpha-Beta修剪迭代加深Negamax

时间:2012-11-25 08:56:19

标签: c++ algorithm

我的程序中有一个有效的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的深度。我发现该算法极难调试,因为如果我打破执行,它会破坏时间。

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

2 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:1)

我也想用抽气窗,但我有点困惑。 http://mediocrechess.blogspot.com/2007/01/guide-aspiration-windows-killer-moves.html 这里说你应该使用 alpha = last_eval+window 但不应该是 -window 因为 alpha 是你可以达到的最小值。我发现很难找到包含吸入窗口示例的良好来源,也许这只是一个糟糕的例子?

也在我们研究的 PVS 算法中 score > alpha and score < beta PVS Wikipedia。但在这个来源中,它说 score < alpha or score > beta。这对我来说实际上更有意义。但为什么这些条件不同?