使用MinMax与Alpha一起寻找最佳举措

我正在为一款游戏开发人工智能,并且我想使用Alpha-Beta修剪的MinMax算法。

我对它是如何工作有一个粗略的想法,但我仍然无法从头开始编写代码,所以我花了最近两天在线寻找某种伪代码。

我的问题是,我在网上找到的每一个伪代码似乎都是基于找到最佳移动的价值,而我需要返回最佳移动本身而不是数字。

我目前的代码是基于这个伪代码(源代码)

minimax(level, player, alpha, beta){  // player may be "computer" or "opponent"
    if (gameover || level == 0)
       return score
    children = all valid moves for this "player"
    if (player is computer, i.e., max's turn){
       // Find max and store in alpha
       for each child {
          score = minimax(level - 1, opponent, alpha, beta)
          if (score > alpha) alpha = score
          if (alpha >= beta) break;  // beta cut-off
       }
       return alpha
    } else (player is opponent, i.e., min's turn)
       // Find min and store in beta
       for each child {
          score = minimax(level - 1, computer, alpha, beta)
          if (score < beta) beta = score
          if (alpha >= beta) break;  // alpha cut-off
       }
       return beta
    }
}

// Initial call with alpha=-inf and beta=inf
minimax(2, computer, -inf, +inf)

正如你所看到的,这段代码返回一个数字,我想这是使一切正常工作所必需的(因为在递归期间使用了返回的数字)。

所以我认为我可以使用外部变量来存储最佳移动,这就是我改变以前的代码的方法:

minimax(level, player, alpha, beta){  // player may be "computer" or "opponent"
    if (gameover || level == 0)
       return score
    children = all valid moves for this "player"
    if (player is computer, i.e., max's turn){
       // Find max and store in alpha
       for each child {
          score = minimax(level - 1, opponent, alpha, beta)
          if (score > alpha) {
              alpha = score
              bestMove = current child // ROW THAT I ADDED TO UPDATE THE BEST MOVE
          }
          if (alpha >= beta) break;  // beta cut-off
       }
       return alpha
    } else (player is opponent, i.e., min's turn)
       // Find min and store in beta
       for each child {
          score = minimax(level - 1, computer, alpha, beta)
          if (score < beta) beta = score
          if (alpha >= beta) break;  // alpha cut-off
       }
       return beta
    }
}

// Initial call with alpha=-inf and beta=inf
minimax(2, computer, -inf, +inf)

现在,这对我来说是有意义的,因为我们需要更新最好的棋步,只有当棋手轮到他时,以及如果棋步比以前好。

所以,虽然我认为这是正确的(即使我不是100%肯定),但是该源代码还有一个java实现,即使在score < beta情况下也更新bestMove ,我不明白为什么。

尝试这种实施方式,导致我的代码选择最好的方式从对手角度出发,这似乎并不正确(假设我是黑人玩家,我正在寻找我可以做出的最佳举动我期待着一个“黑”的举动,而不是一个“白”的)。

我不知道我的伪代码(第二个)是否是使用MinMax进行alpha-beta修剪以找到最佳移动的正确方法,或者即使在得分<beta的情况下我需要更新最佳移动。

如果您愿意,请随时提出任何新的和伪装的伪代码,我不拘泥于任何内容,如果它比我的更好,我不介意重写一些代码。

编辑:

由于我无法理解答复,所以我想这个问题可能不会问我想知道什么,所以我试图在这里写得更好。

假设我想为一名球员获得最佳动作, 并且每次需要新动作(这样minmax(2, black, a, b)返回到MinMax函数在minmax(2, white, a ,b)为白色玩家返回最佳效果时minmax(2, white, a ,b)黑色玩家的最佳举动),你将如何改变第一个伪代码(或源代码中的java实现)来存储这个给定的最佳移动某处?

编辑2:

让我们看看我们是否可以这样做。

这是我的实施,你能告诉我,如果它是正确的?

//PlayerType is an enum with just White and Black values, opponent() returns the opposite player type
protected int minMax(int alpha, int beta, int maxDepth, PlayerType player) {        
    if (!canContinue()) {
        return 0;
    }
    ArrayList<Move> moves = sortMoves(generateLegalMoves(player));
    Iterator<Move> movesIterator = moves.iterator();
    int value = 0;
    boolean isMaximizer = (player.equals(playerType)); // playerType is the player used by the AI        
    if (maxDepth == 0 || board.isGameOver()) {
        value = evaluateBoard();
        return value;
    }
    while (movesIterator.hasNext()) {
        Move currentMove = movesIterator.next();
        board.applyMove(currentMove);
        value = minMax(alpha, beta, maxDepth - 1, player.opponent());
        board.undoLastMove();
        if (isMaximizer) {
            if (value > alpha) {
                selectedMove = currentMove;
                alpha = value;
            }
        } else {
            if (value < beta) {
                beta = value;
            }
        }
        if (alpha >= beta) {
            break;
        }
    }
    return (isMaximizer) ? alpha : beta;
}

编辑3:

基于@ Codor的回答/评论的新实现

private class MoveValue {
    public Move move;
    public int value;

    public MoveValue() {
        move = null;
        value = 0;
    }

    public MoveValue(Move move, int value) {
        this.move = move;
        this.value = value;
    }

    @Override
    public String toString() {
        return "MoveValue{" + "move=" + move + ", value=" + value + '}';
    }

}

protected MoveValue minMax(int alpha, int beta, int maxDepth, PlayerType player) {
    if (!canContinue()) {
        return new MoveValue();
    }
    ArrayList<Move> moves = sortMoves(generateLegalMoves(player));
    Iterator<Move> movesIterator = moves.iterator();
    MoveValue moveValue = new MoveValue();
    boolean isMaximizer = (player.equals(playerType));
    if (maxDepth == 0 || board.isGameOver()) {            
        moveValue.value = evaluateBoard();
        return moveValue;
    }
    while (movesIterator.hasNext()) {
        Move currentMove = movesIterator.next();
        board.applyMove(currentMove);
        moveValue = minMax(alpha, beta, maxDepth - 1, player.opponent());
        board.undoLastMove();
        if (isMaximizer) {
            if (moveValue.value > alpha) {
                selectedMove = currentMove;
                alpha = moveValue.value;
            }
        } else {
            if (moveValue.value < beta) {
                beta = moveValue.value;
                selectedMove = currentMove;
            }
        }
        if (alpha >= beta) {
            break;
        }
    }
    return (isMaximizer) ? new MoveValue(selectedMove, alpha) : new MoveValue(selectedMove, beta);
}

我不知道我是否正确或如果我做错了什么,但我回到了我发布问题时遇到的问题:

调用minMax(Integer.MIN_VALUE, Integer.MAX_VALUE, 1, PlayerType.Black)返回一个只能由白色播放器完成的移动,这不是我所需要的。

我需要给予球员最好的举动,而不是整个球队的最佳举措。


经过一些研究和大量时间浪费解决这个问题,我想出了这个似乎工作的解决方案。

private class MoveValue {

    public double returnValue;
    public Move returnMove;

    public MoveValue() {
        returnValue = 0;
    }

    public MoveValue(double returnValue) {
        this.returnValue = returnValue;
    }

    public MoveValue(double returnValue, Move returnMove) {
        this.returnValue = returnValue;
        this.returnMove = returnMove;
    }

}


protected MoveValue minMax(double alpha, double beta, int maxDepth, MarbleType player) {       
    if (!canContinue()) {
        return new MoveValue();
    }        
    ArrayList<Move> moves = sortMoves(generateLegalMoves(player));
    Iterator<Move> movesIterator = moves.iterator();
    double value = 0;
    boolean isMaximizer = (player.equals(playerType)); 
    if (maxDepth == 0 || board.isGameOver()) {            
        value = evaluateBoard();            
        return new MoveValue(value);
    }
    MoveValue returnMove;
    MoveValue bestMove = null;
    if (isMaximizer) {           
        while (movesIterator.hasNext()) {
            Move currentMove = movesIterator.next();
            board.applyMove(currentMove);
            returnMove = minMax(alpha, beta, maxDepth - 1, player.opponent());
            board.undoLastMove();
            if ((bestMove == null) || (bestMove.returnValue < returnMove.returnValue)) {
                bestMove = returnMove;
                bestMove.returnMove = currentMove;
            }
            if (returnMove.returnValue > alpha) {
                alpha = returnMove.returnValue;
                bestMove = returnMove;
            }
            if (beta <= alpha) {
                bestMove.returnValue = beta;
                bestMove.returnMove = null;
                return bestMove; // pruning
            }
        }
        return bestMove;
    } else {
        while (movesIterator.hasNext()) {
            Move currentMove = movesIterator.next();
            board.applyMove(currentMove);
            returnMove = minMax(alpha, beta, maxDepth - 1, player.opponent());
            board.undoLastMove();
            if ((bestMove == null) || (bestMove.returnValue > returnMove.returnValue)) {
                bestMove = returnMove;
                bestMove.returnMove = currentMove;
            }
            if (returnMove.returnValue < beta) {
                beta = returnMove.returnValue;
                bestMove = returnMove;
            }
            if (beta <= alpha) {
                bestMove.returnValue = alpha;
                bestMove.returnMove = null;
                return bestMove; // pruning
            }
        }
        return bestMove;
    }   
}

由于给定的代码不是实际的Java实现,所以这有点困难; 为了实现你想要的,必须有具体的类型来表示游戏树中的移动和位置。 通常游戏树并没有显式编码,而是以稀疏表示进行导航,其中实现将实际执行有问题的移动,递归地评估生成的较小问题并撤消移动,从而使用调用堆栈使用深度优先搜索,以便表示当前路径。

要获得实际的最佳举措,只需从您的方法中返回实例,以最大化后续评估。 首先实现Minimax算法而不使用alpha-beta-pruning可能会有所帮助,它会在基本结构工作后的后续步骤中添加。

从问题中的链接实施(第1.5节)实际上返回了最佳举措,如下面的评论所示。

/** Recursive minimax at level of depth for either
    maximizing or minimizing player.
    Return int[3] of {score, row, col}  */

这里没有用户定义的类型来表示移动,但是该方法返回三个值,它们是评估的最佳分数和玩家将移动到实际执行最佳移动的坐标(实现已经完成以获得得分),这是实际移动的表示。

链接地址: http://www.djcxy.com/p/56379.html

上一篇: Finding the best move using MinMax with Alpha

下一篇: MinMax algorithm not working properly