React 手把手教你实现 mine-clearance (扫雷)(二)

简介: React 手把手教你实现 mine-clearance (扫雷)(二)

Board 雷区组件


此组件根据输入的参数动态生成游戏面板(雷区) table,包含列和方块组件,Square 代表每个可操作的单元格,给其配置了位置坐标、状态、操纵函数等属性。


const Board = props => {
    // 细胞(最小单位)
    const Square = props => {
      return (
        <td
          onClick={() => {
            props.updateSquareValue(props)
          }}
          className={`td-${props.colCoord}-${props.rowCoord} square`}
        >
          {props.show ? props.value : ''}
        </td>
      )
    }
    // 列
    const trList = square.map((item, index) => {
      return (
        <tr key={index}>
          {item.map((subItem, tdIndex) => {
            return (
              <Square
                updateSquareValue={updateSquareValue}
                colCoord={index}
                rowCoord={tdIndex}
                value={square[index][tdIndex].value}
                key={`${index}-${tdIndex}`}
                show={false}
                hasClearance={false}
              />
            )
          })}
        </tr>
      )
    })
    return (
      <table className="board-container" cellSpacing="0">
        <thead></thead>
        <tbody>{trList}</tbody>
      </table>
    )
  }


随机生成地雷数据,并初始化


creatBoardParams 函数根据输入的地雷数量,为每一个地雷随机分配坐标,返回包含所有坐标数据的数组。

initSquareValue 根据输入的宽和高,给每一个方块设置默认值0,然后第三个参数 mineArray 就是 creatBoardParams 函数所生成的随机地雷坐标,将相关方块值更新。


const creatBoardParams = () => {
const mineArray = []
    for (let i = 0; i < mineNum; i++) {
      mineArray.push({
        col: Number(Number(Math.random() * (width - 1 || 0)).toFixed(0)),
        row: Number(Number(Math.random() * (height - 1 || 0)).toFixed(0)),
      })
    }
    setMineCoord(mineArray)
    setTimeout(() => {
      setSquare(initSquareValue(width, height, mineArray))
    })
  }
  const initSquareValue = (width, height, mineArray) => {
    let square = []
    for (let i = 0; i < height; i++) {
      let a = []
      for (let j = 0; j < width; j++) {
        a.push({ value: 0 })
      }
      square.push(a)
    }
    mineArray.forEach(i => {
      square[i['col']][i['row']].value = 10
    })
    return square
  }


获取并计算邻居方块的地雷数量(递归)


因为要判断每一个方块的8个邻居状态,这里用递归来处理,为防止递归函数无休止的进行,必须在函数内有终止条件,这就要求你必须要考虑好边界条件,不然将引起内存溢出直到程序崩溃。

定义 num 变量用来保存其周围邻居地雷数量,递归每个邻居,如果邻居为空,则以邻居为原点继续递归

根据每一个 Square 的最终 value 来判断其状态,并更新其显示状态。


const updateSquareValue = props => {
    let dom = document.querySelector(`.td-${props.colCoord}-${props.rowCoord}`)
    const v = dom.innerText
    if (v !== '') {
      return
    }
    if (props.value === 10) {
      dom.style.background = `#fff url(${STATUSMAP.get(10)})`
      dom.style.backgroundSize = 'cover'
      setTimeout(() => {
        // if (window.confirm('游戏结束,是否初始化服务?')) {
        //   init()
        // }
      }, 100)
      return
    }
    let neighbor = []
    let num = 0
    for (let i = props.colCoord - 1; i <= props.colCoord + 1; i++) {
      for (let j = props.rowCoord - 1; j <= props.rowCoord + 1; j++) {
        if (
          i < 0 ||
          j < 0 ||
          (i === props.colCoord && j === props.rowCoord) ||
          i >= height ||
          j >= width
        ) {
          continue
        }
        neighbor.push({ colCoord: i, rowCoord: j })
        if (
          mineCoord.filter(item => {
            return item.col === i && item.row === j
          }).length > 0
        ) {
          num++
        }
      }
    }
    // if (STATUSMAP.get(num) === '空') {
    console.log(num)
    if (num === 0) {
      neighbor.forEach(s => {
        window.requestAnimationFrame(() => {
          updateSquareValue(s)
        })
      })
    }
    dom.innerText = STATUSMAP.get(num)
  }


生成游戏面板、重置、游戏结束


通过定义的 creatBoardParams() 、init() 函数来创建、重置游戏,当点击到包含地雷的方块时,停止递归函数,并confirm 提示游戏结束


const init = () => {
    setWidth(16) // 默认面板宽度16
    setHeight(16) // 默认面板高度16
    setMineCoord([]) // 默认地雷坐标数据[]
    setMineNum(50) // 默认地雷数量 50
    setSquare([]) // 默认方块数据
  }
    const initSquareValue = (width, height, mineArray) => {
    let square = []
    for (let i = 0; i < height; i++) {
      let a = []
      for (let j = 0; j < width; j++) {
        a.push({ value: 0 })
      }
      square.push(a)
    }
    mineArray.forEach(i => {
      square[i['col']][i['row']].value = 10
    })
    return square
  }


总结


扫雷游戏的主要逻辑就完成了,基于react技术栈,实现这样一个程序, 只需要两百行左右 jsx 代码就可以了,而且还有巨大优化空间。

你学会了么?

目录
相关文章
|
11月前
|
前端开发 JavaScript Windows
React 手把手教你实现 mine-clearance (扫雷)(一)
React 手把手教你实现 mine-clearance (扫雷)(一)
72 0
|
16小时前
|
设计模式 前端开发 数据可视化
【第4期】一文了解React UI 组件库
【第4期】一文了解React UI 组件库
128 0
|
16小时前
|
存储 前端开发 JavaScript
【第34期】一文学会React组件传值
【第34期】一文学会React组件传值
35 0
|
16小时前
|
前端开发
【第31期】一文学会用React Hooks组件编写组件
【第31期】一文学会用React Hooks组件编写组件
35 0
|
16小时前
|
存储 前端开发 JavaScript
【第29期】一文学会用React类组件编写组件
【第29期】一文学会用React类组件编写组件
39 0
|
16小时前
|
前端开发 开发者
【第26期】一文读懂React组件编写方式
【第26期】一文读懂React组件编写方式
28 0
|
16小时前
|
资源调度 前端开发 JavaScript
React 的antd-mobile 组件库,嵌套路由
React 的antd-mobile 组件库,嵌套路由
47 0
|
16小时前
|
存储 前端开发 中间件
React组件间的通信
React组件间的通信
18 1
|
16小时前
|
前端开发 应用服务中间件 数据库
react服务端组件
react服务端组件
23 0
|
16小时前
|
前端开发 JavaScript
快速上手React:从概述到组件与事件处理
快速上手React:从概述到组件与事件处理