Tic Tac Toe JS游戏故障排查:available数组未清空及电脑连走问题
Tic Tac Toe 代码问题排查与修复
核心问题分析
1. 电脑连续执行多步操作
- 原因:
draw()函数每次调用时,都会为空白格子重复绑定click事件。随着游戏推进,单个格子会被绑定多个点击事件,用户点击一次会触发多次game()函数,而每个game()都会调用nextTurn(),导致电脑连续走多步。
2. available数组无法正确清空
- 原因1:
random()函数逻辑错误,生成的索引可能超出available数组的有效范围(比如数组长度为9时,可能生成索引9,但数组最大索引是8),导致splice()操作无法正确移除元素。 - 原因2:
game()函数中查找并移除available元素的逻辑存在缺陷,当找不到对应元素时会错误删除数组最后一个元素;加上重复触发的问题,进一步导致数组元素无法被正确清理。
分步修复方案
修复随机索引生成函数
替换错误的random()函数,生成有效范围内的随机索引:
function random(max) { return Math.floor(Math.random() * max); }
移除重复的事件绑定
将点击事件绑定从draw()中移出,改为初始化时一次性绑定所有格子,避免重复绑定:
// 初始化时为所有格子绑定点击事件 square.forEach((el, k) => { const i = Math.floor(k / 3); const j = k % 3; el.addEventListener('click', () => game(i, j)); }); // 修改draw函数,仅负责更新界面,不再绑定事件 function draw() { let k = 0; for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { const spot = content[i][j]; p[k].textContent = spot; // 禁用已被占用的格子,防止重复点击 square[k].style.pointerEvents = spot ? 'none' : 'auto'; k++; } } }
优化game()函数的元素移除逻辑
简化available数组的元素查找与移除,同时添加防重复点击的判断:
function game(i, j) { // 若该位置已被占用,直接返回 if (content[i][j] !== '') return; content[i][j] = human; // 找到对应坐标在available中的索引并移除 const index = available.findIndex(coord => coord[0] === i && coord[1] === j); if (index !== -1) { available.splice(index, 1); } // 检查人类是否获胜,若已结束则不再执行电脑回合 if (checkWin()) return; nextTurn(); }
完善nextTurn()和checkWin()的边界处理
- 给
nextTurn()添加空数组判断,避免无可用位置时执行错误操作:
function nextTurn() { if (available.length === 0) return; const index = random(available.length); const spot = available.splice(index, 1)[0]; content[spot[0]][spot[1]] = computer; draw(); checkWin(); }
- 修改
checkWin()使其返回游戏是否结束,方便后续逻辑判断:
function checkWin() { let winner = null; // 横向获胜检查 for (let i = 0; i < 3; i++) { if (equals(content[i][0], content[i][1], content[i][2])) { winner = content[i][0]; results.textContent = winner + ' Wins'; return true; } } // 纵向获胜检查 for (let j = 0; j < 3; j++) { if (equals(content[0][j], content[1][j], content[2][j])) { winner = content[0][j]; results.textContent = winner + ' Wins'; return true; } } // 对角线获胜检查 if (equals(content[0][0], content[1][1], content[2][2])) { winner = content[0][0]; results.textContent = winner + ' Wins'; return true; } if (equals(content[2][0], content[1][1], content[0][2])) { winner = content[2][0]; results.textContent = winner + ' Wins'; return true; } // 平局判断 if (winner === null && available.length === 0) { results.textContent = 'Tie'; return true; } return false; }
完整修复后代码
const board = document.querySelector('#board') const p = document.querySelectorAll('p') const results = document.querySelector('#results') const square = document.querySelectorAll('.kafelek') let content = [ ['','',''], ['','',''], ['','',''], ]; let players = ['X', 'O']; let computer = players[0]; let human = players[1]; let currentPlayer; let available = []; function random(max) { return Math.floor(Math.random() * max); } function setup() { currentPlayer = 0; for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { available.push([i, j]); } } } setup() function equals(a, b, c) { return (a===b && b===c && a !== '') } function checkWin() { let winner = null; // 横向检查 for (let i = 0; i < 3; i++) { if (equals(content[i][0], content[i][1], content[i][2])) { winner = content[i][0]; results.textContent = winner + ' Wins'; return true; } } // 纵向检查 for (let j = 0; j < 3; j++) { if (equals(content[0][j], content[1][j], content[2][j])) { winner = content[0][j]; results.textContent = winner + ' Wins'; return true; } } // 对角线检查 if (equals(content[0][0], content[1][1], content[2][2])) { winner = content[0][0]; results.textContent = winner + ' Wins'; return true; } if (equals(content[2][0], content[1][1], content[0][2])) { winner = content[2][0]; results.textContent = winner + ' Wins'; return true; } if (winner === null && available.length === 0) { results.textContent = 'Tie'; return true; } return false; } function nextTurn() { if (available.length === 0) return; const index = random(available.length); const spot = available.splice(index, 1)[0]; content[spot[0]][spot[1]] = computer; draw(); checkWin(); } // 初始化绑定点击事件 square.forEach((el, k) => { const i = Math.floor(k / 3); const j = k % 3; el.addEventListener('click', () => game(i, j)); }); function draw() { let k = 0; for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { const spot = content[i][j]; p[k].textContent = spot; square[k].style.pointerEvents = spot ? 'none' : 'auto'; k++; } } } function game(i, j) { if (content[i][j] !== '') return; content[i][j] = human; const index = available.findIndex(coord => coord[0] === i && coord[1] === j); if (index !== -1) { available.splice(index, 1); } if (checkWin()) return; nextTurn(); } nextTurn();
内容的提问来源于stack exchange,提问作者NajwiększyPolskiCrusher
相关产品推荐
相关产品推荐

