You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

井字棋游戏中多索引获胜条件判断逻辑错误的求助

井字棋游戏中多索引获胜条件判断逻辑错误的求助

我看了你的代码,马上发现了导致“点一个格子就触发胜利提示”的核心问题,同时也帮你梳理下其他可以优化的点,一步步来解决:

一、致命错误:获胜条件的判断写法完全错了

你写的gameBoard[0, 3, 6] === player.marker这种写法,用了逗号运算符,JavaScript里逗号运算符会依次执行每个表达式,最后返回最后一个表达式的值。所以gameBoard[0,3,6]等价于gameBoard[6]——也就是说,你其实只在判断第7个格子(索引6)的标记,只要这个格子有你的标记,不管其他两个位置是什么,就会触发胜利提示!这就是为什么你点单个格子就弹胜利的原因。

正确的做法是:

  • 先定义所有可能的获胜组合(8种:3横、3竖、2斜)
  • 遍历这些组合,检查组合里的三个索引对应的gameBoard.board值,是否全部等于当前玩家的标记,并且都不为空

二、代码优化与修正步骤

1. 先修复获胜判断逻辑

我们可以单独写一个checkWinner函数,专门用来检查当前棋盘是否有获胜者,这样代码更清晰,还用到了你要求的解构赋值

function checkWinner(board, playerMarker) {
  // 定义所有获胜的索引组合
  const winCombos = [
    [0,1,2], [3,4,5], [6,7,8], // 横向
    [0,3,6], [1,4,7], [2,5,8], // 竖向
    [0,4,8], [2,4,6]            // 斜向
  ];
  // 遍历每个组合,检查三个位置是否都等于当前玩家标记
  return winCombos.some(combo => {
    const [a, b, c] = combo; // 解构组合里的三个索引
    return board[a] === playerMarker && board[b] === playerMarker && board[c] === playerMarker;
  });
}

这里用了some方法,只要有一个组合满足条件,就返回true(表示有获胜者);解构赋值的用法也完全符合你项目的要求~

2. 修正gameController里的其他问题

  • 你在gameController里接收了firstPlayersecondPlayer参数,但又直接赋值为playerplayerTwo,参数等于白传了,可以改成默认参数
  • 点击事件里同时修改gameBoard[index]gameBoard.board,其实gameBoard只有board数组属性,gameBoard[index]是给对象新增了不必要的属性,只需要操作gameBoard.board
  • 没有判断格子是否已经被点击过,导致同一个格子可以反复点击覆盖标记
  • 加了gameOver标记,游戏结束后就不能再操作格子

3. 修正后的完整代码

const gameBoard = {
  board: [
    "", "", "",
    "", "", "",
    "", "", ""
  ],
};

// 工厂函数创建玩家(符合你要求的Family functions)
function gamePlayers(player, marker) {
  return {
    player,
    marker
  };
};

const player = gamePlayers('Me', 'X');
const playerTwo = gamePlayers('You', 'O');

// 新增:检查获胜者的函数
function checkWinner(board, playerMarker) {
  const winCombos = [
    [0,1,2], [3,4,5], [6,7,8],
    [0,3,6], [1,4,7], [2,5,8],
    [0,4,8], [2,4,6]
  ];
  return winCombos.some(combo => {
    const [a, b, c] = combo;
    return board[a] === playerMarker && board[b] === playerMarker && board[c] === playerMarker;
  });
}

// 新增:检查是否平局的函数
function isDraw(board) {
  return board.every(cell => cell !== "");
}

function gameController(firstPlayer = player, secondPlayer = playerTwo) {
  let currentPlayer = firstPlayer;
  let gameOver = false; // 标记游戏是否结束

  const squares = document.querySelectorAll('.squares');
  squares.forEach((square, index) => {
    square.addEventListener('click', () => {
      // 游戏结束或者格子已被占用,直接返回
      if (gameOver || gameBoard.board[index] !== "") return;

      // 更新棋盘数据和DOM
      gameBoard.board[index] = currentPlayer.marker;
      square.textContent = currentPlayer.marker;

      // 检查是否获胜
      if (checkWinner(gameBoard.board, currentPlayer.marker)) {
        alert(`玩家 ${currentPlayer.player} 获胜!`);
        gameOver = true;
        return;
      }

      // 检查是否平局
      if (isDraw(gameBoard.board)) {
        alert("平局!");
        gameOver = true;
        return;
      }

      // 切换玩家
      currentPlayer = currentPlayer === firstPlayer ? secondPlayer : firstPlayer;
    });
  });

  return {
    gameBoard,
    firstPlayer,
    secondPlayer
  };
};

// 启动游戏
gameController();

你的HTML结构可以保持不变:

<div class="game-board">
  <div class="squares" data-index="0"></div>
  <div class="squares" data-index="1"></div>
  <div class="squares" data-index="2"></div>
  <div class="squares" data-index="3"></div>
  <div class="squares" data-index="4"></div>
  <div class="squares" data-index="5"></div>
  <div class="squares" data-index="6"></div>
  <div class="squares" data-index="7"></div>
  <div class="squares" data-index="8"></div>
</div>

三、其他需要注意的点

  • 我把turn变量换成了直接切换currentPlayer对象,逻辑更直观,还形成了闭包currentPlayergameOver被点击事件函数引用,不会被垃圾回收)
  • 加了gameOver标记,游戏结束后就不能再点击格子
  • 加了格子占用判断,防止重复点击同一个格子覆盖标记
  • 把获胜判断抽成单独函数,代码更易维护,以后要改获胜规则只需要改winCombos数组就行

这样修改后,获胜判断就会严格检查三个格子是否都被当前玩家占据,不会再出现点一个格子就触发胜利的情况啦!如果还有其他问题可以再问~

火山引擎 最新活动