Java 跟踪最小最大值的最佳移动

Java 跟踪最小最大值的最佳移动,java,recursion,minimax,alpha-beta-pruning,Java,Recursion,Minimax,Alpha Beta Pruning,我知道以前有人问过这样的问题,但我无法解决我的疑问。 我有一个简单的奥赛罗引擎(实际上它玩得很好),它使用下面的类来获得最佳的移动: import java.util.*; import java.util.concurrent.*; public class MinimaxOthello implements Runnable { private CountDownLatch doneSignal; private int maxDepth; private int ca

我知道以前有人问过这样的问题,但我无法解决我的疑问。 我有一个简单的奥赛罗引擎(实际上它玩得很好),它使用下面的类来获得最佳的移动:

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;
 }  
}
把它传下去?代码是有效的,但我觉得很奇怪

是否有“更好”的方法获得最佳移动或主要变化? 我真的不是递归专家,这是非常难调试和可视化的。 谢谢


**PS:您可以在

处克隆它。看起来您可以将
最佳
参数删除为
最小最大
,从而消除对
垃圾的需要,然后将
最佳
替换为
此。最佳发现
。仅在深度为0时设置bestFound的属性

您可以通过将
this.bestFound
设置为初始空列表来获取主要变量。在
移动
循环之前,创建一个新的移动。在
if(score>alpha)
部分中,将其属性设置为与现在相同的属性。将移动推到循环后的列表。然后,主要变化将与列表相反

如果这很重要,以下是一些可以改进类的多线程性的更改:

  • 不要将
    bestFound
    列表存储为实例变量,而是在
    run
    中将其作为局部变量,并将其作为参数添加到
    minimax
  • Make
    Board.makeMove
    不修改板,而是返回应用移动的板的新实例。您可以通过克隆电路板并将移动代码应用于克隆来实现这一点,而不是对该
进行变异。然后,将克隆板传递给下一次调用minimax
    minimax的第二个参数用于返回最佳移动

    垃圾业务用于保持每回合的最佳移动。对于您提供的代码,这并不重要。但是如果你想产生一系列从当前棋盘到游戏结束的移动,你需要让它们成为独立的移动对象

    在每个回合中使用一个单独的best move对象可以让你在线程方面做很多技巧。首先,你可能想限制奥赛罗AI的思考时间。在每个级别分别跟踪最佳移动意味着您始终拥有迄今为止可用的最佳移动。这也意味着你可以缓存一块板的最佳移动,并在未来的极小极大搜索中查找


    第二,您可能希望并行搜索最佳移动,当每个minimax调用都是独立的时,这一点很容易实现。

    我稍后会尝试,谢谢!主要的变化是什么?我是否需要更多的结构/编码来获得PV,或者一个简单的堆栈就可以了?不这样做的一个原因是它使类对于线程来说不安全。理论上,您可能希望运行多个线程以遵循不同的启动动作。如果它们都共享bestFound变量,那么这将不起作用。我提到这一点是因为我看到类实现了
    Runnable
    。我们缺少的是什么?我以前的代码可以在同一轮中将多个移动推到列表中。我对答案所做的更改应该可以解决这个问题。所以我没有返回最佳移动??minimax的返回值是最佳移动的分数。第二个参数通过引用返回实际的最佳移动。但是在您提供的代码中,在调用
    minimax
    之后,没有任何内容读取第二个参数。所以你是在回击最好的一步,但不是看它。就像我说的,我假设你在代码的其他地方读过它。是的,这就是为什么我使用线程信号,当minmax线程完成时,我调用minmax.getBestFound()并在黑板上播放移动。好的,我知道你现在在做什么了。我用
    垃圾
    编辑了我的答案。
    OthelloMove garbage = new OthelloMove();