为什么我的极小极大算法没有输出最佳走法

我想测试一个通用的极小极大算法,它应该为一个场景返回最佳可能的位置,算法的代码如下:

import java.util.ArrayList;public class AI<E> {    public int go = 0;   protected int minimax(ArrayList<E> position, int depth, int alpha, int beta, boolean maximizingPlayer) {    if (depth == 0 || isGameOver(position)) {        return evaluatePosition(position);    }    if (maximizingPlayer) {        int maxEval = Integer.MIN_VALUE;        for (ArrayList<E> child : getChildren(position, maximizingPlayer)) {            int eval = minimax(child, depth - 1, alpha, beta, false);            maxEval = Math.max(maxEval, eval);            alpha = Math.max(alpha, eval);            if (beta <= alpha) {                break;            }        }        return maxEval;    } else {        int minEval = Integer.MAX_VALUE;        for (ArrayList<E> child : getChildren(position, maximizingPlayer)) {            int eval = minimax(child, depth - 1, alpha, beta, true);            minEval = Math.min(minEval, eval);            beta = Math.min(beta, eval);            if (beta <= alpha) {                break;            }        }        return minEval;    }}    protected boolean isGameOver(ArrayList<E> position) {        return false;    }    protected int evaluatePosition(ArrayList<E> position) {        return 0;    }    protected ArrayList<ArrayList<E>> getChildren(ArrayList<E> position, boolean player) {        return new ArrayList<>();    }    public static <T> void copyArrayList(ArrayList<T> source, ArrayList<T> destination) {        //destination.clear();        destination.addAll(source);    }}

井字游戏代码如下:

import java.util.ArrayList;import java.util.List;import java.util.Random;import java.util.Scanner;public class TicTacToe extends AI<Character> {    private final char EMPTY = ' ';    private final char PLAYER_X = 'X';    private final char PLAYER_O = 'O';    private final int SIZE = 3;    private char currentPlayer = PLAYER_X;    private ArrayList<Character> board = new ArrayList<>(SIZE * SIZE);    private Scanner scanner = new Scanner(System.in);    Random random = new Random();    public TicTacToe() {        super();        initializeBoard();        while (true) {            printBoard();            if (currentPlayer == PLAYER_X) {                playerMove(currentPlayer);            } else {                botMove(currentPlayer);            }            if (checkWinner(board, currentPlayer)) {                printBoard();                System.out.println("玩家 " + currentPlayer + " 获胜!");                break;            }            if (checkDraw(board)) {                printBoard();                System.out.println("游戏平局!");                break;            }            currentPlayer = (currentPlayer == PLAYER_X) ? PLAYER_O : PLAYER_X;        }    }    public static void main(String[] args) {        new TicTacToe();    }    private void initializeBoard() {        for (int i = 0; i < SIZE * SIZE; i++) {            board.add(EMPTY);        }    }    private void printBoard() {        for (int i = 0; i < SIZE; i++) {            for (int j = 0; j < SIZE; j++) {                System.out.print(board.get(i * SIZE + j));                if (j < SIZE - 1) System.out.print("|");            }            System.out.println();            if (i < SIZE - 1) System.out.println("-----");        }    }    private void playerMove(char player) {        while (true) {            System.out.println("请为玩家 " + player + " 输入行(0, 1, 2)和列(0, 1, 2):");            int row = scanner.nextInt();            int col = scanner.nextInt();            int index = row * SIZE + col;            if (row >= 0 && row < SIZE && col >= 0 && col < SIZE && board.get(index) == EMPTY) {                board.set(index, player);                break;            } else {                System.out.println("这个位置已经被占用或超出范围,请重新尝试。");            }        }    }    private void botMove(char player) {        int max = Integer.MIN_VALUE;        int pos = -1;        ArrayList<ArrayList<Character>> children = getChildren(board,false); //andern so das true/ false andert welches zeichen gesetzt wird        for (int i = 0; i < children.size(); i++) {            int depth = 10;            int alpha = Integer.MIN_VALUE;            int beta = Integer.MAX_VALUE;            boolean maximizingPlayer = true;            int result = minimax(children.get(i), depth, alpha, beta, maximizingPlayer);            if (result > max) {                max = result;                pos = i;            }        }        if (pos != -1) {            board = children.get(pos);        } else {            // 如果极小极大算法未找到有效移动,则回退到随机移动      System.out.println("随机");            while (true) {                int row = random.nextInt(SIZE);                int col = random.nextInt(SIZE);                int index = row * SIZE + col;                if (board.get(index) == EMPTY) {                    board.set(index, player);                    break;                }            }        }    }    private boolean checkWinner(ArrayList<Character> board, char player) {    // 检查水平和垂直行    for (int i = 0; i < SIZE; i++) {        // 水平检查        if (board.get(i * SIZE) == player && board.get(i * SIZE + 1) == player && board.get(i * SIZE + 2) == player) {            return true;        }        // 垂直检查        if (board.get(i) == player && board.get(SIZE + i) == player && board.get(2 * SIZE + i) == player) {            return true;        }    }    // 检查对角线(从左上到右下)    if (board.get(0) == player && board.get(4) == player && board.get(8) == player) {        return true;    }    // 检查对角线(从右上到左下)    if (board.get(2) == player && board.get(4) == player && board.get(6) == player) {        return true;    }    return false;}    private boolean checkDraw(ArrayList<Character> pos) {        return pos.stream().noneMatch(spot -> spot == EMPTY);    }    @Override    protected boolean isGameOver(ArrayList<Character> position) {        return checkDraw(position) || checkWinner(position, PLAYER_X) || checkWinner(position, PLAYER_O);    }     @Override    protected int evaluatePosition(ArrayList<Character> position) {    char otherPlayer = currentPlayer == PLAYER_X ? PLAYER_O : PLAYER_X;        if (checkWinner(position, currentPlayer)) { // 这里有问题,不要忘记!!!              return 100000;          } else if (checkWinner(position, otherPlayer)) {            return -100000;  //math.min        } else {            return 0;        }    }  @Overrideprotected ArrayList<ArrayList<Character>> getChildren(ArrayList<Character> position, boolean player) {    ArrayList<ArrayList<Character>> children = new ArrayList<>();    char currentPlayer = player ? PLAYER_X : PLAYER_O;    char otherPlayer = currentPlayer == PLAYER_X ? PLAYER_O : PLAYER_X;    for (int i = 0; i < SIZE * SIZE; i++) {        if (position.get(i) == EMPTY) {            ArrayList<Character> child = new ArrayList<>(position);            child.set(i, currentPlayer);            children.add(child);        }    }    return children;}}

我很确定检查游戏是否结束的代码是正常工作的,而且极小极大算法与许多其他程序中的实现方式相同,所以我猜测在某个地方存在逻辑问题。一个测试它的失败案例:

X|-|- -|-|--|-|-AI:X|-|O -|-|--|-|-

这将导致AI在3步内无论如何都会输。

另外:当我减少算法的深度时,代码甚至无法完成简单任务,如在一步内阻止对方获胜,这可能有助于查找错误。谢谢


回答:

我重写了我的整个代码,现在它可以工作了,我的新极小极大算法:

protected int minimax(ArrayList<E> position, int depth, int alpha, int beta, boolean maximizingPlayer) {    if (depth == 0 || isGameOver(position)) {        return evaluatePosition(position);    }    if (maximizingPlayer) {        int maxEval = Integer.MIN_VALUE;        for (ArrayList<E> child : getChildren(position)) {            int eval = minimax(child, depth - 1, alpha, beta, false);            maxEval = Math.max(maxEval, eval);            alpha = Math.max(alpha, eval);            if (beta <= alpha) {                break;            }        }        return maxEval;    } else {        int minEval = Integer.MAX_VALUE;        for (ArrayList<E> child : getChildren(position)) {            int eval = minimax(child, depth - 1, alpha, beta, true);            minEval = Math.min(minEval, eval);            beta = Math.min(beta, eval);            if (beta <= alpha) {                break;            }        }        return minEval;    }}

希望这对尝试编写极小极大算法的人有所帮助,我还有一个使用二维数组的版本,如果你需要的话请告诉我

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注