话不多说,开整!
1.规划
先打开现有的扫雷小游戏分析一下,都有哪些功能
有一堆格子,要可以选择难度,要按照难度生成地雷,地雷周围的格子要变现出周围有多少雷,格子就直接控制table好了
还有一些小细节,比如计时等等,可以最后在加上
那么就分析完了,开始整活!
2.html结构
html嘛,没什么特别需要介绍的
<div id="main">
<table id="landmine"></table>
<div id="operation">
<div class="tip">
剩余雷数:<span id="landMineCount">0</span> 个
</div>
<div class="tip">
持续时间: <span id="costTime">0</span> 秒
</div>
<fieldset>
<legend>难度选择:</legend>
<input type="radio" name="level" id="llevel" checked="checked" value="10" /><label for="llevel">初级(10*10)</label><br />
<input type="radio" name="level" id="mlevel" value="15" /><label for="mlevel">中级(15*15)</label><br />
<input type="radio" name="level" id="hlevel" value="20" /><label for="hlevel">高级(20*20)</label><br />
</fieldset>
<input type="button" id="begin" value="开始游戏" /><br />
大概就是这样子,非常的简洁美观
那么再下来就是画格子了
3.画格子
先初始化一下,构建一下绘制小格子的方法draw()
这个方法需要四个参数:
- rowCount:行数
- colCount:列数
- minLandMineCount:最少雷数
- maxLandMineCount:最多雷数
var Mine=function(
rowCount,
colCount,
minLandMineCount,
maxLandMineCount
){
this.table=document.getElementById("landmine");//获取那个表格
this.cells = document.getElementsByTagName('td'); //获取所有小格子
this.rowCount = rowCount || 10; //格子行数
this.colCount = colCount || 10; //格子列数
this.landMineCount = 0; //地雷个数
this.markLandMineCount = 0; //标记的地雷个数
this.minLandMineCount = minLandMineCount || 10; //地雷最少个数
this.maxLandMineCount = maxLandMineCount || 20; //地雷最多个数
}
}
这样就做好了画格子的数据准备了
接下来咱们需要想想
还有什么是这个游戏没有实现的?
- 第一,我们要记录玩家标记了哪些格子,所以我们需要一个数组
- 第二,玩家每次标记雷,需要一个方法,更新上面的数组和雷的数量
- 第三,当踩到雷的时候,需要一个方法,告诉玩家他挂了
- 第四,要记录玩家标记雷次数和游戏时间
第二、第三两条,方法以后再考虑如何实现,先用null代替
于是在构造器在再增加下面的属性
this.arrs = [];
this.beginTime = null; //游戏开始时间
this.endTime = null; //游戏结束时间
this.currentSetpCount = 0; //标记雷次数
this.trueCount = 0; //标记正确的雷的次数
this.endCallBack = null; //踩到雷的方法
this.landMineCallBack = null; //标记为地雷时的方法
然后,开始画格子!
我们在Mine上追加一个原型方法draw,用来画格子:
在追加单元格的同时,一并生成地雷,需要一个数组,记录地雷的位置
Mine.prototype={
draw:function(){
var gz="";
for(var i=0;i<this.rowCount;i++){
gz+="<tr>";
for(var j=0;j<this.colCount;j++){
gz+=`<td id="mine${i+"_"+j}"></td>`;
}
gz+="</tr>";
}
document.getElementById("landmine").innerHTML=gz;
}
}
目前,初始化的js是这样:
var Mine=function(
rowCount,
colCount,
minLandMineCount,
maxLandMineCount
){
this.table=document.getElementById("landmine");//获取那个表格
this.cells = document.getElementsByTagName('td'); //获取所有小格子
this.rowCount = rowCount || 10; //格子行数
this.colCount = colCount || 10; //格子列数
this.landMineCount = 0; //地雷个数
this.markLandMineCount = 0; //标记的地雷个数
this.minLandMineCount = minLandMineCount || 10; //地雷最少个数
this.maxLandMineCount = maxLandMineCount || 20; //地雷最多个数
this.arrs = []; //后面会用,地雷地图用的
this.beginTime = null; //游戏开始时间
this.endTime = null; //游戏结束时间
this.currentSetpCount = 0; //标记雷次数
this.trueCount = 0; //标记正确的雷的次数
this.endCallBack = null; //踩到雷的方法
this.landMineCallBack = null; //标记为地雷时的方法
}
Mine.prototype={
draw:function(){
var gz="";
for(var i=0;i<this.rowCount;i++){
gz+="<tr>";
for(var j=0;j<this.colCount;j++){
gz+=`<td id="mine${i+"_"+j}"></td>`;
}
gz+="</tr>";
}
document.getElementById("landmine").innerHTML=gz;
}
}
现在,加一加css,然后看看效果:
td{background-color:green;width:15px;height: 15px}
👆执行以下,得到👇
似乎问题不大,把这一个文件命名为Main.js,引用一下
然后,就要进行事件绑定了
我们把调用的js放另外一个文件里,叫main.js吧
调用的js
首先,再思考一下,我们需要什么
我们要获取三个单选框,还有那个按钮的dom,然后把用户选择的难度传给开始游戏的方法,并绑定到开始游戏的按钮上
好办
var radios=document.getElementsByName("level");
for(var i=0;i<3;i++){
radios[i].onclick=function(){
var value=this.value;
document.getElementById("begin").onclick=function(){
//这里
}
}
}
然后,我们要肯定要记录一下我们创建的Main对象,先用null代替,所以在全局声明一个
var lei=null;
然后我们需要一个开始游戏的方法start,用这个方法调用上面的Main方法,所以这个方法需要什么参数呢?需要游戏的难度,需要行、列、雷数量等等等等
雷的数量按照如下计算
最多:(长宽)/5
最少:(长宽)/5-长
然后绑定在开始游戏的按钮上
那我们就对上面的代码加点东西:
var lei=null;
var radios=document.getElementsByName("level");
for(var i=0;i<3;i++){
radios[i].onclick=function(){
var value=this.value;
document.getElementById("begin").onclick=function(){
start(value,value,value*value/5,value*value/5 -value)
}
}
}
function start(rowCount, colCount, minLandMineCount, maxLandMineCount){
//这里还需要一些别的,以后再加
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
这样,点击单选框就会改变格子的样子了。
接着,我们需要做一些判断
比如,点击单选框后,会创建刺激的新游戏,万一是误触怎么办呢?
所以,在执行start()前,先判断lei是不是null,不是的话,最后确认一下:
把start()改成:
function start(rowCount, colCount, minLandMineCount, maxLandMineCount){
//这里还需要一些别的,以后再加
if(lei!=null){
var jg=confirm("开始新游戏?")
if(jg){
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
}
else{
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
}
汇总一下开始游戏的代码(也就是main.js):
var lei=null;
var radios=document.getElementsByName("level");
for(var i=0;i<3;i++){
radios[i].onclick=function(){
var value=this.value;
document.getElementById("begin").onclick=function(){
start(value,value,value*value/5,value*value/5 -value)
}
}
}
function start(rowCount, colCount, minLandMineCount, maxLandMineCount){
//这里还需要一些别的,以后再加
if(lei!=null){
var jg=confirm("开始新游戏?")
if(jg){
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
}
else{
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
}
到此为止,我们就实现了地雷地图的绘制
然后,我们要进行的是生成地雷
生成地雷
我们把所有格子绘制成一个数组,初始值用0表示,再最随机生成地雷,用9代替
其他的则填入为格子附近的雷的数量
数组中值的意义
0:周围每一个雷
9:我自己是雷
1-8:周围雷的个数
那么,我们在Mine的prototype中再加入一个方法:
DrawMine:function(){
for(var i=0;i<this.rowCount;i++){
this.arrs[i] = [];
//arr再前面Mine的构造里已经声明过了
for(var j=0;j<this.colCount;j++){
this.arrs[i][j] = 0;
}
}
}
然后就要构思构思了,雷的随机生成应该怎么搞?
在前面,我们已经声明过了,最小的雷数和最大的雷数minLandMineCount, maxLandMineCount,毫无疑问要传给这个方法,因为后面还要用到随机生成雷,所以这里可以封装一个随机数的函数
那么就可以这么写:
SJCount: function (FirstValue, LastValue) {
var Choices = LastValue - FirstValue + 1;//确保最大值能取到
return Math.floor(Math.random() * Choices + FirstValue);
},
然后,让雷的数量的值等于这个
this.landMineCount = this.SJCount(this.minLandMineCount, this.maxLandMineCount);
接下来,咱们思考一下,怎么生成这些雷呢?
我们要先知道总共有多少格子,也就是arr数组的长度
然后,为了保证随机生成的雷位置不同,我们还要加一个去重操作
那么:
landMine:function(){
var all=this.rowCount*this.colCount;
var MineList=[];
for(var i = 0; i < this.landMineCount; i++){
var randomNum = this.SJCount(0, allCount-1);
var row= parseInt(randomNum / this.colCount),col= randomNum % this.colCount;
//从0到格子总数-1随机出来一个数作为雷,然后算出来这个数对应的行和列分别是多少
if (randomNum in MineList) {
i--;
continue;
}
//如果随机出来的数已经在列表里面了,那么往回退,重新生成
this.arrs[row][col] = 9;
MineList[randomNum] = randomNum;
}
}
如此,我们就随机生成了若干个雷,并且记录到了数组里。
现在,数组只剩最后一种,也就是表示附近雷的个数的值
那么只用遍历一遍所有格子,每个格子周围的值是9的个数
注意,要跳过边界的情况,否则会数组越界
所以,我们继续给Mine里添加方法,用于计算周围雷的数量:
everyCount: function () {
for (var i = 0; i < this.rowCount; i++) {
for (var j = 0; j < this.colCount; j++) {
if (this.arrs[i][j] == 9)
continue;
if (i > 0 && j > 0) {
if (this.arrs[i - 1][j - 1] == 9)
this.arrs[i][j]++;
}
if (i > 0) {
if (this.arrs[i - 1][j] == 9)
this.arrs[i][j]++;
}
if (i > 0 && j < this.colCount - 1) {
if (this.arrs[i - 1][j + 1] == 9)
this.arrs[i][j]++;
}
if (j > 0) {
if (this.arrs[i][j - 1] == 9)
this.arrs[i][j]++;
}
if (j < this.colCount - 1) {
if (this.arrs[i][j + 1] == 9)
this.arrs[i][j]++;
}
if (i < this.rowCount - 1 && j > 0) {
if (this.arrs[i + 1][j - 1] == 9)
this.arrs[i][j]++;
}
if (i < this.rowCount - 1) {
if (this.arrs[i + 1][j] == 9)
this.arrs[i][j]++;
}
if (i < this.rowCount - 1 && j < this.colCount - 1) {
if (this.arrs[i + 1][j + 1] == 9)
this.arrs[i][j]++;
}
}
}
}
好耶!汇总一下Main.js
var Mine=function(
rowCount,
colCount,
minLandMineCount,
maxLandMineCount
){
this.table=document.getElementById("landmine");//获取那个表格
this.cells = document.getElementsByTagName('td'); //获取所有小格子
this.rowCount = rowCount || 10; //格子行数
this.colCount = colCount || 10; //格子列数
this.landMineCount = this.SJCount(this.minLandMineCount, this.maxLandMineCount); //地雷个数
this.markLandMineCount = 0; //标记的地雷个数
this.minLandMineCount = minLandMineCount || 10; //地雷最少个数
this.maxLandMineCount = maxLandMineCount || 20; //地雷最多个数
this.arrs = []; //后面会用,地雷地图用的
this.beginTime = null; //游戏开始时间
this.endTime = null; //游戏结束时间
this.currentSetpCount = 0; //标记雷次数
this.trueCount = 0; //标记正确的雷的次数
this.endCallBack = null; //踩到雷的方法
this.landMineCallBack = null; //标记为地雷时的方法
}
Mine.prototype={
draw:function(){
var gz="";
for(var i=0;i<this.rowCount;i++){
gz+="<tr>";
for(var j=0;j<this.colCount;j++){
gz+=`<td id="mine${i+"_"+j}"></td>`;
}
gz+="</tr>";
}
document.getElementById("landmine").innerHTML=gz;
},
DrawMine:function(){
for(var i=0;i<this.rowCount;i++){
this.arrs[i] = [];
//arr再前面Mine的构造里已经声明过了
for(var j=0;j<this.colCount;j++){
this.arrs[i][j] = 0;
}
}
},
SJCount: function (FirstValue, LastValue) {
var Choices = LastValue - FirstValue + 1;//确保最大值能取到
return Math.floor(Math.random() * Choices + FirstValue);
},
landMine:function(){
var all=this.rowCount*this.colCount;
var MineList=[];
for(var i = 0; i < this.landMineCount; i++){
var randomNum = this.SJCount(0, allCount-1);
var row= parseInt(randomNum / this.colCount),col= randomNum % this.colCount;
//从0到格子总数-1随机出来一个数作为雷,然后算出来这个数对应的行和列分别是多少
if (randomNum in MineList) {
i--;
continue;
}
//如果随机出来的数已经在列表里面了,那么往回退,重新生成
this.arrs[row][col] = 9;
MineList[randomNum] = randomNum;
}
},
everyCount: function () {
for (let i = 0; i < this.rowCount; i++) {
for (let j = 0; j < this.colCount; j++) {
if (this.arrs[i][j] == 9)
continue;
if (i > 0 && j > 0) {
if (this.arrs[i - 1][j - 1] == 9)
this.arrs[i][j]++;
}
if (i > 0) {
if (this.arrs[i - 1][j] == 9)
this.arrs[i][j]++;
}
if (i > 0 && j < this.colCount - 1) {
if (this.arrs[i - 1][j + 1] == 9)
this.arrs[i][j]++;
}
if (j > 0) {
if (this.arrs[i][j - 1] == 9)
this.arrs[i][j]++;
}
if (j < this.colCount - 1) {
if (this.arrs[i][j + 1] == 9)
this.arrs[i][j]++;
}
if (i < this.rowCount - 1 && j > 0) {
if (this.arrs[i + 1][j - 1] == 9)
this.arrs[i][j]++;
}
if (i < this.rowCount - 1) {
if (this.arrs[i + 1][j] == 9)
this.arrs[i][j]++;
}
if (i < this.rowCount - 1 && j < this.colCount - 1) {
if (this.arrs[i + 1][j + 1] == 9)
this.arrs[i][j]++;
}
}
}
}
}
再往后,就是给每个格子添加点击事件啦。
格子的点击事件
我们先构思一下,每当我点击一个格子,会有几种情况?
左键:
第一,是数字为0的格子,一下子展开紧邻的所有为0的格子
第二,数字是1-8的格子,就展开这一个格子
第三,雷,游戏结束
右键:标记雷
好耶!思路清晰了!
我们先把左右键给格子绑定上:
我们先把标记成雷的css设置成
.flag{background-color:red;}
$: function (id) {
return document.getElementById(id);
},//用于获取元素
bingMine:function(){
for (let i = 0; i < this.rowCount; i++) {
for (let j = 0; j < this.colCount; j++) {
this.$("mine" + i + "_" + j).onmousedown=
function(e){
e = e || window.event;
var mouseNum = e.button;//获取是按得那个键
var className = this.className;
if (mouseNum == 2) {
if (className == "flag") {
this.className = "";
self.markLandMineCount--;
} else {
this.className = "flag";
self.markLandMineCount++;
}
if (self.landMineCallBack) {
self.landMineCallBack(self.landMineCount - self.markLandMineCount);
}
if (self.trueCount == self.landMineCount ) {
self.success();
}
} else if (mouseNum == 1||className != "flag") {
//打开相连的所有数为0的格子的方法
}
};
}
}
}
这时我们发现,右键已经可以标红格子了
不过!会弹出浏览器菜单!这可不行!
我们要加入这一句,禁用右键:
加在Main的构造函数里
document.oncontextmenu = function () {
//禁用右键菜单
return false;
};
然后我们就可以用lei.bingMain()调用一下了
var lei=null;
var radios=document.getElementsByName("level");
for(var i=0;i<3;i++){
radios[i].onclick=function(){
var value=this.value;
document.getElementById("begin").onclick=function(){
start(value,value,value*value/5,value*value/5 -value)
}
}
}
function start(rowCount, colCount, minLandMineCount, maxLandMineCount){
//这里还需要一些别的,以后再加
if(lei!=null){
var jg=confirm("开始新游戏?")
if(jg){
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
else{return;}
}
else{
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw();
}
lei.DrawMine()
lei.landMine()
lei.everyCount()
lei.bingMine()
}
(右键点击格子就会变红了)
我们打开控制台,走一下lei.arrs,看看数组怎么样:
看上去,一点问题都没有嘛
然后我们就可以写一下展开地图了
思路也很简单
如果左键点击的这个格子对应的不是9,那么显示这个数字
如果展开
也很简单嘛
open: function (obj, x, y) {
if (this.arrs[x][y] != 9) {
this.currentSetpCount++;
obj.innerHTML = this.arrs[x][y];
obj.className = "clear";
obj.onmousedown = null;
} else {
this.failed();
}
},
然后如果我们点击到0,会点开所有的0,以及其周围不是雷的格子
怎么实现呢?
我们现在open进行判定,如果是0的话,执行一个方法,接收坐标信息,然后判断3*3内有没有0,如果有的话,再判断是否被标记为雷,如果也没有,就调用open,实现递归展开(并需要跳过自身)
所以open变成
open: function (obj, x, y) {
if (this.arrs[x][y] != 9) {
this.currentSetpCount++;
obj.innerHTML = this.arrs[x][y];
obj.className = "clear";
obj.onmousedown = null;
if (this.arrs[x][y] == 0) {
this.showNoLandMine.call(this, x, y);
}
} else {
this.failed();
}
},
然后showNoLandMine就这么写:
showNoLandMine: function (x, y) {
for (var i = x - 1; i < x + 2; i++)
for (var j = y - 1; j < y + 2; j++) {
if (!(i == x && j == y)) {
var ele = this.$("mine" + i + "_" + j);
if (ele && ele.className == "") {
this.open.call(this, ele, i, j);
}
}
}
},
目前为止,扫雷就是这个样子
(中间没有数字的就是雷了)
然后,我们在里面添加失败的方法和成功的方法:
failed:function(){
alert("你踩雷了")
for (var i = 0; i < this.rowCount; i++) {
for (var j = 0; j < this.colCount; j++) {
if (this.arrs[i][j] == 9)
this.$("mine" + i + "_" + j).innerHTML="雷"
else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
}
}
},
success:function(){
alert("你赢了")
for (var i = 0; i < this.rowCount; i++) {
for (var j = 0; j < this.colCount; j++) {
if (this.arrs[i][j] == 9)
this.$("mine" + i + "_" + j).innerHTML="雷"
else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
}
}
}
这两个方法的核心就是显示出所有的块块
(这就是是最后的样子)
然后我们完善一下其他的内容,比如显示标记的雷的数量,以及时间等等
比如在最开始,声明变量的地方加入
document.querySelector("#landMineCount").innerHTML=this.landMineCount;
以及在点击时添加一句
document.querySelector("#landMineCount").innerHTML=self.landMineCount-self.markLandMineCount
然后时间上,也就是一个简简单单的计数器:
this.ts=0;
this.jsq=setInterval(()=>{
this.beginTime=Date.parse(new Date());
this.ts++;
document.querySelector("#costTime").innerHTML=this.ts;
},1000)
然后在赢或者死了的时候,停止计时:
failed:function(){
for (var i = 0; i < this.rowCount; i++) {
for (var j = 0; j < this.colCount; j++) {
if (this.arrs[i][j] == 9)
this.$("mine" + i + "_" + j).innerHTML="雷"
else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
}
clearInterval(this.jsq)
}
},
success:function(){
alert("win")
for (var i = 0; i < this.rowCount; i++) {
for (var j = 0; j < this.colCount; j++) {
if (this.arrs[i][j] == 9)
this.$("mine" + i + "_" + j).innerHTML="雷"
else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
}
clearInterval(this.jsq)
}
}
那么现在,几乎全部功能就实现了,我们在稍微修改修改CSS
(毕竟太丑了啊喂!)
现在这里送上没有修改css的版本,方便大家理解:
(js css html写到一个文件里了)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
td{background-color:green;width:15px;height: 15px}
.flag{background-color:red;}
</style>
<script>
var Mine=function(
rowCount,
colCount,
minLandMineCount,
maxLandMineCount
){
this.table=document.getElementById("landmine");//获取那个表格
this.cells = document.getElementsByTagName('td'); //获取所有小格子
this.rowCount = rowCount || 10; //格子行数
this.colCount = colCount || 10; //格子列数
this.markLandMineCount = 0; //标记的地雷个数
this.minLandMineCount = minLandMineCount || 10; //地雷最少个数
this.maxLandMineCount = maxLandMineCount || 20; //地雷最多个数
this.landMineCount = this.SJCount(this.minLandMineCount, this.maxLandMineCount); //地雷个数
this.arrs = []; //后面会用,地雷地图用的
this.beginTime = null; //游戏开始时间
this.endTime = null; //游戏结束时间
this.currentSetpCount = 0; //标记雷次数
this.trueCount = 0; //标记正确的雷的次数
this.endCallBack = null; //踩到雷的方法
this.landMineCallBack = null; //标记为地雷时的方法
this.ts=0;
this.jsq=setInterval(()=>{
this.beginTime=Date.parse(new Date());
this.ts++;
document.querySelector("#costTime").innerHTML=this.ts;
},1000);
document.querySelector("#landMineCount").innerHTML=this.landMineCount;
document.oncontextmenu = function () {
//禁用右键菜单
return false;
};
}
Mine.prototype={
draw:function(){
var gz="";
for(var i=0;i<this.rowCount;i++){
gz+="<tr>";
for(var j=0;j<this.colCount;j++){
gz+=`<td id="mine${i+"_"+j}"></td>`;
}
gz+="</tr>";
}
document.getElementById("landmine").innerHTML=gz;
},
DrawMine:function(){
for(var i=0;i<this.rowCount;i++){
this.arrs[i] = [];
//arr再前面Mine的构造里已经声明过了
for(var j=0;j<this.colCount;j++){
this.arrs[i][j] = 0;
}
}
},
SJCount: function (FirstValue, LastValue) {
var Choices = LastValue - FirstValue + 1;//确保最大值能取到
return Math.floor(Math.random() * Choices + FirstValue);
},
landMine:function(){
var all=this.rowCount*this.colCount;
var MineList=[];
for(var i = 0; i < this.landMineCount; i++){
var randomNum = this.SJCount(0, all-1);
var row= parseInt(randomNum / this.colCount),col= randomNum % this.colCount;
//从0到格子总数-1随机出来一个数作为雷,然后算出来这个数对应的行和列分别是多少
if (randomNum in MineList) {
i--;
continue;
}
//如果随机出来的数已经在列表里面了,那么往回退,重新生成
this.arrs[row][col] = 9;
MineList[randomNum] = randomNum;
}
},
everyCount: function () {
for (var i = 0; i < this.rowCount; i++) {
for (var j = 0; j < this.colCount; j++) {
if (this.arrs[i][j] == 9)
continue;
if (i > 0 && j > 0) {
if (this.arrs[i - 1][j - 1] == 9)
this.arrs[i][j]++;
}
if (i > 0) {
if (this.arrs[i - 1][j] == 9)
this.arrs[i][j]++;
}
if (i > 0 && j < this.colCount - 1) {
if (this.arrs[i - 1][j + 1] == 9)
this.arrs[i][j]++;
}
if (j > 0) {
if (this.arrs[i][j - 1] == 9)
this.arrs[i][j]++;
}
if (j < this.colCount - 1) {
if (this.arrs[i][j + 1] == 9)
this.arrs[i][j]++;
}
if (i < this.rowCount - 1 && j > 0) {
if (this.arrs[i + 1][j - 1] == 9)
this.arrs[i][j]++;
}
if (i < this.rowCount - 1) {
if (this.arrs[i + 1][j] == 9)
this.arrs[i][j]++;
}
if (i < this.rowCount - 1 && j < this.colCount - 1) {
if (this.arrs[i + 1][j + 1] == 9)
this.arrs[i][j]++;
}
}
}
},
$: function (id) {
return document.getElementById(id);
},//用于获取元素
bingMine:function(){
var self=this;
for (let i = 0; i < this.rowCount; i++) {
for (let j = 0; j < this.colCount; j++) {
self.$("mine" + i + "_" + j).onmousedown = function (e) {
e = e || window.event;
var mouseNum = e.button;
var className = this.className;
if (mouseNum == 2) {
if (className == "flag") {
this.className = "";
self.markLandMineCount--;
} else {
this.className = "flag";
self.markLandMineCount++;
if(self.arrs[i][j]==9){
self.trueCount++;
}
} document.querySelector("#landMineCount").innerHTML=self.landMineCount-self.markLandMineCount
if (self.landMineCallBack) {
self.landMineCallBack(self.landMineCount - self.markLandMineCount);
}
if (self.trueCount == self.landMineCount ) {
self.success();
}
} else if (mouseNum == 1||className != "flag") {
self.open.call(self,this, i, j);
}
};
}
}
},
showNoLandMine: function (x, y) {
for (var i = x - 1; i < x + 2; i++)
for (var j = y - 1; j < y + 2; j++) {
if (!(i == x && j == y)) {
var ele = this.$("mine" + i + "_" + j);
if (ele && ele.className == "") {
this.open.call(this, ele, i, j);
}
}
}
},
open: function (obj, x, y) {
if (this.arrs[x][y] != 9) {
this.currentSetpCount++;
obj.innerHTML = this.arrs[x][y];
obj.className = "clear";
// alert( this.trueCount)
obj.onmousedown = null;
if (this.arrs[x][y] == 0) {
this.showNoLandMine.call(this, x, y);
}
} else {
this.failed();
}
},
failed:function(){
for (var i = 0; i < this.rowCount; i++) {
for (var j = 0; j < this.colCount; j++) {
if (this.arrs[i][j] == 9)
this.$("mine" + i + "_" + j).innerHTML="雷"
else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
}
clearInterval(this.jsq)
}
},
success:function(){
alert("win")
for (var i = 0; i < this.rowCount; i++) {
for (var j = 0; j < this.colCount; j++) {
if (this.arrs[i][j] == 9)
this.$("mine" + i + "_" + j).innerHTML="雷"
else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
}
clearInterval(this.jsq)
}
}
}
</script>
<title>Document</title>
</head>
<body>
<div id="main">
<table id="landmine"></table>
<div id="operation">
<div class="tip">
剩余雷数:<span id="landMineCount">0</span> 个
</div>
<div class="tip">
持续时间: <span id="costTime">0</span> 秒
</div>
<fieldset>
<legend>难度选择:</legend>
<input type="radio" name="level" id="llevel" value="10" /><label for="llevel">初级(10*10)</label><br />
<input type="radio" name="level" id="mlevel" value="15" /><label for="mlevel">中级(15*15)</label><br />
<input type="radio" name="level" id="hlevel" value="20" /><label for="hlevel">高级(20*20)</label><br />
</fieldset>
<input type="button" id="begin" value="开始游戏" /><br />
<script>
var lei=null;
var radios=document.getElementsByName("level");
for(var i=0;i<3;i++){
radios[i].onclick=function(){
var value=this.value;
document.getElementById("begin").onclick=function(){
start(value,value,value*value/5,value*value/5 -value)
}
}
}
function start(rowCount, colCount, minLandMineCount, maxLandMineCount){
//这里还需要一些别的,以后再加
if(lei!=null){
var jg=confirm("开始新游戏?")
if(jg){
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
else{return;}
}
else{
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw();
}
lei.DrawMine()
lei.landMine()
lei.everyCount()
lei.bingMine()
}
</script>
</body>
</html>
以及修改CSS等等后的版本:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
td{background-color:rgb(191,191,191);border-left:3px white solid;border-top:3px white solid;border-right:3px rgb(126,126,124) solid;border-bottom:3px rgb(126,126,124) solid;width:20px;height: 20px;text-align: center;line-height: 20px;}
.clear{background-color: rgb(193,193,193);border:2px #585858 solid}
.flag{background:url(https://pic.imgdb.cn/item/61b72ab72ab3f51d9163640f.png);background-color: rgb(193,193,193);border:2px #585858 solid}
</style>
<script>
var Mine=function(
rowCount,
colCount,
minLandMineCount,
maxLandMineCount
){
this.table=document.getElementById("landmine");//获取那个表格
this.cells = document.getElementsByTagName('td'); //获取所有小格子
this.rowCount = rowCount || 10; //格子行数
this.colCount = colCount || 10; //格子列数
this.markLandMineCount = 0; //标记的地雷个数
this.minLandMineCount = minLandMineCount || 10; //地雷最少个数
this.maxLandMineCount = maxLandMineCount || 20; //地雷最多个数
this.landMineCount = this.SJCount(this.minLandMineCount, this.maxLandMineCount); //地雷个数
this.arrs = []; //后面会用,地雷地图用的
this.beginTime = null; //游戏开始时间
this.endTime = null; //游戏结束时间
this.currentSetpCount = 0; //标记雷次数
this.trueCount = 0; //标记正确的雷的次数
this.endCallBack = null; //踩到雷的方法
this.landMineCallBack = null; //标记为地雷时的方法
this.ts=0;
this.jsq=setInterval(()=>{
this.beginTime=Date.parse(new Date());
this.ts++;
document.querySelector("#costTime").innerHTML=this.ts;
},1000);
document.querySelector("#landMineCount").innerHTML=this.landMineCount;
document.oncontextmenu = function () {
//禁用右键菜单
return false;
};
}
Mine.prototype={
draw:function(){
var gz="";
for(var i=0;i<this.rowCount;i++){
gz+="<tr>";
for(var j=0;j<this.colCount;j++){
gz+=`<td id="mine${i+"_"+j}"></td>`;
}
gz+="</tr>";
}
document.getElementById("landmine").innerHTML=gz;
},
DrawMine:function(){
for(var i=0;i<this.rowCount;i++){
this.arrs[i] = [];
//arr再前面Mine的构造里已经声明过了
for(var j=0;j<this.colCount;j++){
this.arrs[i][j] = 0;
}
}
},
SJCount: function (FirstValue, LastValue) {
var Choices = LastValue - FirstValue + 1;//确保最大值能取到
return Math.floor(Math.random() * Choices + FirstValue);
},
landMine:function(){
var all=this.rowCount*this.colCount;
var MineList=[];
for(var i = 0; i < this.landMineCount; i++){
var randomNum = this.SJCount(0, all-1);
var row= parseInt(randomNum / this.colCount),col= randomNum % this.colCount;
//从0到格子总数-1随机出来一个数作为雷,然后算出来这个数对应的行和列分别是多少
if (randomNum in MineList) {
i--;
continue;
}
//如果随机出来的数已经在列表里面了,那么往回退,重新生成
this.arrs[row][col] = 9;
MineList[randomNum] = randomNum;
}
},
everyCount: function () {
for (var i = 0; i < this.rowCount; i++) {
for (var j = 0; j < this.colCount; j++) {
if (this.arrs[i][j] == 9)
continue;
if (i > 0 && j > 0) {
if (this.arrs[i - 1][j - 1] == 9)
this.arrs[i][j]++;
}
if (i > 0) {
if (this.arrs[i - 1][j] == 9)
this.arrs[i][j]++;
}
if (i > 0 && j < this.colCount - 1) {
if (this.arrs[i - 1][j + 1] == 9)
this.arrs[i][j]++;
}
if (j > 0) {
if (this.arrs[i][j - 1] == 9)
this.arrs[i][j]++;
}
if (j < this.colCount - 1) {
if (this.arrs[i][j + 1] == 9)
this.arrs[i][j]++;
}
if (i < this.rowCount - 1 && j > 0) {
if (this.arrs[i + 1][j - 1] == 9)
this.arrs[i][j]++;
}
if (i < this.rowCount - 1) {
if (this.arrs[i + 1][j] == 9)
this.arrs[i][j]++;
}
if (i < this.rowCount - 1 && j < this.colCount - 1) {
if (this.arrs[i + 1][j + 1] == 9)
this.arrs[i][j]++;
}
}
}
},
$: function (id) {
return document.getElementById(id);
},//用于获取元素
bingMine:function(){
var self=this;
for (let i = 0; i < this.rowCount; i++) {
for (let j = 0; j < this.colCount; j++) {
self.$("mine" + i + "_" + j).onmousedown = function (e) {
e = e || window.event;
var mouseNum = e.button;
var className = this.className;
if (mouseNum == 2) {
if (className == "flag") {
this.className = "";
self.markLandMineCount--;
} else {
if(self.landMineCount-self.markLandMineCount<=0)return;
this.className = "flag";
self.markLandMineCount++;
if(self.arrs[i][j]==9){
self.trueCount++;
}
}
document.querySelector("#landMineCount").innerHTML=self.landMineCount-self.markLandMineCount
if (self.landMineCallBack) {
self.landMineCallBack(self.landMineCount - self.markLandMineCount);
}
if (self.trueCount == self.landMineCount ) {
self.success();
}
} else if (mouseNum == 1||className != "flag") {
self.open.call(self,this, i, j);
}
};
}
}
},
showNoLandMine: function (x, y) {
for (var i = x - 1; i < x + 2; i++)
for (var j = y - 1; j < y + 2; j++) {
if (!(i == x && j == y)) {
var ele = this.$("mine" + i + "_" + j);
if (ele && ele.className == "") {
this.open.call(this, ele, i, j);
}
}
}
},
open: function (obj, x, y) {
if (this.arrs[x][y] != 9) {
this.currentSetpCount++;
obj.innerHTML = this.arrs[x][y]==0?"":this.arrs[x][y];
obj.className = "clear";
// alert( this.trueCount)
obj.onmousedown = null;
if (this.arrs[x][y] == 0) {
this.showNoLandMine.call(this, x, y);
}
} else {
this.failed();
}
},
failed:function(){
for (var i = 0; i < this.rowCount; i++) {
for (var j = 0; j < this.colCount; j++) {
if (this.arrs[i][j] == 9)
this.$("mine" + i + "_" + j).innerHTML="<img src='https://pic.imgdb.cn/item/61b72a2d2ab3f51d91632728.png' width=20 height=20>"
else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
}
clearInterval(this.jsq)
}
},
success:function(){
alert("win")
for (var i = 0; i < this.rowCount; i++) {
for (var j = 0; j < this.colCount; j++) {
if (this.arrs[i][j] == 9)
this.$("mine" + i + "_" + j).innerHTML="<img src='https://pic.imgdb.cn/item/61b72a2d2ab3f51d91632728.png' width=20 height=20>"
else{this.$("mine" + i + "_" + j).innerHTML=this.arrs[i][j]}
}
clearInterval(this.jsq)
}
}
}
</script>
<title>Document</title>
</head>
<body>
<fieldset style="width: 45%;float:left">
<legend>难度选择:</legend>
<input type="radio" name="level" id="llevel" value="10" /><label for="llevel">初级(10*10)</label><br />
<input type="radio" name="level" id="mlevel" value="15" /><label for="mlevel">中级(15*15)</label><br />
<input type="radio" name="level" id="hlevel" value="20" /><label for="hlevel">高级(20*20)</label><br />
<input type="button" id="begin" value="开始游戏" /><br />
</fieldset>
<div id="main">
<fieldset style="width: 45%;float:right">
<legend>游戏数据:</legend>
<br>
<div id="operation">
<div class="tip">
剩余雷数:<span id="landMineCount">0</span> 个
</div>
<div class="tip">
持续时间: <span id="costTime">0</span> 秒
</div><br>
</fieldset>
<div style="clear: both;"></div>
<table id="landmine" cellpadding="0" cellspacing="0"></table>
<script>
var lei=null;
var radios=document.getElementsByName("level");
for(var i=0;i<3;i++){
radios[i].onclick=function(){
var value=this.value;
document.getElementById("begin").onclick=function(){
start(value,value,value*value/5,value*value/5 -value)
}
}
}
function start(rowCount, colCount, minLandMineCount, maxLandMineCount){
//这里还需要一些别的,以后再加
if(lei!=null){
var jg=confirm("开始新游戏?")
if(jg){
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw()
}
else{return;}
}
else{
lei=new Mine(rowCount, colCount, minLandMineCount, maxLandMineCount)
lei.draw();
}
lei.DrawMine()
lei.landMine()
lei.everyCount()
lei.bingMine()
}
</script>
</body>
</html>
这是最后美化一下下的样子