原生js扫雷代码
思路要点:
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扫雷代码的更多相关文章
- 原生JS 选项卡代码实现
可实现同页面多个选项卡 效果图: 代码实现: HTML部分 <div class="main" id="tabs"> <div class=& ...
- 原生js一行代码实现简易轮播图
这是一个简易的js无限循环轮播图,只用了一行js代码就实现了无限循环,记录一下三目运算符的伟大! <!DOCTYPE html><html lang="en"&g ...
- 原生JS常用代码汇总
数组相关 var codes = new Array( ); //创建数组codes.length //数组长度 动态插入数组 codes.push(value);
- 抛弃JQ,回归原生js……
之前我写过一篇文章叫做<jq不会被淘汰>--而事实上它真的不会被淘汰,因为即使在mvvm框架盛行的今天,原生js的api越来越友好的今天,jq依然在用户量上是霸主-- 但是今天我们要讨论的 ...
- 原生js瀑布流
HTML部分代码............................... CSS部分代码........................... 原生js部分代码................. ...
- jQuery和原生JS的对比
原生JS的缺点: 不能添加多个入口函数(window.onload),如果添加了多个,后面的会把前面的覆盖掉 原生js的api名字太长,难以书写,不易记住 原生js有的代码冗余 原生js中的属性或者方 ...
- 原生JS实现购物车结算功能代码+zepto版
html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...
- 原生js实现tab选项卡里内嵌图片滚动特效代码
<!DOCTYPE HTML><html lang="en-US"><head><meta charset="UTF-8&quo ...
- 原生js 当前时间 倒计时代码
源:https://www.oschina.net/code/snippet_2318153_54763 <!DOCTYPE html> <html> <head> ...
随机推荐
- 跟我一起做一个vue的小项目(七)
先看下我们所做项目的效果 这些数据都是我们在data中定义的,不是从后端数据中请求的.那么 接下来我们使用axios渲染数据 npm install axios --save 每个组件里面的数据都不相 ...
- 关于Git回退再前进造成本地代码和远程仓库代码不一致的问题
事情经过: git push 提交之后(版本2.0), 回退, 然后做了一些修改, 发现有问题,于是脑抽回退git reset --hard HEAD^ (版本1,0), 然后又前进到之前那个版本( ...
- goland设置go build的工作目录
- loj2322 「清华集训 2017」Hello world!
https://loj.ac/problem/2322 先吐槽一下,sb数据毁我青春败我前程. 首先,一个数开根开不了多少次. 当我们把它开到1的时候,我们以后就不需要开他了,我们可以利用并查集跳过他 ...
- 转var,let,const,js严格模式的详解
最近看微信公众账号/知乎网上的文章说,现在的前端的人都注重用什么框架,一问原生js感觉都没有用到工作中.用不到的,学这些意义没有.上午我刚面试了一个前端,工作4年吧.最初是北大青鸟培训的,做后端.ne ...
- 前端路由的实现(三) —— History的pushState和replaceState用法
HTML5中history提供的pushState, replaceState这两个API.它们提供了操作浏览器历史栈的方法. pushState能够在不加载页面的情况下改变浏览器的URL.这个方法接 ...
- mybatis深入理解(八)-----关联表查询
一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关 ...
- MySQL数据库起步 关于数据库的基本操作(更新中...)
mysql的基本操作 连接指定的服务器(需要服务器开启3306端口) mysql -h ip地址 -P 端口号 -u 账号 -p 密码 删除游客模式 mysql -h ip地址 -P 端口号 -u 账 ...
- python基础--迭代器、生成器、内置函数、面向对象编程
迭代器:迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问完结束.迭代器只能往前不会后退 迭代:更新换代(重复)的过程,每次的迭代都必须基于上一次的结果 迭代器:迭代取值的工具 使用迭代器的 ...
- 【CodeVS】1978 Fibonacci数列3
1978 Fibonacci数列 3 时间限制: 1 s 空间限制: 64000 KB 题目等级 : 青铜 Bronze 题目描述 Description 斐波纳契数列是这样的数列: f1 = 1 f ...