You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 19:24:58