当前位置: 首页 > 知识库问答 >
问题:

使用迭代深化实现极小极大搜索

祖翰音
2023-03-14

我在做什么:我正在用C编写一个象棋引擎。我最近更新了我的引擎的minimax搜索算法,该算法使用alpha-beta修剪来利用迭代深化,以便在时间限制下运行。这是它的外观:


//I return a string, int pair. The string represents the best move found, while the int represents the engine evaluation for the node after said move is made

static std::pair<string, int> iterativeDeepeningSearch(Board initialPosition, int maxDepth, milliseconds maxSearchTime)
    {
        std::pair<string, int> bestMove;
        milliseconds startTime = duration_cast<milliseconds>(
            system_clock::now().time_since_epoch());

        for (int currentDepth = 1; currentDepth <= maxDepth; currentDepth++)
        {

            milliseconds currentTime = duration_cast<milliseconds>(
                system_clock::now().time_since_epoch());
            if (currentTime > startTime + maxSearchTime)
            {
                return bestMove;
            }
            std::pair<string, int> bestMoveFoundFromMinimax = minimax(initialPosition, currentDepth, INT_MIN, INT_MAX, "", "", startTime + maxSearchTime);
            if (bestMoveFoundFromMinimax.first != "") {
                bestMove = bestMoveFoundFromMinimax;
            }
        }
        return bestMove;
    }

我的问题:这个实现的问题是,当搜索任何大于1的深度时,它将在搜索所需深度之前搜索所有之前的深度。也就是说,此迭代深化搜索首先搜索深度为1的所有移动。然后,它将再次搜索深度1,然后再搜索深度2,而不是在下一次搜索时选择深度2。然后,它将在搜索深度3之前搜索深度1和2。等等

我的问题是:这就是迭代深化的工作方式吗?每次增加深度时,我们也会搜索所有父节点?我设想,每次我们增加深度时,它只会在新深度搜索所有同级节点。如果这实际上是迭代深化的工作方式,那么如何只搜索新的深度而不搜索所有父节点呢?

作为参考,下面是我的(草率的)利用alpha-beta修剪的minimax函数:

static std::pair<string, int> minimax(Board node, int depth, int alpha, int beta, string move, string firstMove, milliseconds breakOffTime)
{
    if (breakOffTime != std::chrono::milliseconds(0))
    {
        milliseconds currentTime = duration_cast<milliseconds>(
            system_clock::now().time_since_epoch());
        if (currentTime > breakOffTime)
        {
            return std::make_pair("", 0);
        }
    }

    Moves &moves = Moves::getInstance();
    if (moves.isCheckmate(node))
    {
        if (node.getWhiteToMove())
        {
            return std::make_pair(firstMove, INT_MIN);
        }
        else
        {
            return std::make_pair(firstMove, INT_MAX);
        }
    }
    if (depth == 0)
    {
        return std::make_pair(firstMove, Rating::getCentipawnValue(node));
    }
    if (node.getWhiteToMove())
    {
        string bestMove = firstMove;
        int bestValue = INT_MIN;
        string pseudoLegalMoves = moves.pseudoLegalMovesW(node);
        if (pseudoLegalMoves.length() == 0)
        {
            return std::make_pair(firstMove, 0);
        }
        for (int i = 0; i < pseudoLegalMoves.length(); i += 4)
        {
            string individualMoveString;
            individualMoveString += pseudoLegalMoves[i];
            individualMoveString += pseudoLegalMoves[i + 1];
            individualMoveString += pseudoLegalMoves[i + 2];
            individualMoveString += pseudoLegalMoves[i + 3];
            Board tNode = moves.makeMoveAll(node, individualMoveString);
            if ((moves.unsafeForWhite(tNode) & tNode.getWK()) == 0)
            {
                std::pair<string, int> ab;
                if (firstMove == "")
                {
                    ab = minimax(tNode, depth - 1, alpha, beta, individualMoveString, individualMoveString, breakOffTime);
                }
                else
                {
                    ab = minimax(tNode, depth - 1, alpha, beta, individualMoveString, firstMove, breakOffTime);
                }
                int val = ab.second;
                string move = ab.first;
                if (val > bestValue || (val == bestValue && bestMove == ""))
                {
                    bestValue = val;
                    bestMove = move;
                }
                alpha = max(alpha, bestValue);
                if (alpha >= beta)
                {
                    break;
                }
            }
        }
        return std::make_pair(bestMove, bestValue);
    }
    else
    {
        string bestMove = firstMove;
        int bestValue = INT_MAX;
        string pseudoLegalMoves = moves.pseudoLegalMovesB(node);
        if (pseudoLegalMoves.length() == 0)
        {
            return std::make_pair(firstMove, 0);
        }
        for (int i = 0; i < pseudoLegalMoves.length(); i += 4)
        {
            string individualMoveString;
            individualMoveString += pseudoLegalMoves[i];
            individualMoveString += pseudoLegalMoves[i + 1];
            individualMoveString += pseudoLegalMoves[i + 2];
            individualMoveString += pseudoLegalMoves[i + 3];
            Board tNode = moves.makeMoveAll(node, individualMoveString);
            if ((moves.unsafeForBlack(tNode) & tNode.getBK()) == 0)
            {
                std::pair<string, int> ab;
                if (firstMove == "")
                {
                    ab = minimax(tNode, depth - 1, alpha, beta, individualMoveString, individualMoveString, breakOffTime);
                }
                else
                {
                    ab = minimax(tNode, depth - 1, alpha, beta, individualMoveString, firstMove, breakOffTime);
                }
                int val = ab.second;
                string move = ab.first;
                if (val < bestValue || (val == bestValue && bestMove == ""))
                {
                    bestValue = ab.second;
                    bestMove = ab.first;
                }
                beta = min(beta, bestValue);
                if (alpha >= beta)
                {
                    break;
                }
            }
        }
        return std::make_pair(bestMove, bestValue);
    }
}

此外,如果有人有兴趣检查它,这是我的完整项目:https://github.com/ChopinDavid/Maestro-cpp

我不是C开发人员,所以这可能很糟糕。

共有1个答案

史宸
2023-03-14

这就是迭代深化的工作方式吗?每次增加深度时,我们也会搜索所有父节点?

是的-但是由于您在进行迭代深化时保存了之前搜索中的最佳移动,并且会在向下的过程中首先尝试这些移动,因此您通常会在每个级别的第一个移动中找到最佳移动,因此修剪将非常有效。

如何只搜索新的深度而不是搜索所有的父节点?

如果这是您想要的,您可以放弃迭代深化,只需按您想要的深度进行一次搜索,但这可能是一个错误。在使用该解决方案之前,先计算有无迭代深化的评估板的数量。

 类似资料:
  • 我已经实现了一个带有alpha beta修剪的NegaMax算法(这只是一个较短版本的极小值算法)。现在我想实现迭代深化,这样我就可以为每个深度找到最佳移动,然后根据之前层的分数重新排序树下的节点,以便我的alphabeta修剪工作更有效。 以下是我迄今为止所做的工作: 这里gs是随每一步移动而变化的游戏属性,包含了所有关于游戏在t点的信息,比如是否可以施法或者是否有可能的内移。我的egamax算

  • 我想我终于对minimax和Alpha-beta修剪有所了解了,但实现它完全是另一回事! 根据我的理解,基础是:您为某些动作分配一个启发式函数分数(Gomoku为例)。 如果一行有5个,我们应该分配一个高值,比如9999,因为这是一个胜利的举动 当我们必须在Java中实现这一点时,我的问题来了! 我有一块彩色[][]板(8x8),其中黑色是播放器1,白色是播放器2,null表示空白,我不知道我们应

  • 我想使用极小极大搜索(带有alpha-beta修剪),或者更确切地说是内极大搜索,来使计算机程序玩纸牌游戏。 纸牌游戏实际上由4个玩家组成。所以为了能够使用极小极大等等。,我把游戏简化为“我”对抗“别人”。每次“走位”后,你都可以从游戏本身客观地读出当前状态的评价。当所有4个玩家都放好牌后,最高的玩家赢得所有人,并且牌的价值也算在内。 由于您不知道其他 3 名玩家之间的卡牌分布情况,我认为您必须使

  • 计算机科学中最有趣的事情之一就是编写一个人机博弈的程序。有大量的例子,最出名的是编写一个国际象棋的博弈机器。但不管是什么游戏,程序趋向于遵循一个被称为Minimax算法,伴随着各种各样的子算法在一块。本篇将简要介绍 minimax 算法,并通过实例分析帮助大家更好的理解。 一、概念 Minimax算法又名极小化极大算法,是一种找出失败的最大可能性中的最小值的算法。Minimax算法常用于棋类等由两

  • 我到处寻找修复代码的答案,但在花了很长时间调试代码后,我发现自己陷入了绝望。问题是,我的minimax函数不会为可能的最佳移动返回正确的值,我甚至试图通过存储最佳的第一个移动(当深度=0时)来修复它,但如果解决方案不明显,那么该算法将严重失败。我还尝试修改基本案例的返回值,以便优先考虑早期的胜利,但这并没有解决问题。 目前我正在TictoE板上测试这个函数,助手类(如getMoves()或getW

  • 我已经为游戏跳棋编写了一个带有alpha-beta修剪的minimax算法,现在我正尝试使用negamax方法重写它。我希望这两者是等价的,因为negamax只是一种编写minimax的技术。但由于某种原因,我的两种算法表现不同。当我在相同的输入上运行它们时,negamax版本似乎评估了更多的状态,所以我认为alpha-beta修剪一定有问题。 下面的代码显示了这两种算法(