首先,让我们来看看效果图~
用户手册:
上方有两个按钮Start 和 Restart 分别用来开始游戏和刷新页面重新开始,按钮的右侧是一个下拉框用于选择难度模式,分别有Easy 、Medium 、Hard 三种模式,分别对应了6*6、10*10、15*15的区域大小,其中分别包含6、10、15个雷~
上方左侧有两个标签span 标签 Mine 和 Time 分别用来表示剩余的雷数 和 已用时间 ~
实现过程:
首先,该程序由几个模块组成,各功能如图所示
其中的customize_Alert的js和css是我在网上找的一个蛮好看的alert美化代码,直接在html主体Sweep.html中引入即可,然后将所有的alert换成swal即可!
这里给大家放上这个alert美化的链接 https://blog.csdn.net/windy1001/article/details/82685977
首先让我们看看html主体Sweep.html
其中的<head></head>部分就引入了我们前面所述的几个模块
在<body></body>部分定义了两个span标签用来计时和计雷数,一个button用来开始游戏绑定了start()函数,另一个按钮用来重新开始游戏绑定了restart()函数,然后还有一个下拉框用来选择三种难度
在<script></script>部分首先定义了几个常量,然后是start()函数的定义,其中是一个switch语句,读取下拉框选中的值,对应不同的难度,进行不同的初始化。最后就是restart()函数,用来刷新页面。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>扫雷游戏</title>
<link rel="stylesheet" type="text/css" href="ground.css">
<link rel="stylesheet" type="text/css" href="customize_Alert.css">
<script type="text/javascript" src="initial_Ground.js"></script>
<script type="text/javascript" src="block_Click.js"></script>
<script type="text/javascript" src="block_Open.js"></script>
<script type="text/javascript" src="customize_Alert.js"></script>
</head>
<!-- ondragstart:防拖拽生成新页面 oncontextmenu:屏蔽右键菜单-->
<body ondragstart='return false' oncontextmenu='self.event.returnValue=false' id="body">
<p id='p'>
<span class='span'>Mine: <label id='count'>0</label></span>
<span class='span'>Time: <label id='time'>0</label>s</span>
<button onclick="start()">Start</button>
<input type="button" value="Restart" align="center" onclick="restart()">
<select id="s">
<option>--Please choose a difficulty degree--</option>
<option>Easy</option>
<option>Medium</option>
<option>Hard</option>
</select>
</p>
<table id='ground'></table>
<script>
var row; //数组行数
var col; //数组列数
var maxCount; //最大地雷数量
var isFirstOpen = true; //第一次打开方格
var count; //剩余雷数
var ground;//初始化,得到二维数组
var time; //计时
var timer; //定时器 100ms执行一次
function start() {
swal(document.getElementById("s").value + " level")
switch (document.getElementById("s").value) {
case "Easy": {
row = 6; //数组行数
col = 6; //数组列数
maxCount = 6; //最大地雷数量
isFirstOpen = true;//第一次打开方格
count = document.getElementById('count'); //剩余雷数
ground = initial_ground(); //初始化,得到二维数组
time = document.getElementById('time'); //计时
timer = setInterval(function () {
let seconds = (parseFloat(time.innerHTML) + 0.1).toFixed(1); //保留一位小数
time.innerHTML = seconds;
}, 100) //定时器 100ms执行一次
break;
}
case "Medium": {
row = 10; //数组行数
col = 10; //数组列数
maxCount = 10; //最大地雷数量
isFirstOpen = true;//第一次打开方格
count = document.getElementById('count'); //剩余雷数
ground = initial_ground(); //初始化,得到二维数组
time = document.getElementById('time'); //计时
timer = setInterval(function () {
let seconds = (parseFloat(time.innerHTML) + 0.1).toFixed(1); //保留一位小数
time.innerHTML = seconds;
}, 100) //定时器 100ms执行一次
break;
}
case "Hard": {
row = 15; //数组行数
col = 15; //数组列数
maxCount = 15; //最大地雷数量
isFirstOpen = true;//第一次打开方格
count = document.getElementById('count'); //剩余雷数
ground = initial_ground(); //初始化,得到二维数组
time = document.getElementById('time'); //计时
timer = setInterval(function () {
let seconds = (parseFloat(time.innerHTML) + 0.1).toFixed(1); //保留一位小数
time.innerHTML = seconds;
}, 100) //定时器 100ms执行一次
break;
}
}
}
function restart() {
window.location.reload();
}
</script>
</body>
</html>
接着让我们来看看最重要的三个函数:
1. initial_Ground.js
这个函数用于初始化,将场地转换为一个二维数组ground返回
/**
* 初始化矩阵
*/
function initial_ground() {
let groundHtml = '';
for (let i = 0; i < row; i++) {
groundHtml += '<tr>'//行标签
for (let j = 0; j < col; j++) {
//列标签,每个列标签中包含一个span标签,表示方格
groundHtml +=
'<td><span class="blocks" onm ousedown="block_click(' + i + ',' + j + ',event)"></span></td>';
}
groundHtml += '<tr>'
}
document.getElementById('ground').innerHTML = groundHtml;//写入body中定义的table标签中
let span_Blocks = document.getElementsByClassName('blocks');//得到span标签的一维数组
//将一维数组转换为二维数组
let ground = new Array();
for (let i = 0; i < span_Blocks.length; i++) {
if (i % col === 0) //每一行创建一个一维数组
ground.push(new Array());
span_Blocks[i].count = 0;//给每个方格定义一个count属性,记录四周九宫格内雷的数量,均初始化为0
ground[parseInt(i / col)].push(span_Blocks[i]);//向二维数组添加span标签方格,每个方格包含count属性
}
return ground;//返回二维数组
}
2. block_Click.js
这个函数用于处理点击方格事件,函数的形参分别代表方格对应二维数组的横坐标和纵坐标以及鼠标事件
其中分为三种情况:
- 如果方格已经打开,则直接退出
- 点击了鼠标左键,且方格没有打开,判断是不是第一次打开,如果是的话,则随机生成对应的雷数,并用接下来要讲的的block_open打开方格
- 点击了鼠标右键,如果该方格不是红旗,则将该方格内容置为红旗,如果是红旗,则将该方格内容置为空
- 最后遍历整个二维数组,根据对应的红旗数减去对应的剩余雷数,并且判断游戏是否结束,如果结束了则停止计时
/**
* 方格点击事件
* @param _i 横坐标
* @param j 纵坐标
* @param e 鼠标事件
*/
function block_click(i, j, e) {
//给每个方格定义一个isOpen属性,用来判断该方格是否打开
if (ground[i][j].isOpen) {//该方格以打开
return;//退出
}
//鼠标左键,用于打开方格
if (e.button === 0) {
//判断是否第一次打开
if (isFirstOpen) {
isFirstOpen = false;//如果是第一次打开则将该标志置false
let count = 0; //初始化当前地雷数为0
//生成maxCount个地雷
while (count < maxCount) {
//生成两个0~9的随机坐标
let x = Math.floor(Math.random() * row);
let y = Math.floor(Math.random() * col);
// 给每个方格定义了isMine属性,用来标志该方格是否为雷
// 当雷的坐标不等于第一次点击方格的坐标且该方格不是雷时,则将该方格置为雷
if (!(x === i && y === j) && !ground[x][y].isMine) {
ground[x][y].isMine = true; //自定义属性isMine代表方格为地雷
count++; //当前地雷数+1
//更新九宫格内非雷方格的计雷数
for (let i = x - 1; i < x + 2; i++) {
for (let j = y - 1; j < y + 2; j++) {
//判断坐标防越界
if (i > -1 && j > -1 && i < row && j < col) {
//该方格四周九宫格内计雷数+1
ground[i][j].count++;
}
}
}
}
}
}
//执行打开方格函数
block_Open(i, j);
}
//鼠标右键,用于标记方格
else if (e.button === 2) {
let block = ground[i][j];
if (block.innerHTML !== "