Tic Tac Toe 游戏提前判定胜利失效的修复指南

4次阅读

Tic Tac Toe 游戏提前判定胜利失效的修复指南

本文详解 tic tac toe 游戏中“获胜后仍继续输入”的典型 bug,核心原因是 `haslastmoverwon` 函数参数顺序错误及 `prompt` 返回值处理不当,导致胜负检测逻辑失效。

在实现井字棋(Tic Tac Toe)这类回合制游戏时,一个常见却隐蔽的逻辑缺陷是:游戏在某一方已达成三连胜条件后,仍未终止循环,反而继续提示玩家输入,直至棋盘填满才判定为平局。这不仅破坏用户体验,更暴露了状态检测与控制流设计的关键漏洞。

问题根源在于两个关键函数的签名与调用不匹配:

? 1. 参数顺序错误:hasLastMoverWon 的致命错位

原始代码中,hasLastMoverWon 定义为:

function hasLastMoverWon(currentPlayerSymbol, gameBoard) { ... }

但在 isGameOver 中却被错误地调用为:

hasLastMoverWon(gameBoard, currentPlayerSymbol) // ❌ 顺序颠倒!

由于 JavaScript 不校验参数类型与顺序,该调用会静默传入错误参数:currentPlayerSymbol(如 “X”)被当作 gameBoard 数组使用,而 gameBoard 被误作 currentPlayerSymbol 字符串。结果是 gameBoard[i1] 实际访问的是字符串 “X”[0](即 “X”),后续所有索引访问均返回 undefined,导致胜利检测恒为 false。

修复方案:统一函数签名与调用顺序,确保 gameBoard 始终为第一参数:

function hasLastMoverWon(gameBoard, currentPlayerSymbol) { /* 正确实现 */ } // 对应调用: if (hasLastMoverWon(gameBoard, currentPlayerSymbol)) { ... }

⚠️ 2. 输入处理缺陷:prompt 返回值未正确解析

原始 getUserInput 中对 prompt 结果直接使用 +prompt(…) 强转数字:

return +prompt(...); // 若用户点取消 → 返回 NaN;点确定但留空 → 返回 0

这会导致:

  • 用户点击「取消」时,NaN 被传入 isMoveValid,gameBoard[NaN] 为 undefined → undefined === NULL 为 false,验证失败,但 do…while 循环可能因 NaN 的特殊性陷入异常行为;
  • 更稳妥的做法是显式检查 null(用户取消)并抛出异常或退出。

增强健壮性:在 makeAMove 中添加取消处理:

function makeAMove(gameBoard, nextPlayerSymbol) {   const newGameBoard = [...gameBoard];   let move;   do {     move = getUserInput(nextPlayerSymbol, gameBoard);     if (move === null) throw new Error("Game interrupted by user");   } while (!isMoveValid(move, gameBoard));   newGameBoard[move] = nextPlayerSymbol;   return newGameBoard; }  function getUserInput(nextPlayerSymbol, gameBoard) {   const input = prompt(`${getboardstring(gameBoard)}n dove vuoi posizionare la ${nextPlayerSymbol}?`);   return input === null ? null : number(input); // 显式转换,保留 null 语义 }

✅ 完整修复后的核心逻辑(精简版)

function isGameOver(gameBoard, currentPlayerSymbol) {   // ✅ 正确调用:board 在前,符号在后   if (hasLastMoverWon(gameBoard, currentPlayerSymbol)) {     alert(`${currentPlayerSymbol} has won the game!`);     return true;   }   if (!gameBoard.includes(null)) {     alert(`Game ended in a draw`);     return true;   }   return false; }  function ticTacToe() {   let gameBoard = Array(9).fill(null);   let currentPlayerSymbol = 'X'; // 初始化为 X,避免 null 切换歧义   while (true) {     gameBoard = makeAMove(gameBoard, currentPlayerSymbol);     if (isGameOver(gameBoard, currentPlayerSymbol)) break;     currentPlayerSymbol = currentPlayerSymbol === 'X' ? 'O' : 'X';   } }

? 注意事项与最佳实践

  • 参数顺序一致性:在多人协作或长期维护项目中,建议使用 JSDoc 注释明确参数含义,或借助 typescript 提供编译期检查。
  • 输入验证前置:prompt/confirm 等用户输入应视为不可信源,始终验证 null、空字符串、非数字等边界情况。
  • 避免隐式类型转换:+prompt() 易引发 NaN 问题,推荐 Number(prompt()) 配合显式 isNaN() 检查。
  • 循环结构优化:do…while 适合“至少执行一次”的场景,但此处更推荐 while(true) + break,逻辑更清晰且易控制退出点。

修复后,游戏将在任一玩家达成胜利组合(如 [0,1,2] 全为 ‘X’)的立即下一轮检测中弹出胜利提示并终止,彻底解决“赢了还继续下”的体验断层。

text=ZqhQzanResources