原⽣JS实现扫雷(分析+代码实现)阅读这篇⽂章需要掌握的基础知识:Html5、CSS、JavaScript
在线Demo:
扫雷规则
在写扫雷之前,我们先了解下它的游戏规则
●扫雷是⼀个矩阵,地雷随机分布在⽅格上。
●⽅格上的数字代表着这个⽅格所在的九宫格内有多少个地雷。
●⽅格上的旗帜为玩家所作标记。
●踩到地雷,游戏失败。
●打开所有⾮雷⽅格,游戏胜利。
功能实现思路分析
矩阵的⽣成
矩阵的⽣成有多种⽅式可以实现,我们这⾥使⽤<table>+<span>标签。
通过 js 给定⾏数与列数在<table>的 innerHtml 写⼊<span>标签来动态⽣成矩阵。
⽅格的打开与标记
通过 onmousedown 事件,传⼊点击的⽅格的坐标及event,判断event为左键还是右键。
左键打开⽅格,右键标记⽅格。
地雷的随机分布
由于第⼀次打开的⽅格不能为地雷所以我们把⽣成地雷的函数放在第⼀次点击⽅格时。
我们通过循环⽤ Math.random() 函数来随机⽣成地雷的⼆维坐标。
判断坐标是否不为第⼀次点击⽅格的坐标以及没有雷存在。
是则将⽅格设置为地雷,当前地雷数+1,并且将九宫格内的⽅格的计雷数+1。
否则跳过进⼊下个循环,直到地雷的数量达到设定的最⼤雷数,结束循环。
踩到地雷游戏结束
打开⽅格为地雷时,提⽰游戏结束。
通过遍历矩阵来打开所有地雷
连锁打开⽅格
当打开的⽅格为计雷数为0的⽅格,⾃动打开九宫格内的⾮雷⽅格。
如果打开的⾮雷⽅格九宫格内仍有⾮雷⽅格,继续打开九宫格内的⾮雷⽅格,直到没有为⽌。
游戏胜利条件
当所有⾮雷⽅格被打开即为游戏胜利。
在每次打开⽅格函数中都遍历⼀遍矩阵,当到有未打开的⾮雷⽅格时则结束遍历。
当遍历完未到未打开的⾮雷⽅格则提⽰游戏胜利。
剩余地雷数与计时器
地雷的总数减去玩家标记的⽅格数即为剩余地雷数
计时器可以⽤setInterval()函数实现
代码实现
⽣成矩阵
我们先在<body>⾥写⼀个<table>标签,设定个 id='grid'
<table id='grid'></table>
然后在<script>⾥定义两个变量 row--⾏数 col--列数
通过两个for循环把 (⽅格)<span> 写⼊到 (矩阵)<table> ⾥,通过<td><tr>标签控制⾏列。
var row = 10; //⾏数
var col = 10; //列数
//⽣成矩阵html <tr>--⾏标签 <td>--列标签
let gridHtml = '';
for (let i = 0; i < row; i++) {
gridHtml += '<tr>'
for (let j = 0; j < col; j++) {
gridHtml += '<td><span class="blocks"></span></td>';
}
gridHtml += '<tr>'
}
//写⼊html
写⼀下矩阵和⽅格的CSS样式。
#grid {
margin: auto; /* 让矩阵居中显⽰于页⾯ */
}
.blocks {
width: 30px;
height: 30px;
line-height: 30px;
display: block; /* 让span以block⽅式显⽰ */
text-align: center;
border: solid 1px #000;
user-select: none; /* 设置不可拖拽选中 */
cursor: pointer; /* 设置⿏标停留样式 */
}
.blocks:hover {
background: #0af; /* ⿏标停留时背景颜⾊变化 */
}
⾄此打开页⾯,矩阵就初步显⽰出来了。
把矩阵的⽅格放⼊⼆维数组中
我们先定义⼀个全局变量grid。
把刚才写的⽣成矩阵的代码写成⼀个函数 function init_grid()
然后把返回值赋值给grid --- grid = init_grid();
var row = 10; //⾏数
var col = 10; //列数
var grid = init_grid();
//初始化矩阵 (row-⾏数 col-列数)
function init_grid() {
//⽣成矩阵html <tr>--⾏标签 <td>--列标签
let gridHtml = '';
for (let i = 0; i < row; i++) {
gridHtml += '<tr>'
for (let j = 0; j < col; j++) {
gridHtml +=
'<td><span class="blocks"></span></td>';
}
gridHtml += '<tr>'
}
//写⼊html
//返回矩阵⼆维数组
let blocks = ElementsByClassName('blocks');
let grid = new Array();
for (let i = 0; i < blocks.length; i++) {
if (i % col === 0) {
grid.push(new Array());
}
//初始化计雷数
blocks[i].count = 0;
grid[parseInt(i / col)].push(blocks[i]);
}
return grid;
}
写完了这段我们先写⼀段代码测试下grid有没有赋值成功,遍历grid把⽅格的值改为对应的坐标。
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
grid[i][j].innerHTML = i + ',' + j;
}
}
可以看到 grid 已经赋值成功!没成功的回去检查下代码。(Tip:测试完记得把测试代码删除)
⽅格的点击事件
定义⼀个函数 function block_click( _i, _j, e) 的⼤致框架
e为传⼊的⿏标事件,e.button ( 0为左键,2为右键 )。
isOpen属性为⾃定义属性,⽤来判断⽅格是否打开。
//⽅格点击事件 _i:坐标i _j:坐标j e:⿏标事件
function block_click(_i, _j, e) {
//跳过已打开的⽅格
if (grid[_i][_j].isOpen) {
return;
}
//⿏标左键打开⽅格
if (e.button === 0) {
}
//⿏标右键标记⽅格
else if (e.button === 2) {
}
}
然后修改下之前写在 init_grid 函数⾥的<span>的属性,绑定 onmousedown 事件,传⼊ i,j 坐标,和
⿏标事件 event gridHtml += '<td><span class="blocks" onmousedown="block_click(' + i + ',' + j + ',event)"></span></td>';
修改下body的属性加⼊防拖拽⽣成新页⾯和屏蔽右键菜单。
<!-- ondragstart:防拖拽⽣成新页⾯ oncontextmenu:屏蔽右键菜单-->
<body ondragstart='return false' oncontextmenu='urnValue=false'>
我们在⿏标左键事件⾥⾯写下测试代码,当左键⽅格时显⽰它的坐标。
//⿏标左键打开⽅格
if (e.button === 0) {
grid[_i][_j].innerHTML = _i + ',' + _j;
}
效果如下,没成功的回去检查下代码。(Tip:测试完记得把测试代码删除)
⽅格的标记
在⿏标右键事件写标记代码,这⾥⽤▲来作为标记。
右击⼀次添加标记,再次右击删除标记。
//⿏标右键标记⽅格
else if (e.button === 2) {
let block = grid[_i][_j];
if (block.innerHTML !== '▲') {
block.innerHTML = '▲';
} else {
block.innerHTML = '';
}
}
效果如下:
随机⽣成地雷
由于第⼀次打开的⽅格不能为地雷所以我们把⽣成地雷的函数放在第⼀次点击⽅格时。
先定义全局变量 maxCount --- 最⼤地雷数 isFirstOpen --- 是否第⼀次打开⽅格。
var row = 10; //⾏数
var col = 10; //列数
var grid = init_grid();
var maxCount = 10; //最⼤地雷数量
var isFirstOpen = true; //第⼀次打开⽅格
在⿏标左键事件⾥⾯写第⼀次打开⽅格⽣成地雷的代码的⼤致框架。
//⿏标左键打开⽅格
if (e.button === 0) {
//第⼀次打开
if (isFirstOpen) {
isFirstOpen = false;
let count = 0; //当前地雷数
//⽣成地雷
while (count < maxCount) {
//........
}
}
}
完善⽣成地雷代码:
⽣成随机坐标 ri,rj,判断该坐标不等于第⼀次点击⽅格的坐标以及该坐标表⽅格不为地雷。
条件成⽴,将坐标对应⽅格的 isMine 设置为true,当前地雷数+1,并使九宫格内⾮雷⽅格的计雷数 count +1⾃定义属性isMine代表⽅格为地雷。
⾃定义属性count为计雷数。
当地雷数⼤于最⼤地雷数,结束循环。
//⽣成地雷
while (count < maxCount) {
//⽣成随机坐标
let ri = Math.floor(Math.random() * row);
let rj = Math.floor(Math.random() * col);
//坐标不等于第⼀次点击⽅格的坐标 && ⾮雷⽅格
if (!(ri === _i && rj === _j) && !grid[ri][rj].isMine) {
grid[ri][rj].isMine = true; //⾃定义属性isMine代表⽅格为地雷
count++; //当前地雷数+1
//更新九宫格内⾮雷⽅格的计雷数
js 二维数组for (let i = ri - 1; i < ri + 2; i++) {
for (let j = rj - 1; j < rj + 2; j++) {
//判断坐标防越界
if (i > -1 && j > -1 && i < row && j < col) {
//计雷数+1
grid[i][j].count++;
}
}
}
}
}
写个测试代码在⽣成地雷后显⽰所有⽅格的状态。(Tip:测试完记得把测试代码删除) for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
//判断⽅格是否为雷
if (grid[i][j].isMine) {
//显⽰为雷
grid[i][j].innerHTML = '雷';
} else {
//否则显⽰计雷数
grid[i][j].innerHTML = grid[i][j].count;
}
}
}
效果如下:可以看到已经随机⽣成了雷,计雷数也正确显⽰了。
⽅格的打开事件
在⽣成地雷的代码下,加⼊⽅格打开代码函数 block_open(_i,_j) 的⼤致框架。
定义 function op(block) 函数设定打开⽅格的状态与样式。
判定打开的⽅格的类型
block.isMine 为打开地雷⽅格 --> 游戏结束
else 为打开计雷数⼤于0的⽅格 --> 显⽰⽅格计雷数
//⿏标左键打开⽅格
if (e.button === 0) {
//第⼀次打开
if (isFirstOpen) {
//.......
}
//执⾏打开⽅格函数
block_open(_i, _j);
//打开⽅格函数
function block_open(_i, _j) {
let block = grid[_i][_j];
op(block);
//设定打开⽅格的状态与样式
function op(block) {
block.isOpen = true; //isOpen为⾃定义属性,设置为true代表已打开
block.style.background = '#ccc'; //将背景设置为灰⾊
block.style.cursor = 'default'; //将⿏标停留样式设置为默认
}
if (block.isMine) {
//踩雷
} else if (unt === 0) {
//打开计雷数为0的⽅格
} else {
//打开计雷数不为0的⽅格
}
}
}
打开⾮雷⽅格显⽰计雷数
我们先把最简单的显⽰⽅格计雷数搞定。
else {
//打开计雷数不为0的⽅格
block.innerHTML = unt; //显⽰计雷数
}
效果如下:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论