当前位置:首页 > 日记 > 正文

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

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

初学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

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

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

相关文章

WPS表格怎么把数据分列WPS表格把数

WPS表格怎么把数据分列WPS表格把数

数据,步骤,方法,表格,电脑软件,  有时候会拿到一个用txt或者其他文本记录的数据,要求你把这些数据录入进excel里面,数据多一个一个录入是不可能的事了。那么有是什么方法可以批量完成呢?下面小编教你数据分列的方法步骤,希望对你有帮助!WPS表…

JavaScript中 this 指向问题深度解

JavaScript中 this 指向问题深度解

深度,电脑软件,JavaScript,JavaScript 中的 this 指向问题有很多文章在解释,仍然有很多人问。上周我们的开发团队连续两个人遇到相关问题,所以我不得不将关于前端构建技术的交流会延长了半个时候讨论 this 的问题。与我们常见的很多语言不同,J…

通过js修改input、select默认字体

通过js修改input、select默认字体

默认,字体颜色,修改,电脑软件,js,textarea默认字颜色以及获取焦点后的字的颜色,焦点获取后使默认消失<textarea cols="50" rows="5" id="textarea" onfocus="if(value=='获取到元素焦点'){value='';document.getElementById('te…

2016年qq给力签名

2016年qq给力签名

大全,电脑软件,qq,  你真的得碰上那个你能降得住又能降得住你的人才算是适合!哪些签名是你喜欢的呢?下面小编给大家分享了关于2016年qq给力签名,希望你喜欢。2016年qq给力签名大全1) 我知道回不去,但还是会想念会回忆会心疼到无法自拔。2) …

浅析为什么a=abc 不等于 a=new Str

浅析为什么a=abc 不等于 a=new Str

不等于,电脑软件,abc,String,显而易见a="abc"typeof a //stringb=new String("abc")typeof b // objecta==b //truea===b //false但是为什么呢?看了很多书,问了好几个大神,其实自己还是稀里糊涂的。这里记录一下,备查。在js中,区分原始资料…

JavaScript函数中的this四种绑定形

JavaScript函数中的this四种绑定形

绑定,函数,四种,形式,电脑软件,正文 javascript中的this和函数息息相关,所以今天,我就给大家详细地讲述一番:javascript函数中的this一谈到this,很多让人晕晕乎乎的抽象概念就跑出来了,这里我就只说最核心的一点——函数中的this总指向调用它的…

浅谈JS中的反柯里化 |  uncurrying

浅谈JS中的反柯里化 | uncurrying

浅谈,柯里,电脑软件,JS,uncurrying,反柯里化相反,反柯里化的作用在与扩大函数的适用性,使本来作为特定对象所拥有的功能的函数可以被任意对象所用.即把如下给定的函数签名,obj.func(arg1, arg2)转化成一个函数形式,签名如下:func(obj, arg1, arg…

JavaScript的事件机制详解

JavaScript的事件机制详解

事件机制,详解,电脑软件,JavaScript,事件是将JavaScript脚本与网页联系在一起的主要方式,是JavaScript中最重要的主题之一,深入理解事件的工作机制以及它们对性能的影响至关重要。本文将详细探讨JavaScript的事件机制,并对比分析了浏览器之间…

thinkPHP5.0框架简单配置作用域的

thinkPHP5.0框架简单配置作用域的

作用域,框架,配置,方法,简单,本文实例讲述了thinkPHP5.0框架简单配置作用域的方法。分享给大家供大家参考,具体如下:配置参数支持作用域的概念,默认情况下,所有参数都在同一个系统默认作用域下面。如果你的配置参数需要用于不同的项目或者相互…

Jquery根据浏览器窗口改变调整大小

Jquery根据浏览器窗口改变调整大小

浏览器,方法,调整,大小,窗口,如下所示:当浏览器窗口发生该表示,会触发resize函数// 浏览器大小发生变化时,给id为main-container的容器设置最小高度<script type="text/javascript"> $(function(){ $(window).resize(function(){ $…

excel2013如何设置纵横页面布局

excel2013如何设置纵横页面布局

页面布局,方法,设置,如何设置,电脑软件,  通常在Excel2013中记录数据,都是采用的纵排排列,就是以列的形式呈现出来,但是有时候用种方式不适合怎么办?下面小编就教你怎么设置excel2013的纵横布局,希望看完本教程的朋友都能学会并运用起来。exc…

word2013怎么插入窗口域word2013插

word2013怎么插入窗口域word2013插

步骤,方法,窗口,电脑软件,strong,  我们在使用word的时候,相信很多人都不知道窗体域这个功能,那么今天小编就来告诉你怎么插入这个功能吧。word2013插入窗口域的步骤第一步,点击&ldquo;文件&rdquo;并选择&ldquo;选项&rdquo;word2013插入窗口…