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
相关产品推荐
相关产品推荐

