基于模块模式开发Tic-Tac-Toe:实现获胜算法与完善游戏功能
模块模式实现井字棋游戏的修复方案
问题梳理
当前开发存在的核心问题:
- 未用
board数组存储游戏状态,直接操作DOM导致状态不同步 - 获胜组合数组存在语法错误,无法正常遍历
- 未实现获胜判断逻辑,无法触发胜负提示
- 重启功能未正确重置游戏状态和DOM内容
- 玩家回合切换逻辑存在错误
修复后的完整代码
JavaScript
// 玩家工厂函数 const createPlayer = (name, marker) => { return { name, marker } } // 游戏棋盘模块 const gameBoard = (() => { let board = Array(9).fill(""); const fields = document.querySelectorAll(".field"); const messageEl = document.querySelector(".message"); const restartBtn = document.getElementById("restart-button"); // 初始化游戏 const init = () => { board = Array(9).fill(""); fields.forEach((field, index) => { field.innerHTML = ""; field.classList.remove("marker"); field.removeEventListener('click', handleClick); field.addEventListener('click', () => handleClick(index), { once: true }); }); game.resetGameState(); messageEl.textContent = "Player X's turn"; }; // 点击处理逻辑 const handleClick = (index) => { const activePlayer = game.getActivePlayer(); // 更新board数组和DOM board[index] = activePlayer.marker.toUpperCase(); fields[index].innerHTML = activePlayer.marker.toUpperCase(); fields[index].classList.add("marker"); // 检查胜负 if (game.checkWinner(board)) { messageEl.textContent = `${activePlayer.name} wins!`; disableAllFields(); return; } // 检查平局 if (game.checkTie(board)) { messageEl.textContent = "It's a tie!"; return; } // 切换玩家 game.switchPlayer(); messageEl.textContent = `${game.getActivePlayer().name}'s turn`; }; // 禁用所有棋盘格点击 const disableAllFields = () => { fields.forEach(field => { field.removeEventListener('click', handleClick); }); }; // 绑定重启事件 restartBtn.addEventListener("click", init); // 暴露必要方法 return { init, getBoard: () => [...board] }; })(); // 游戏逻辑模块 const game = (() => { // 创建玩家 const playerOne = createPlayer("Player X", "x"); const playerTwo = createPlayer("Player O", "o"); // 游戏状态 let activePlayer = playerOne; let winnerDeclared = false; // 获胜条件(修复语法错误,修正错误组合) const winningConditions = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ]; // 检查获胜 const checkWinner = (board) => { return winningConditions.some(combination => { const [a, b, c] = combination; return board[a] && board[a] === board[b] && board[a] === board[c]; }); }; // 检查平局 const checkTie = (board) => { return board.every(cell => cell !== "") && !checkWinner(board); }; // 切换玩家 const switchPlayer = () => { activePlayer = activePlayer === playerOne ? playerTwo : playerOne; }; // 重置游戏状态 const resetGameState = () => { activePlayer = playerOne; winnerDeclared = false; }; // 暴露方法 return { getActivePlayer: () => activePlayer, checkWinner, checkTie, switchPlayer, resetGameState }; })(); // 页面加载完成后初始化游戏 document.addEventListener("DOMContentLoaded", gameBoard.init);
CSS
:root{ --blue1: #546482; --blue2: #312d44ca; --yellow1: #EBEC9A; --eggshell: #c5c3c6; --header: #bbf0fb; --font: 'Varela Round', sans-serif; } body{ display: grid; font-family: var(--font); margin: 0; padding: 0; } .container{ width: 100vw; height: 100vh; } .header{ width: 100vw; height: 20vh; background-color: var(--blue1); border-bottom: 3px solid var(--blue2); display: flex; align-items: center; justify-content: center; color: var(--header); font-size: 2.5rem; font-weight: bold; } .body-container{ width: 100vw; height: 80vh; background-color: var(--eggshell); display: flex; flex-direction: column; justify-content:space-around; align-items: center; } .message{ font-size: 50px; color: var(--blue2); margin: 0; } #restart-button{ border-radius: 5px; font-weight: 1000; font-size: 1.5rem; background-color: var(--blue1); color: var(--header); border: none; letter-spacing: 0.8px; padding: 10px 20px 10px 20px; min-width: 130px; cursor: pointer; } #restart-button:hover{ background-color: #4a5466; transition: background-color 0.1s ease; } .gameboard{ display: grid; grid-template-columns: repeat(3,1fr); grid-gap: 5px; } .field{ width: 150px; height: 150px; background-color: #ededee; display: flex; align-items: center; justify-content: center; cursor: pointer; } .field:hover{ background-color: rgb(248,248,255); } .marker{ color: var(--blue2); font-size: 100px; text-align: center; cursor: default; }
HTML
<div class="container"> <div class="header"> <div class="header-text"> Tic-Tac-Toe </div> </div> <div class="body-container"> <div class="message">Player X's turn</div> <div class="gameboard"> <div class="field"></div> <div class="field"></div> <div class="field"></div> <div class="field"></div> <div class="field"></div> <div class="field"></div> <div class="field"></div> <div class="field"></div> <div class="field"></div> </div> <div class="center"> <button id="restart-button">Restart</button> </div> </div> </div>
关键修复说明
- board数组同步更新:点击棋盘格时同时更新
board数组与DOM,确保游戏状态唯一可信来源是数组而非DOM - 修复获胜条件数组:补充分隔逗号,修正竖列组合
[0,3,8]为正确的[0,3,6] - 实现胜负判断:
checkWinner函数遍历所有获胜组合,对比board数组对应位置的值是否一致 - 完善重启功能:
init函数重置board数组、清空DOM棋盘格、重新绑定点击事件并重置游戏状态 - 优化玩家切换逻辑:使用
switchPlayer方法统一管理玩家切换,修复原代码中turn %= 2的赋值错误 - 添加平局判断:当所有棋盘格填满且无获胜者时触发平局提示
- 禁用获胜后点击:获胜后禁用所有棋盘格的点击事件,防止后续操作干扰游戏结果
内容的提问来源于stack exchange,提问作者Master1
相关产品推荐
相关产品推荐

