思路要点:

1. 随机地雷放到一个二维数组中;

2. 每一个格子要统计周围有几颗雷;

3. 每一个格子是否处于打开状态,用于判断是否赢得游戏;

4. 如果点击到周围没有雷的地方,把周围的打开;

具体的见代码

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
*{padding: 0;margin: 0;}
canvas{margin: 0 auto;display: block;}
.btns{margin: 0 auto;text-align: center;padding: 10px 0;}
</style>
</head>
<body>
<canvas id="mineMap"></canvas>
<div class="btns">
<button id="playAgain">再来一局</button>
</div> <script src="mineClearance.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript"> var game = new MineClearance("mineMap", 15, 0.15);
var restart = document.getElementById("playAgain");
restart.onclick = function (){
game.resetData();
}
</script>
</body>
</html>

  

class MineClearance{
constructor(canvasId, rowCount, mineRate){
this.rowCount = rowCount || 10;
this.mineRate = mineRate || 0.1; // 地雷所占的百分比
this.canvasId = canvasId;
this.resetData();
} // 重置数据
resetData(){
this.mines = []; // 所有地雷的数组
this.mineCount = 0; // 地雷总数量,由地雷百分比算出
// this.mineRate = 0.15; // 地雷所占的百分比
this.minePositions = []; // 所有地雷的坐标
this.mineTip = []; // 地雷的提示数组
this.openStatus = []; // 所有位置的打开状态
this.mineFlag = [];
          this.gameOver = false;
this.cellWidth = 0; this.initMines();
this.initCanvas();
} // 初始化canvas
initCanvas(){
var width = document.documentElement.clientWidth ||document.body.clientWidth;
var height = document.documentElement.clientHeight || document.body.clientHeight;
var minWidth = Math.min(width, (height - 50));
this.cellWidth = Math.floor(minWidth / this.rowCount);
this.canvas = document.getElementById(this.canvasId);
this.ctx = this.canvas.getContext("2d"); this.canvas.width = this.cellWidth * this.rowCount;
this.canvas.height = this.cellWidth * this.rowCount; this.ctx.fillStyle = "yellow";
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); this.ctx.beginPath();
for(var i = 0; i <= this.rowCount; i ++){
this.ctx.moveTo(i * this.cellWidth, 0);
this.ctx.lineTo(i * this.cellWidth, this.rowCount * this.cellWidth);
}
for(var i = 0; i <= this.rowCount; i ++){
this.ctx.moveTo(0, i * this.cellWidth);
this.ctx.lineTo(this.rowCount * this.cellWidth, i * this.cellWidth);
}
this.ctx.strokeStyle = '#e73480';
this.ctx.stroke();
// this.ctx.closePath(); this.bindEvent();
} // 初始化地雷
initMines(){
this.mines = [];
for(var i = 0; i < this.rowCount; i ++){
this.mines[i] = [];
for(var j = 0; j < this.rowCount; j ++){
this.mines[i][j] = 0;
}
} // 随机地雷坐标
this.mineCount = parseInt(this.rowCount * this.rowCount * this.mineRate);
var minePositions = [];
console.time();
while(this.mineCount){
var x = parseInt(Math.random() * this.rowCount);
var y = parseInt(Math.random() * this.rowCount);
if(minePositions.indexOf(x + "," + y) == -1){
minePositions.push(x + "," + y);
this.mineCount --;
}
}
console.timeEnd();
this.minePositions = minePositions; // 地雷按号入座
for(var i = 0; i < this.minePositions.length; i ++){
var p = this.minePositions[i].split(",");
this.mines[p[0]][p[1]] = 1;
}
console.log(this.mines); // 统计数组
var countArr = [];
for(var i = 0; i < this.mines.length; i ++){
countArr[i] = [];
for(var j = 0; j < this.mines[i].length; j ++){
var sum = 0;
for(var ii = -1; ii <= 1; ii ++){
for(var jj = -1; jj <= 1; jj ++){
if(ii == 0 && jj == 0){
continue;
}
if(this.mines[i + ii] && this.mines[i + ii][j + jj]){
sum += this.mines[i + ii][j + jj];
}
}
}
if(this.mines[i][j] == 1){
countArr[i][j] = 9; // 雷 此处用9代表地雷,反正周围有9个雷的话说明你必踩雷了,这里是为了数组输出时显示整齐
}else{
countArr[i][j] = sum;
} }
}
this.mineTip = countArr;
console.log(this.mineTip) // 每个格子的打开情况
for(var i = 0; i < this.rowCount; i ++){
this.openStatus[i] = [];
for(var j = 0; j < this.rowCount; j ++){
this.openStatus[i][j] = false;
}
} // 标记数组(插旗情况)
for(var i = 0; i < this.rowCount; i ++){
this.mineFlag[i] = [];
for(var j = 0; j < this.rowCount; j ++){
this.mineFlag[i][j] = false;
}
}
} // 绑定事件
bindEvent(){
var that = this;
document.body.oncontextmenu = function (){
return false;
}
this.canvas.onmousedown = function(evt){
var e = evt || window.event;
// console.log(this.getBoundingClientRect().left,e.clientX,that.cellWidth);
// x,y与数组中的位置对应位置,请注意
var x = Math.floor((e.clientX - this.getBoundingClientRect().left) / that.cellWidth);
var y = Math.floor((e.clientY - this.getBoundingClientRect().top) / that.cellWidth);
console.log(x,y)
if(e.which == 3){ // 1左键 2滚轮 3右键
// 插旗或取消插旗
that.changeFlag(x,y);
}else if(e.which == 1){
// 打开格子
that.openCell(x, y);
}
}
} // 画一个地雷
drawOneMine(x,y){
var xx = this.cellWidth * x;
var yy = this.cellWidth * y; this.ctx.fillStyle = "#fff";
this.ctx.fillRect(xx, yy, this.cellWidth, this.cellWidth); // 写对应的数字
this.ctx.font = `${this.cellWidth*0.75}px 方正舒体`; // 隶书 方正舒体
this.ctx.fillStyle = "red";
this.ctx.textBaseline = "middle";
this.ctx.textAlign = "center";
this.ctx.fillText("雷", xx + this.cellWidth / 2, yy + this.cellWidth / 2); this.drawBorder(x,y);
} // 画雷区标记
drawFlag(x, y){
var xx = this.cellWidth * x;
var yy = this.cellWidth * y; this.ctx.fillStyle = "#fff";
this.ctx.fillRect(xx, yy, this.cellWidth, this.cellWidth); // 写对应的数字
this.ctx.font = `${this.cellWidth*0.75}px 方正舒体`; // 隶书 方正舒体
this.ctx.fillStyle = "blue";
this.ctx.textBaseline = "middle";
this.ctx.textAlign = "center";
this.ctx.fillText("旗", xx + this.cellWidth / 2, yy + this.cellWidth / 2); this.drawBorder(x,y);
} // 取消雷区标记
cancelFlag(x, y){
var xx = this.cellWidth * x;
var yy = this.cellWidth * y; this.ctx.fillStyle = "yellow";
this.ctx.fillRect(xx, yy, this.cellWidth, this.cellWidth);
this.drawBorder(x,y);
} // 插旗或取消插旗
changeFlag(x,y){
// 更改数据
if(this.mineFlag[x][y]){
console.log("取消插旗");
this.mineFlag[x][y] = false;
this.openStatus[x][y] = false;
this.cancelFlag(x, y);// 渲染canvas
}else{
console.log("插旗");
this.mineFlag[x][y] = true;
this.openStatus[x][y] = true;
this.drawFlag(x, y);// 渲染canvas
}
} // 打开一个格子
openCell(x, y){
if(this.gameOver){
return;
}
if(!this.openStatus[x] || typeof this.openStatus[x][y] !== "boolean"){
return;
}
if(this.openStatus[x][y]){
console.warn("此位置已打开");
return;
}else{
this.openStatus[x][y] = true;
} var xx = this.cellWidth * x;
var yy = this.cellWidth * y;
// console.log(x,y,xx,yy)
if(!this.mines[x][y]){ // 空位置
if(this.mineTip[x][y]){ // 有数字的位置
// this.ctx.clearRect(xx, yy, xx + this.cellWidth, yy + this.cellWidth);
this.ctx.fillStyle = "#fff";
this.ctx.fillRect(xx, yy, this.cellWidth, this.cellWidth); // 写对应的数字
this.ctx.font = `${this.cellWidth*0.75}px 方正舒体`; // 隶书 方正舒体
this.ctx.fillStyle = "#e73480";
this.ctx.textBaseline = "middle";
this.ctx.textAlign = "center";
this.ctx.fillText(this.mineTip[x][y], xx + this.cellWidth / 2, yy + this.cellWidth / 2); this.drawBorder(x,y);
this.isWin();
}else{ // 无数字的位置
console.log("周围无雷");
this.ctx.fillStyle = "#fff";
this.ctx.fillRect(xx, yy, this.cellWidth, this.cellWidth);
this.drawBorder(x,y); // this.openCell(x - 1, y - 1); // 左上
this.openCell(x - 1, y); // 左
// this.openCell(x - 1, y + 1); // 左下 this.openCell(x, y - 1); // 上
this.openCell(x, y + 1); // 下 // this.openCell(x + 1, y - 1); // 右上
this.openCell(x + 1, y); // 右
// this.openCell(x + 1, y + 1); // 右下
}
}else{ // 踩地雷了
console.log("gameOver");
this.gameOver = true;
for(var i = 0, len = this.mines.length; i < len; i++){
for(var j = 0, jlen = this.mines[i].length; j < jlen; j++){
if(this.mines[i][j]){
this.drawOneMine(i,j);
}
}
} this.toast("game over");
// alert("game over");
}
} // 画一格格子的边框
drawBorder(x,y){
var xx = this.cellWidth * x;
var yy = this.cellWidth * y;
this.ctx.moveTo(xx, yy);
this.ctx.lineTo(xx + this.cellWidth, yy);
this.ctx.lineTo(xx + this.cellWidth, yy + this.cellWidth);
this.ctx.lineTo(xx, yy + this.cellWidth);
this.ctx.strokeStyle = "#e73480";
this.ctx.stroke();
} // 判断赢
isWin(){
var openCount = 0;
this.openStatus.reduce(function (pre, cur){
cur.reduce(function(pre1,cur1){
if(cur1){
openCount ++;
}
},0);
},0);
// console.log(openCount);
if(this.rowCount * this.rowCount - this.mineCount == openCount){
this.toast("恭喜过关");
}
} toast(text){
setTimeout(function(){
alert(text);
},30);
} // 数组乱序
disorder(arr){ } }

  

原生js扫雷代码的更多相关文章

  1. 原生JS 选项卡代码实现

    可实现同页面多个选项卡 效果图: 代码实现: HTML部分 <div class="main" id="tabs"> <div class=& ...

  2. 原生js一行代码实现简易轮播图

    这是一个简易的js无限循环轮播图,只用了一行js代码就实现了无限循环,记录一下三目运算符的伟大! <!DOCTYPE html><html lang="en"&g ...

  3. 原生JS常用代码汇总

    数组相关 var codes = new Array( ); //创建数组codes.length //数组长度 动态插入数组 codes.push(value);

  4. 抛弃JQ,回归原生js……

    之前我写过一篇文章叫做<jq不会被淘汰>--而事实上它真的不会被淘汰,因为即使在mvvm框架盛行的今天,原生js的api越来越友好的今天,jq依然在用户量上是霸主-- 但是今天我们要讨论的 ...

  5. 原生js瀑布流

    HTML部分代码............................... CSS部分代码........................... 原生js部分代码................. ...

  6. jQuery和原生JS的对比

    原生JS的缺点: 不能添加多个入口函数(window.onload),如果添加了多个,后面的会把前面的覆盖掉 原生js的api名字太长,难以书写,不易记住 原生js有的代码冗余 原生js中的属性或者方 ...

  7. 原生JS实现购物车结算功能代码+zepto版

    html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...

  8. 原生js实现tab选项卡里内嵌图片滚动特效代码

    <!DOCTYPE HTML><html lang="en-US"><head><meta charset="UTF-8&quo ...

  9. 原生js 当前时间 倒计时代码

    源:https://www.oschina.net/code/snippet_2318153_54763 <!DOCTYPE html> <html> <head> ...

随机推荐

  1. LA3177 Beijing Guards

    Beijing Guards Beijing was once surrounded by four rings of city walls: the Forbidden City Wall, the ...

  2. PYTHON__ ITERTOOLS模块

    组成 总体,整体了解 无限迭代器 迭代器 参数 结果 例子 count() start, [step] start, start+step, start+2*step, ... count(10) - ...

  3. c++新特性实验(5)声明与定义:属性列表(C++11 起)

    1.初识属性 1.1 实验A: noreturn 属性 [[ noreturn ]] static void thread1(void *data){ cout << "nore ...

  4. 【python之路35】FTP文件断电续传作业

    开发一个支持多用户在线FTP程序: 要求: 1.用户MD5加密认证 2.允许同时多用户登陆(socketserver) 3.执行命令 4.上传文件 文件传输过程中显示进度条 支持文件的断点续传

  5. javascript go()函数

    <a href="javascript:history.go(-1);">返回上一页</a> put this in your input tag < ...

  6. LintCode 链表倒数第n个节点

    找到单链表倒数第n个节点,保证链表中节点的最少数量为n. 样例 给出链表 3->2->1->5->null和n = 2,返回倒数第二个节点的值1. 分析:设两个指针 p1和p2 ...

  7. E浏览器常见的9个css Bug以及解决办法

    我们在浏览网页的时候经常看见这样的现象:某个网页在IE6浏览器中打开很正常,但是在IE8里面打开可能完全变形了.或者也有可能出现完全相反的现象.这让Web程序员及设计师往往为了其CSS在各个IE版本下 ...

  8. 如何获取SpringBoot项目的applicationContext对象

    ApplicationContext对象是Spring开源框架的上下文对象实例,在项目运行时自动装载Handler内的所有信息到内存.传统的获取方式有很多种,不过随着Spring版本的不断迭代,官方也 ...

  9. 加载selenium2Library失败---robotframework环境搭建(site-packages下无selenium2library文件夹)

    加载Selenium2library失败,检查D:\Python27\Lib\site-packages 目录下是否有Selenium2Library 目录,没有该目录,事情就尴尬了. 自己安装的版本 ...

  10. 手机号测吉凶python代码

    根据数理数来测电话后四位吉凶: 数理数 解释批注 0点特殊.......大吉 1大展鸿图.可获成功吉 2一盛一衰.劳而无功凶 3蒸蒸日上.百事顺遂吉 4坎坷前途.苦难折磨凶 5生意欣荣.名利双收吉 6 ...