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

React实现10x10扫雷棋盘随机埋雷 所有单元格isBomb异常为true

React扫雷棋盘全单元格被标记为炸弹问题修复

问题现象

初始化10×10规格二维数组作为扫雷棋盘,编写随机坐标添加炸弹逻辑后,控制台调试发现棋盘所有单元格的isBomb字段均被错误设置为true,不符合仅随机位置生成炸弹的预期。
相关错误实现核心代码:

import React, { useEffect, useState } from 'react'

const INITIAL_CELL = {
    isClicked: false,
    isBomb: false,
    isFlagged: false,
}

const CreateInitialBoard = () => {
    let board = Array.from({ length: BOARD_SIZE }, () => Array.from({ length: BOARD_SIZE }, () => INITIAL_CELL))
    board = AddBombs(board);
    return board;
}

const AddBombs = (board) => {
    let _board = board;
    console.log(board, "before");
    let randomX, randomY, bombsAdded = 0;

    while (bombsAdded < BOMB_COUNT) {
        randomX = Math.floor(Math.random() * BOARD_SIZE);
        randomY = Math.floor(Math.random() * BOARD_SIZE);
        _board[randomX][randomY].isBomb = true;
        bombsAdded++;
    }
    console.log(_board, "after");
    return _board
}

const BOARD_SIZE = 10;
const BOMB_COUNT = 10;

const INITIAL_BOARD = CreateInitialBoard();

export default function Board() {
    const [board, setBoard] = useState(INITIAL_BOARD);

    return (
        <div className='board'>
            {
                board.map((row, rowIndex) => {
                    return row.map((cell, cellIndex) => {
                        return (
                            <div key={`${rowIndex}-${cellIndex}`} className='cell'>
                                {rowIndex}, {cellIndex}
                            </div>
                        )
                    })
                })
            }
        </div>
    )
}

错误原因

  • 核心问题是JavaScript对象为引用类型,创建棋盘时所有单元格都直接指向同一个INITIAL_CELL对象,没有生成独立副本。修改任意一个单元格的isBomb属性,本质都是修改这个共享的全局对象,最终所有引用该对象的单元格都会表现为isBomb: true。
  • 埋雷逻辑缺少重复坐标校验:随机坐标可能命中已经被标记为炸弹的位置,此时会重复计数,导致实际有效炸弹数少于配置值。

修复代码

  1. 创建棋盘时为每个单元格生成独立的对象副本,不要复用全局INITIAL_CELL引用
  2. 埋雷时增加判断,如果随机到的坐标已经是炸弹,则跳过本次循环不增加计数,直到成功埋下指定数量的不重复炸弹
    修复后完整代码:
import React, { useState } from 'react'

// 把单元格配置改成工厂函数,每次调用返回全新对象
const createCell = () => ({
    isClicked: false,
    isBomb: false,
    isFlagged: false,
})

const CreateInitialBoard = () => {
    // 每个单元格调用工厂函数生成独立对象,不存在引用共享问题
    let board = Array.from({ length: BOARD_SIZE }, () => 
        Array.from({ length: BOARD_SIZE }, () => createCell())
    )
    board = AddBombs(board);
    return board;
}

const AddBombs = (board) => {
    let randomX, randomY, bombsAdded = 0;

    while (bombsAdded < BOMB_COUNT) {
        randomX = Math.floor(Math.random() * BOARD_SIZE);
        randomY = Math.floor(Math.random() * BOARD_SIZE);
        // 跳过已经是炸弹的位置,避免重复埋雷
        if (board[randomX][randomY].isBomb) continue
        board[randomX][randomY].isBomb = true;
        bombsAdded++;
    }
    return board
}

const BOARD_SIZE = 10;
const BOMB_COUNT = 10;

const INITIAL_BOARD = CreateInitialBoard();

export default function Board() {
    const [board, setBoard] = useState(INITIAL_BOARD);

    return (
        <div className='board'>
            {
                board.map((row, rowIndex) => {
                    return row.map((cell, cellIndex) => {
                        return (
                            <div key={`${rowIndex}-${cellIndex}`} className='cell'>
                                {cell.isBomb ? '💣' : `${rowIndex}, ${cellIndex}`}
                            </div>
                        )
                    })
                })
            }
        </div>
    )
}

注意:原代码中引入的useEffect没有实际使用,修复时可以直接移除,减少不必要的依赖。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 13:39:16