原生js扫雷代码
思路要点:
1. 随机地雷放到一个二维数组中;
2. 每一个格子要统计周围有几颗雷;
3. 每一个格子是否处于打开状态,用于判断是否赢得游戏;
4. 如果点击到周围没有雷的地方,把周围的打开;
具体的见代码
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){
}
}