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

Java井字棋MiniMax算法实现异常求助:AI无法阻止玩家获胜

修复井字棋MiniMax算法:让AI正确选择最优落子

你的井字棋AI没能正确阻止玩家获胜,核心问题出在两个关键逻辑错误上——棋盘状态没有同步更新到胜负检查类,以及胜负判断依赖了错误的全局变量。咱们一步步拆解问题,然后给出修复方案:

问题1:AIcheck的状态从未更新

你在makeTurn()里只实例化了一次AIcheck,但在calcScore()的递归过程中,每次模拟落子都会修改currentBoard,可AIcheck始终指向最初的棋盘状态!这意味着checkWin()和checkTie()永远在检查AI还没落子的初始棋盘,递归里的胜负判断完全失效,MiniMax根本没法评估每个落子的真实结果,自然只能按顺序选空位。

问题2:胜负判断依赖全局回合数错误

用turnCounter % 2来判断返回1还是-1完全不靠谱——这个计数器是真实游戏的回合数,而递归里的模拟落子是虚拟的,和真实回合数没有关联。比如递归模拟人类落子后,turnCounter并没有增加,这会导致胜负判断完全颠倒或者错误。正确的做法是直接从当前棋盘判断获胜者是谁,再对应返回分数。


修复后的完整代码

1. 修正calcScore()方法

每次递归时都基于当前棋盘创建新的Check实例,并且直接从棋盘判断获胜者:

private int calcScore(Button[][] currentBoard, int depth, boolean isMax) {
    // 每次递归都用当前棋盘创建检查实例,确保状态同步
    Check currentCheck = new Check(currentBoard);
    
    // 判断当前棋盘的胜负
    if (currentCheck.checkWin()) {
        // AI(O)获胜返回+1,人类(X)获胜返回-1
        return currentCheck.getWinner().equals("O") ? 1 : -1;
    } else if (currentCheck.checkTie()) {
        return 0;
    }

    int bestScore = isMax ? Integer.MIN_VALUE : Integer.MAX_VALUE;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (currentBoard[i][j].getText().equals("")) {
                // 根据当前玩家落子:Max是AI(O),Min是人类(X)
                String player = isMax ? "O" : "X";
                currentBoard[i][j].setText(player);
                
                // 递归计算下一层的分数
                int score = calcScore(currentBoard, depth + 1, !isMax);
                
                // 回溯:撤销落子
                currentBoard[i][j].setText("");
                
                // 更新最优分数:Max取最大值,Min取最小值
                bestScore = isMax ? Math.max(bestScore, score) : Math.min(bestScore, score);
            }
        }
    }
    return bestScore;
}

2. 简化makeTurn()方法

移除多余的全局AIcheck实例,确保每次模拟落子后正确调用calcScore:

public void makeTurn(Button[][] currentBoard) {
    int bestScore = Integer.MIN_VALUE;
    int[] bestMove = new int[2];
    
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (currentBoard[i][j].getText().equals("")) {
                // 模拟AI落子(O)
                currentBoard[i][j].setText("O");
                // AI落子后轮到人类(Min玩家),所以isMax传false
                int score = calcScore(currentBoard, 0, false);
                // 回溯
                currentBoard[i][j].setText("");
                
                // 更新最优落子
                if (score > bestScore) {
                    bestScore = score;
                    bestMove = new int[]{i, j};
                }
            }
        }
    }
    Board.getInstance().getField(bestMove[0], bestMove[1]).performClick();
}

3. 完善Check类

确保Check类能正确判断获胜者并返回结果:

public class Check {
    private Button[][] board;
    private String winner;

    public Check(Button[][] board) {
        this.board = board;
        this.winner = null;
    }

    // 判断是否有玩家获胜,并记录获胜者
    public boolean checkWin() {
        // 检查行
        for (int i = 0; i < 3; i++) {
            String val = board[i][0].getText();
            if (!val.equals("") && val.equals(board[i][1].getText()) && val.equals(board[i][2].getText())) {
                winner = val;
                return true;
            }
        }
        // 检查列
        for (int j = 0; j < 3; j++) {
            String val = board[0][j].getText();
            if (!val.equals("") && val.equals(board[1][j].getText()) && val.equals(board[2][j].getText())) {
                winner = val;
                return true;
            }
        }
        // 检查对角线
        String centerVal = board[1][1].getText();
        if (!centerVal.equals("")) {
            if (centerVal.equals(board[0][0].getText()) && centerVal.equals(board[2][2].getText())) {
                winner = centerVal;
                return true;
            }
            if (centerVal.equals(board[0][2].getText()) && centerVal.equals(board[2][0].getText())) {
                winner = centerVal;
                return true;
            }
        }
        return false;
    }

    public String getWinner() {
        return winner;
    }

    // 判断是否平局(棋盘填满且无获胜者)
    public boolean checkTie() {
        // 检查是否还有空位
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (board[i][j].getText().equals("")) {
                    return false;
                }
            }
        }
        // 无空位且无获胜者则为平局
        return !checkWin();
    }
}

可选优化:加入深度加权

如果想让AI优先选择更快获胜的路径,或者尽量拖延失败,可以在返回分数时加入深度权重:

// 替换calcScore里的胜负返回逻辑
if (currentCheck.checkWin()) {
    // AI获胜:分数=1-深度(越早获胜分数越高)
    // 人类获胜:分数=深度-1(越晚失败分数越高)
    return currentCheck.getWinner().equals("O") ? 1 - depth : depth - 1;
}

这样AI会优先选择能最快赢的落子,而不是随便选一个能赢的位置;如果必输,也会尽量拖延回合。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 13:27:57