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

如何将井字棋的X/O字段值存入board数组以实现胜负判定?

井字棋:同步board数组与完成胜负判定方案

核心问题解决步骤:

1. 同步标记到board数组

原代码点击棋盘时仅更新DOM,未同步到gameBoard的board数组。需给每个棋盘格子绑定索引,点击时将对应标记存入数组:

  • 在fields.forEach循环中添加index参数,用于定位board数组位置
  • 修正回合判断逻辑(原turn %= 2是赋值操作,会导致回合混乱)
  • 根据当前回合的玩家标记,同步更新board[index]

2. 实现胜负判定

在game对象的checkWinner函数中,遍历获胜组合数组,对比gameBoard.board中对应位置的标记是否一致且非空:

  • 每次点击后调用checkWinner,若检测到胜者则更新提示信息并标记游戏结束
  • 当剩余可点击格子为0且无胜者时,触发平局提示

3. 修复重启按钮功能

原重启按钮仅尝试清空DOM,但未重置游戏状态和重新绑定事件:

  • 循环清空每个field的内容和样式
  • 重置board数组、回合计数、剩余格子数、胜者状态
  • 重新给每个field绑定点击事件(因原事件使用{once:true},重启后需重新添加)

修改后的完整代码

JavaScript

// player factory function
const createPlayer = (name, marker) => {
    return { name, marker }
}

// gameboard object
const gameBoard = (() => {
    let board = Array(9).fill("")
    const fields = document.querySelectorAll(".field")
    const nextTurn = document.querySelector(".message")
    let turn = 0

    // 绑定点击事件的函数,方便重启时复用
    function bindFieldEvents() {
        fields.forEach((field, index) => {
            field.addEventListener('click', () => handleClick(field, index), { once: true })
        })
    }

    function handleClick(field, index) {
        const currentMarker = turn % 2 === 0 ? "X" : "O"
        // 更新DOM
        field.innerHTML = currentMarker
        field.classList.add("marker")
        // 同步到board数组
        board[index] = currentMarker
        // 更新回合提示
        nextTurn.innerHTML = turn % 2 === 0 ? "Player O's turn" : "Player X's turn"
        turn++
        // 调用胜负判定
        game.checkWinner()
    }

    // 重启游戏
    const restart = document.getElementById("restart-button")
    restart.addEventListener("click", restartGame)

    function restartGame() {
        // 重置board数组
        board = Array(9).fill("")
        turn = 0
        game.resetGameState()
        // 清空DOM
        fields.forEach(field => {
            field.innerHTML = ""
            field.classList.remove("marker")
        })
        nextTurn.innerHTML = "Player X's turn"
        // 重新绑定事件
        bindFieldEvents()
    }

    // 初始化绑定事件
    bindFieldEvents()

    return {
        getBoard: () => [...board], // 返回数组副本避免外部直接修改
        restartGame
    }
})()

// game object
const game = (() => {
    const playerOne = createPlayer("player x", "X")
    const playerTwo = createPlayer("player o", "O")
    let activePlayer = playerOne
    let winnerDeclared = false
    let remainingSpots = 9
    const playerName = document.querySelector('.message')
    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]
    ];

    function checkWinner() {
        const board = gameBoard.getBoard()
        // 遍历所有获胜组合
        for (const condition of winningConditions) {
            const [a, b, c] = condition
            if (board[a] && board[a] === board[b] && board[a] === board[c]) {
                winnerDeclared = true
                playerName.innerHTML = `${board[a]} wins!`
                disableAllFields()
                return
            }
        }
        // 检查平局
        remainingSpots--
        if (!winnerDeclared && remainingSpots === 0) {
            declareTie()
        }
    }

    function declareTie() {
        playerName.innerHTML = "It's a tie!"
    }

    function disableAllFields() {
        document.querySelectorAll(".field").forEach(field => {
            field.replaceWith(field.cloneNode(true))
        })
    }

    function resetGameState() {
        winnerDeclared = false
        remainingSpots = 9
        activePlayer = playerOne
    }

    return {
        checkWinner,
        resetGameState
    }
})()

CSS

.gameboard{
    display: grid;
    grid-template-columns: repeat(3,1fr);
    grid-gap: 5px;
    margin: 20px 0;
}
.field{
    width: 150px;
    height: 150px;
    background-color:  #ededee;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 4rem;
    cursor: pointer;
    font-family: Arial, sans-serif;
}
.marker {
    color: #2c3e50;
}
.center {
    text-align: center;
}
#restart-button {
    padding: 10px 20px;
    font-size: 1rem;
    background-color: #3498db;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}
#restart-button:hover {
    background-color: #2980b9;
}
.header-text {
    font-size: 2rem;
    text-align: center;
    padding: 15px 0;
    font-weight: bold;
    color: #2c3e50;
}
.message {
    text-align: center;
    font-size: 1.2rem;
    margin-bottom: 10px;
    color: #34495e;
}

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>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 05:15:39