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。 - 埋雷逻辑缺少重复坐标校验:随机坐标可能命中已经被标记为炸弹的位置,此时会重复计数,导致实际有效炸弹数少于配置值。
修复代码
- 创建棋盘时为每个单元格生成独立的对象副本,不要复用全局
INITIAL_CELL引用 - 埋雷时增加判断,如果随机到的坐标已经是炸弹,则跳过本次循环不增加计数,直到成功埋下指定数量的不重复炸弹
修复后完整代码:
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
相关产品推荐
相关产品推荐

