【问题标题】:Make Minimax return best move instead of score resulted from best move使 Minimax 返回最佳移动而不是最佳移动产生的分数
【发布时间】:2020-03-18 05:09:15
【问题描述】:

从我看到的所有示例中,minimax 算法将返回一个 int 值,该值表示最佳得分或最佳棋局将产生的棋盘状态。如何返回与此分数相关的最佳移动?谢谢

private Integer minimax(Board board, Integer depth, Color current, Boolean maximizingPlayer, Integer maxPlayerBestVal, Integer minPlayerBestVal) {
    Integer bestValue;
    if (0 == depth)
        return ((current == selfColor) ? 1 : -1) * this.evaluateBoard(board, current);

    Integer val;
    if (maximizingPlayer) {
        bestValue = -INF;
        for (Move m : board.getPossibleMoves(current)) {
            board.apply(m);
            val = minimax(board, depth - 1, Boolean.FALSE, 
                      minPlayerBestVal, maxPlayerBestVal); // swap here 
            bestValue = Math.max(bestValue, val);
            board.revert(m);
            if (bestValue >= minPlayerBestVal) // too good for the minPlayer
                return bestValue;              // so cut here (pruning)
            }
        return bestValue;
    } else {
        [...] min player
    }
}

the evaluate function

private Integer evaluateBoard(Board board, Color player) {
    return board.pawns(player) - board.pawns(player.other());
}

【问题讨论】:

  • 该方法被递归调用,返回值需要是(或至少包括)此移动的得分(在此实现中)。最简单的方法是将最佳移动保存到全局变量中,并在方法返回分数后检查哪个是最佳移动。
  • 所以每次我通过循环我都会改变这个全局移动的值?
  • 每次你发现一个比当前最佳移动更好的移动时,你都会更新它。所以最后你有最好的移动存储在全局变量中。
  • 如何检查移动是否比上一个更好?感谢您的帮助
  • 您可以通过计算返回的分数来查看。如果分数高于当前最好的分数,则移动更好。但是您只需要检查最大化玩家部分。否则你也会针对对手玩家进行优化。

标签: java minimax


【解决方案1】:

一种策略是使用类范围的实例变量存储最佳移动(另一种方法可能是返回一对值、值和关联的移动)。每当您发现自己处于顶级递归调用深度并具有新的更好的移动时,设置此最佳移动变量(在初始深度,我们正在检查每一个可能的移动并选择最终导致具有最佳评估的节点的移动) .

由于我们只希望我们的最佳移动是从原始状态可到达的,我们可以跟踪深度并仅在第一次递归调用时设置最佳移动,或者在我们找到最佳移动时设置它child (当新的最佳返回给调用者时,它将被覆盖,因此我们最终会从原点获得可用的移动之一)。

请注意,如果从起始板开始的路径在此之前仅遇到终端状态,则您可能永远不会到达深度 0。例如,探索深度可能为 8,但必须捕获所有棋子,并且游戏在接下来的 2 步内结束,因此对 board.getPossibleMoves() 的调用返回一个空数组。这将留下最好的举动。添加对 isTerminal(board) 之类的检查将处理这种情况。

我注意到minPlayerBestVal 和maxPlayerBestVal(alpha-beta pruning 边界)在提供的实现中似乎没有更新。您的递归调用也缺少Color current 参数。

没有必要使用原始数据类型的盒装版本;使用int 和boolean。

最后,在不了解您正在编程的游戏的情况下(我想像只有棋子的国际象棋之类的游戏),您为评估提供的启发式方法可能不完整,并且可能需要考虑下一次没有发生捕获的位置depth 移动(如果游戏足够简单,例如 hexapawn,可以进行完整搜索,完全跳过深度限制)。

以下是上述几点的示例。由于我没有您的支持课程,因此您可能需要稍微调整一下:

private Move bestMove;

public Move getBestMove(Board board) {
    minimax(board, 42, selfColor, true, -INF, INF);
    return bestMove;
}

private int minimax(Board board, int depth, Color current, 
                    boolean maximizing, int alpha, int beta) {
    if (depth == 0/* || isTerminal(board)*/) {
        return ((current == selfColor) ? 1 : -1) * 
                   this.evaluateBoard(board, current);
    }
    else if (maximizing) {
        int best = -INF;

        for (Move m : board.getPossibleMoves(current)) {
            board.apply(m);
            int childVal = minimax(board, depth - 1, current, 
                                   false, alpha, beta);
            board.revert(m);

            if (childVal > best) {
                best = childVal;
                alpha = Math.max(alpha, best);
                this.bestMove = m;

                if (alpha >= beta) {
                    break;
                }
            }
        }

        return best;
    }

    int best = INF;

    for (Move m : board.getPossibleMoves(current)) {
        board.apply(m);
        best = Math.min(best, minimax(board, depth - 1, current, 
                                      true, alpha, beta));
        board.revert(m);
        beta = Math.min(beta, best);

        if (alpha >= beta) {
            break;
        }
    }

    return best;
}

【讨论】:

  • 谢谢!那么当我在终端节点中时,我要设置什么移动?
  • 当你在终端节点时,你不需要设置移动,只需将终端状态的值传回父节点。如果最终状态被评估为最大化玩家的胜利,那么父节点将识别这一点并相应地设置移动。最终,这个最佳值将达到顶层,并且将设置的最终移动将从源节点到达。如果最终状态是最大化者的失败或平局,但这是我们能做的最好的,最好的移动仍然会相应地设置。
  • 嗯好的!我可以更改 int best = -INF;阿尔法?例如,alpha = -INF;然后我把 alpha = Math.max(alpha, best);在递归 minmax 调用之后而不是在 if 语句中?
  • 我不确定我是否理解它会实现什么,即使它是正确的(我不确定它是否正确,但听起来很合理)。
【解决方案2】:

一种解决方案是返回一个存储最佳移动和最佳得分的对象。 另一种是只搜索调用另一个搜索函数但返回最佳移动的根。 如果移动是整数形式,您可以检查您是否在根上,然后返回移动而不是分数。例如,在国际象棋编程中,通常移动是存储信息的整数,例如移动包含哪一块,如果移动是捕获......这是通过按位运算完成的。最后一个解决方案并不适合所有问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-10
    相关资源
    最近更新 更多