【问题标题】:MiniMax chess algoritm returns bad movesMiniMax 国际象棋算法返回错误的移动
【发布时间】:2019-03-24 15:11:17
【问题描述】:

我在为我的国际象棋游戏实施 MiniMax 算法时遇到问题。它的大部分似乎都有效,但它要么永远不会做出好的动作,要么对它们的评估(基于两个玩家的活跃棋子的分数)有问题。 例如,如果我设置检查(例如傻瓜的伴侣),人工智能会做一些随机的事情而不是杀死国王。我真的无法指出我做错了什么。

评估板的类 StandardBoardEvaluator 在经过一些测试后似乎可以工作,因此问题很可能在 MiniMax 实现中的某个地方。游戏由一个 Board 类组成,它具有我自己的 Square 类的 8x8 对象的 2D 数组,它本身具有对 Piece 的引用(可以为 null,或任何典型的棋子)。 在算法中,我不断地在 searchthree 中创建新的 Board 实例,这就是为什么我在 Board 和 Square 中制作这些“深度克隆”构造函数的原因,所以这似乎不是问题。像这样:

public Board(Board originalBoard) {
        this.turnIsWhite = originalBoard.getTurnIsWhite();
        winner = null;
        squares = new Square[8][8];

        for (int rank=0; rank<squares.length; rank++) {
            for(int file=0; file<squares[rank].length; file++) {
                squares[rank][file] = new Square(originalBoard.getSquare(posStringFromFileRank(rank, file)));
            }
        }
    }

public Square(Square originalSquare) {
        this.pos = new String(originalSquare.getPos());
        this.piece = originalSquare.getPiece();
    }

我有一个典型的命令类,MovePiece,用于移动棋子。这使用另一个类 MoveCheck 来检查移动命令是否合法。 MovePiece 返回一个布尔值,表示移动是否合法。这两个类都经过了严格的测试并且正在运行,所以我认为问题不在这些类中。

这是算法:

public class MiniMax implements MoveStrategy{
    BoardEveluator bV;
    MoveGenerator mGen;
    int depth;

    public MiniMax(int depth){
        bV = new StandardBoardEvaluator();
        mGen = new MoveGenerator();
        this.depth = depth;
    }

    @Override
    public MovePiece execute(Board board) {
        MovePiece bestMove = null;
        int lowestValue = Integer.MAX_VALUE;
        int highestValue = Integer.MIN_VALUE;
        int currentValue = 0;

        String color = (board.getTurnIsWhite() ? "white" : "black");
        System.out.println(color + " is evaluation best move with MiniMax depth " + depth);
        List<MovePiece> allPossibleMoves = mGen.getLegalMoves(board, board.getTurnIsWhite());

        for (MovePiece mp : allPossibleMoves){
            Board tempBoard = new Board(board);
            mp.setBoard(tempBoard);
            if (mp.execute()){
                currentValue = tempBoard.getTurnIsWhite() ? min(tempBoard, depth -1) : max(tempBoard, depth -1);
                if (board.getTurnIsWhite() && currentValue >= highestValue){
                    highestValue = currentValue;
                    bestMove = mp;
                }
                else if (!board.getTurnIsWhite() && currentValue <= lowestValue){
                    lowestValue = currentValue;
                    bestMove = mp;
                }
                mp.unexecute();
            }
        }
        return bestMove;
    }



    int min (Board board, int depth){
        if (depth == 0 || board.getWinner() != null){
            return bV.eveluate(board);
        }
        int lowestValue = Integer.MAX_VALUE;
        List<MovePiece> legalMoves = mGen.getLegalMoves(board, board.getTurnIsWhite());
        for (MovePiece mp : legalMoves){
            Board tempBoard = new Board(board);
            mp.setBoard(tempBoard);
            if (mp.execute()){
                int currentValue = max(tempBoard, depth - 1);
                if (currentValue <= lowestValue){
                    lowestValue = currentValue;
                }
                mp.unexecute();
            }

        }
        return lowestValue;
    }
    int max (Board board, int depth){
        if (depth == 0 || board.getWinner() != null){
            return bV.eveluate(board);
        }
        int highestValue = Integer.MIN_VALUE;
        List<MovePiece> legalMoves = mGen.getLegalMoves(board, board.getTurnIsWhite());
        for (MovePiece mp : legalMoves){
            Board tempBoard = new Board(board);
            mp.setBoard(tempBoard);
            if (mp.execute()){
                int currentValue = min(tempBoard, depth - 1);
                if (currentValue >= highestValue){
                    highestValue = currentValue;
                }
                mp.unexecute();
            }
        }
        return highestValue;
    }

还有评估者类

public class StandardBoardEvaluator implements BoardEveluator {
    private int scorePlayer(Board board, boolean isWhite){
        return pieceValue(board, isWhite) + mobolity(isWhite, board);
    }
    private int mobolity(boolean isWhite, Board board){
        return (int) (board.getActiveSquares(isWhite).size() * 1.5);
    }
    private static int pieceValue(Board board, boolean isWhite){
        int piceValueScore = 0;
        for (Square square : board.getActiveSquares(isWhite)){
            piceValueScore += square.getPiece().getPieceValue();
        }
        return piceValueScore;
    }
    @Override
    public int eveluate(Board board) {
        return scorePlayer(board, true) - scorePlayer(board, false);
    }
}

这是 MovePiece 类:

private Square from;
    private Square to;
    private Board board;
    private MoveCheck mCheck;
    private RulesCheck rCheck;
    private boolean done = false;
    private Piece killed;

    public MovePiece(Board board, String from, String to) {
        this.board = board;
        this.from = board.getSquare(from);
        this.to = board.getSquare(to);
        mCheck = new MoveCheck();
    }
    public MovePiece(Board board, Square from, Square to) {
        this.board = board;
        this.from = from;
        this.to = to;
        mCheck = new MoveCheck();
        rCheck = new RulesCheck(board);
    }
    public void setBoard(Board board) {
        this.board = board;
    }
    public Board getBoard() {
        return board;
    }

    public Square getFrom() {
        return from;
    }
    public Square getTo() {
        return to;
    }
    public void setFrom(Square from) {
        this.from = from;
    }
    public void setTo(Square to) {
        this.to = to;
    }
    public void setFrom(String from) {
        this.from = board.getSquare(from);
    }
    public void setTo(String to) {
        this.to = board.getSquare(to);
    }
    @Override
    public boolean execute() {
        rCheck = new RulesCheck(board);
        if (done) {
            board.movePiece(from, to);
            return true;
        }
        else if (mCheck.isLegal(board, from, to)){
            if (to.getPiece() != null) {
                killed = to.getPiece();
                rCheck.winCheck(killed);
            }
            board.setGameOutput("Moved " + from.pieceToString() + " at " + from.getPos() + " - to " + to.getPos() + "(" + to.pieceToString() + ")");
            board.movePiece(from, to);
            rCheck.checkPromotion(to);
            done = true;
            return true;
        }
        return false;
    }

    @Override
    public void unexecute() {
        if (to.getPiece().getClass() == Pawn.class)
            ((Pawn) to.getPiece()).decreaseMoves();
        board.movePiece(to, from);
        if (killed != null) {
            to.setPiece(killed);
        }

    }

MoveCheck 类仅查看移动对于棋子是否合法(路径清晰,目标是敌人还是空等),不要认为这与我的问题有关,因为代码已经过测试并且可以工作。

片段值在抽象类 Piece 的子类(所有片段类型)中声明为 int。兵100分,象马300分,车500分,后900分,国王10000分。

如果有人能帮助我找出问题所在,我将永远感激不尽!如果您需要查看我没有显示的其他代码,请告诉我。

【问题讨论】:

  • 你解决问题了吗?
  • 不幸的是,我厌倦了我,所以我一起切换了算法。我可能会在其他时间回顾它,但现在我不能被打扰。不过感谢您的帮助!我很感激。

标签: java chess minimax


【解决方案1】:

您既没有分享MovePiece 的实现,也没有分享主游戏循环,但我在MiniMax.execute 方法中发现了两个可能的问题:

currentValue = tempBoard.getTurnIsWhite() ? min(tempBoard, depth -1) : max(tempBoard, depth -1)

根据上面的代码,您假设 MinMax 播放器将始终为黑色,因为它计算 min 为白色,max 为黑色。对于通用算法,这是一个错误的假设,但不知道它是否适合您。

第二件事是在调用mp.execute() 并分配bestMove = mp 之后调用mp.unexecute(),因此有效地调用bestMove.unexecute(),因为变量指向同一个对象。

请考虑以上建议,如果不能解决问题,请分享上述实现部分。

【讨论】:

  • 感谢您的意见!我很确定 currentValue 行是正确的,因为稍后在执行方法中我会处理是否需要高值或低值。 Java if (board.getTurnIsWhite() &amp;&amp; currentValue &gt;= highestValue){ highestValue = currentValue; bestMove = mp; } else if (!board.getTurnIsWhite() &amp;&amp; currentValue &lt;= lowestValue){ lowestValue = currentValue; bestMove = mp; }
  • 此外,未执行也不是问题,因为我仍然将 from 和 to 方块存储在 MovePiece 对象中。当执行方法返回它时,我只是在 MiniMax 类之外再次调用它。当然你不可能知道,所以我会在我的原帖中分享MovePiece类!
  • 好的,我如何一步一步地看到它:1)我想看到玩家 X 的最佳移动 2)我调用 MiniMax.execute 方法为我创建了当前棋盘的根节点状态(我始终是此根节点的起始玩家) 3)从根节点我将子节点的 always max 值作为我的首选移动 4)从根计算子节点的值我需要去每个节点并获取其子节点的 min 值。 5)这导致我进一步下降(直到达到叶子或最大深度)但记住相应地取最小值/最大值。
  • 现在您根据玩家颜色计算最小值或最大值,如果当前玩家(在根节点中)是白色的,则最终计算 min。这基本上让他总是选择最糟糕的举动。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-06
  • 1970-01-01
  • 1970-01-01
  • 2013-05-24
相关资源
最近更新 更多