思路要点:

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. Luogu P1979 华容道(bfs+最短路)

    P1979 华容道 题意 题目描述 小B最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成, 最少需要多少时间. ...

  2. 国内在Amazon fireTV或者fire平板下载应用(netflix\hulu\YouTube)的方法

    1.首先需要vpn翻墙至U.S. 2.需要一个美国亚马逊账户,并设置收货地址 (Manage Your Fire & Kindle 1-Click Payment Settings ),如果只 ...

  3. mysql导出数据库某些表的数据

    # 导出数据,一般从从库导出,减少主库的压力.mysqldump -hhostname -P3306 --single-transaction --master-data= database_name ...

  4. 9.2专项测试-Android性能测试黑盒分析-1

    1. 专项测试 业务测试:面向新需求 回归测试:面向已交付需求 专项测试:面向非功能需求的各类质量唯独特征 表现 用户维度 技术维度 崩溃 crash,弱网 检测崩溃1.某个页面,因为研发处理不合适, ...

  5. CSS作业问题 内容回顾

    CSS作业问题 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  6. loj2324 「清华集训 2017」小 Y 和二叉树

    https://loj.ac/problem/2324 太智障,一开始以为中序遍历的第一个点一定是一个叶子,想了个贪心.然而,手算了一下,第一个点都过不了啊. input 5 2 3 4 1 3 3 ...

  7. Hdu 4965(矩阵快速幂)

    题目链接 Fast Matrix Calculation Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K ...

  8. hbase phoenix char may not be null

    在使用phoenix做hbase的相关測试的时候.会出现 char may not be null 的错误. 这是因为建表和导入的数据不匹配导致的.主要是char的定义,假如一个字段定义为char类型 ...

  9. Binder对象死亡通知机制

    本文參考<Android系统源码情景分析>,作者罗升阳. 一.Binder库(libbinder)代码:        ~/Android/frameworks/base/libs/bin ...

  10. 我的第一个可用的Windows驱动完成了

    看到了一些希望,就值得我继续执着下去. 虽然是很简单的一个小驱动,但是它包含了我学编程两年来的憧憬与努力... 在2011年5月份,我就想学驱动,但是多次的失败,让我很不耐烦,所以暂时搁置了.... ...