我知道此前已经提出过这样的问题,但我无法解决我的疑虑。 我有一个简单的奥赛罗引擎(它实际上非常好),使用下面的类来获得最佳动作:
import java.util.*;
import java.util.concurrent.*;
public class MinimaxOthello implements Runnable
{
private CountDownLatch doneSignal;
private int maxDepth;
private int calls;
private OthelloMove bestFound;
private OthelloBoard board;
private static float INFINITY = Float.MAX_VALUE/1000;
private boolean solve = false;
private Comparator<OthelloMove> comparator = Collections.reverseOrder(new MoveComparator());
public MinimaxOthello (OthelloBoard board, int maxDepth, CountDownLatch doneSignal, boolean solve)
{
this.board = board;
this.bestFound = new OthelloMove();
bestFound.setPlayer(board.getCurrentPlayer());
this.maxDepth = maxDepth;
this.doneSignal = doneSignal;
this.solve = solve;
}
public OthelloMove getBestFound()
{
return this.bestFound;
}
public void run()
{
float val = minimax(board, bestFound, -INFINITY, INFINITY, 0);
System.out.println("calls: " + calls);
System.out.println("eval: " + val);
System.out.println();
doneSignal.countDown();
}
private float minimax(OthelloBoard board, OthelloMove best, float alpha, float beta, int depth)
{
calls++;
OthelloMove garbage = new OthelloMove();
int currentPlayer = board.getCurrentPlayer();
if (board.checkEnd())
{
int bd = board.countDiscs(OthelloBoard.BLACK);
int wd = board.countDiscs(OthelloBoard.WHITE);
if ((bd > wd) && currentPlayer == OthelloBoard.BLACK)
{
return INFINITY/10;
}
else if ((bd < wd) && currentPlayer == OthelloBoard.BLACK)
{
return -INFINITY/10;
}
else if ((bd > wd) && currentPlayer == OthelloBoard.WHITE)
{
return -INFINITY/10;
}
else if ((bd < wd) && currentPlayer == OthelloBoard.WHITE)
{
return INFINITY/10;
}
else
{
return 0.0f;
}
}
if (!solve)
{
if (depth == maxDepth)
return OthelloHeuristics.eval(currentPlayer, board);
}
ArrayList<OthelloMove> moves = board.getAllMoves(currentPlayer);
if (moves.size() > 1)
{
OthelloHeuristics.scoreMoves(moves);
Collections.sort(moves, comparator);
}
for (OthelloMove mv : moves)
{
board.makeMove(mv);
float score = - minimax(board, garbage, -beta, -alpha, depth + 1);
board.undoMove(mv);
if(score > alpha)
{
alpha = score;
best.setFlipSquares(mv.getFlipSquares());
best.setIdx(mv.getIdx());
best.setPlayer(mv.getPlayer());
}
if (alpha >= beta)
break;
}
return alpha;
}
}
我有一个 bestFound 实例变量,我的疑问是,为什么必须调用
OthelloMove garbage = new OthelloMove();
并传递它?代码有效,但对我来说似乎很奇怪!
是否有更好的方式来获得最佳移动或主要变化? 我真的不是一个递归专家,这非常难以调试和可视化。 谢谢!
** PS:你可以在https://github.com/fernandotenorio/
克隆它答案 0 :(得分:1)
看起来您可以将best
参数移除到minimax
,从而无需garbage
,然后将best
替换为this.bestFound
。如果深度= 0,则仅设置bestFound
的属性。
您可以通过将this.bestFound
设置为最初为空的列表来获取主要变体。在moves
循环之前,创建一个新的移动。在if (score > alpha)
部分中,将其属性设置为与现在相同。循环后立即将移动推到列表中。然后,主要变化将与列表相反。
如果它很重要,可以进行一些更改,以改善班级的多线程性:
bestFound
列表存储为实例变量,而是将其作为run
中的局部变量并将其作为参数添加到minimax
Board.makeMove
不修改电路板,而是在应用移动的情况下返回电路板的新实例。您可以通过克隆电路板并将移动代码应用于克隆而不是变异this
来实现。然后,将克隆的板传递给minimax的下一次调用。答案 1 :(得分:0)
minimax
的第二个参数用于返回最佳动作。
garbage
的商家用于保持每个回合的最佳移动。使用您提供的代码,这并不重要。但是如果你想从当前棋盘到游戏结束产生一系列动作,你需要让它们成为单独的移动对象。
每回合使用一个单独的最佳移动对象,您可以使用线程进行多项技巧。首先,您可能希望限制奥赛罗AI的思考时间。在每个级别单独跟踪最佳移动意味着您始终拥有迄今为止最好的移动。这也意味着您可以缓存电路板的最佳移动,并在未来的minimax搜索中查看它。
其次,您可能希望并行搜索最佳移动,当每个minimax调用独立时,这是微不足道的。