做个小项目~纯原生JS手把手逐句解释写一个扫雷小游戏(附源码)

话不多说,开整!

1.规划

先打开现有的扫雷小游戏分析一下,都有哪些功能
在这里插入图片描述
有一堆格子,要可以选择难度,要按照难度生成地雷,地雷周围的格子要变现出周围有多少雷,格子就直接控制table好了
还有一些小细节,比如计时等等,可以最后在加上
那么就分析完了,开始整活!

2.html结构

html嘛,没什么特别需要介绍的

<div id="main">
 	<table id="landmine"></table>
 	<div id="operation">
 		<div class="tip">
 			剩余雷数:<span id="landMineCount">0</span></div>
 		<div class="tip">
 			持续时间: <span id="costTime">0</span></div>
 		<fieldset>
 			<legend>难度选择:</legend>
 			<input type="radio" name="level" id="llevel" checked="checked" value="10" /><label for="llevel">初级(10*10)</label><br />
 			<input type="radio" name="level" id="mlevel" value="15" /><label for="mlevel">中级(15*15)</label><br />
 			<input type="radio" name="level" id="hlevel" value="20" /><label for="hlevel">高级(20*20)</label><br />
 		</fieldset>
 		<input type="button" id="begin" value="开始游戏" /><br />

大概就是这样子,非常的简洁美观
在这里插入图片描述
那么再下来就是画格子了

3.画格子

先初始化一下,构建一下绘制小格子的方法draw()
这个方法需要四个参数:

  • rowCount:行数
  • colCount:列数
  • minLandMineCount:最少雷数
  • maxLandMineCount:最多雷数

var Mine=function(
		rowCount,
        colCount,
        minLandMineCount,
        maxLandMineCount
        ){
        
	this.table=document.getElementById("landmine");//获取那个表格
    this.cells = document.getElementsByTagName('td'); //获取所有小格子
    this.rowCount = rowCount || 10; //格子行数
    this.colCount = colCount || 10; //格子列数
    this.landMineCount = 0; //地雷个数
    this.markLandMineCount = 0; //标记的地雷个数
    this.minLandMineCount = minLandMineCount || 10; //地雷最少个数
    this.maxLandMineCount = maxLandMineCount || 20; //地雷最多个数
}
}

这样就做好了画格子的数据准备了
接下来咱们需要想想
还有什么是这个游戏没有实现的?

  • 第一,我们要记录玩家标记了哪些格子,所以我们需要一个数组
  • 第二,玩家每次标记雷,需要一个方法,更新上面的数组和雷的数量
  • 第三,当踩到雷的时候,需要一个方法,告诉玩家他挂了
  • 第四,要记录玩家标记雷次数和游戏时间

第二、第三两条,方法以后再考虑如何实现,先用null代替
于是在构造器在再增加下面的属性

	this.arrs = []; 
    this.beginTime = null; //游戏开始时间
    this.endTime = null; //游戏结束时间
    this.currentSetpCount = 0; //标记雷次数
    this.trueCount = 0; //标记正确的雷的次数
    this.endCallBack = null; //踩到雷的方法
    this.landMineCallBack = null; //标记为地雷时的方法

然后,开始画格子!
我们在Mine上追加一个原型方法draw,用来画格子:

在追加单元格的同时,一并生成地雷,需要一个数组,记录地雷的位置

Mine.prototype={
draw:function(){
var gz="";
for(var i=0;i<this.rowCount;i++){
gz+="<tr>";
for(var j=0;j<this.colCount;j++){
gz+=`<td id="mine${i+"_"+j}"></td>`;
}
gz+="</tr>";
}
document.getElementById("landmine").innerHTML=gz;
}
}

目前,初始化的js是这样:


var Mine=function(
		rowCount,
        colCount,
        minLandMineCount,
        maxLandMineCount
        ){
        
	this.table=document.getElementById("landmine");//获取那个表格
    this.cells = document.getElementsByTagName('td'); //获取所有小格子
    this.rowCount = rowCount || 10; //格子行数
    this.colCount = colCount || 10; //格子列数
    this.landMineCount = 0; //地雷个数
    this.markLandMineCount = 0; //标记的地雷个数
    this.minLandMineCount = minLandMineCount || 10; //地雷最少个数
    this.maxLandMineCount = maxLandMineCount || 20; //地雷最多个数
    this.arrs = []; //后面会用,地雷地图用的
    this.beginTime = null; //游戏开始时间
    this.endTime = null; //游戏结束时间
    this.currentSetpCount = 0; //标记雷次数
    this.trueCount = 0; //标记正确的雷的次数
    this.endCallBack = null; //踩到雷的方法
    this.landMineCallBack = null; //标记为地雷时的方法
}


Mine.prototype={
draw:function(){
var gz="";
for(var i=0;i<this.rowCount;i++){
gz+="<tr>";
for(var j=0;j<this.colCount;j++){
gz+=`<td id="mine${i+"_"+j}"></td>`;
}
gz+="</tr>";
}
document.getElementById("landmine").innerHTML=gz;
}
}

现在,加一加css,然后看看效果:

td{background-color:green;width:15px;height: 15px}

在这里插入图片描述
?执行以下,得到?
在这里插入图片描述
似乎问题不大,把这一个文件命名为Main.js,引用一下
然后,就要进行事件绑定了
我们把调用的js放另外一个文件里,叫main.js吧

调用的js

首先,再思考一下,我们需要什么
我们要获取三个单选框,还有那个按钮的dom,然后把用户选择的难度传给开始游戏的方法,并绑定到开始游戏的按钮上
好办

var radios=document.getElementsByName("level");
for(var i=0;i<3;i++){
radios[i].onclick=function(){
var value=this.value;
document.getElementById("begin").onclick=function(){
//这里
}
}
}

然后,我们要肯定要记录一下我们创建的Main对象,先用null代替,所以在全局声明一个

var lei=null;

然后我们需要一个开始游戏的方法start,用这个方法调用上面的Main方法,所以这个方法需要什么参数呢?需要游戏的难度,需要行、列、雷数量等等等等

雷的数量按照如下计算
最多:(长宽)/5
最少:(长
宽)/5-长

然后绑定在开始游戏的按钮上
那我们就对上面的代码加点东西:

var lei=null;
var radios=document.getElementsByName("level");
for(var i=0;i<3;i++){
radios[i].onclick=function(){
var value=this.value;
document.getElementById("begin").onclick=function(){
start(value,value,value*value/5,value*value/5 -value)
}
}
}

function start(rowCount, colCount, minLandMineCount, maxLandMineCount){
//这里还需要一些别的,以后再加
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}

这样,点击单选框就会改变格子的样子了。
在这里插入图片描述

接着,我们需要做一些判断
比如,点击单选框后,会创建刺激的新游戏,万一是误触怎么办呢?
所以,在执行start()前,先判断lei是不是null,不是的话,最后确认一下:
把start()改成:

function start(rowCount, colCount, minLandMineCount, maxLandMineCount){
//这里还需要一些别的,以后再加
if(lei!=null){
var jg=confirm("开始新游戏?")
if(jg){
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
}
else{
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
}

汇总一下开始游戏的代码(也就是main.js):

var lei=null;
var radios=document.getElementsByName("level");
for(var i=0;i<3;i++){
radios[i].onclick=function(){
var value=this.value;
document.getElementById("begin").onclick=function(){
start(value,value,value*value/5,value*value/5 -value)
}
}
}
function start(rowCount, colCount, minLandMineCount, maxLandMineCount){
//这里还需要一些别的,以后再加
if(lei!=null){
var jg=confirm("开始新游戏?")
if(jg){
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
}
else{
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
}

到此为止,我们就实现了地雷地图的绘制
然后,我们要进行的是生成地雷

生成地雷

我们把所有格子绘制成一个数组,初始值用0表示,再最随机生成地雷,用9代替
其他的则填入为格子附近的雷的数量

数组中值的意义
0:周围每一个雷
9:我自己是雷
1-8:周围雷的个数

那么,我们在Mine的prototype中再加入一个方法:

DrawMine:function(){
for(var i=0;i<this.rowCount;i++){
	this.arrs[i] = [];
	//arr再前面Mine的构造里已经声明过了
	for(var j=0;j<this.colCount;j++){
		this.arrs[i][j] = 0;
}
}
}

然后就要构思构思了,雷的随机生成应该怎么搞?
在前面,我们已经声明过了,最小的雷数和最大的雷数minLandMineCount, maxLandMineCount,毫无疑问要传给这个方法,因为后面还要用到随机生成雷,所以这里可以封装一个随机数的函数
那么就可以这么写:

SJCount: function (FirstValue, LastValue) {
    var Choices = LastValue - FirstValue + 1;//确保最大值能取到
    return Math.floor(Math.random() * Choices + FirstValue);
},

然后,让雷的数量的值等于这个

this.landMineCount = this.SJCount(this.minLandMineCount, this.maxLandMineCount);

接下来,咱们思考一下,怎么生成这些雷呢?
我们要先知道总共有多少格子,也就是arr数组的长度
然后,为了保证随机生成的雷位置不同,我们还要加一个去重操作
那么:

landMine:function(){
var all=this.rowCount*this.colCount;
var MineList=[];
for(var i = 0; i < this.landMineCount; i++){
var randomNum = this.SJCount(0, allCount-1);
var row= parseInt(randomNum / this.colCount),col= randomNum % this.colCount;
//从0到格子总数-1随机出来一个数作为雷,然后算出来这个数对应的行和列分别是多少
if (randomNum in MineList) {
            i--;
            continue;
        }
//如果随机出来的数已经在列表里面了,那么往回退,重新生成
        this.arrs[row][col] = 9;
        MineList[randomNum] = randomNum;
    }
}

如此,我们就随机生成了若干个雷,并且记录到了数组里。
现在,数组只剩最后一种,也就是表示附近雷的个数的值
那么只用遍历一遍所有格子,每个格子周围的值是9的个数
注意,要跳过边界的情况,否则会数组越界

所以,我们继续给Mine里添加方法,用于计算周围雷的数量:

everyCount: function () {
    for (var i = 0; i < this.rowCount; i++) {
        for (var j = 0; j < this.colCount; j++) {
            if (this.arrs[i][j] == 9)
                continue;
            if (i > 0 && j > 0) {
                if (this.arrs[i - 1][j - 1] == 9)
                    this.arrs[i][j]++;
            }
            if (i > 0) {
                if (this.arrs[i - 1][j] == 9)
                    this.arrs[i][j]++;
            }
            if (i > 0 && j < this.colCount - 1) {
                if (this.arrs[i - 1][j + 1] == 9)
                    this.arrs[i][j]++;
            }
            if (j > 0) {
                if (this.arrs[i][j - 1] == 9)
                    this.arrs[i][j]++;
            }
            if (j < this.colCount - 1) {
                if (this.arrs[i][j + 1] == 9)
                    this.arrs[i][j]++;
            }
            if (i < this.rowCount - 1 && j > 0) {
                if (this.arrs[i + 1][j - 1] == 9)
                    this.arrs[i][j]++;
            }
            if (i < this.rowCount - 1) {
                if (this.arrs[i + 1][j] == 9)
                    this.arrs[i][j]++;
            }
            if (i < this.rowCount - 1 && j < this.colCount - 1) {
                if (this.arrs[i + 1][j + 1] == 9)
                    this.arrs[i][j]++;
            }
        }
    }
}

好耶!汇总一下Main.js

var Mine=function(
		rowCount,
        colCount,
        minLandMineCount,
        maxLandMineCount
        ){
        
	this.table=document.getElementById("landmine");//获取那个表格
    this.cells = document.getElementsByTagName('td'); //获取所有小格子
    this.rowCount = rowCount || 10; //格子行数
    this.colCount = colCount || 10; //格子列数
    this.landMineCount = this.SJCount(this.minLandMineCount, this.maxLandMineCount); //地雷个数
    this.markLandMineCount = 0; //标记的地雷个数
    this.minLandMineCount = minLandMineCount || 10; //地雷最少个数
    this.maxLandMineCount = maxLandMineCount || 20; //地雷最多个数
    this.arrs = []; //后面会用,地雷地图用的
    this.beginTime = null; //游戏开始时间
    this.endTime = null; //游戏结束时间
    this.currentSetpCount = 0; //标记雷次数
    this.trueCount = 0; //标记正确的雷的次数
    this.endCallBack = null; //踩到雷的方法
    this.landMineCallBack = null; //标记为地雷时的方法
}


Mine.prototype={
draw:function(){
var gz="";
for(var i=0;i<this.rowCount;i++){
gz+="<tr>";
for(var j=0;j<this.colCount;j++){
gz+=`<td id="mine${i+"_"+j}"></td>`;
}
gz+="</tr>";
}
document.getElementById("landmine").innerHTML=gz;
},
DrawMine:function(){
for(var i=0;i<this.rowCount;i++){
	this.arrs[i] = [];
	//arr再前面Mine的构造里已经声明过了
	for(var j=0;j<this.colCount;j++){
		this.arrs[i][j] = 0;
}
}
},
SJCount: function (FirstValue, LastValue) {
    var Choices = LastValue - FirstValue + 1;//确保最大值能取到
    return Math.floor(Math.random() * Choices + FirstValue);
},
landMine:function(){
var all=this.rowCount*this.colCount;
var MineList=[];
for(var i = 0; i < this.landMineCount; i++){
var randomNum = this.SJCount(0, allCount-1);
var row= parseInt(randomNum / this.colCount),col= randomNum % this.colCount;
//从0到格子总数-1随机出来一个数作为雷,然后算出来这个数对应的行和列分别是多少
if (randomNum in MineList) {
            i--;
            continue;
        }
//如果随机出来的数已经在列表里面了,那么往回退,重新生成
        this.arrs[row][col] = 9;
        MineList[randomNum] = randomNum;
    }
},
everyCount: function () {
    for (let i = 0; i < this.rowCount; i++) {
        for (let j = 0; j < this.colCount; j++) {
            if (this.arrs[i][j] == 9)
                continue;
            if (i > 0 && j > 0) {
                if (this.arrs[i - 1][j - 1] == 9)
                    this.arrs[i][j]++;
            }
            if (i > 0) {
                if (this.arrs[i - 1][j] == 9)
                    this.arrs[i][j]++;
            }
            if (i > 0 && j < this.colCount - 1) {
                if (this.arrs[i - 1][j + 1] == 9)
                    this.arrs[i][j]++;
            }
            if (j > 0) {
                if (this.arrs[i][j - 1] == 9)
                    this.arrs[i][j]++;
            }
            if (j < this.colCount - 1) {
                if (this.arrs[i][j + 1] == 9)
                    this.arrs[i][j]++;
            }
            if (i < this.rowCount - 1 && j > 0) {
                if (this.arrs[i + 1][j - 1] == 9)
                    this.arrs[i][j]++;
            }
            if (i < this.rowCount - 1) {
                if (this.arrs[i + 1][j] == 9)
                    this.arrs[i][j]++;
            }
            if (i < this.rowCount - 1 && j < this.colCount - 1) {
                if (this.arrs[i + 1][j + 1] == 9)
                    this.arrs[i][j]++;
            }
        }
    }
}

}

再往后,就是给每个格子添加点击事件啦。

格子的点击事件

我们先构思一下,每当我点击一个格子,会有几种情况?

左键:
第一,是数字为0的格子,一下子展开紧邻的所有为0的格子
第二,数字是1-8的格子,就展开这一个格子
第三,雷,游戏结束
右键:标记雷

好耶!思路清晰了!
我们先把左右键给格子绑定上:
我们先把标记成雷的css设置成

.flag{background-color:red;}
$: function (id) {
    return document.getElementById(id);
},//用于获取元素
bingMine:function(){
 for (let i = 0; i < this.rowCount; i++) {
        for (let j = 0; j < this.colCount; j++) {
        this.$("mine" + i + "_" + j).onmousedown=
        function(e){
			e = e || window.event;
			 var mouseNum = e.button;//获取是按得那个键
                    var className = this.className;
                    if (mouseNum == 2) {
                        if (className == "flag") {
                            this.className = "";
                            self.markLandMineCount--;
                        } else {
                            this.className = "flag";
                            self.markLandMineCount++;
                        }
                        if (self.landMineCallBack) {
                         self.landMineCallBack(self.landMineCount - self.markLandMineCount);
                        }
                        if (self.trueCount == self.landMineCount ) {
                        self.success();
                         }
                    } else if (mouseNum == 1||className != "flag") {
                        //打开相连的所有数为0的格子的方法
                    }
                };
}
}
}

这时我们发现,右键已经可以标红格子了
不过!会弹出浏览器菜单!这可不行!
我们要加入这一句,禁用右键:
加在Main的构造函数里

document.oncontextmenu = function () {
      //禁用右键菜单
      return false;
    };

然后我们就可以用lei.bingMain()调用一下了

var lei=null;
var radios=document.getElementsByName("level");
for(var i=0;i<3;i++){
radios[i].onclick=function(){
var value=this.value;
document.getElementById("begin").onclick=function(){
start(value,value,value*value/5,value*value/5 -value)
}
}
}
function start(rowCount, colCount, minLandMineCount, maxLandMineCount){
//这里还需要一些别的,以后再加
if(lei!=null){
var jg=confirm("开始新游戏?")
if(jg){
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
else{return;}
}
else{
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw();

}
lei.DrawMine()
lei.landMine()
lei.everyCount()
lei.bingMine()
}

在这里插入图片描述
(右键点击格子就会变红了)
我们打开控制台,走一下lei.arrs,看看数组怎么样:
在这里插入图片描述
看上去,一点问题都没有嘛
然后我们就可以写一下展开地图了
思路也很简单
如果左键点击的这个格子对应的不是9,那么显示这个数字
如果展开

也很简单嘛

open: function (obj, x, y) {
    if (this.arrs[x][y] != 9) {
        this.currentSetpCount++;
        obj.innerHTML = this.arrs[x][y];
        obj.className = "clear";
        obj.onmousedown = null;
    } else {
        this.failed();
    }
},

然后如果我们点击到0,会点开所有的0,以及其周围不是雷的格子
怎么实现呢?
我们现在open进行判定,如果是0的话,执行一个方法,接收坐标信息,然后判断3*3内有没有0,如果有的话,再判断是否被标记为雷,如果也没有,就调用open,实现递归展开(并需要跳过自身)
所以open变成

open: function (obj, x, y) {
    if (this.arrs[x][y] != 9) {
        this.currentSetpCount++;
        obj.innerHTML = this.arrs[x][y];
        obj.className = "clear";
        obj.onmousedown = null;
        if (this.arrs[x][y] == 0) {
            this.showNoLandMine.call(this, x, y);
        }
    } else {
        this.failed();
    }
},

然后showNoLandMine就这么写:

showNoLandMine: function (x, y) {
    for (var i = x - 1; i < x + 2; i++)
        for (var j = y - 1; j < y + 2; j++) {
            if (!(i == x && j == y)) {
                var ele = this.$("mine" + i + "_" + j);
                if (ele && ele.className == "") {
                    this.open.call(this, ele, i, j);
                }
            }
        }
},

在这里插入图片描述
目前为止,扫雷就是这个样子
(中间没有数字的就是雷了)
然后,我们在里面添加失败的方法和成功的方法:

failed:function(){
	alert("你踩雷了")
    for (var i = 0; i < this.rowCount; i++) {
        for (var j = 0; j < this.colCount; j++) {
            if (this.arrs[i][j] == 9)
                this.$("mine" + i + "_" + j).innerHTML="雷"
            else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
        }
}
},
success:function(){
    alert("你赢了")
    for (var i = 0; i < this.rowCount; i++) {
        for (var j = 0; j < this.colCount; j++) {
            if (this.arrs[i][j] == 9)
                this.$("mine" + i + "_" + j).innerHTML="雷"
            else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
        }
}
}

这两个方法的核心就是显示出所有的块块
在这里插入图片描述
(这就是是最后的样子)
然后我们完善一下其他的内容,比如显示标记的雷的数量,以及时间等等
比如在最开始,声明变量的地方加入

document.querySelector("#landMineCount").innerHTML=this.landMineCount;

以及在点击时添加一句

document.querySelector("#landMineCount").innerHTML=self.landMineCount-self.markLandMineCount

然后时间上,也就是一个简简单单的计数器:

	this.ts=0;
	this.jsq=setInterval(()=>{
	this.beginTime=Date.parse(new Date());
		this.ts++;
		document.querySelector("#costTime").innerHTML=this.ts;
		},1000)

然后在赢或者死了的时候,停止计时:

failed:function(){
    for (var i = 0; i < this.rowCount; i++) {
        for (var j = 0; j < this.colCount; j++) {
            if (this.arrs[i][j] == 9)
                this.$("mine" + i + "_" + j).innerHTML="雷"
            else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
        }
		clearInterval(this.jsq)
}
},
success:function(){
    alert("win")
    for (var i = 0; i < this.rowCount; i++) {
        for (var j = 0; j < this.colCount; j++) {
            if (this.arrs[i][j] == 9)
                this.$("mine" + i + "_" + j).innerHTML="雷"
            else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
        }
		clearInterval(this.jsq)
}
}

那么现在,几乎全部功能就实现了,我们在稍微修改修改CSS
(毕竟太丑了啊喂!)
现在这里送上没有修改css的版本,方便大家理解:
(js css html写到一个文件里了)

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<style>
td{background-color:green;width:15px;height: 15px}
.flag{background-color:red;}
	</style>	
	<script>
var Mine=function(
		rowCount,
        colCount,
        minLandMineCount,
        maxLandMineCount
        ){
        
	this.table=document.getElementById("landmine");//获取那个表格
    this.cells = document.getElementsByTagName('td'); //获取所有小格子
    this.rowCount = rowCount || 10; //格子行数
    this.colCount = colCount || 10; //格子列数
    this.markLandMineCount = 0; //标记的地雷个数
    this.minLandMineCount = minLandMineCount || 10; //地雷最少个数
    this.maxLandMineCount = maxLandMineCount || 20; //地雷最多个数
    this.landMineCount = this.SJCount(this.minLandMineCount, this.maxLandMineCount); //地雷个数
    this.arrs = []; //后面会用,地雷地图用的
    this.beginTime = null; //游戏开始时间
    this.endTime = null; //游戏结束时间
    this.currentSetpCount = 0; //标记雷次数
    this.trueCount = 0; //标记正确的雷的次数
    this.endCallBack = null; //踩到雷的方法
    this.landMineCallBack = null; //标记为地雷时的方法
	this.ts=0;
	this.jsq=setInterval(()=>{
	this.beginTime=Date.parse(new Date());
		this.ts++;
		document.querySelector("#costTime").innerHTML=this.ts;
		},1000);
	document.querySelector("#landMineCount").innerHTML=this.landMineCount;
    document.oncontextmenu = function () {
      //禁用右键菜单
      return false;
    };
}


Mine.prototype={
draw:function(){
var gz="";
for(var i=0;i<this.rowCount;i++){
gz+="<tr>";
for(var j=0;j<this.colCount;j++){
gz+=`<td id="mine${i+"_"+j}"></td>`;
}
gz+="</tr>";
}
document.getElementById("landmine").innerHTML=gz;
},
DrawMine:function(){
for(var i=0;i<this.rowCount;i++){
	this.arrs[i] = [];
	//arr再前面Mine的构造里已经声明过了
	for(var j=0;j<this.colCount;j++){
		this.arrs[i][j] = 0;
}
}
},
SJCount: function (FirstValue, LastValue) {
    var Choices = LastValue - FirstValue + 1;//确保最大值能取到
    return Math.floor(Math.random() * Choices + FirstValue);
},
landMine:function(){
var all=this.rowCount*this.colCount;
var MineList=[];
for(var i = 0; i < this.landMineCount; i++){
var randomNum = this.SJCount(0, all-1);
var row= parseInt(randomNum / this.colCount),col= randomNum % this.colCount;
//从0到格子总数-1随机出来一个数作为雷,然后算出来这个数对应的行和列分别是多少
if (randomNum in MineList) {
            i--;
            continue;
        }
//如果随机出来的数已经在列表里面了,那么往回退,重新生成
        this.arrs[row][col] = 9;
        MineList[randomNum] = randomNum;
    }
},
everyCount: function () {
    for (var i = 0; i < this.rowCount; i++) {
        for (var j = 0; j < this.colCount; j++) {
            if (this.arrs[i][j] == 9)
                continue;
            if (i > 0 && j > 0) {
                if (this.arrs[i - 1][j - 1] == 9)
                    this.arrs[i][j]++;
            }
            if (i > 0) {
                if (this.arrs[i - 1][j] == 9)
                    this.arrs[i][j]++;
            }
            if (i > 0 && j < this.colCount - 1) {
                if (this.arrs[i - 1][j + 1] == 9)
                    this.arrs[i][j]++;
            }
            if (j > 0) {
                if (this.arrs[i][j - 1] == 9)
                    this.arrs[i][j]++;
            }
            if (j < this.colCount - 1) {
                if (this.arrs[i][j + 1] == 9)
                    this.arrs[i][j]++;
            }
            if (i < this.rowCount - 1 && j > 0) {
                if (this.arrs[i + 1][j - 1] == 9)
                    this.arrs[i][j]++;
            }
            if (i < this.rowCount - 1) {
                if (this.arrs[i + 1][j] == 9)
                    this.arrs[i][j]++;
            }
            if (i < this.rowCount - 1 && j < this.colCount - 1) {
                if (this.arrs[i + 1][j + 1] == 9)
                    this.arrs[i][j]++;
            }
        }
    }
},
$: function (id) {
    return document.getElementById(id);
},//用于获取元素
bingMine:function(){
	var self=this;
 for (let i = 0; i < this.rowCount; i++) {
        for (let j = 0; j < this.colCount; j++) {
        self.$("mine" + i + "_" + j).onmousedown = function (e) {
                    e = e || window.event;
                    var mouseNum = e.button;
                    var className = this.className;
                    if (mouseNum == 2) {
                        if (className == "flag") {
                            this.className = "";
                            self.markLandMineCount--;

                        } else {
                            this.className = "flag";
                            self.markLandMineCount++;
                            if(self.arrs[i][j]==9){
                                self.trueCount++;
                            }
                        }					document.querySelector("#landMineCount").innerHTML=self.landMineCount-self.markLandMineCount
                        if (self.landMineCallBack) {
                            self.landMineCallBack(self.landMineCount - self.markLandMineCount);
                        }
                        if (self.trueCount == self.landMineCount ) {
                                self.success();
                         }
                    } else if (mouseNum == 1||className != "flag") {
                        self.open.call(self,this, i, j);

                    }
                };
}
}
},
showNoLandMine: function (x, y) {
    for (var i = x - 1; i < x + 2; i++)
        for (var j = y - 1; j < y + 2; j++) {
            if (!(i == x && j == y)) {
                var ele = this.$("mine" + i + "_" + j);
                if (ele && ele.className == "") {
                    this.open.call(this, ele, i, j);
                }
            }
        }
},
open: function (obj, x, y) {
    if (this.arrs[x][y] != 9) {
        this.currentSetpCount++;
        obj.innerHTML = this.arrs[x][y];
        obj.className = "clear";
        // alert( this.trueCount)
        obj.onmousedown = null;
        if (this.arrs[x][y] == 0) {
            this.showNoLandMine.call(this, x, y);
        }
    } else {
        this.failed();
    }
},
failed:function(){
    for (var i = 0; i < this.rowCount; i++) {
        for (var j = 0; j < this.colCount; j++) {
            if (this.arrs[i][j] == 9)
                this.$("mine" + i + "_" + j).innerHTML="雷"
            else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
        }
		clearInterval(this.jsq)
}
},
success:function(){
    alert("win")
    for (var i = 0; i < this.rowCount; i++) {
        for (var j = 0; j < this.colCount; j++) {
            if (this.arrs[i][j] == 9)
                this.$("mine" + i + "_" + j).innerHTML="雷"
            else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
        }
		clearInterval(this.jsq)
}
}

}
	</script>

	<title>Document</title>
</head>
<body>
	<div id="main">
 	<table id="landmine"></table>
 	<div id="operation">
 		<div class="tip">
 			剩余雷数:<span id="landMineCount">0</span></div>
 		<div class="tip">
 			持续时间: <span id="costTime">0</span></div>
 		<fieldset>
 			<legend>难度选择:</legend>
 			<input type="radio" name="level" id="llevel" value="10" /><label for="llevel">初级(10*10)</label><br />
 			<input type="radio" name="level" id="mlevel" value="15" /><label for="mlevel">中级(15*15)</label><br />
 			<input type="radio" name="level" id="hlevel" value="20" /><label for="hlevel">高级(20*20)</label><br />
 		</fieldset>
 		<input type="button" id="begin" value="开始游戏" /><br />
 		<script>
var lei=null;
var radios=document.getElementsByName("level");
for(var i=0;i<3;i++){
radios[i].onclick=function(){
var value=this.value;
document.getElementById("begin").onclick=function(){
start(value,value,value*value/5,value*value/5 -value)
}
}
}
function start(rowCount, colCount, minLandMineCount, maxLandMineCount){
//这里还需要一些别的,以后再加
if(lei!=null){
var jg=confirm("开始新游戏?")
if(jg){
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
else{return;}
}
else{
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw();

}
lei.DrawMine()
lei.landMine()
lei.everyCount()
lei.bingMine()
}
 		</script>
</body>
</html>

以及修改CSS等等后的版本:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<style>
td{background-color:rgb(191,191,191);border-left:3px white solid;border-top:3px white solid;border-right:3px rgb(126,126,124) solid;border-bottom:3px rgb(126,126,124) solid;width:20px;height: 20px;text-align: center;line-height: 20px;}
.clear{background-color: rgb(193,193,193);border:2px #585858 solid}
.flag{background:url(https://pic.imgdb.cn/item/61b72ab72ab3f51d9163640f.png);background-color: rgb(193,193,193);border:2px #585858 solid}
	</style>	
	<script>
var Mine=function(
		rowCount,
        colCount,
        minLandMineCount,
        maxLandMineCount
        ){
        
	this.table=document.getElementById("landmine");//获取那个表格
    this.cells = document.getElementsByTagName('td'); //获取所有小格子
    this.rowCount = rowCount || 10; //格子行数
    this.colCount = colCount || 10; //格子列数
    this.markLandMineCount = 0; //标记的地雷个数
    this.minLandMineCount = minLandMineCount || 10; //地雷最少个数
    this.maxLandMineCount = maxLandMineCount || 20; //地雷最多个数
    this.landMineCount = this.SJCount(this.minLandMineCount, this.maxLandMineCount); //地雷个数
    this.arrs = []; //后面会用,地雷地图用的
    this.beginTime = null; //游戏开始时间
    this.endTime = null; //游戏结束时间
    this.currentSetpCount = 0; //标记雷次数
    this.trueCount = 0; //标记正确的雷的次数
    this.endCallBack = null; //踩到雷的方法
    this.landMineCallBack = null; //标记为地雷时的方法
	this.ts=0;
	this.jsq=setInterval(()=>{
	this.beginTime=Date.parse(new Date());
		this.ts++;
		document.querySelector("#costTime").innerHTML=this.ts;
		},1000);
	document.querySelector("#landMineCount").innerHTML=this.landMineCount;
    document.oncontextmenu = function () {
      //禁用右键菜单
      return false;
    };
}


Mine.prototype={
draw:function(){
var gz="";
for(var i=0;i<this.rowCount;i++){
gz+="<tr>";
for(var j=0;j<this.colCount;j++){
gz+=`<td id="mine${i+"_"+j}"></td>`;
}
gz+="</tr>";
}
document.getElementById("landmine").innerHTML=gz;
},
DrawMine:function(){
for(var i=0;i<this.rowCount;i++){
	this.arrs[i] = [];
	//arr再前面Mine的构造里已经声明过了
	for(var j=0;j<this.colCount;j++){
		this.arrs[i][j] = 0;
}
}
},
SJCount: function (FirstValue, LastValue) {
    var Choices = LastValue - FirstValue + 1;//确保最大值能取到
    return Math.floor(Math.random() * Choices + FirstValue);
},
landMine:function(){
var all=this.rowCount*this.colCount;
var MineList=[];
for(var i = 0; i < this.landMineCount; i++){
var randomNum = this.SJCount(0, all-1);
var row= parseInt(randomNum / this.colCount),col= randomNum % this.colCount;
//从0到格子总数-1随机出来一个数作为雷,然后算出来这个数对应的行和列分别是多少
if (randomNum in MineList) {
            i--;
            continue;
        }
//如果随机出来的数已经在列表里面了,那么往回退,重新生成
        this.arrs[row][col] = 9;
        MineList[randomNum] = randomNum;
    }
},
everyCount: function () {
    for (var i = 0; i < this.rowCount; i++) {
        for (var j = 0; j < this.colCount; j++) {
            if (this.arrs[i][j] == 9)
                continue;
            if (i > 0 && j > 0) {
                if (this.arrs[i - 1][j - 1] == 9)
                    this.arrs[i][j]++;
            }
            if (i > 0) {
                if (this.arrs[i - 1][j] == 9)
                    this.arrs[i][j]++;
            }
            if (i > 0 && j < this.colCount - 1) {
                if (this.arrs[i - 1][j + 1] == 9)
                    this.arrs[i][j]++;
            }
            if (j > 0) {
                if (this.arrs[i][j - 1] == 9)
                    this.arrs[i][j]++;
            }
            if (j < this.colCount - 1) {
                if (this.arrs[i][j + 1] == 9)
                    this.arrs[i][j]++;
            }
            if (i < this.rowCount - 1 && j > 0) {
                if (this.arrs[i + 1][j - 1] == 9)
                    this.arrs[i][j]++;
            }
            if (i < this.rowCount - 1) {
                if (this.arrs[i + 1][j] == 9)
                    this.arrs[i][j]++;
            }
            if (i < this.rowCount - 1 && j < this.colCount - 1) {
                if (this.arrs[i + 1][j + 1] == 9)
                    this.arrs[i][j]++;
            }
        }
    }
},
$: function (id) {
    return document.getElementById(id);
},//用于获取元素
bingMine:function(){
	var self=this;
 for (let i = 0; i < this.rowCount; i++) {
        for (let j = 0; j < this.colCount; j++) {
        self.$("mine" + i + "_" + j).onmousedown = function (e) {
                    e = e || window.event;
                    var mouseNum = e.button;
                    var className = this.className;
                    if (mouseNum == 2) {
                        if (className == "flag") {
                            this.className = "";
                            self.markLandMineCount--;

                        } else {
							if(self.landMineCount-self.markLandMineCount<=0)return;
                            this.className = "flag";
                            self.markLandMineCount++;
                            if(self.arrs[i][j]==9){
                                self.trueCount++;
                            }
							
                        }
						document.querySelector("#landMineCount").innerHTML=self.landMineCount-self.markLandMineCount
                        if (self.landMineCallBack) {
                            self.landMineCallBack(self.landMineCount - self.markLandMineCount);
                        }
                        if (self.trueCount == self.landMineCount ) {
                                self.success();
                         }
                    } else if (mouseNum == 1||className != "flag") {
                        self.open.call(self,this, i, j);

                    }
                };
}
}
},
showNoLandMine: function (x, y) {
    for (var i = x - 1; i < x + 2; i++)
        for (var j = y - 1; j < y + 2; j++) {
            if (!(i == x && j == y)) {
                var ele = this.$("mine" + i + "_" + j);
                if (ele && ele.className == "") {
                    this.open.call(this, ele, i, j);
                }
            }
        }
},
open: function (obj, x, y) {
    if (this.arrs[x][y] != 9) {
        this.currentSetpCount++;
        obj.innerHTML = this.arrs[x][y]==0?"":this.arrs[x][y];
        obj.className = "clear";
        // alert( this.trueCount)
        obj.onmousedown = null;
        if (this.arrs[x][y] == 0) {
            this.showNoLandMine.call(this, x, y);
        }
    } else {
        this.failed();
    }
},
failed:function(){
    for (var i = 0; i < this.rowCount; i++) {
        for (var j = 0; j < this.colCount; j++) {
            if (this.arrs[i][j] == 9)
                this.$("mine" + i + "_" + j).innerHTML="<img src='https://pic.imgdb.cn/item/61b72a2d2ab3f51d91632728.png' width=20 height=20>"
            else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
        }
		clearInterval(this.jsq)
}
},
success:function(){
    alert("win")
    for (var i = 0; i < this.rowCount; i++) {
        for (var j = 0; j < this.colCount; j++) {
            if (this.arrs[i][j] == 9)
                this.$("mine" + i + "_" + j).innerHTML="<img src='https://pic.imgdb.cn/item/61b72a2d2ab3f51d91632728.png' width=20 height=20>"
            else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
        }
		clearInterval(this.jsq)
}
}

}
	</script>

	<title>Document</title>
</head>
<body>
	
	<fieldset style="width: 45%;float:left">
 			<legend>难度选择:</legend>
 			<input type="radio" name="level" id="llevel" value="10" /><label for="llevel">初级(10*10)</label><br />
 			<input type="radio" name="level" id="mlevel" value="15" /><label for="mlevel">中级(15*15)</label><br />
 			<input type="radio" name="level" id="hlevel" value="20" /><label for="hlevel">高级(20*20)</label><br />
			<input type="button" id="begin" value="开始游戏" /><br />
			
 		</fieldset>

	<div id="main">
 	<fieldset style="width: 45%;float:right">
		<legend>游戏数据:</legend>
		<br>
 	<div id="operation">
 		<div class="tip">
 			剩余雷数:<span id="landMineCount">0</span></div>
 		<div class="tip">
 			持续时间: <span id="costTime">0</span></div><br>
		</fieldset>
		<div style="clear: both;"></div>
		<table id="landmine" cellpadding="0" cellspacing="0"></table>
 		<script>
var lei=null;
var radios=document.getElementsByName("level");
for(var i=0;i<3;i++){
radios[i].onclick=function(){
var value=this.value;
document.getElementById("begin").onclick=function(){
start(value,value,value*value/5,value*value/5 -value)
}
}
}
function start(rowCount, colCount, minLandMineCount, maxLandMineCount){
//这里还需要一些别的,以后再加
if(lei!=null){
var jg=confirm("开始新游戏?")
if(jg){
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
else{return;}
}
else{
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw();

}
lei.DrawMine()
lei.landMine()
lei.everyCount()
lei.bingMine()
}
 		</script>
</body>
</html>

在这里插入图片描述
这是最后美化一下下的样子

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>