从我所看到的所有示例中,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());
}
答案 0 :(得分:0)
一种策略是使用类范围的实例变量存储最佳移动(另一种方法可能是返回一对值,该值和关联的移动)。只要您发现自己在顶级递归调用深度处有了新的更好的移动,就设置此最佳移动变量(在初始深度,我们正在检查所有可能的移动,并选择最终导致评估结果最佳的那个移动) 。
由于我们只希望最好的移动是从原点状态可以到达的,因此我们可以跟踪深度并仅在第一次递归调用时设置最佳移动,或者只要我们在移动中找到最佳移动就将其设置子级(当新的最佳返回给呼叫者时,它将被覆盖,因此我们最终将从原点获得可用的移动之一。)
请注意,如果从起始板开始的路径在此之前仅遇到终端状态,则可能永远不会达到深度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;
}
答案 1 :(得分:0)
一种解决方案是返回一个同时存储最佳移动和最佳分数的对象。 另一种方法是只搜索调用另一个搜索函数但返回最佳移动的根。 如果移动是整数形式,您可以检查您是否在根上,然后返回移动而不是分数。例如,在国际象棋编程中,通常移动是存储信息的整数,例如移动包含哪个棋子,如果移动是捕获....这是通过按位运算完成的。最后一个解决方案并不适合所有问题。