井字棋游戏Bug排查:X三连有胜利提示,O三连无响应
井字棋游戏胜利提示异常问题排查与修复
问题现象
开发的井字棋游戏存在以下异常:
- 玩家(X)连成三连时,能正常弹出胜利提示;
- 电脑(O)连成三连时,无任何胜利提示;
- 切换初始玩家为O后,问题反转,仅O能触发胜利提示,X无法触发。
代码片段
HTML代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="styles.css"> <title>Tic Tac Toe</title> </head> <body> <div id="board" class="container"> <!-- Create a 3x3 grid here --> </div> <script src="script.js"></script> </body> </html>
JavaScript代码
document.addEventListener('DOMContentLoaded', () => { const board = document.getElementById('board'); let currentPlayer = 'X'; let gameStarted = false; let gameOver = false; // 跟踪游戏状态变量 // 创建棋盘 function createBoard() { for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { const cell = document.createElement('div'); cell.classList.add('cell'); cell.setAttribute('data-row', i); cell.setAttribute('data-col', j); board.appendChild(cell); cell.addEventListener('click', handleCellClick); } } } // 处理单元格点击事件 function handleCellClick(event) { if (gameOver) return; // 游戏结束则不响应点击 const clickedCell = event.target; // 检查单元格是否已被填充 if (!clickedCell.textContent.trim()) { clickedCell.textContent = currentPlayer; clickedCell.classList.add('clicked'); // 检查胜利或平局 if (checkWinner(row, col)) { announceWinner(); } else if (checkTie()) { announceTie(); } else { // 延迟切换玩家并执行电脑落子 setTimeout(() => { currentPlayer = 'O'; makeComputerMove(); currentPlayer = 'X'; }, 500); } } } // 电脑随机落子 function makeComputerMove() { const emptyCells = document.querySelectorAll('.cell:not(.clicked)'); if (emptyCells.length > 0) { const randomIndex = Math.floor(Math.random() * emptyCells.length); const randomCell = emptyCells[randomIndex]; randomCell.textContent = currentPlayer; randomCell.classList.add('clicked'); } } // 检查是否有玩家获胜 function checkWinner(row, col) { // 检查行 if ( checkLine(0, 0, 0, 1, 0, 2) || checkLine(1, 0, 1, 1, 1, 2) || checkLine(2, 0, 2, 1, 2, 2) ) { return true; } // 检查列 if ( checkLine(0, 0, 1, 0, 2, 0) || checkLine(0, 1, 1, 1, 2, 1) || checkLine(0, 2, 1, 2, 2, 2) ) { return true; } // 检查对角线 if ( checkLine(0, 0, 1, 1, 2, 2) || checkLine(0, 2, 1, 1, 2, 0) ) { return true; } return false; } // 辅助函数:检查一行/列/对角线是否连成三连 function checkLine(row1, col1, row2, col2, row3, col3) { const cell1 = document.querySelector(`[data-row="${row1}"][data-col="${col1}"]`); const cell2 = document.querySelector(`[data-row="${row2}"][data-col="${col2}"]`); const cell3 = document.querySelector(`[data-row="${row3}"][data-col="${col3}"]`); return ( cell1.textContent.trim() !== '' && cell1.textContent === cell2.textContent && cell1.textContent === cell3.textContent ); } // 检查是否平局 function checkTie() { const cells = document.querySelectorAll('.cell'); for (const cell of cells) { if (!cell.textContent.trim()) { return false; } } return true; } // 重置游戏 function resetGame() { currentPlayer = 'X'; gameOver = false; const cells = document.querySelectorAll('.cell'); cells.forEach(cell => { cell.textContent = ''; cell.classList.remove('clicked'); }); } // 弹出胜利提示 function announceWinner() { const winner = currentPlayer === 'X' ? 'Player X' : 'Player O'; popUpMenu.popupBox(`${winner} wins!`); gameOver = true; } // 弹出平局提示 function announceTie() { popUpMenu.popupBox(`It's a Tie!`); gameOver = true; } // 弹窗模块 const popUpMenu = (() => { const resetPage = () => { location.reload(); } const popupBox = (outcome) => { const overlay = document.createElement('div'); overlay.className = 'overlay'; const popup = document.createElement('div'); popup.className = 'popup-box'; const result = document.createElement('div'); result.id = 'win-lose'; result.textContent = outcome; const replayBtn = document.createElement('button'); replayBtn.id = 'play-again-btn'; replayBtn.textContent = 'Play Again?'; replayBtn.addEventListener('click', resetPage); popup.appendChild(result); popup.appendChild(replayBtn); overlay.appendChild(popup); document.body.appendChild(overlay); }; return { popupBox }; })(); // 页面加载时创建棋盘 createBoard(); });
核心问题分析
问题根源在于电脑(O)落子后未执行胜利检查逻辑:
- 玩家(X)落子后,代码会调用
checkWinner检查是否胜利,若满足条件则弹出提示; - 但电脑(O)落子后,仅完成落子操作,直接切换回X玩家,完全跳过了胜利检查和平局检查流程;
- 因此当O连成三连时,游戏无法触发
announceWinner函数,自然不会弹出胜利提示。
修复方案
1. 给电脑落子函数添加胜利检查
修改makeComputerMove,在落子完成后立即检查胜利或平局:
function makeComputerMove() { const emptyCells = document.querySelectorAll('.cell:not(.clicked)'); if (emptyCells.length > 0) { const randomIndex = Math.floor(Math.random() * emptyCells.length); const randomCell = emptyCells[randomIndex]; randomCell.textContent = currentPlayer; randomCell.classList.add('clicked'); // 新增:电脑落子后检查胜利状态 if (checkWinner()) { announceWinner(); } else if (checkTie()) { announceTie(); } } }
2. 移除checkWinner的无用参数
原checkWinner函数接收的row, col参数并未实际使用,直接移除即可:
function checkWinner() { // 保留原有检查逻辑,删除冗余参数 if ( checkLine(0, 0, 0, 1, 0, 2) || checkLine(1, 0, 1, 1, 1, 2) || checkLine(2, 0, 2, 1, 2, 2) ) { return true; } if ( checkLine(0, 0, 1, 0, 2, 0) || checkLine(0, 1, 1, 1, 2, 1) || checkLine(0, 2, 1, 2, 2, 2) ) { return true; } if ( checkLine(0, 0, 1, 1, 2, 2) || checkLine(0, 2, 1, 1, 2, 0) ) { return true; } return false; }
3. 同步修改玩家落子的调用逻辑
将handleCellClick里调用checkWinner的地方,去掉多余的row, col参数:
// 检查胜利或平局 if (checkWinner()) { announceWinner(); } else if (checkTie()) { announceTie(); }
额外优化建议
- 移除未使用的
gameStarted变量,或补充游戏初始化逻辑; - 将弹窗中的
location.reload()改为调用resetGame(),避免页面重载带来的体验损耗。
内容的提问来源于stack exchange,提问作者DurianDragon
相关产品推荐
相关产品推荐

