思路要点:

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){ } }

  

最新文章

  1. .NET Core 构建配置文件从 project.json 到 .csproj
  2. AIR call dll
  3. quick cocos map使用
  4. Poj(2679),SPFA,二级比较
  5. 【Linux C中文函数手册】之 内存和字符串函数
  6. PHP的会话处理函数session
  7. [反汇编练习] 160个CrackMe之020
  8. O(1)时间删除链表的已知结点
  9. 淘淘商城_day05_课堂笔记
  10. B树(B-树)
  11. 1023: [SHOI2008]cactus仙人掌图(DP+单调队列优化)
  12. ArcGisEngine图层操作(随笔,不全)
  13. (转) Linux中profile、bashrc、bash_profile之间的区别和联系
  14. Java基础---集合框架---迭代器、ListIterator、Vector中枚举、LinkedList、ArrayList、HashSet、TreeSet、二叉树、Comparator
  15. Android开源项目——带图标文字的底部导航栏IconTabPageIndicator
  16. PTA L2-011 玩转二叉树 二叉树+bfs
  17. Java实现单例模式的9种方法
  18. h3c mstp的举例
  19. fiddler查看IP地址和请求响应时间
  20. C#关于线程的问题

热门文章

  1. TensorFlow的安装 (python3.6在有pip的条件下如何安装TensorFlow)
  2. pytorch 多GPU处理过程
  3. PHP苹果推送实现(APNS)
  4. 使用uni-app(Vue.js)创建运行微信小程序项目步骤
  5. 搭建php虚拟环境
  6. jeecms 基本架构研究
  7. spring基于xml的声明式事务控制配置步骤
  8. NFS挂载服务具体的实施方案
  9. WebSocket前后端实现
  10. 2019.9.28 csp-s模拟测试54 反思总结