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

井字棋游戏报错Uncaught TypeError: Cannot read properties of undefined (reading '0')求修复

井字棋游戏错误分析与修正

错误原因

  1. 未传入参数导致类型错误
    在makeMove函数中调用checkWinner()时未传入棋盘状态参数,使得checkWinner函数内的board参数为undefined。当函数遍历获胜条件数组时,尝试访问board[a](如board[0])就会触发Uncaught TypeError: Cannot read properties of undefined (reading '0')错误。

  2. 全局状态被递归调用意外修改
    checkWinner函数中直接修改全局的gameActive变量,而AI的minimax算法会递归调用checkWinner模拟各种走法,这会导致全局gameActive被提前设为false,打断正常游戏流程(比如AI还未完成落子,游戏就被判定结束)。

修正后的完整代码

HTML

<div id="board">board</div>
<div id="message">message</div>
<button id="resetBtn" type="button">Start/Reset</button>

CSS

#board{ 
  max-width: 6.5em;
}
.cell {
  display: inline-block;
  width: 2em;
  height: 2em;
  text-align: center;
  border: thin solid blue;
  box-sizing: border-box; /* 修正原错误属性名box-model */
  vertical-align: top;
  line-height: 2;
}

JavaScript

document.addEventListener('DOMContentLoaded', function () {
  const board = document.getElementById('board');
  const resetBtn = document.getElementById('resetBtn');
  const message = document.getElementById('message');

  let currentPlayer = 'X';
  let gameActive = true;
  let boardState = ['', '', '', '', '', '', '', '', ''];

  // 纯函数:仅判断胜负,不修改全局状态
  function checkWinner(board) {
    const winConditions = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6],
    ];

    for (let condition of winConditions) {
      const [a, b, c] = condition;
      if (board[a] !== '' && board[a] === board[b] && board[a] === board[c]) {
        return board[a];
      }
    }

    if (!board.includes('')) {
      return 'tie';
    }

    return null;
  }

  function makeMove(index) {
    if (boardState[index] === '' && gameActive) {
      boardState[index] = currentPlayer;
      renderBoard();

      // 传入当前棋盘状态并处理结果
      const winner = checkWinner(boardState);
      if (winner) {
        gameActive = false;
        message.innerText = winner === 'tie' ? '平局!' : `${winner} 获胜!`;
        return;
      }

      currentPlayer = currentPlayer === 'X' ? 'O' : 'X';

      if (currentPlayer === 'O' && gameActive) {
        makeComputerMove();
      }
    }
  }

  function makeComputerMove() {
    let bestScore = -Infinity;
    let bestMove;

    for (let i = 0; i < boardState.length; i++) {
      if (boardState[i] === '') {
        boardState[i] = 'O';
        let score = minimax(boardState.slice(), 0, false);
        boardState[i] = '';

        if (score > bestScore) {
          bestScore = score;
          bestMove = i;
        }
      }
    }

    makeMove(bestMove);
  }

  function minimax(board, depth, isMaximizing) {
    const result = checkWinner(board);
    const scores = {
      X: -10,
      O: 10,
      tie: 0,
    };

    if (result !== null) {
      return scores[result];
    }

    if (isMaximizing) {
      let bestScore = -Infinity;
      for (let i = 0; i < board.length; i++) {
        if (board[i] === '') {
          board[i] = 'O';
          let score = minimax(board.slice(), depth + 1, false);
          board[i] = '';
          bestScore = Math.max(score, bestScore);
        }
      }
      return bestScore;
    } else {
      let bestScore = Infinity;
      for (let i = 0; i < board.length; i++) {
        if (board[i] === '') {
          board[i] = 'X';
          let score = minimax(board.slice(), depth + 1, true);
          board[i] = '';
          bestScore = Math.min(score, bestScore);
        }
      }
      return bestScore;
    }
  }

  function renderBoard() {
    board.innerHTML = '';
    boardState.forEach((cell, index) => {
      const cellElement = document.createElement('div');
      cellElement.classList.add('cell');
      cellElement.innerText = cell;
      cellElement.addEventListener('click', () => makeMove(index));
      board.appendChild(cellElement);
    });
  }

  function resetBoard() {
    currentPlayer = 'X';
    gameActive = true;
    boardState = ['', '', '', '', '', '', '', '', ''];
    renderBoard();
    message.innerText = '';
  }

  resetBtn.addEventListener('click', resetBoard);
  renderBoard();
});

关键修正点

  1. 修复参数缺失问题:在makeMove中调用checkWinner时传入boardState,确保函数获取正确的棋盘状态。
  2. 分离纯函数与状态修改:将checkWinner改为纯函数,仅返回胜负结果,不在函数内修改全局gameActive,而是在主逻辑中根据返回结果处理游戏状态和提示消息,避免递归调用干扰全局状态。
  3. 修正CSS属性错误:将原CSS中的box-model改为正确的box-sizing,确保单元格边框不会超出设定尺寸。
  4. 优化变量作用域:将scores对象移至minimax函数内部,减少全局变量污染。

内容的提问来源于stack exchange,提问作者jing bing

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 06:25:02