js实现扫雷源代码

经过一段时间学习,对javascript有了一个初步的了解自己制作了一个扫雷,源代码+详细注释放在后面,先看下效果图。

初始化界面:

游戏界面:

难易程度切换:

游戏结束:

思路

采用构造函数的形式进行全局开发

生成游戏棋盘

  • 利用双层for循环创建设定的棋盘大小
  • 为每个单元格的dom元素创建一个属性,该属性用于保存单元格的所有信息,如x,y坐标,value,是否为雷等

随机生成炸弹

  • 利用随机数,随机生成炸弹x,y坐标,并将符合该坐标信息的单元格的属性更改为雷
  • 炸弹是在用户第一次点击的时候生成,防止用户第一次点击到炸弹
  • 将生成的每个炸弹信息都保存到一个this变量中,方便后续使用
  • 遍历每个炸弹周围的非炸弹方格,每遍历一次value值+1

鼠标左键点击

  • 点击的时候需要考虑该单元格是否有被标记小旗子(isFlag属性),如果有则无法点击
  • 判断是雷还是数字,雷的话则游戏结束,数字则继续判断是否等于0,等于0则使用递归显示空白区域
  • 每次打开一个单元格,需要更改该单元格的isOpen属性,表示单元格被打开

鼠标右键点击

  • 点击时需要考虑该单元格的isOpen属性是否被打开,打开的话则无法点击
  • 当该单元格没有标记旗帜时标记,如果有标记旗帜则取消标记
  • 每标记一个方格,剩余炸弹数量-1,取消标记则+1

游戏结束

  • 当左键点击到炸弹的时候游戏结束。失败
  • 剩余炸弹数量为0时。判断旗帜标记是否正确,正确游戏胜利,标记有误则失败

HTML代码

超短的HTML代码

<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Document</title>
 <link rel="stylesheet" href="css/index.css" >
</head>

<body>
 <div class="main">
  <header class="header">
   <button>初级</button>
   <button>中级</button>
   <button>高级</button>
  </header>
  <div class="gameBox" id="gameBox"></div>
  <footer class="footer">剩余雷数量:<span id="surplusMine"></span>
  </footer>
 </div>
</body>
<script src="js/index.js"></script>

</html>

CSS代码

.main .header {
 text-align: center;
 margin: 20px auto;
}

.main .gameBox table {
 border-spacing: 1px;
 background-color: rgb(170, 170, 170);
 text-align: center;
 margin: 20px auto;
}

.main .gameBox table td.mine {
 /* 游戏结束时显示 */
 border: none;
 background: url(./../img/mine.png) no-repeat;
 background-size: 90% 90%;
 background-color: #e9e6e6;
 background-position: 2px 0;
}

.main .gameBox table td.targetMine {
 /* 游戏结束时显示,触发雷的单元格 */
 border: none;
 background: url(./../img/mine.png) no-repeat;
 background-size: 90% 90%;
 background-color: #ff4b4b;
 background-position: 2px 0;
}

.main .gameBox table td.targetFlag {
 /* 右键标记方格时显示 */
 background: url(./../img/flag.png) no-repeat;
 background-size: 90% 90%;
 background-position: 2px 0;
 background-color: #e9e6e6;
}

.main .gameBox table td {
 /* 单元格初始样式 */
 width: 20px;
 height: 20px;
 box-sizing: border-box;
 border: 2px solid;
 border-color: #eee #ccc #ccc #eee;
 background-color: #e9e6e6;
 font-size: 1px;
 font-weight: 800;
}

.gameBox table td.zero,
.gameBox table td.one,
.gameBox table td.two,
.gameBox table td.three,
.gameBox table td.four,
.gameBox table td.five,
.gameBox table td.six,
.gameBox table td.seven,
.gameBox table td.eight,
.gameBox table td.nine {
 border: none;
 background-color: rgb(211, 200, 200);
}

.gameBox table td.zero {}

.gameBox table td.one {
 color: blue;
}

.gameBox table td.two {
 color: rgb(5, 93, 5);
}

.gameBox table td.three {
 color: #008c8c;
}

.gameBox table td.four {
 color: crimson;
}

.gameBox table td.five {
 color: rgb(228, 91, 0);
}

.gameBox table td.six {
 color: darkorange;
}

.gameBox table td.seven {
 color: rgb(193, 196, 50);
}

.gameBox table td.eight {
 color: pink;
}

.main .footer {
 text-align: center;
}

javaScript代码

核心代码

function Game(tr, td, mineNum) {
  this.td = td;
  this.tr = tr;
  this.mineNum = mineNum; //存储预设或设定的炸弹总数,用于后续判断是否胜利使用
  this.surplusMine = 0; //剩余雷数
  this.mineInfo = []; //用于接收随机生成的雷的信息
  this.tdsArr = [] //存放单元格的信息
  this.isPlay = false; //是否开始玩
  this.openClass = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
  this.gameBox = document.getElementById("gameBox");
  this.table = document.createElement("table"); //生成table标签
  this.footerNum = document.getElementById("surplusMine"); //剩余炸弹数量显示框

 }

 Game.prototype.creatDom = function() { //创建游戏区域,在玩家第一次点击游戏区域的时候执行
  this.table.oncontextmenu = function() { return false }; //清除默认右键单机事件
  for (var i = 0; i < this.gameBox.children.length; i++) { //为防止重新开始游戏时,重复生成多个table,在添加之前先删除之前的
   var childNod = this.gameBox.children[i];
   this.gameBox.removeChild(childNod);
  }

  for (var i = 0; i < this.tr; i++) {
   var tr = document.createElement("tr");
   this.tdsArr[i] = []; //为每一行生成一个数组

   for (var j = 0; j < this.td; j++) {
    var td = document.createElement("td");
    tr.appendChild(td); //将生成的td插入到tr中
    this.tdsArr[i][j] = td;
    td.info = { //info属性包括了单元格的所有信息,很重要
     type: "number", //格子类型,用于判断是否时炸弹
     x: i, //行
     y: j, //列
     value: 0, //当该格子周围有炸弹时显示该数值,生成炸弹的时候会++
     isOpen: false, //判断该单元格是否被打开
     isFlag: false //判断是否有标记flag
    }
   }
   this.table.appendChild(tr); //见tr插入到table中
  }
  this.gameBox.appendChild(this.table);
 }

 Game.prototype.creatMine = function(event, target) { //生成炸弹,该方法会在用户第一次点击棋盘的时候执行一次

  var This = this;
  for (var i = 0; true; i++) { //随机生成炸弹,生成扎当数与设定扎当书mineNum相同时终止循环
   var randomX = Math.floor(Math.random() * this.tr), //随机生成炸弹的行数
    randomY = Math.floor(Math.random() * this.td); //随机生成炸弹的列数
   // console.log(randomX + " " + randomY)
   if (target.info.x != randomX || target.info.y != randomY) { //保证第一次点击的时候不是炸弹
    if (this.tdsArr[randomX][randomY].info.type != "mine") { //保证每次生成的雷的位置不重复

     this.tdsArr[randomX][randomY].info.type = "mine"; //单元格更改属性为雷
     this.surplusMine++; //生成雷的数量+1
     this.mineInfo.push(this.tdsArr[randomX][randomY]); //将生成的雷的信息存放到this变量中,方便后续使用

    }
    if (this.surplusMine >= this.mineNum) { //当生成的炸弹数量等于设定的数量后跳出循环
     break;
    }
   }
  }

  //为每个炸弹周围的方格添加数字
  for (var i = 0; i < this.mineInfo.length; i++) {
   var around = this.getAround(this.mineInfo[i], This); //获取每个炸弹的周围方格
   // console.log(this.getAround(this.mineInfo[i], This))

   for (var j = 0; j < around.length; j++) { //将周围每个方格的value++
    around[j].info.value += 1;
   }
  }

 }

 Game.prototype.getAround = function(thisCell, This) { //获取某个方格的周围非炸弹方格,需要传递一个单元格dom元素,Game的this
  var x = thisCell.info.x, //行
   y = thisCell.info.y, //列
   result = [];
  // x-1,y-1  x-1,y  x-1,y+1
  // x,y-1   x,y   x,y+1
  // x+1,y-1  x+1y  x+1,y+1
  for (var j = x - 1; j <= x + 1; j++) {
   for (var k = y - 1; k <= y + 1; k++) {
    if ( //游戏区域的边界,行数x和列数y不能为负数,且不能超过设定的行数和列数
     j < 0 ||
     k < 0 ||
     j > (This.tr - 1) ||
     k > (This.td - 1) ||
     //同时跳过自身和周边是雷的方格
     This.tdsArr[j][k].info.type == "mine" ||
     (j == x && k == y)

    ) {
     continue; //满足上述条件是则跳过当此循环;
    } else {
     result.push(This.tdsArr[j][k]) //将符合的单元格push到result中返回
    }
   }
  }
  return result;
 }

 Game.prototype.lifeMouse = function(event, target) { //左键点击事件
  var This = this; //用变量的方式将Game的this传递到函数中
  var noOpen = 0; //没有被打开的格子数量
  if (!target.info.isFlag) { //表示该必须没有被右键标记才能鼠标左击
   if (target.info.type == "number") { //是数字时,则可视化

    function getAllZero(target, This) { //递归函数
     // console.log(target.info)
     if (target.info.isFlag) { //当这个单元格之前有被标记过flag时,则将剩余炸弹数+1
      This.surplusMine += 1;
      target.info.isFlag = false; //单元格被打开后初始化flag
     }
     if (target.info.value == 0) { //等于格子的value等于0的时候

      target.className = This.openClass[target.info.value]; //可视化

      target.info.isOpen = true; //表示该单元格被打开

      var thisAround = This.getAround(target, This); //获取该单元格周围的格子信息

      for (var i = 0; i < thisAround.length; i++) {
       // console.log(thisAround[i].info.isOpen)
       if (!thisAround[i].info.isOpen) { //递归的条件,当格子的open为true时不执行

        getAllZero(thisAround[i], This) //执行递归
       }

      }

     } else {
      target.innerHTML = target.info.value;
      target.className = This.openClass[target.info.value]; //可视化
      target.info.isOpen = true; //表示单元格被打开
      target.info.isFlag = false; //单元格被打开后初始化flag

     }
    }

    getAllZero(target, This); //首次执行

    //每次鼠标左键点击的时候都需要检查一下没有被打开的方格数量,每有一个则noOpen++
    for (var i = 0; i < this.tr; i++) {
     for (var j = 0; j < this.tr; j++) {
      if (this.tdsArr[i][j].info.isOpen == false) {
       noOpen++;
      }
     }

    }
    //当noOpen的数量与炸弹数量相同时,说明剩余的方格全是雷,游戏通过
    if (noOpen == this.mineNum) {
     console.log(noOpen)
     this.gameWin();
    }

   } else { //点击到了炸弹,游戏结束
    this.gameOver(target)
   }
  }

 }

 Game.prototype.rightMouse = function(target) { //鼠标右键点击执行
  if (!target.info.isOpen) {
   if (!target.info.isFlag) { //标记
    target.className = "targetFlag"; //显示旗帜
    target.info.isFlag = true; //表示该方格已经被标记
    this.surplusMine -= 1; //每标记一个方格,剩余炸弹数量-=1
    // console.log(this.surplusMine)
   } else { //取消标记
    target.className = ""; //去掉旗帜
    target.info.isFlag = false;
    this.surplusMine += 1;
    // console.log(this.surplusMine)

   }

   var isWin = true;
   if (this.surplusMine == 0) { //标记完所有flag时,遍历所有单元格
    // console.log(this.mineInfo.length)
    for (var i = 0; i < this.mineInfo.length; i++) {
     console.log(this.mineInfo[i].info.isFlag)
     if (!this.mineInfo[i].info.isFlag) { //检查每个雷的isFlag属性是否被标记,只要有一个为false则输掉游戏
      isWin = false;
      this.gameOver(target, 1);
      break;
     }
    }
    isWin ? this.gameWin(1) : 0; //三目运算符号
   }

   // if (this.surplusMine == 0) { //标记完所有flag时,遍历所有单元格
   //  for (var i; i < this.tr; i++) {
   //   for (var j; j < this.td; j++) {
   //    if()
   //   }

   //  }
   // }
  }
 }

 Game.prototype.gameOver = function(target, code) { //游戏结束,code为触发代码,当旗用完了时为1,点击到炸弹为0
  // console.log(this.mineInfo)
  var mineInfoLen = this.mineInfo.length;
  for (var i = 0; i < mineInfoLen; i++) { //显示每个雷的位置
   this.mineInfo[i].className = "mine";
  }
  this.table.onmousedown = false; //取消鼠标事件

  if (code) {
   alert("旗帜用完了,没有排除所有雷,游戏结束")
  } else {
   target.className = "targetMine"; //触发雷标红色
   alert("你被炸弹炸死了,游戏结束")
  }

 }

 Game.prototype.gameWin = function(code) { //游戏胜利
  if (code) {
   alert("你成功标记所有地雷,游戏通过")
  } else {
   alert("你找到了所有安全区域,游戏通过")
  }
  this.table.onmousedown = false;

 }

 Game.prototype.play = function() {
  var This = this; //需要将this传递到事件函数中使用
  this.table.onmousedown = function(event) {
   event = event || window.event; //兼容IE
   target = event.target || event.srcElement //兼容IE

   if (!this.isPlay) { //首次点击初始化棋盘,随机生成炸弹
    this.isPlay = true;
    This.creatMine(event, target);
   }

   if (event.button == 0) { //鼠标左键点击时执行
    This.lifeMouse(event, target);

   } else if (event.button == 2) { //右键点击执行
    This.rightMouse(target)
   }
   This.footerNum.innerHTML = This.surplusMine; //每次点击右键,刷新页面下方的剩余雷数
  }
 }

 Game.prototype.tablePos = function() { //将table居中显示
  var width = this.table.offsetWidth,
   height = this.table.offsetHeight;
  // console.log(this.table.offsetWidth)
  this.table.style.width = width + "px ";
  this.table.style.height = height + "px "

 }

 function addEvent(elem, type, handle) { //添加事件函数
  if (elem.addEventListener) { //w3c标准
   elem.addEventListener(type, handle, false);
  } else if (elem.attachEvent) { //IE9及以下
   elem.attachEvent("on" + type, function() {
    handle.call(elem);
   })
  } else { //其他情况
   elem["on" + type] = handle;
  }
 }

 Game.prototype.setDegree = function() { //调整难度
  var button = document.getElementsByTagName("button");

  addEvent(button[0], "click", function() { //简单
   var game = new Game(10, 10, 10);
   game.creatDom();
   game.play();
   game.tablePos();
  });

  addEvent(button[1], "click", function() { //一般
   var game = new Game(16, 16, 50);
   game.creatDom();
   game.play();
   game.tablePos();
  });

  addEvent(button[2], "click", function() { //困难
   var game = new Game(30, 30, 125);
   game.creatDom();
   game.play();
   game.tablePos();
  });

 }

 // 默认棋盘
 var game = new Game(10, 10, 10);
 game.creatDom();
 game.play();
 game.tablePos();
 game.setDegree()

总结一下,该游戏个人觉得难点有4个:

  • 没有思路,在bilibili看了一个教学视频,但是比较难理解,在原有基础上增加了自己的一些想法
  • 递归
  • 获取某一个方格周围的八个方格
  • 多层的if嵌套和循环

缺点:

  • 性能不佳,存在大量for循环,且没有优化
  • 某些时候界面显示的剩余炸弹数量不准确(已修复)
  • 代码冗余较多

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

(0)

相关推荐

  • js实现简单扫雷

    本文实例为大家分享了js实现简单扫雷的具体代码,供大家参考,具体内容如下 实现效果: 总体思想:这个扫雷难点主要就两点:第一点就是雷的随机分布,我这里使用js动态生成了10X10的table表格,然后设定生成20个雷,然后在生成每个格子代表的数字,雷我们用9表示,因为0-8要被其他非雷格子使用,然后我们只要一格是雷区那么周围的格子都加一,这样我们的雷区就生成好了,然后玩法的编写主要难点就是在当点击数字为0时的格子时要把它周围的0区也显示出来,因此这里我们使用递归来实现 1.界面的生成 //生成界

  • js版扫雷实现代码 原理不错

    效果图:以下代码复制粘贴到记事本后保存为.html文件,打开后会出现提示,右击允许即可: 寻雷-----by 魅月 var data=new Array( [0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0

  • HTML+JavaScript实现扫雷小游戏

    本文实例为大家分享了JavaScript实现扫雷小游戏的具体代码,供大家参考,具体内容如下 工具:Sublime Text / Dreamweaver /Hbuilder <!doctype html> <html> <head> <meta charset="utf-8"> <title>SaoLei</title> <style type="text/css"> table {

  • 使用纯javascript实现经典扫雷游戏

    很久以前写的 当时都没写注释的 刚加上了 (尼玛,好多自己都不认识了 ... ) 不足的地方就是本来想写个游戏排名的统计的,等有空了再加上(好像每次都这么说 然后就等好久好久...) 还有就是没有实现:点击第一个格子不能是雷的功能 <style> ul{padding:0;list-style:none;} #mine{overflow:hidden;width:30px;height:30px;border:1px solid #966;} #mine li{float:left;width

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

    初学javascript,写了一个扫雷程序练练手! 扫雷规则及功能 扫雷想必大家都不陌生,就是windows上点击排雷的小游戏,它的主要规则有 1.左键点击显示当前格子是否为雷,如果为雷的话,GameOver啦,如果不是雷的话,这个格子会显示周围八个格子内的雷数量. 2.鼠标右键标记,标记可能的雷,标记了之后取消需要再次右键点击该格子,左键无效果. 3.鼠标中键(滚轮)按下后,快捷扫雷(如果周围雷数和未被标记且未被翻开的格子相等,会将这些格子一并翻开) 主要功能基本完全复刻了windows7扫雷

  • 分享自己用JS做的扫雷小游戏

    引用了jQuery,节省了很多鼠标点击上的判断.界面显然都是照搬Windows的扫雷啦,详细的内容注释里都有,我就不啰嗦啦~ 先上截图~ 引用了jQuery,节省了很多鼠标点击上的判断 界面显然都是照搬Windows的扫雷啦 详细的内容注释里都有,我就不啰嗦啦~ JS部分 var mineArray, //地雷数组 lastNum, //剩余雷数 countNum, //未被揭开的方块数 inGame = 0, //游戏状态,0为结束,1为进行中,2为初始化完毕但未开始 startTime; /

  • javascript 扫雷游戏

    "); for (var i = 0; i "); for(var j = 0; j   "); buffer.append(" "); } buffer.append(" "); var workarea = document.getElementById("workarea"); workarea.innerHTML = buffer.toString(); mine(); // lay mines at the

  • JavaScript制作windows经典扫雷小游戏

    代码其实很简单,这里就不多废话了 <html> <head> <meta http-equiv="Content-Language" content="zh-cn"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>扫雷-JavaScript Mine Sweeper

  • JavaScript版经典游戏之扫雷游戏完整示例【附demo源码下载】

    本文实例讲述了JavaScript扫雷游戏.分享给大家供大家参考,具体如下: 翻出年初写的游戏贴上来,扫雷相信大家都玩过,先上图: 源码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.or

  • js+canvas实现简单扫雷小游戏

    扫雷小游戏作为windows自带的一个小游戏,受到很多人的喜爱,今天我们就来尝试使用h5的canvas结合js来实现这个小游戏. 要写游戏,首先要明确游戏的规则,扫雷游戏是一个用鼠标操作的游戏,通过点击方块,根据方块的数字推算雷的位置,标记出所有的雷,打开所有的方块,即游戏成功,若点错雷的位置或标记雷错误,则游戏失败. 具体的游戏操作如下 1.可以通过鼠标左键打开隐藏的方块,打开后若不是雷,则会向四个方向扩展 2.可以通过鼠标右键点击未打开的方块来标记雷,第二次点击取消标记 3.可以通过鼠标右键

随机推荐