使Minimax返回最佳移动,而不是最佳移动产生的得分

时间:2019-11-22 06:52:57

标签: java minimax

从我所看到的所有示例中,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());
}

2 个答案:

答案 0 :(得分:0)

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

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

请注意,如果从起始板开始的路径在此之前仅遇到终端状态,则可能永远不会达到深度0。例如,探索深度可能是8,但是必须捕获所有的棋子,并且游戏在接下来的2个动作中结束,因此对board.getPossibleMoves()的调用将返回一个空数组。这将使最佳动作保持不变。添加类似isTerminal(board)之类的支票可以解决这种情况。

我注意到minPlayerBestValmaxPlayerBestValalpha-beta pruning范围)在提供的实现中似乎没有更新。您的递归调用也缺少Color current参数。

不需要使用原始数据类型的盒装版本;使用intboolean

最后,在不知道要编写游戏的情况下(我想像是仅当棋的游戏),您提供的用于评估的启发式方法可能不完整,并且可能需要考虑下一次未发生捕获的位置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;
}

答案 1 :(得分:0)

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