井字棋minimax算法无法拦截部分对角线落子的问题排查
井字棋Minimax算法对角线防守失效修复
Minimax实现存在4处核心逻辑错误,直接导致部分对角线对局中AI无法识别玩家制胜路径,不会拦截底角关键位:
- 双方落子符号写反:最大化玩家为AI,持
X(对应得分10),行动时应落X;最小化玩家为人类,持O(对应得分-10),行动时应落O。原代码max分支落O、min分支落X,完全颠倒了双方落子身份。 - 递归层玩家标记未翻转:一方落子后下一轮应切换到另一方行动,但原代码max分支递归时仍传
maximizingPlayer = true,min分支递归时仍传maximizingPlayer = false,整个递归过程固定在同一玩家视角,没有模拟双方交替落子的规则。 - 胜负判断引用错误:递归过程中修改的是传入函数的
board参数,但原代码调用checkWinner时传入全局变量gameBoard.currentPlayer,没有基于当前递归层的棋盘状态判断胜负,返回的分数和当前局面完全不匹配。 - 初始调用玩家标记错误:外层选点逻辑中AI已经落下
X,接下来递归模拟的是人类玩家(最小化方)的应对,原代码初始调用minimax时传maximizingPlayer = true,第一步递归就进入了错误视角。
修正后代码
核心Minimax函数
let scores = { X: 10, O: -10, tie: 0 } const minimax = (board, depth, maximizingPlayer) => { // 传入当前递归层的棋盘判断胜负,不要依赖全局状态 let result = checkWinner(board); if (result !== null) { // 可选优化:结合深度调整分数,优先选择最快获胜、最慢落败的路径 if (result === 'X') return scores[result] - depth; if (result === 'O') return scores[result] + depth; return scores[result]; } if (maximizingPlayer) { let bestScore = -Infinity; for (let i = 0; i < 9; i++) { if (board[i] == '') { board[i] = 'X'; // 递归时翻转玩家标记,下一轮轮到最小化方 let score = minimax(board, depth + 1, false); board[i] = ''; if (score > bestScore) { bestScore = score; } } } return bestScore; } else { let bestScore = Infinity; for (let i = 0; i < 9; i++) { if (board[i] == '') { board[i] = 'O'; // 递归时翻转玩家标记,下一轮轮到最大化方 let score = minimax(board, depth + 1, true); board[i] = ''; if (score < bestScore) { bestScore = score; } } } return bestScore; } }
AI选点调用逻辑
let compMove; let bestScore = -Infinity; for (let i = 0; i < 9; i++) { if (gameBoard.array[i] == '') { gameBoard.array[i] = 'X'; // AI落子后轮到人类(最小化方)行动,第三个参数传false let score = minimax(gameBoard.array, 0, false); gameBoard.array[i] = ''; if (score > bestScore) { bestScore = score; compMove = i; } } }
注意:需要同步修改
checkWinner函数,让它接收棋盘数组作为入参,遍历传入的棋盘判断三子连线结果,不要直接依赖全局棋盘/玩家状态,否则递归过程的胜负判断依然会出错。
修正后AI可正确识别所有进攻、防守点位,覆盖对角线场景下的底角防守需求。
内容的提问来源于stack exchange,提问作者Wakana
相关产品推荐
相关产品推荐

