小清新版js扫雷(使用原生js)

扫雷小游戏

预览

在这里插入图片描述

游戏讲解

扫雷是一个益智游戏,通过格盘上的数字提醒我们当前格子周围有几个雷,踩到雷时游戏结束。利用这个游戏规则,可以找到每个雷,遍历其周围八个格子。
以一个九宫格为单元,将雷周围的八个格子(若不是雷)的数字都+1操作
在这里插入图片描述
若周围格子出现雷,则不对其+1,不改变其雷的属性:
在这里插入图片描述
于是可以继续得到:
在这里插入图片描述
以此类推,继续遍历,直到普及整个格盘(这里以四个雷为例):
在这里插入图片描述
从上面格盘可以清楚的知道,每个数字的周围(以一个九宫格为单元)都有其本身数字大小的雷数,如:1周围有1个雷,3周围有三个雷。

界面

考虑到简单,一般,困难三个扫雷区域的格子数都不同,所以界面上的扫雷区域是用js动态生成。
先搭好整体html框架:

	<div class="container">
		<div class="level">
			<button class="select">简单</button>
			<button>一般</button>
			<button>困难</button>
			<button>重新开始</button>
		</div>
		<div class="mine">
			//扫雷区域
		</div>
		<div class="last">总共雷数 : <span class="mineNum"></span></div>
	</div>

首先用面向对象的思想,写一个MineSweeper()构造方法:

function MineSweeper(tr, td, mineNum) {
	this.tr = tr; //行
	this.td = td; //列
	this.mineNum = mineNum; //雷数
	this.area = [];  //存取每个格子信息
	this.doms = [];  //存储格子DOM,用来动态创建DOM
	this.lastMineNum = mineNum; //剩余雷数

	this.parent = document.querySelector('.mine');
	this.num = document.querySelector('.last .mineNum');
}

扫雷区域用一个二维数组表示并存储:

/*
* 扫雷游戏区域area(二维数组)
* [
*      [type: mine/number, x1, y1],
*      [type: mine/number, x2, y2],
*       ... ,
*      [type: mine/number, xn, yn]
* ]
*/

其中type有两个值,mine表示当前格子是一个雷;number表示当前格子是一个数字。
在构造方法的原型上添加方法创建DOM表格,用来动态生成扫雷区域界面:

MineSweeper.prototype.create = function() {
	var _this = this;
	var table = document.createElement('table');
	for(var i = 0; i < this.tr; i++) {
		var trDom = document.createElement('tr');
		//创建行
		this.doms[i] = [];
		for(var j = 0; j < this.td; j++) {
			//创建列
			var tdDom = document.createElement('td');
			this.doms[i][j] = tdDom;
			trDom.appendChild(tdDom); //往行中添加列
		}
		table.appendChild(trDom);//往table中添加行
	}
	this.parent.appendChild(table); //将table添加到界面
};

逻辑设计

寻找目标格子周围格子

在构造函数的原型上添加一个方法,用来寻找目标格子周围的格子。并标记是数字or雷or四个角?以便后续是否对指定格子做+1操作。如果是雷则不加,如果在四个角则看情况+1操作。
找目标格子(target)周围格子的思路:

/*
* around...
*  [x-1, y-1]   [x, y-1]   [x+1, y-1]
* 
*  [x-1, y]      target    [x+1, y]
* 
*  [x-1, y+1]   [x, y+1]   [x+1, y+1]
* 用双层for循环
MineSweeper.prototype.mineAround = function(target) {
	var x = target.x;
	var y = target.y;
	var result = []; //二位数组,存储周围格子的坐标
	for(var i = x-1; i <= x+1; i++) {
		for(var j = y-1; j <= y+1; j++) {
			/*排除四个角and周围是雷and本身的情况,
			以便后续对周围格子数值进行重叠的number++操作*/
			if(
                  i < 0 || j < 0 || i > this.td - 1 || j > this.tr - 1 ||  //排除四个角
                  (i == x && j == y) ||                            //排除周围是雷
                  this.area[j][i].type === 'mine'               //排除本身, [j][i]? --> 行列,非循环表示的坐标
			  ){
				continue;
			}
			result.push([j, i]);
		}
	}
	return result;  //[j][i] --> 表行列,而上面的i,j表示的是坐标x,y;这里x表示列,y表示行
};
更新数字

遍历扫雷区域数组,当遇到雷时,取到其周围的格子,如果是数字,则number都+1;如果是雷,则该格子不作操作。

MineSweeper.prototype.updateNumber = function() {
	for(var i = 0; i < this.tr; i++) {
		for(var j = 0; j < this.td; j++) {
			if(this.area[i][j].type == 'number') {
				continue;  //遇到数字格子跳过,不需要取其周围的格子
			}
			var nums = this.mineAround(this.area[i][j]);  //获取雷周围的格子
			for(var k = 0; k < nums.length; k++) {
				//雷周围的格子的number都要+1
				this.area[nums[k][0]][nums[k][1]].value += 1;
			}
		}
	}
};
游戏结束

踩到雷时,为当前点击的雷添加一个样式(这里为其添加一个红色背景),显示所有雷,并且玩家无法继续点击扫雷区域。

MineSweeper.prototype.gameOver = function(downMine) {
	for(var i = 0; i < this.tr; i++) {
		for(var j = 0; j < this.td; j++) {
			if(this.area[i][j].type === 'mine') {
				this.doms[i][j].className = 'mine';
			}
			this.doms[i][j].onmousedown = null;
		}
	}
	if(downMine) {
		downMine.style.backgroundColor = '#f40';
	}
}
开始游戏

玩家点击上面"简单",“一般”,"困难"按钮切换游戏模式,点击"重新开始"则刷新当前模式重新玩游戏。

function startGame() {
	var btn = document.querySelectorAll('.container .level>button');
	var arr = [[10,10,15],[15,15,40],[20,20,80]];
	var select = 0; //当前选中状态的按钮
	var mine = null;
	for(let i = 0; i < btn.length - 1; i++) {
		console.log(i);
		console.log(arr);
		btn[i].onclick = function(e) {
			btn[select].className = '';
			this.className = 'select';
			select = i;
			mine = new MineSweeper(...arr[i]);
			console.log(arr[i]);
			mine.init();
		}
	}
	btn[0].onclick();
	btn[3].onclick = function() {
		mine.init();
	}

}

总结

以上仅讲述了核心模块。思路仅供参考。

完整代码

HTML
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>MineSweeper</title>
	<link rel="stylesheet" href="./css/index.css">
</head>
<body>
	<div class="container">
		<div class="level">
			<button class="select">简单</button>
			<button>一般</button>
			<button>困难</button>
			<button>重新开始</button>
		</div>
		<div class="mine">
			
		</div>
		<div class="last">总共雷数 : <span class="mineNum"></span></div>
	</div>
<script src="./js/index.js"></script>
</body>
</html>
css
.container {
	margin: 30px auto;
	text-align: center;
}
.container .level button {
	background-color: #e5c1cd;
	outline-style: none;
	border: none;
	cursor: pointer;
	color: #fff;
}
.container .level button.select {
	background-color: #b6b4c2;
}
.container .mine table {
	border-spacing: 1px;
	margin: 10px auto;
}
.container .mine table td {
	width: 18px;
	height: 18px;
	padding: 0;
	background-color: #bfb8da;
	border: 2px solid;
	border-color: #ebd7d4 #a56781 #a56781 #ebd7d4;
	text-align: center;
	line-height: 16px;
	font-weight: bold;
}
.container .mine table td.select,
.container .mine table td.mine {
	background-color: #bfb8da;
	border: 1px solid #ebd7d4;
}
.container .mine table td.mine {
	background-image: url("../image/mine.png");
	background-repeat: no-repeat;
	background-size: cover;
}
.container .mine table td.flag {
	background-image: url("../image/flag.png");
	background-repeat: no-repeat;
	background-size: cover;
}
.container .last {
	color: #d87f81;
}

js
function MineSweeper(tr, td, mineNum) {
	this.tr = tr; //行
	this.td = td; //列
	this.mineNum = mineNum; 
	this.area = []; 
	this.doms = []; 
	this.lastMineNum = mineNum; 
	this.parent = document.querySelector('.mine');
	this.num = document.querySelector('.last .mineNum');
}
//初始化
MineSweeper.prototype.init = function() {
	var rn = this.randomNum(); //获得type: mine 的索引
	var n = 0; //记录格子索引
	for(var i = 0; i < this.tr; i++) {
		this.area[i] = [];
		for(var j = 0; j < this.td; j++) {
			n ++;
			if(rn.indexOf(n) !== -1) {
				this.area[i][j] = {
					type: 'mine',
					x: j,
					y: i
				};
			} else {
				this.area[i][j] = {
					type: 'number',
					x: j,
					y: i,
					value: 0
				};
			}
		}
	}
	// console.log(this.area);
	this.num.innerHTML = this.mineNum; //初始化雷数
    this.parent.oncontextmenu = function() {
    	return false; //阻止右击菜单事件
    }
    this.updateNumber();
	//创建表格
	this.parent.innerHTML = "";
	this.create();
}

//创建DOM表格
MineSweeper.prototype.create = function() {
	var _this = this;
	var table = document.createElement('table');
	for(var i = 0; i < this.tr; i++) {
		var trDom = document.createElement('tr');
		this.doms[i] = [];
		for(var j = 0; j < this.td; j++) {
			var tdDom = document.createElement('td');
			this.doms[i][j] = tdDom;
			trDom.appendChild(tdDom);

            tdDom.pos = [i, j];
			tdDom.onmousedown = function(event) {
				if(event.button === 0) {  //鼠标左键
					var curArea = _this.area[this.pos[0]][this.pos[1]];
					console.log(curArea)
					if(curArea.type === 'mine') {
						// console.log('踩到雷了!')
						this.className = 'mine';
						_this.gameOver(this);
						
					} else {
						// console.log('is number')
						if(!curArea.value) {  //踩到0,出现一大片
							// console.log(0);
							this.className = 'select'; //先显示自己
							this.innerHTML = '';
							
							function getAllZero(area) {
								var around = _this.mineAround(area); //找其周围的格子
								for(var i = 0; i < around.length; i++) {
									var x = around[i][0]; //行
									var y = around[i][1]; //列
									_this.doms[x][y].className = 'select';
									if(!_this.area[x][y].value) {
										if(!_this.doms[x][y].isHas) {  
											_this.doms[x][y].isHas = true; //标记被找过的元素,避免格子重复重复被调用,导致内存资源被滥用
											arguments.callee(_this.area[x][y])
										}
									} else {
										_this.doms[x][y].innerHTML = _this.area[x][y].value;
									}
								}
							}
							getAllZero(curArea);

						} else {
							this.className = 'select';
							this.innerHTML = curArea.value;
						}
						
					}
				
				} else if(event.button === 2) {  //鼠标右键
					this.className = this.className == 'flag'? '':'flag';
					//标记小旗子,则剩余雷数-1
					if(this.className === 'flag') {
						_this.num.innerHTML = --_this.lastMineNum;
					} else {
						_this.num.innerHTML = ++_this.lastMineNum;
					}
				}
			}
		}
		table.appendChild(trDom);
	}
	this.parent.appendChild(table);
};

//生成指定数量的不重复的数字
MineSweeper.prototype.randomNum = function() {
	var mineArr = new Array(this.tr*this.td); //该数组用来存储所有格子下标
	for(var i = 0; i < mineArr.length; i++) {
		mineArr[i] = i;
	}

	mineArr.sort(function() {return 0.5 - Math.random()}); //将数组乱序排序
	return mineArr.slice(0, this.mineNum); //随机取得放置雷的下标
};

//找目标格子周围的格子, 雷周围的格子都需要number++
MineSweeper.prototype.mineAround = function(target) {
	var x = target.x;
	var y = target.y;
	var result = []; //二位数组,存储周围格子的坐标
	for(var i = x-1; i <= x+1; i++) {
		for(var j = y-1; j <= y+1; j++) {
			if(
                  i < 0 || j < 0 || i > this.td - 1 || j > this.tr - 1 ||  //排除四个角
                  (i == x && j == y) ||                            //排除周围是雷
                  this.area[j][i].type === 'mine'    
			  ){
				continue;
			}
			result.push([j, i]);
		}
	}
	return result; 
};

//更新所有数字
MineSweeper.prototype.updateNumber = function() {
	for(var i = 0; i < this.tr; i++) {
		for(var j = 0; j < this.td; j++) {
			if(this.area[i][j].type == 'number') {
				continue;
			}
			var nums = this.mineAround(this.area[i][j]);  //获取雷周围的格子
			for(var k = 0; k < nums.length; k++) {
				//雷周围的格子的number都要+1
				this.area[nums[k][0]][nums[k][1]].value += 1;
			}
		}
	}
};

//gameOver
MineSweeper.prototype.gameOver = function(downMine) {
	for(var i = 0; i < this.tr; i++) {
		for(var j = 0; j < this.td; j++) {
			if(this.area[i][j].type === 'mine') {
				this.doms[i][j].className = 'mine';
			}
			this.doms[i][j].onmousedown = null;
		}
	}
	if(downMine) {
		downMine.style.backgroundColor = '#f40';
	}

}
function startGame() {
	var btn = document.querySelectorAll('.container .level>button');
	var arr = [[10,10,15],[15,15,40],[20,20,80]];
	var select = 0; //当前选中状态的按钮
	var mine = null;
	for(let i = 0; i < btn.length - 1; i++) {
		console.log(i);
		console.log(arr);
		btn[i].onclick = function(e) {
			btn[select].className = '';
			this.className = 'select';
			select = i;
			mine = new MineSweeper(...arr[i]);
			console.log(arr[i]);
			mine.init();
		}

	}
	btn[0].onclick();
	btn[3].onclick = function() {
		mine.init();
	}
}
startGame();
  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值