开会不准带电脑,手机app玩扫雷玩到眼快瞎,而且每次都要忍受长达十秒的广告,自己写一个算了。
详细代码在git里,还在更新。

第一步: React & webpack setup

用webpack主要目的是为了搭建一个简单的webpack server, 顺带着直接用它打包发布好了.

set up React

React需要安装一系列相关包,什么ReactDom啊之类的,直接 npm install XX --save-dev安装之就好。

set up webpack

npm install webpack --save-dev
npm install webpack-dev-server --save-dev
npm install html-webpack-plugin --save-dev

--save-dev是因为这些包只为开发用,不是给user用的
然后配置一下webpack.config.js:

    entry: { //程序的入口,类似于定义java的main方法,逻辑从这里开始app: path.resolve(__dirname, 'src') + '/index.js'},devServer: {//定义webpack-serverport: 8089, // localhost:8089/会访问到本地的页面contentBase: [path.join(__dirname, 'site/'), // localhost:8089/index.html会访问到server的site/index.html],},output: { //所有的js代码都输出到bundle.js里,然后放到site/文件夹里filename: 'bundle.js',path: path.resolve(__dirname, 'site/')},resolve: { modules: ['node_modules'],extensions: ['.js', '.jsx'], //import xx.js或者xx.jsx可以省略后缀},plugins: [ // 因为有一个html template,所以需要html-webpack-plugin来添加html文件。new HtmlWebpackPlugin({filename: 'index.html',template: 'src/index.html'//html template直接放在src根目录里。}),],

然后在package.json里面的
scripts里面添加:

 "build": "webpack -d","start:dev": "webpack -d && webpack-dev-server --progress --colors --config ./webpack.config.js"

这样就可以用 npm run build来打包所有代码到site里
npm run start:dev来启动本地的webpack server

webpack要点大概就这些了。配置好了之后就开始写代码了。

第二步: 生成一个布好雷的方格:

数据结构:

扫雷的数据结构没什么可以纠结的,一个二维数组。

数据类型:

也没啥好纠结的,数字嘛。
数字范围就是1-8,也没啥悬念嘛。 表示周围有1-8个雷(玩了几千把扫雷,只遇到过一次8)。
空白的格子就用0表示了,表示周围没有雷嘛。
还剩下一个9就用来表示雷好雷嘛。到此为止,一位数的数字全部用上了。

生成数组:

剩下的问题就是,怎么把这个数组生成出来。
首先,根据我几千把扫雷的经验,这应该是个随机生成的数组。不大可能有什么库之类的。
那么如何随机生成?
每一盘,雷的个数是不变的,那也就是说,我们可以随机找几个为止把雷放进去,然后计算剩下的每个格子周围有多少个雷就可以了。
事情就简单起来了:(以简单为例子,10*8的矩阵,10个雷。)

  1. 生成一个10*8长度的数组。塞进去10个9.(怎么喜欢怎么来)
    - 可以先放进去10个9,70个0,然后shuffle 打乱顺序
    - 也可以先生成一个80个0 的数组,然后随机抽取10个位置替换为9.
  2. 把这个数组每8个一截,折断成10截,放进一个二维数组。
  3. 开始计算这个二维数组所有目前为0的位置周围有多少个9,然后替换成真正的数字。

一个由0-9组成的二维数组就好了。注意数雷的时候小心处理边界条件。

第三步:template和js入口

html

react的话 html 的template就很简单了。只有几行:

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta content="width=device-width, initial-scale=1" name="viewport" /><title>saolei</title>
</head>
<body>
<div id="game-wrapper"/>
</body>
</html>

第二行 meta是为了以后调mobile用的css用的
实际上html的本体只有一行,game-wrapper。 不用提react的事,也不用提css,也不用提scripts。

react入口:

上面的webpack说好了,入口要从src/index.js开始, 所以这个文件就是入口了,也很简单:

import React from 'react';
import ReactDOM from 'react-dom'
import Game from './components/Game';ReactDOM.render(<Game />, document.getElementById('game-wrapper'));

将id为game-wrapper的元素替换为Game component. Game是从 ./components/Game里import的,但是不用提html的事情。 这两个会在webpack打包的时候结合为一体。
打包之后的html: site/index.html

<body><div id="game-wrapper"/><script type="text/javascript" src="bundle.js"></script>
</body>

第四步: Components:

请不要吐槽变量名字

最顶层的Components是Game
然后看,Game要包含啥
扫雷嘛,就一个MN的格子加上几个按钮(开始,难度),所以需要两种Component: GameBoardButton
最后:
GameBoard其实是有M
N个小的格子组成,所以GameBoard需要一种Component: SingleGrid
所以components包如图:

在下面解释中,GameGameBoardButton的爸爸,GameBoardSingleGrid的爸爸。

Game.jsx:

作为最顶层的Game,

  1. 它要知道自己的状态(在玩着还是已经结束(赢或者输))记录在status
  2. 需要知道自己的难度(多少行,多少列,多少雷)
import React from 'react';
import GameBoard from './GameBoard';
import Button from './Button';
import '../styles/index.css';class Game extends React.Component {constructor(props) {super(props);this.state = {totalRows: 10,totalColumns: 8,totalLei: 10,status: 'inProgress',gameIndex: 0,};//function bind this省略}gameFinish() {/*游戏结束时触发这个方法*/}/*button们的方法在这里*/setEasyGame() {/*省略*/}setMediateGame() {/*省略*/}setHardGame() {/*省略*/}reStart() {/*省略*/}render () {return (<React.Fragment><div className={`gameboard${this.state.totalColumns}`}><GameBoard key={this.state.gameIndex} {/*把方法和属性传给儿子GameBoard*/}/></div><div className={`buttons${this.state.totalColumns}`}><Button content="New Game" color="green"  onClick={this.reStart}/><Button content="Easy" color="green" onClick={this.setEasyGame}/>//还有俩button</div></React.Fragment>)}
}export default Game

GameBoard.jsx:

作为大格子,需要知道它爸爸定义了的游戏等级,自己有多少行多少列(从Game那里得到)

  1. 需要知道每个格子都是什么数值。 gameMatrix。 每个新的游戏都要generate一个新的矩阵。
  2. 需要知道游戏进度 游戏失败或者成功都是从一次鼠标点击小格子的事件触发的
    - fail: 左键点击到了一个雷。
    - win: 左键点击到了最后一个还没打开的数字。
    - 都不是上面两种情况,则是还在进行中。

除此之外,还需要知道哪些格子被打开了(如果点到空白的格子,需要检查周围的格子)。所以,在这一层维护了一个opened数组,用来记录被打开了的格子,和方便计算成功或者失败。

import React from 'react';
import SingleGrid from './SingleGrid';
import {generateGame} from "../service/dataService";
import '../styles/grid.css'class GameBoard extends React.Component {constructor(props) {super(props);const {totalRows, totalColumns, totalLei} = props;this.state = {gameMatrix:  generateGame(totalRows, totalColumns, totalLei),opened: [],};//方法绑定this}gameFail(status) {/*被儿子调用,然后调用爸爸的gameFail方法*/};openGrid(position) {/*被儿子调用然后计算是不是最后一个数字格子,如果是,调用爸爸的gameWin方法*/};handleZero(position, totalRows, totalColumns){/*如果被儿子通知,点到空白的格子了,*//*则需要检查四周格子,把所有空白格子都打开*/}render () {const { totalRows, totalColumns, gameStatus } = this.props;return (<ul> // 用两个map加载小格子们{this.state.gameMatrix.map((row, rowNumber)=>(<p className="row" key={ rowNumber }>{row.map((grid, gridNumber) =><SingleGrid value={grid} {/*其他需要传给小格子的属性*/}></SingleGrid>)}</p>))}</ul>)}
}
export default GameBoard

SingleGrid.jsx:

作为最小的单元,也是和user直接交互的component,需要知道:

  1. 自己的值是什么(数字或者雷或者空白)
  2. 自己是被打开了还是被标记成雷了
  3. 如果被打开了,是被用户点击打开的还是被爸爸通知打开的(检查空白格子产生情况)
  4. 另外,还需要知道整个游戏的状态: 如果赢了或者输了,则所有的格子都不能被点击
    如果输了,还需要判断被标记的状态和实际状态是不是相符,如果不是需要给出错误判断:
/*** Created by xzou2 on 11/13/18.*/
import React from 'react';
import '../styles/grid.css'class SingleGrid extends React.Component {constructor(props) {super(props);this.state = {status: 'default', opened: false};//方法绑定this}handleClick() {/*左键点击,需要根据当前自己的状态来判断状态变化*/}handleRightClick(e) {/*右键点击,设置为小红旗或者是取消小红旗*/}handleTouchEnd(){/*略*/}touchStart(){/*给手机等触屏玩儿的*/}componentWillReceiveProps(nextProps) {/*检查从爸爸那里得来的opened属性,*//*如果是爸爸让自己打开,而自己还没有打开,那么改变状态*/}render () {const { value, gameStatus } = this.props;let currentStatue = '';if (gameStatus !== 'fail') {//判断每个格子需要显示的东西} else {//游戏已经输了判断每个格子的}return <spanclassName={`grid${currentStatue} grid ${currentStatue && currentStatue!== 'marked' ? 'open' : 'default' }`}onClick={this.handleClick}onContextMenu={this.handleRightClick}onTouchStart={this.touchStart}onTouchEnd={this.handleTouchEnd}>{/[1-8]/.test(currentStatue) || currentStatue === 'X' ? currentStatue: ''}</span>}
}
export default SingleGrid

Button.jsx:

一个简单的component

class Button extends React.Component {render() {const {color,content,onClick,} = this.props;return (<button className={color} onClick={onClick}>{content}</button>)}
}
export default Button

第五步: 处理用户点击事件:

这大概是最好玩儿最头疼的一个步骤了。逻辑比较复杂,如果需要,请看源码
在最开始的时候,所有的小格子都需要藏起来,这时候小格子的状态是没被打开过的。

左键点击

根据上面的component设计,用户点击的是每一个小格子。每个小格子都知道自己的值,用户点击之后:

  • 如果小格子的值是9, 那就是点到雷了,应该直接宣布失败。这时候要一层层的告诉爸爸和爸爸的爸爸,游戏失败了。
  • 如果小格子的值是0,那么就是点到空白地方了,这时候要往外扩散。所以除了改自己的状态之外,还需要告诉爸爸,点到0了,让爸爸去打开自己周围的格子,这时候爸爸需要一个递归,来打开这个格子周围的所有为0的格子以及0外面一圈的格子。这时候,这个格子周围的格子的状态是由爸爸改变的,而不是通过用户点击。
  • 如果小格子的值是1-8,这是最简单的,直接翻开显示数字就行。小格子的状态改为打开。这时候有可能是最后一个还没打开的格子,所以需要告诉爸爸,自己打开了,求检查是不是最后一个,如果是,爸爸则会宣布游戏结束。
  • 如果这个格子已经被打开了,则什么都不能做了
  • 如果这个格子被标记成小红旗了,则改到问号状态,或者是标记成问号状态,则改回到小红旗。

右键点击事件:

小红旗和隐藏切换。
右键点击事件比较抓狂的是,手机不支持右键,这个纠结过程在另一个博客里了

最后,图片

小红旗的图片和雷的图片用css就可以绑定到状态上。className可以根据情况拼一个字符串出来。

最后的最后,戳这里可以直接开会的时候玩儿扫雷了。

网页版扫雷 -- React练习相关推荐

  1. C语言实现网页版扫雷

    扫雷作为一款流行的小游戏,我们一定玩过吧,今天我将用简短的C语言代码实现扫雷小游戏,源码在最后哦: 一.首先我们从主函数开始写 1.我们需要打印菜单提醒玩家进行选择,因为玩家可能玩多局所以我们要用循坏 ...

  2. 网页版扫雷游戏···

    闲的没事 写个扫雷, 算法 不太好·····凑合 <!DOCTYPE html> <html> <head> <metacharset="utf-8 ...

  3. 收藏网页版小游戏:蜘蛛纸牌、扫雷、水果忍者、打地鼠、吃豆人

    学习之余当然是摸鱼了,这里分享几个不用下载直接在线玩耍的游戏.有蜘蛛纸牌网页版在线玩.在线扫雷小游戏.在线玩的水果忍者.吃豆人.打地鼠.3D模仿. 下面我将一个个列出来.欢迎体验收藏! 蜘蛛纸牌:这是 ...

  4. 扫雷游戏网页版_做游戏,单人行动可行吗(3.让我试试扫雷)

    17年底,三国依然在线稳定运行.一个偶然的机会发现了一个网页的扫雷游戏(mienfield),是一个无限边界的扫雷游戏,全世界所有人玩一盘没有边界的扫雷.看着非常酷,我搜索了一下各大手机平台,没有同款 ...

  5. JavaScript网页特效-“扫雷”游戏随机布雷功能

    "扫雷"游戏是一款经典益智小游戏.游戏目标是在最短的时间内找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输.本节介绍"扫雷"游戏随机布雷功能的设计与实现. ...

  6. springboot+netty 仿微信网页版聊天工具

    本程序仿照微信界面进行开发,使用springboot+netty完成整体的框架开发,数据库方面使用h2数据库,前端部分使用thymeleaf,后期将会继续开发Ant Design React版.打包时 ...

  7. 使用reveal.js制作精美的网页版PPT

    前言 最近在做季度总结和技术分享,所以需要做个PPT, 来回顾这半年来的技术贡献. 但苦于mac上运行PPT那感人的流畅度, 成功的激起了笔者的强迫症, 所以索性想办法通过技术的手段来做个网页版PPT ...

  8. 能做pc网页吗_梦幻西游网页版:如今还能抽金伙伴吗?玩家亲自验证,感觉还行...

    说起"金伙伴",相信各位玩家并不陌生.作为梦幻西游网页版中的强力助手,他们的各方面表现相当优秀,因此不少玩家都想抽到.然而,越珍贵的东西就越难抽出来,想要在荟英楼看到金伙伴,并不是 ...

  9. 如何打印网页版的发票_梦幻西游网页版:陷入瓶颈期,如何快速提升战力?氪金能解决问题...

    在梦幻西游网页版中,战力的高低直接影响着玩家的游戏体验,因此大家都将注意力放到了这上面.然而,提升战力并非易事,随着等级越来越高,难度也会逐渐提升.前不久,就有一位玩家遇到了麻烦,据他描述,目前的战力 ...

  10. Python 写了一个网页版的「P图软件」,惊呆了!

    作者 | 小欣 来源 | Python爱好者集中营 今天是开工第一天,这篇文章可以算作是虎年的第一篇干货技术类文章了,今天小编用Python做了一个网页版的"P图软件",大致的流程 ...

最新文章

  1. Ubuntu 下载安装删除方式(自己常用,保持更新)
  2. 【机器学习】基于opencv实现目标检测,error LNK2001: unresolved external symbol public: virtual bool CvSVM::train...
  3. ajax跨域服务器404,Ajax和跨域
  4. mysql 非空语法_mysql从入门到优化(1)基本操作上
  5. 【2018.3.17】模拟赛之四-ssl1864jzoj1368 燃烧木棒【最短路,Floyd】
  6. 我了解到的面试的一些小内幕!顺利通过阿里Android岗面试
  7. kolla-ansible-----常用命令
  8. 实验吧之【Forms、天网管理系统】
  9. 同事之间关系可以,吃饭喝酒没问题,怎么没有一个说知心话的人?
  10. 基于系统的流量控制(Qos)
  11. Q102:光线追踪场景(1)——地球仪
  12. 数字通信原理的几个理解
  13. 干净卸载sqlserver2019 亲测有效!
  14. python网易云付费歌曲下载_python 根据网易云歌曲的ID 直接下载歌曲的实例
  15. 数据挖掘问题进行细分,主要分为哪四类问题
  16. java rxtx下载_rxtx-2.1-7r2 jav
  17. mariaDB安装与配置
  18. sql之分组TOPN
  19. antdv 表格标题换行
  20. ROS2与C++入门教程-在C++包里增加python支持

热门文章

  1. 迅雷极速版禁止自动升级的方法
  2. echarts3与echarts2区别
  3. Cydia Substrate插件编写
  4. 亲身体验过13款滚动截屏App,谁才是最好用的iPhone长截屏工具?
  5. 二次规划的对偶形式(CVX)
  6. Unreal Engine 4 手绘风滤镜(Paint Filter)即 桑原滤镜(Kuwahara Filter)教程(下)
  7. pr计算机相关知识,影视制作技术第一讲认识premiere(pr)与视频基础知识.ppt
  8. MySQL的存储函数与存储过程的区别
  9. latex数学符号加粗_latex 数学符号加粗
  10. 疫情地图 | 低代码制作全国重点管控地区行政区地图(截至4月16日)