js实现扫雷小程序的示例代码


Posted in Javascript onSeptember 27, 2017

初学javascript,写了一个扫雷程序练练手!

扫雷规则及功能

扫雷想必大家都不陌生,就是windows上点击排雷的小游戏,它的主要规则有

1.左键点击显示当前格子是否为雷,如果为雷的话,GameOver啦,如果不是雷的话,这个格子会显示周围八个格子内的雷数量。
2.鼠标右键标记,标记可能的雷,标记了之后取消需要再次右键点击该格子,左键无效果。
3.鼠标中键(滚轮)按下后,快捷扫雷(如果周围雷数和未被标记且未被翻开的格子相等,会将这些格子一并翻开)

主要功能基本完全复刻了windows7扫雷的功能

扫雷github地址:扫雷github地址

扫雷算法

1.首先我定义了一个构造函数,里面是一系列的属性:

var mineCraft = function(num1,num2,mine_num,obj,type){
    this.num1 = num1;            //整个扫雷的行数
    this.num2 = num2;            //整个扫雷的列数 
    this.mine_num = mine_num;        //雷的个数
    this.tiles = [];             //数组里面存放的是每个小格子
    this.obj = obj;             //扫雷放置的对象
    this.flag = true;            
    this.arr = [];
    this.arr_2 = [];
    this.time_dsq = null;     
    this.time_dc ='';
    this.time_arr = [[],[],[]];       //时间统计信息
    this.details = [[],[],[]];       // 游戏统计详情
    this.type = type;           //游戏类型:初级/中级/高级/自定义
    this.buildTiles();           //创建游戏函数
  };

2.在页面上创建扫雷的界面 函数buildTiles

buildTiles:function(){
      this.obj.style.width = 51*this.num1+'px'; //在传进来的对象上画整体格子,每个小格子51px大小,总大小就为个数*单个大小
      this.obj.style.height = 51*this.num2+'px';
      var indexOfdiv = 0;           
      for(var i = 0;i<this.num2;i++){
        for(var j = 0;j<this.num1;j++){
          var tile = document.createElement('div');
          tile.className = 'tile';     //定义小格子class
          tile.index = indexOfdiv;     //为每个小格子添加索引
          this.tiles[indexOfdiv] = tile;  //将小格子存入数组中
          indexOfdiv++;
          this.obj.appendChild(tile);    //将小格子插入到整个扫雷界面中 
        }
      }
      this.obj.oncontextmenu = function(){  //取消浏览器的默认右键菜单事件
        return false;
      }
      this.event();            //点击事件
    },

3.绑事件函数:

event : function(){
      var _this = this;
      this.obj.onmouseover = function(e){    //鼠标悬停事件---
        if(e.target.className == 'tile'){
          e.target.className = 'tile current';
        }
      }
      this.obj.onmouseout = function(e){    //鼠标移出事件--
        if(e.target.className == 'tile current'){
          e.target.className = 'tile';
        }
      }
      this.obj.onmousedown = function(e){    //鼠标按下事件
        var index = e.target.index;
        if(e.button == 1){          //e.button属性 左键0/中键1/右键2
          event.preventDefault();    //取消默认
        }
        _this.changeStyle(e.button,e.target,index);
      }
      this.obj.onmouseup = function(e){   //鼠标弹起事件
        if(e.button == 1){
          _this.changeStyle(3,e.target);
        }
      }
    },

4.点击调用的函数:

changeStyle:function(num1,obj,num_index){     
      if(num1 == 0){         //是左键的话
        if(this.flag){       //this.flag 是之前定义的用于判断是否为第一次点击
          this.store(num_index);     //store函数,存放被点击的格子周围的8个格子
          this.setMineCraft(this.mine_num,this.arr,num_index); //如果是第一次点击 即调用布雷函数 更改flag状态
          this.flag = false;
          this.detail_statistics(0,false);   //开始信息统计函数
        }        
        if(obj.className != 'tile'&&obj.className !='tile current'){//如果不是第一次点击,被点击的格子不是未点击状态,无效
          return false;
        }
        if(obj.getAttribute('val') == 0){  //如果不是雷。改为翻开状态
          obj.className = "showed";    
          obj.innerHTML = obj.getAttribute('value') == 0?'':obj.getAttribute('value');          //显示周围雷数
          this.showAll(obj.index);   //递归函数判断周围格子的情况,就是扫雷游戏上一点开会出现一片的那种
        }
        if(this.over(obj)){       //判断游戏是否结束
          this.last();     
        }
      }
      if(num1 == 2){            //右键标记事件
        if(obj.className == 'biaoji'){
          obj.className = 'tile';
        }else if(obj.className !='biaoji'&&obj.className != 'showed'){
          obj.className = 'biaoji';
        }
      }
      if(num1 == 1){           // 中键事件
        if(obj.className =="showed"){
          this.show_zj1(obj.index);
        }
      }
      if(num1 == 3){          //鼠标弹起事件
        
        if (obj.className == "showed") {
          var flag1 = this.show_zj2(obj.index,0);
        }else{
          this.show_zj2(obj.index,1)
          return false;
        }

        if(flag1&&this.over()){     //弹起判断是否结束
          this.last();
        }
      }
    },

5.布雷:我之前的布雷是在页面加载在buildTiles()的时候布雷的,但是这样会导致有可能你电机的第一个格子就是雷(游戏性不强),后来修改到第一次点击完成之后布雷(确保第一下点的不是雷),避开直接炸死的现象.所以把调用放在后面的event后触发的changeStyle函数中

setMineCraft:function(num,arr_first,num_first){ //雷的个数、最开始被点击的格子周围的八个、被点击的那个格子
      var arr_index = [];          
      for(var i = 0;i<arr_first.length;i++){
        arr_index.push(arr_first[i].index);
      }
      var length = this.tiles.length;
      for (var i = 0; i < length; i++) {
        this.tiles[i].setAttribute("val", 0);
      }
      for (var i = 0; i < num; i++) {       
        var index_Mine = Math.floor(Math.random() * this.tiles.length);
        if(index_Mine == num_first||arr_index.lastIndexOf(index_Mine)>-1){//如果是属于第一次点击的周围的直接跳过在该位置布雷
          num++;
          continue;
        }
        
        if (this.tiles[index_Mine].getAttribute("val") == 0) {
          this.tiles[index_Mine].setAttribute("val", 1);
        }else {
          num++;
        }
      }
      this.showValue();
      this.event()
    },

6.存储周围格子的函数:

store : function(num) {  //传入格子的index.
      var tiles_2d = [];
      var indexs = 0;
      for(var i = 0;i<this.num2;i++){
        tiles_2d.push([]);
        for(var j = 0;j<this.num1;j++){
          tiles_2d[i].push(this.tiles[indexs]);
          indexs++;
        } 
      }
      var j = num % this.num1;
      var i = (num - j) / this.num1;
      this.arr = [];
        //左上
      if (i - 1 >= 0 && j - 1 >= 0) {
        this.arr.push(tiles_2d[i - 1][j - 1]);
      }
        //正上
      if (i - 1 >= 0) {
        this.arr.push(tiles_2d[i - 1][j]);
      }
        //右上
      if (i - 1 >= 0 && j + 1 <= this.num1-1) {
        this.arr.push(tiles_2d[i - 1][j + 1]);
      }
        //左边
      if (j - 1 >= 0) {
        this.arr.push(tiles_2d[i][j - 1]);
      }
        //右边
      if (j + 1 <= this.num1-1) {
        this.arr.push(tiles_2d[i][j + 1]);
      }
        //左下
      if (i + 1 <= this.num2-1 && j - 1 >= 0) {
        this.arr.push(tiles_2d[i + 1][j - 1]);
      }
        //正下
      if (i + 1 <= this.num2-1) {
        this.arr.push(tiles_2d[i + 1][j]);
      }
        //右下
      if (i + 1 <= this.num2-1 && j + 1 <= this.num1-1) {
        this.arr.push(tiles_2d[i + 1][j + 1]);
      }
    },

7.showAll函数:作用是如果该格子周围没有雷,自动翻开周围8个格子,然后再判断周围八个格子的周围8隔格子是否有雷,利用了递归的方法
 

showAll:function(num){
      if (this.tiles[num].className == "showed" && this.tiles[num].getAttribute("value") == 0){
        this.store(this.tiles[num].index);
        var arr2 = new Array();
        arr2 = this.arr;
        for (var i = 0; i < arr2.length; i++) {
          if (arr2[i].className != "showed"&&arr2[i].className !='biaoji') {
            if (arr2[i].getAttribute("value") == 0) {
              arr2[i].className = "showed";
              this.showAll(arr2[i].index);
            } else {
              arr2[i].className = "showed";
              arr2[i].innerHTML = arr2[i].getAttribute("value");
            }
          }
        }
      }
    },

8.show_zj函数:主要是中键按钮的作用中键点击后的函数,这里的show_zj1是鼠标键按下后的显示效果,
show_zj2函数就是

show_zj1:function(num){
      this.store(this.tiles[num].index);
      for (var i = 0; i < this.arr.length; i++) {
        if (this.arr[i].className == "tile") {
          this.arr_2.push(this.arr[i]);
          this.arr[i].className = "showed";
          // this.arr[i].className = "test";
        }
      }
    },
    show_zj2:function(num,zt){
      
      var count = 0;
      this.store(this.tiles[num].index);      
      
      for(var i = 0,len = this.arr_2.length;i<len;i++){
        this.arr_2[i].className = 'tile';     //按下效果恢复原状
      }

      this.arr_2.length = 0;
      for(var i = 0;i<this.arr.length;i++){
        this.arr[i].className == 'biaoji'&&count++;
      }
      if(zt == 1){
        return false;
      }
      var numofmines = this.tiles[num].getAttribute("value");
      if(numofmines == count){               //如果周围雷数和周围被标记数相等就翻开周围的格子
          var arr = new Array(this.arr.length);
          for(var i = 0;i<this.arr.length;i++){
            arr[i] = this.arr[i];
          }
          for (var i = 0,length = arr.length; i < length; i++) { 
            if (arr[i].className == "tile" && arr[i].getAttribute("val") != 1) {//如果周围格子无雷则继续。
              arr[i].className = "showed";
              arr[i].innerHTML = arr[i].getAttribute("value") == 0?"":arr[i].getAttribute("value");
              this.showAll(arr[i].index);
            } else if (arr[i].className == "tile" && arr[i].getAttribute("val") == 1) {  //如果周围格子有雷,游戏结束
              this.over(arr[i]);
              this.last();
              return false;
            }
          }
      }
      return true;
    },

9.结束判断:

over:function(obj){
      var flag = false;
      var showed = document.getElementsByClassName('showed');  
      var num = this.tiles.length - this.mine_num;     
      if(showed.length == num){           //如果被排出来的格子数等于总格子数-雷数,这游戏成功结束  
        this.detail_statistics(1,true);      //游戏统计 ,true代表胜,false,代表失败
        alert('恭喜你获得成功');
        flag = true;
      }else if(obj&&obj.getAttribute('val') == 1){   //如果被点击的是雷,则炸死
        this.detail_statistics(1,false);
        alert('被炸死!');
        flag = true;

      }
      return flag;
    },

10.结束后的显示函数:

last:function(){   
      var len = this.tiles.length;
      for(var i = 0;i<len;i++){
        this.tiles[i].className = this.tiles[i].getAttribute('val') == 1?'boom':'showed';
        if(this.tiles[i].className != 'boom'){  //
          this.tiles[i].innerHTML = this.tiles[i].getAttribute('value') == 0?'':this.tiles[i].getAttribute('value');
        }
      }
      this.obj.onclick = null;
      this.obj.oncontextmenu = null;
    },

11 统计信息:还是比较全的和windows7扫雷版的判断项目是一样的,使用的是每次结束游戏后将数据存入localStorage中,

//已玩游戏,已胜游戏,胜率,最多连胜,最多连败,当前连局;
    detail_statistics:function(num,zt){
      var time_pay = 1;
      var _this = this;
      if(num == 0){
        this.time_dsq = setInterval(function(){
          $('#time_need').text(time_pay);
          _this.time_dc =time_pay;
          time_pay++;
         },1000);
    
      }
      else if(num == 1){
        clearInterval(this.time_dsq);
        if(this.type == 4){return false;}
        if(localStorage.details == undefined){          
          localStorage.details = JSON.stringify([[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0]]); //这里存放的就是上面注释中的六项数据
        }
        if(JSON.parse(localStorage.details) instanceof Array){
          this.details = JSON.parse(localStorage.details);       
        }
        this.details[this.type][0] += 1;
        if(zt == false){
          if(this.details[this.type][5]>=0){
            this.details[this.type][5] = -1;
          }else{
            this.details[this.type][5] -= 1;
          }  
          if(this.details[this.type][5]<this.details[this.type][4]){
            this.details[this.type][4] = this.details[this.type][5];
          }
          this.details[this.type][2] = this.toPercent(this.details[this.type][2]/this.details[this.type][0]);        
          localStorage.details = JSON.stringify(this.details);
          return false;
        }

        if(this.details[this.type][5]>=0){
          this.details[this.type][5] += 1;
        }else{
          this.details[this.type][5] = 1;
        }
        if(this.details[this.type][5]>this.details[this.type][3]){
          this.details[this.type][3] = this.details[this.type][5];
        }
        this.details[this.type][3] += 1;
        this.details[this.type][2] = this.toPercent(this.details[this.type][4]/this.details[this.type][0]);
        localStorage.details = JSON.stringify(this.details);
        
        var time1 = new Date();        
        var time_str = time1.getFullYear()+'/'+time1.getMonth()+'/'+time1.getDate()+' '+time1.getHours()+':'+time1.getMinutes();
        if(localStorage.time == undefined){
          localStorage.time = JSON.stringify([[],[],[]]);
        }
        if(JSON.parse(localStorage.time) instanceof Array){
          this.time_arr = JSON.parse(localStorage.time);
        }

        this.time_arr[this.type].push([this.time_dc,time_str]);
        this.time_arr[this.type].sort(function(a,b){
          return a[0]-b[0];
        });
        if(this.time_arr[this.type].length>5){
          this.time_arr[this.type].pop();
        }
        localStorage.time = JSON.stringify(this.time_arr);
      
      }
    },

扫雷的主要部分就是这些了,还有一些小功能包括没写来,要看完整的可以看gitHub

之前看书学习总觉得学了就忘,感觉懂了公式却不知道怎么用,写完扫雷小程序觉得收获了很多

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript YUI 读码日记之 YAHOO.util.Dom - Part.4
Mar 22 Javascript
通过Jscript中@cc_on 语句识别IE浏览器及版本的代码
May 07 Javascript
JavaScript插件化开发教程 (二)
Jan 27 Javascript
jQuery+HTML5加入购物车代码分享
Oct 29 Javascript
让图片跳跃起来  javascript图片轮播特效
Feb 16 Javascript
深入理解js中this的用法
May 28 Javascript
浅谈JS的基础类型与引用类型
Sep 13 Javascript
BootStrap实现邮件列表的分页和模态框添加邮件的功能
Oct 13 Javascript
详解Vue 非父子组件通信方法(非Vuex)
May 24 Javascript
js逆向解密之网络爬虫
May 30 Javascript
vue iview多张图片大图预览、缩放翻转
Jul 13 Javascript
使用Vue.js中的过滤器实现幂方求值的方法
Aug 27 Javascript
Three.js如何实现雾化效果示例代码
Sep 27 #Javascript
浅谈Angular4中常用管道
Sep 27 #Javascript
深入理解Vue.js源码之事件机制
Sep 27 #Javascript
js截取字符串功能的实现方法
Sep 27 #Javascript
详解node+express+ejs+bootstrap构建项目
Sep 27 #Javascript
Three.js基础学习之场景对象
Sep 27 #Javascript
vue父组件中获取子组件中的数据(实例讲解)
Sep 27 #Javascript
You might like
php 高效率写法 推荐
2010/02/21 PHP
PHP快速按行读取CSV大文件的封装类分享(也适用于其它超大文本文件)
2014/04/10 PHP
ExtJS 2.0实用简明教程之应用ExtJS
2009/04/29 Javascript
基于jQuery的日期选择控件
2009/10/27 Javascript
JQuery Ajax 跨域访问的解决方案
2010/03/12 Javascript
巧用js提交表单轻松解决一个页面有多个提交按钮
2013/11/17 Javascript
js中一个函数获取另一个函数返回值问题探讨
2013/11/21 Javascript
js关于字符长度限制的问题示例探讨
2014/01/24 Javascript
jQuery制作简单柱状图实例
2015/01/28 Javascript
js实现文字跟随鼠标移动而移动的方法
2015/02/28 Javascript
详解javascript的变量与标识符
2016/01/04 Javascript
JavaScript实现输入框(密码框)出现提示语
2016/01/12 Javascript
基于jQuery仿淘宝产品图片放大镜特效
2020/10/19 Javascript
jquery 实时监听输入框值变化的完美方法(必看)
2017/01/26 Javascript
JavaScript实现求最大公共子串的方法
2018/02/03 Javascript
微信小程序页面间值传递的两种方法
2018/11/26 Javascript
js中Array对象的常用遍历方法详解
2019/01/17 Javascript
解决vue scoped html样式无效的问题
2020/10/24 Javascript
[53:15]2018DOTA2亚洲邀请赛3月29日 小组赛A组 LGD VS TNC
2018/03/30 DOTA
Python3 能振兴 Python的原因分析
2014/11/28 Python
Python的另外几种语言实现
2015/01/29 Python
python检索特定内容的文本文件实例
2018/06/05 Python
使用50行Python代码从零开始实现一个AI平衡小游戏
2018/11/21 Python
Django生成PDF文档显示在网页上以及解决PDF中文显示乱码的问题
2019/07/04 Python
检测python爬虫时是否代理ip伪装成功的方法
2019/07/12 Python
基于Python实现ComicReaper漫画自动爬取脚本过程解析
2019/11/11 Python
python3 自动打印出最新版本执行的mysql2redis实例
2020/04/09 Python
Python实现Canny及Hough算法代码实例解析
2020/08/06 Python
recorder.js 基于Html5录音功能的实现
2020/05/26 HTML / CSS
百丽国际旗下购物网站:优购
2017/02/28 全球购物
J2EE面试题大全
2016/08/06 面试题
会计专业大学生职业生涯规划书
2014/02/11 职场文书
小学数学教研活动总结
2014/07/01 职场文书
教师党员学习群众路线心得体会
2014/11/04 职场文书
2019年暑期法院实习报告
2019/12/18 职场文书
编写python程序的90条建议
2021/04/14 Python