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

MinMax算法用于井字棋未拦截玩家落子问题求助

井字棋MinMax算法只进攻不防守的问题修复

核心问题分析

你的MinMax算法存在两个致命逻辑错误,导致AI只会优先自己获胜,完全不考虑拦截对手:

  • 返回值职责混淆:当前函数同时承担「返回局面评估分」和「返回最佳落子位置」两个职责,递归过程中评估分的传递被破坏,无法正确比较不同落子的优劣。
  • 最佳落子更新逻辑错误:循环中每次迭代都直接覆盖bestMove,而非仅当当前落子的评估分优于之前的最优值时才更新,导致最后一个合法落子被误判为最佳选择。

另外,Tableau类中getWinner的状态更新不及时,递归时无法正确获取当前局面的胜负结果,也会影响算法判断。

修复方案

1. 拆分MinMax逻辑:分离评估与选位

重新设计函数,用一个辅助函数返回局面评估分,主函数负责遍历所有可能落子,筛选出评估分最优的位置。

修复后的playMiniMax相关代码:

// 辅助函数:返回当前局面的评估分数
private int evaluate(Tableau t, int depth) {
    char winner = t.getWinner();
    if (winner == 'x') {
        // 胜利分数随深度递减,鼓励更快获胜
        return 10 + depth;
    } else if (winner == 'o') {
        // 失败分数随深度递减,鼓励避免快速失败
        return -10 - depth;
    } else if (winner == 'd') {
        return 0;
    } else if (depth == 0) {
        return 0;
    }
    return 0;
}

// 主函数:返回最佳落子位置(1-9)
public int playMiniMax(Tableau t, char maximizingPlayer, int depth) {
    int bestMove = -1;
    int bestValue;

    // 先检查当前局面是否已结束
    char winner = t.getWinner();
    if (winner != 'n' || depth == 0) {
        // 递归到叶子节点时返回评估分,顶层调用返回落子位置
        return evaluate(t, depth);
    }

    if (maximizingPlayer == 'x') {
        bestValue = Integer.MIN_VALUE;
        for (int i = 1; i <= 9; ++i) {
            if (t.chooseCase(i, 'x')) {
                // 递归获取对手回合的评估分
                int currentValue = playMiniMax(t, 'o', depth - 1);
                // 仅当当前值更优时,更新最佳值和落子位置
                if (currentValue > bestValue) {
                    bestValue = currentValue;
                    bestMove = i;
                }
                t.resetCase(i);
            }
        }
        // 区分顶层调用和递归调用的返回值
        return depth == 3 ? bestMove : bestValue;
    } else {
        bestValue = Integer.MAX_VALUE;
        for (int i = 1; i <= 9; ++i) {
            if (t.chooseCase(i, 'o')) {
                int currentValue = playMiniMax(t, 'x', depth - 1);
                if (currentValue < bestValue) {
                    bestValue = currentValue;
                    bestMove = i;
                }
                t.resetCase(i);
            }
        }
        return depth == 3 ? bestMove : bestValue;
    }
}

2. 修复Tableau类的状态更新逻辑

确保每次落子后,winner状态能正确更新,修改chooseCase方法,落子后主动检查胜负和平局:

public boolean chooseCase(int number, char player) {
    int c = 1;
    for (int i = 0; i < this.charArray.length; ++i) {
        for (int j = 0; j < this.charArray.length; ++j) {
            if (c == number && this.charArray[i][j] == '-') {
                this.charArray[i][j] = player;
                // 落子后立即检查胜负和平局,更新winner状态
                checkWin(player);
                checkDraw(player);
                return true;
            }
            ++c;
        }
    }
    return false;
}

同时,简化checkWin方法的冗余判断逻辑:

public boolean checkWin(char player) {
    // 检查行
    for (int i = 0; i < 3; i++) {
        if (charArray[i][0] == player && charArray[i][1] == player && charArray[i][2] == player) {
            gameIsOver = true;
            winner = player;
            System.out.println("Dear " + player + " you won!");
            return true;
        }
    }
    // 检查列
    for (int j = 0; j < 3; j++) {
        if (charArray[0][j] == player && charArray[1][j] == player && charArray[2][j] == player) {
            gameIsOver = true;
            winner = player;
            System.out.println("Dear " + player + " you won!");
            return true;
        }
    }
    // 检查对角线
    if ((charArray[0][0] == player && charArray[1][1] == player && charArray[2][2] == player) ||
        (charArray[0][2] == player && charArray[1][1] == player && charArray[2][0] == player)) {
        gameIsOver = true;
        winner = player;
        System.out.println("Dear " + player + " you won!");
        return true;
    }
    return false;
}

3. 调用方式调整

主函数中调用逻辑保持不变即可:

int movei = playerAI.playMiniMax(t, 'x', 3);
boolean correctCase = t.chooseCase(movei, 'x');

关键优化说明

  • 给评估分数加入深度权重:AI获胜时,深度越小(越快赢)分数越高;AI失败时,深度越小(越快输)分数越低,这样算法会优先选择最快获胜的路径,同时优先拦截对手的即时获胜。
  • 修复最佳落子的更新逻辑:仅当当前落子的评估分优于之前的最优值时,才更新bestMove,确保选出真正的最优解。
  • 及时更新局面状态:落子后立即检查胜负,保证递归过程中getWinner能返回正确结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 05:03:36