You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

井字棋MinMax算法返回不合逻辑走法,请求排查代码逻辑错误

井字棋MinMax算法逻辑错误排查

问题描述

实现井字棋MinMax3x3类的getBestMove方法时出现逻辑错误:传入正确棋盘后,算法返回不合逻辑的走法,未拦截玩家的获胜趋势(预期走法为[2,1],实际返回[0,2])。已确认棋盘传递正确,代码如下:

public class MinMax3x3 {

    public int[] getBestMove(char[][] board) {
        char computerSymbol = Symbol.X;
        int bestScore = Integer.MIN_VALUE;
        int row = -1;
        int col = -1;

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (board[i][j] == Symbol.EMPTY_FIELD) {
                    board[i][j] = computerSymbol;
                    int score = minimax(board, 0, false);
                    board[i][j] = Symbol.EMPTY_FIELD;
                    if (score > bestScore) {
                        bestScore = score;
                        row = i;
                        col = j;
                    }
                }
            }
        }
        int[] bestMove = {row, col};
        return bestMove;
    }

    public int minimax(char[][] board, int depth, boolean isMaximizing) {
        char playerSymbol = Symbol.O;
        char computerSymbol = Symbol.X;
        int result = analyze3x3(board, playerSymbol, computerSymbol);

        if (result != 0) {
            return result;
        }

        if (isMaximizing) {
            int bestScore = Integer.MIN_VALUE;
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    if (board[i][j] == Symbol.EMPTY_FIELD) {
                        board[i][j] = computerSymbol;
                        int score = minimax(board, depth + 1, false);
                        board[i][j] = Symbol.EMPTY_FIELD;
                        bestScore = Math.max(score, bestScore);
                    }
                }
            }
            return bestScore;
        } else {
            int bestScore = Integer.MAX_VALUE;
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    if (board[i][j] == Symbol.EMPTY_FIELD) {
                        board[i][j] = playerSymbol;
                        int score = minimax(board, depth + 1, true);
                        board[i][j] = Symbol.EMPTY_FIELD;
                        bestScore = Math.min(score, bestScore);
                    }
                }
            }
            return bestScore;
        }
    }

    private int analyze3x3(char[][] board, char playerSymbol, char computerSymbol) {
        // Check rows
        for (int row = 0; row < 3; row++) {
            if (board[row][0] == playerSymbol && board[row][1] == playerSymbol && board[row][2] == playerSymbol) {
                return 1; // Player wins
            } else if (board[row][0] == computerSymbol && board[row][1] == computerSymbol && board[row][2] == computerSymbol) {
                return -1; // Computer wins
            }
        }

        // Check columns
        for (int column = 0; column < 3; column++) {
            if (board[0][column] == playerSymbol && board[1][column] == playerSymbol && board[2][column] == playerSymbol) {
                return 1; // Player wins
            } else if (board[0][column] == computerSymbol && board[1][column] == computerSymbol && board[2][column] == computerSymbol) {
                return -1; // Computer wins
            }
        }

        // Check diagonals
        if ((board[0][0] == playerSymbol && board[1][1] == playerSymbol && board[2][2] == playerSymbol) ||
                (board[0][2] == playerSymbol && board[1][1] == playerSymbol && board[2][0] == playerSymbol)) {
            return 1; // Player wins
        } else if ((board[0][0] == computerSymbol && board[1][1] == computerSymbol && board[2][2] == computerSymbol) ||
                (board[0][2] == computerSymbol && board[1][1] == computerSymbol && board[2][0] == computerSymbol)) {
            return -1; // Computer wins
        }

        // It's a draw
        return 0;
    }

}


public class GameRunner {
    public static void main(String[] args) {

        char board[][] = {{'x', 'o', ' '},
                          {' ', 'o', ' '},
                          {' ', ' ', ' '}};

        MinMax3x3 bestMove = new MinMax3x3();
        int[] lol = bestMove.getBestMove(board);
        System.out.printf(lol[0] + ", " + lol[1]);

}

核心错误点分析

  • 胜负分数逻辑颠倒:analyze3x3中玩家获胜返回1、电脑获胜返回-1,但MinMax算法中电脑是最大化方,应追求更高分数,当前逻辑会让电脑主动避开获胜走法,反而优先选择对玩家有利的走法。
  • 未处理平局终止条件:minimax方法仅在有胜负时返回分数,棋盘填满无胜负时会继续递归,导致逻辑混乱。
  • 符号大小写不匹配:测试棋盘使用小写'x'/'o',但代码中Symbol.X/Symbol.O应为大写,导致空位判断和胜负识别出错。

修正后的代码

public class MinMax3x3 {

    public int[] getBestMove(char[][] board) {
        char computerSymbol = 'X';
        int bestScore = Integer.MIN_VALUE;
        int row = -1;
        int col = -1;

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (board[i][j] == ' ') {
                    board[i][j] = computerSymbol;
                    int score = minimax(board, 0, false);
                    board[i][j] = ' ';
                    if (score > bestScore) {
                        bestScore = score;
                        row = i;
                        col = j;
                    }
                }
            }
        }
        return new int[]{row, col};
    }

    public int minimax(char[][] board, int depth, boolean isMaximizing) {
        char playerSymbol = 'O';
        char computerSymbol = 'X';
        int result = analyze3x3(board, playerSymbol, computerSymbol);

        if (result != 0) {
            // 加入深度权重,优先选择最快获胜/最慢失败的走法
            return result * (10 - depth);
        }

        // 判断棋盘是否已满(平局)
        if (isBoardFull(board)) {
            return 0;
        }

        if (isMaximizing) {
            int bestScore = Integer.MIN_VALUE;
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    if (board[i][j] == ' ') {
                        board[i][j] = computerSymbol;
                        int score = minimax(board, depth + 1, false);
                        board[i][j] = ' ';
                        bestScore = Math.max(score, bestScore);
                    }
                }
            }
            return bestScore;
        } else {
            int bestScore = Integer.MAX_VALUE;
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    if (board[i][j] == ' ') {
                        board[i][j] = playerSymbol;
                        int score = minimax(board, depth + 1, true);
                        board[i][j] = ' ';
                        bestScore = Math.min(score, bestScore);
                    }
                }
            }
            return bestScore;
        }
    }

    private int analyze3x3(char[][] board, char playerSymbol, char computerSymbol) {
        // 检查行
        for (int row = 0; row < 3; row++) {
            if (board[row][0] == playerSymbol && board[row][1] == playerSymbol && board[row][2] == playerSymbol) {
                return -1; // 玩家获胜对电脑是负面结果,返回负分
            } else if (board[row][0] == computerSymbol && board[row][1] == computerSymbol && board[row][2] == computerSymbol) {
                return 1; // 电脑获胜返回正分
            }
        }

        // 检查列
        for (int column = 0; column < 3; column++) {
            if (board[0][column] == playerSymbol && board[1][column] == playerSymbol && board[2][column] == playerSymbol) {
                return -1;
            } else if (board[0][column] == computerSymbol && board[1][column] == computerSymbol && board[2][column] == computerSymbol) {
                return 1;
            }
        }

        // 检查对角线
        if ((board[0][0] == playerSymbol && board[1][1] == playerSymbol && board[2][2] == playerSymbol) ||
                (board[0][2] == playerSymbol && board[1][1] == playerSymbol && board[2][0] == playerSymbol)) {
            return -1;
        } else if ((board[0][0] == computerSymbol && board[1][1] == computerSymbol && board[2][2] == computerSymbol) ||
                (board[0][2] == computerSymbol && board[1][1] == computerSymbol && board[2][0] == computerSymbol)) {
            return 1;
        }

        return 0;
    }

    private boolean isBoardFull(char[][] board) {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (board[i][j] == ' ') {
                    return false;
                }
            }
        }
        return true;
    }
}

public class GameRunner {
    public static void main(String[] args) {
        // 统一符号大小写为大写,与代码匹配
        char board[][] = {{'X', 'O', ' '},
                          {' ', 'O', ' '},
                          {' ', ' ', ' '}};

        MinMax3x3 bestMove = new MinMax3x3();
        int[] result = bestMove.getBestMove(board);
        System.out.printf("%d, %d", result[0], result[1]); // 输出应为2,1
    }
}

修正说明

  1. 颠倒胜负分数逻辑:电脑获胜返回1,玩家获胜返回-1,让最大化方(电脑)优先选择获胜走法。
  2. 添加isBoardFull方法处理平局,避免无限递归。
  3. 统一符号大小写,确保代码与测试棋盘符号匹配。
  4. 加入深度权重,让算法优先选择最快获胜或最慢失败的走法,优化决策逻辑。

内容的提问来源于stack exchange,提问作者reizorwins

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 20:45:00