使用js来制作一个扫雷游戏,可以分为以下几个步骤

1、根据不同难度构建扫雷游戏区域;
2、在游戏区域中放置地雷;
3、处理点击事件;
4、处理游戏结束事件

1、根据不同难度构建扫雷游戏区域

创建一个二维数组进行扫雷区域的描述,这样就可以根据两个索引获取到每一个格子的状态描述,我给每一个格子设置了三个属性:

isMark:该格子是否已经被标记;
isOpen:该格子是否已经被点开;
value:该格子的内部的数字(‘’表示周围没有雷,1-8表示周围的雷数,9表示雷);

然后使用vue的v-for将雷区绘制出来;

2、在游戏区域中放置地雷

这部分主要思路是使用while循环,每次循环产生行和列的随机数,然后将对应行列位置的value设置为9(如果已经存在,则该次循环无效),直到将指定的地雷放置完毕;

             let putedMineNum=0;  //已经放置完成雷的个数while(putedMineNum<mine){  //mine表示总共需要放置多少个雷let putRow=Math.floor(Math.random()*row);let putColumn=Math.floor(Math.random()*column);let putPosition=numArr[putRow][putColumn];//如果随机出来的位置上没有数据,放雷if(!putPosition){numArr[putRow][putColumn]={value:9,isMark:false,isOpen:false};putedMineNum++;}}

放置地雷完成之后,就需要计算雷区的提示数字了

这部分主要思路就是遍历每一个格子,然后获取其周围格子value==9的数量,思路不难,就是遍历比较多

 function _getEveryMineNum(){let {numArr}=this;let rowLength=numArr.length;let columnLength=numArr[0].length;for(let row=0;row<rowLength;row++){for(let column=0;column<columnLength;column++){let curContent=numArr[row][column]; //格子对象// 如果该格子是雷块,跳过if(curContent&&curContent.value===9){continue;}//格式周围的八个格子let posArr=[[row-1,column],[row-1,column-1],[row-1,column+1],[row,column-1],[row,column+1],[row+1,column+1],[row+1,column-1],[row+1,column]];let mineNum=0;for(let i=0;i<8;i++){let _row=posArr[i][0];let _column=posArr[i][1];//如果周围的格子超出了整个雷区的处理if(_row<0||_column<0||_row>numArr.length-1||_column>numArr[0].length){continue}let curPos=numArr[_row][_column];if(curPos&&curPos.value==9){mineNum++}}numArr[row][column]={value:mineNum||'',isMark:false,isOpen:false}this.$set(this.numArr,row,numArr[row])}}},

经过这两步,我们就可以获取到整个雷区的构造了。

3、处理点击事件

通过event.which可以判断鼠标是点击左键(1)还是右键(3),点击事件的规则是:

1、点击右键打上标记或者取消标记,
2、点击左键进行判断:

  • 该格子是雷,游戏结束;
  • 该格子是数字,相安无事;
  • 该格子是空白(周围无雷)

其中当格子是空白时,会打开周围的格子,直到碰到数字格子或者游戏边界为止,如下图

         function _searchMine(e,row,column){let {numArr,isEnd}=this;if(isEnd){return}let curPos=numArr[row][column];//点击鼠标右键let {which}=e;if(which==3){//点击右键this._rightClick(row,column);return }//已被标记,不可左击if(curPos.isMark){return}if(curPos.value==9){//点击到雷,游戏结束this._failGame();return}//点击左键this._leftClick(row,column);//判断是否点击完所有格子this._isAllClick();},

右键部分的逻辑判断很简单,只需要判断该格子的isMark状态,进行取反操作:

         //点击右键_rightClick(row,column){let {numArr,leftMineNum,gameStep}=this;let curBlock=numArr[row][column];let {isMark}=curBlock;curBlock.isMark=!isMark;//标记数加或者减leftMineNum=leftMineNum+(isMark?1:-1);gameStep=gameStep+(isMark?1:-1);this.numArr=numArr;this.leftMineNum=leftMineNum;this.gameStep=gameStep;//如果已经标记完毕,立刻开始游戏结算if(leftMineNum<=0){this._endGame();return }},

左键的逻辑如下:

         //点击左键_leftClick(row,column){let {numArr,gameStep}=this;let rowMax=numArr.length;let columnMax=numArr[0].length;//判断是否超出游戏边界if(row<0||column<0||row>rowMax-1||column>columnMax-1){return}let curPos=numArr[row][column];//isOpen表示格子是否已经点开if(curPos.isOpen){return}curPos.isOpen=true;//gameStep用来判断格子是否全部被点击gameStep--;this.gameStep=gameStep;this.$set(this.numArr,row,numArr[row]);//如果该格子有数字,returnif(curPos.value){return}//剩下的便是格子是空白的情况,向四周进行延伸this._leftClick(row-1,column);this._leftClick(row+1,column);this._leftClick(row,column-1);this._leftClick(row,column+1);}

4、处理游戏结束事件

游戏结束有两种情况:

1、点开+标记=== 所有格子;
2、标记数=== 雷的数量

第一种情况,我是使用了一个变量gameStep,每次gameStep的初始值等于行数(row) * 列数(column),每次打上标记或者点开格子,gameStep减1。只要判断gameStep是否小于等于0,就可以判断游戏是否结束;

         //判断是否将格子全部点击完_isAllClick(){let {gameStep}=this;if(gameStep>0){return}this._endGame()},

第二种情况,同样是使用了一个变量leftMineNum,每次右键时判断该变量是否小于等于0

             //如果已经标记完毕,立刻开始结算if(leftMineNum<=0){this._endGame();return }

通关游戏只需要判断所有格子中雷是否已经全部被打上标记(也就是value==9&&isMark)

         //进行游戏结算_endGame(){let {numArr}=this;let isAllMark=numArr.every(rowItem=>{return rowItem.every(columnItem=>{//如果value==9必须isMark==truereturn (columnItem.value==9&&columnItem.isMark)||columnItem.value!=9})})if(isAllMark){this._passGame()}else{this._failGame();}},
         //游戏通关_passGame(){this.isEnd=true;alert('恭喜你,游戏通关!')},//游戏失败,将显示出来所有的雷的位置,也就是将value==9的格子isOpen设置为true_failGame(){let {numArr}=this;numArr.forEach(rowItem=>{rowItem.forEach(columnItem=>{let {value}=columnItem;if(value==9){columnItem.isOpen=true}})})this.numArr=numArr;this.isEnd=true;setTimeout(function(){alert('游戏失败!')},300)}

注意事项

1、因为整个区域是使用二维数组进行构建的,同时又使用了vue进行数据绑定,因此在触发页面重新渲染的操作可能和一维数组不太一样,可以参考这个链接 vue中如何对多维数组进行渲染

2、vue的数据渲染是个异步操作,最后的游戏失败提示,可以使用定时器,将alert的弹出置于渲染数据完成之后;

源码

<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=750, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><link rel="stylesheet" type="text/css" href="http://at.alicdn.com/t/font_963134_2tgi4v5uqgb.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.min.js"></script><style type="text/css">*{margin:0;padding:0;}.input-container{margin:10px 0;}.btn{width:60px;line-height: 30px;background: #3388ff;color:#fff;border:0 none;margin:0 20px;}.type-item{margin-bottom:10px;display: flex;align-items:center;justify-content: center;}.type-item .item{width:180px;}.left-mine{text-align: center;}.wrapper{display: flex;justify-content: center;}.content{margin-top:10px;padding:4px;border-radius: 4px;background: lightblue;}.content .line{margin:4px 0;display: flex;justify-content: center;}.content .line .item{margin:0 2px;width:20px;height:20px;border-radius:2px;background: gray;box-shadow: 0 0 4px 1px rgba(255,255,255,0.3);cursor:pointer;text-align: center;font-size:12px;color:#fff;line-height: 20px;transition: all 0.5s;position:relative;overflow: hidden;}.content .line .item .block{width:100%;height:100%;display: block;position:absolute;top:0;left:0;text-align: center;}.content .line .item.active{background: black;}.content .line .item.danger{background: #fff;color:red;}.content .line .item .mark{font-size:30px;}</style>
</head>
<body><div id="container" oncontextmenu="return false"><div class="type" v-cloak><div class="type-item" v-for="(item,index) in typeArr" :key="index"><div class="item">难度{{index+1}}:{{item.row}}&times;{{item.column}},{{item.mine}}个雷</div><button class="btn" @click="_initArr(index)">选择</button></div></div><div class="left-mine"><span>剩余雷数:</span><span>{{leftMineNum}}</span></div><div class="wrapper"><div class="content" v-show="numArr.length"><div class="line" v-for="(rowItem,rowIndex) in numArr" :key="rowIndex"><div class="item"v-for="(columnItem,columnIndex) in rowItem" :key="columnIndex"@mouseDown="_searchMine($event,rowIndex,columnIndex)":class="{active:columnItem.isOpen&&columnItem.value!='9',danger:columnItem.isOpen&&columnItem.value=='9'}">  <div v-if="columnItem.isOpen" class="block"><span class="iconfont icon-dilei" v-if='columnItem.value==9'></span><span v-if='columnItem.value!=9'>{{columnItem.value}}</span></div><div v-if="columnItem.isMark" class="block"><span class="iconfont icon-qizhi"></span><span>&times;</span></div></div></div></div></div></div>
</body><script type="text/javascript">let app=new Vue({el:'#container',data:{typeArr:[{row:9,column:9,mine:10},{row:16,column:16,mine:40},{row:16,column:40,mine:99}],curType:{row:0,column:0,mine:0},numArr:[],leftMineNum:0,gameStep:0,isEnd:true},methods:{_initArr(index){this.curType=this.typeArr[index];let {row,column,mine}=this.curType;this.leftMineNum=mine;this.gameStep=row*column;this.isEnd=false;this._createMineArea();},_createMineArea(){this._putMineInArea();this._getEveryMineNum();},//将雷放入区域_putMineInArea(){let {curType}=this;let {row,column,mine}=curType;let numArr=new Array(row);for(let i=0;i<row;i++){numArr[i]=new Array(column)}let putedMineNum=0;while(putedMineNum<mine){let putRow=Math.floor(Math.random()*row);let putColumn=Math.floor(Math.random()*column);let putPosition=numArr[putRow][putColumn];if(!putPosition){numArr[putRow][putColumn]={value:9,isMark:false,isOpen:false};putedMineNum++;}}this.numArr=numArr;},//获取每一个格子附近的雷数_getEveryMineNum(){let {numArr}=this;let rowLength=numArr.length;let columnLength=numArr[0].length;for(let row=0;row<rowLength;row++){for(let column=0;column<columnLength;column++){let curContent=numArr[row][column];if(curContent&&curContent.value===9){continue;}let posArr=[[row-1,column],[row-1,column-1],[row-1,column+1],[row,column-1],[row,column+1],[row+1,column+1],[row+1,column-1],[row+1,column]];let mineNum=0;for(let i=0;i<8;i++){let _row=posArr[i][0];let _column=posArr[i][1];if(_row<0||_column<0||_row>numArr.length-1||_column>numArr[0].length){continue}let curPos=numArr[_row][_column];if(curPos&&curPos.value==9){mineNum++}}numArr[row][column]={value:mineNum||'',isMark:false,isOpen:false}this.$set(this.numArr,row,numArr[row])}}console.log(numArr);},_searchMine(e,row,column){let {numArr,isEnd}=this;if(isEnd){return}let curPos=numArr[row][column];//点击鼠标右键let {which}=e;if(which==3){//点击右键this._rightClick(row,column);return }//已被标记,不可左击if(curPos.isMark){return}if(curPos.value==9){//点击到雷,游戏结束this._failGame();return}//点击左键this._leftClick(row,column);this._isAllClick();},//点击左键_leftClick(row,column){let {numArr,gameStep}=this;let rowMax=numArr.length;let columnMax=numArr[0].length;//判断是否超出游戏边界if(row<0||column<0||row>rowMax-1||column>columnMax-1){return}let curPos=numArr[row][column];//isOpen表示格子是否已经点开if(curPos.isOpen){return}curPos.isOpen=true;//gameStep用来判断格子是否全部被点击gameStep--;this.gameStep=gameStep;this.$set(this.numArr,row,numArr[row]);//如果该格子有数字,returnif(curPos.value){return}//剩下的便是格子是空白的情况,向四周进行延伸this._leftClick(row-1,column);this._leftClick(row+1,column);this._leftClick(row,column-1);this._leftClick(row,column+1);},//点击右键_rightClick(row,column){let {numArr,leftMineNum,gameStep}=this;let curBlock=numArr[row][column];let {isMark}=curBlock;curBlock.isMark=!isMark;leftMineNum=leftMineNum+(isMark?1:-1);gameStep=gameStep+(isMark?1:-1);this.numArr=numArr;this.leftMineNum=leftMineNum;this.gameStep=gameStep;//如果已经标记完毕,立刻开始结算if(leftMineNum<=0){this._endGame();return }},//判断是否将格子全部点击完_isAllClick(){let {gameStep}=this;if(gameStep>0){return}this._endGame()},//进行游戏结算_endGame(){let {numArr}=this;let isAllMark=numArr.every(rowItem=>{return rowItem.every(columnItem=>{return (columnItem.value==9&&columnItem.isMark)||columnItem.value!=9})})if(isAllMark){this._passGame()}else{this._failGame();}},//游戏通关_passGame(){this.isEnd=true;alert('恭喜你,游戏通关!')},//结束游戏_failGame(){let {numArr}=this;numArr.forEach(rowItem=>{rowItem.forEach(columnItem=>{let {value,isOpen,isMark}=columnItem;if(value==9){columnItem.isOpen=true}})})this.numArr=numArr;this.isEnd=true;setTimeout(function(){alert('游戏失败!')},300)}}})
</script>
</html>

用js制作一个扫雷游戏(vue版)相关推荐

  1. 使用 Vue.js 制作一个简单的调查问卷平台

    使用 Vue.js 制作一个简单的调查问卷平台 原文  https://github.com/pramper/Demos/tree/master/Vue-Demos/Questionnaire 主题  ...

  2. vue.js中经典扫雷游戏的实现

    可视化 (vue-defuse) An implementation of the classical minesweeper game in vue.js. vue.js中经典扫雷游戏的实现. Vi ...

  3. 表白小游戏教程:制作一个小游戏送给喜欢的TA(不会编程也能学会哦)

    大家好哇~ 欢迎翻开波波和阿菌的长篇故事~ 不过捏,今天我们不讲故事,今天将由阿菌和大家分享一个小教程:教大家制作一个小游戏,可以用来送给喜欢的人.无须任何计算机专业基础,只要能操作电脑,并在初一的数 ...

  4. java控制台扫雷_java实现扫雷游戏控制台版

    本文实例为大家分享了java实现扫雷游戏控制台版,供大家参考,具体内容如下 扫雷游戏 a.游戏的分析 在游戏中需要存在对象包含哪些. 格子对象(grid): 属性:内容(content).状态(typ ...

  5. java扫雷雷区的统计数据代码_java实现扫雷游戏控制台版

    本文实例为大家分享了java实现扫雷游戏控制台版,供大家参考,具体内容如下 扫雷游戏 a.游戏的分析 在游戏中需要存在对象包含哪些. 格子对象(Grid): 属性:内容(content).状态(typ ...

  6. 用Phaser来制作一个html5游戏——flappy bird (一)

    用Phaser来制作一个html5游戏--flappy bird (一) Phaser是一个简单易用且功能强大的html5游戏框架,利用它可以很轻松的开发出一个html5游戏.在这篇文章中我就教大家如 ...

  7. 使用 ale.js 制作一个小而美的表格编辑器(2)

    今天来教大家如何使用 ale.js 制作一个小而美的表格编辑器,首先先上 gif: 是不是还是有一点非常 cool 的感觉的?那么我们现在开始吧! 这是我们这篇文章结束后完成的效果(如果想继续完成请访 ...

  8. 用 JS 做一个数独游戏(二)

    用 JS 做一个数独游戏(二) 在 上一篇博客 中,我们通过 Node 运行了我们的 JavaScript 代码,在控制台中打印出来生成好的数独终盘.为了让我们的数独游戏能有良好的体验,这篇博客将会为 ...

  9. 使用 ale.js 制作一个小而美的表格编辑器(3)

    今天来教大家如何使用 ale.js 制作一个小而美的表格编辑器,首先先上 gif: 是不是还是有一点非常 cool 的感觉的?那么我们现在开始吧! 这是我们这篇文章结束后完成的效果(如果想继续完成请访 ...

最新文章

  1. MBProgressHUD 使用详解
  2. python批量生成图_python图像处理-批量生成纯色图片
  3. simulink设计PID控制器及其封装详解
  4. java精准查询mysql时间_在mysql查询中查找与指定日期时间最接近的日期时间
  5. Vue父子组件间的通信
  6. mybatis 之 parameterType=Map
  7. 让前端走进微时代, 微微一弄很哇塞!
  8. 详细讲解修改allure报告自定义的logo和名称中文
  9. 以太网UDP协议讲解
  10. 快捷键,photoshop常用快捷键大全
  11. 神经机器翻译(Neural machine translation, NMT)学习笔记
  12. 运维GO-2021年书单-产品运营 篇
  13. 一条 SQL 语句是如何执行的
  14. 《东周列国志》第五十二回 公子宋尝鼋构逆 陈灵公衵服戏朝
  15. 支持向量机(SVM)MATLAB 实例讲解,及选择训练方式使误差率尽可能减为0
  16. 计算机毕业设计之Python+Spark汽车推荐系统 汽车可视化 汽车数据分析 汽车大数据 汽车推荐app 汽车小程序 大数据毕业设计 汽车爬虫
  17. 人的天性是贪嗔痴和戒定慧的等量叠加态
  18. KMP - Oulipo - HDU - 1686
  19. 计算机组装与维修 字长,计算机组装与维修知识点总结.doc
  20. jstree树插件checkbox的选中事件

热门文章

  1. 文章评论:“鞋服企业以销定产-零库存不难”
  2. jQuery做轮播图
  3. linux中上锁的文件夹,怎么用linux命令给自己的文件上锁
  4. 周数转日期和日期转周数
  5. python余弦相似度_python实现字符串余弦相似度算法
  6. 关于《【校园招聘】被南瑞集团坑了。。。》的补充说明和思考20121128
  7. STM32HAL库GPIO和EXTI
  8. What is Plone?
  9. 安卓手改装成kali linux,人手一份核武器:Android手机装Kali Linux
  10. ext引用模板的方法