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

React井字棋(TicTacToe)Minimax算法评估异常求助

井字棋Minimax算法评估结果异常问题修复

你的代码存在三个关键逻辑错误,导致所有空位评估值一致,电脑无法选择最优落子:

1. 胜负检查逻辑错误(evaluation函数)

原代码在同一个循环中用else if同时检查行和列,导致每一轮循环只能检查行或列中的一种,会遗漏大量胜负情况(比如第一行没赢但第一列赢了的场景)。

修复后的evaluation函数:

function evaluation(arr){
    let c = 0;
    // 计算剩余空位数量
    for(let i = 0; i < 3; i++){ 
        for(let j = 0; j < 3; j++){
            if(arr[i][j] === ''){
                c++;
            }
        }
    }

    // 单独检查所有行
    for(let i = 0; i < 3; i++){
        if(arr[i][0] !== '' && arr[i][0] === arr[i][1] && arr[i][1] === arr[i][2]){
            return arr[i][0] === player ? -10 - c : 10 + c;
        }
    }

    // 单独检查所有列
    for(let i = 0; i < 3; i++){
        if(arr[0][i] !== '' && arr[0][i] === arr[1][i] && arr[1][i] === arr[2][i]){
            return arr[0][i] === player ? -10 - c : 10 + c;
        }
    }

    // 检查对角线
    if(arr[1][1] !== ''){
        if(arr[0][0] === arr[1][1] && arr[1][1] === arr[2][2]){
            return arr[1][1] === player ? -10 - c : 10 + c;
        } else if(arr[0][2] === arr[1][1] && arr[1][1] === arr[2][0]){
            return arr[1][1] === player ? -10 - c : 10 + c;
        }
    }

    return 0;
}

2. Alpha-Beta剪枝不彻底(minimax函数)

原代码触发剪枝时break仅退出内层列循环,外层行循环仍会继续执行,导致剪枝失效,无法正确传递最优评估值。

修复后的minimax函数:

function minimax(newBoard, moves, alfa, beta, maximizingPlayer){
    let e = evaluation(newBoard);
    if(moves === 0 || e !== 0){
        return e;
    }
    
    let resEval = maximizingPlayer ? -100 : 100;
    let needPrune = false; // 标记是否需要终止外层循环
    for(let i = 0; i < 3 && !needPrune; i++){
        for(let j = 0; j < 3; j++){
            if(newBoard[i][j] === ''){
                newBoard[i][j] = maximizingPlayer ? computer : player; 
                // 简化递归调用逻辑,直接取反maximizingPlayer
                let point = minimax(newBoard, moves - 1, alfa, beta, !maximizingPlayer);
                resEval = maximizingPlayer ? Math.max(resEval, point) : Math.min(resEval, point);
                newBoard[i][j] = '';
                
                // 更新alpha/beta值
                if(maximizingPlayer){
                    alfa = Math.max(alfa, point);
                } else {
                    beta = Math.min(beta, point);
                }
                
                // 触发剪枝时标记并退出内层循环
                if(beta <= alfa){
                    needPrune = true;
                    break;
                }
            }
        }
    }
    return resEval;
}

3. 步数计算依赖外部变量(bestMove函数)

原代码依赖外部moves变量,容易出现步数计算错误,导致minimax递归的终止条件失效。改为在函数内部计算剩余空位数量:

修复后的bestMove函数:

function bestMove(newBoard) {
    let best = -100;
    let c1, c2;
    
    // 计算当前剩余空位数量(即剩余步数)
    let moves = 0;
    for(let i = 0; i < 3; i++){
        for(let j = 0; j < 3; j++){
            if(newBoard[i][j] === '') moves++;
        }
    }

    for(let i = 0; i < 3; i++){
        for(let j = 0; j < 3; j++){
            if (newBoard[i][j] === '') {
                newBoard[i][j] = computer;
                // 递归时传入剩余步数-1(已落子一步)
                let mm = minimax(newBoard, moves - 1, -100, 100, false);
                if (mm > best) {
                    best = mm;
                    c1 = i;
                    c2 = j;
                }
                newBoard[i][j] = '';
            }
        }
    }

    return { c1, c2 };
}

额外注意事项

  • 确认player和computer的取值与棋盘上的标记一致(比如player是'X',computer是'O')
  • 调用bestMove时,最好传入当前棋盘的深拷贝,避免修改原棋盘数据(比如用JSON.parse(JSON.stringify(currentBoard))生成拷贝)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:02:10