js實(shí)現(xiàn)掃雷小程序的示例代碼
初學(xué)javascript,寫了一個(gè)掃雷程序練練手!
掃雷規(guī)則及功能
掃雷想必大家都不陌生,就是windows上點(diǎn)擊排雷的小游戲,它的主要規(guī)則有
1.左鍵點(diǎn)擊顯示當(dāng)前格子是否為雷,如果為雷的話,GameOver啦,如果不是雷的話,這個(gè)格子會(huì)顯示周圍八個(gè)格子內(nèi)的雷數(shù)量。
2.鼠標(biāo)右鍵標(biāo)記,標(biāo)記可能的雷,標(biāo)記了之后取消需要再次右鍵點(diǎn)擊該格子,左鍵無效果。
3.鼠標(biāo)中鍵(滾輪)按下后,快捷掃雷(如果周圍雷數(shù)和未被標(biāo)記且未被翻開的格子相等,會(huì)將這些格子一并翻開)
主要功能基本完全復(fù)刻了windows7掃雷的功能
掃雷github地址:掃雷github地址
掃雷算法
1.首先我定義了一個(gè)構(gòu)造函數(shù),里面是一系列的屬性:
var mineCraft = function(num1,num2,mine_num,obj,type){
this.num1 = num1; //整個(gè)掃雷的行數(shù)
this.num2 = num2; //整個(gè)掃雷的列數(shù)
this.mine_num = mine_num; //雷的個(gè)數(shù)
this.tiles = []; //數(shù)組里面存放的是每個(gè)小格子
this.obj = obj; //掃雷放置的對(duì)象
this.flag = true;
this.arr = [];
this.arr_2 = [];
this.time_dsq = null;
this.time_dc ='';
this.time_arr = [[],[],[]]; //時(shí)間統(tǒng)計(jì)信息
this.details = [[],[],[]]; // 游戲統(tǒng)計(jì)詳情
this.type = type; //游戲類型:初級(jí)/中級(jí)/高級(jí)/自定義
this.buildTiles(); //創(chuàng)建游戲函數(shù)
};
2.在頁面上創(chuàng)建掃雷的界面 函數(shù)buildTiles
buildTiles:function(){
this.obj.style.width = 51*this.num1+'px'; //在傳進(jìn)來的對(duì)象上畫整體格子,每個(gè)小格子51px大小,總大小就為個(gè)數(shù)*單個(gè)大小
this.obj.style.height = 51*this.num2+'px';
var indexOfdiv = 0;
for(var i = 0;i<this.num2;i++){
for(var j = 0;j<this.num1;j++){
var tile = document.createElement('div');
tile.className = 'tile'; //定義小格子class
tile.index = indexOfdiv; //為每個(gè)小格子添加索引
this.tiles[indexOfdiv] = tile; //將小格子存入數(shù)組中
indexOfdiv++;
this.obj.appendChild(tile); //將小格子插入到整個(gè)掃雷界面中
}
}
this.obj.oncontextmenu = function(){ //取消瀏覽器的默認(rèn)右鍵菜單事件
return false;
}
this.event(); //點(diǎn)擊事件
},
3.綁事件函數(shù):
event : function(){
var _this = this;
this.obj.onmouseover = function(e){ //鼠標(biāo)懸停事件---
if(e.target.className == 'tile'){
e.target.className = 'tile current';
}
}
this.obj.onmouseout = function(e){ //鼠標(biāo)移出事件--
if(e.target.className == 'tile current'){
e.target.className = 'tile';
}
}
this.obj.onmousedown = function(e){ //鼠標(biāo)按下事件
var index = e.target.index;
if(e.button == 1){ //e.button屬性 左鍵0/中鍵1/右鍵2
event.preventDefault(); //取消默認(rèn)
}
_this.changeStyle(e.button,e.target,index);
}
this.obj.onmouseup = function(e){ //鼠標(biāo)彈起事件
if(e.button == 1){
_this.changeStyle(3,e.target);
}
}
},
4.點(diǎn)擊調(diào)用的函數(shù):
changeStyle:function(num1,obj,num_index){
if(num1 == 0){ //是左鍵的話
if(this.flag){ //this.flag 是之前定義的用于判斷是否為第一次點(diǎn)擊
this.store(num_index); //store函數(shù),存放被點(diǎn)擊的格子周圍的8個(gè)格子
this.setMineCraft(this.mine_num,this.arr,num_index); //如果是第一次點(diǎn)擊 即調(diào)用布雷函數(shù) 更改flag狀態(tài)
this.flag = false;
this.detail_statistics(0,false); //開始信息統(tǒng)計(jì)函數(shù)
}
if(obj.className != 'tile'&&obj.className !='tile current'){//如果不是第一次點(diǎn)擊,被點(diǎn)擊的格子不是未點(diǎn)擊狀態(tài),無效
return false;
}
if(obj.getAttribute('val') == 0){ //如果不是雷。改為翻開狀態(tài)
obj.className = "showed";
obj.innerHTML = obj.getAttribute('value') == 0?'':obj.getAttribute('value'); //顯示周圍雷數(shù)
this.showAll(obj.index); //遞歸函數(shù)判斷周圍格子的情況,就是掃雷游戲上一點(diǎn)開會(huì)出現(xiàn)一片的那種
}
if(this.over(obj)){ //判斷游戲是否結(jié)束
this.last();
}
}
if(num1 == 2){ //右鍵標(biāo)記事件
if(obj.className == 'biaoji'){
obj.className = 'tile';
}else if(obj.className !='biaoji'&&obj.className != 'showed'){
obj.className = 'biaoji';
}
}
if(num1 == 1){ // 中鍵事件
if(obj.className =="showed"){
this.show_zj1(obj.index);
}
}
if(num1 == 3){ //鼠標(biāo)彈起事件
if (obj.className == "showed") {
var flag1 = this.show_zj2(obj.index,0);
}else{
this.show_zj2(obj.index,1)
return false;
}
if(flag1&&this.over()){ //彈起判斷是否結(jié)束
this.last();
}
}
},
5.布雷:我之前的布雷是在頁面加載在buildTiles()的時(shí)候布雷的,但是這樣會(huì)導(dǎo)致有可能你電機(jī)的第一個(gè)格子就是雷(游戲性不強(qiáng)),后來修改到第一次點(diǎn)擊完成之后布雷(確保第一下點(diǎn)的不是雷),避開直接炸死的現(xiàn)象.所以把調(diào)用放在后面的event后觸發(fā)的changeStyle函數(shù)中
setMineCraft:function(num,arr_first,num_first){ //雷的個(gè)數(shù)、最開始被點(diǎn)擊的格子周圍的八個(gè)、被點(diǎn)擊的那個(gè)格子
var arr_index = [];
for(var i = 0;i<arr_first.length;i++){
arr_index.push(arr_first[i].index);
}
var length = this.tiles.length;
for (var i = 0; i < length; i++) {
this.tiles[i].setAttribute("val", 0);
}
for (var i = 0; i < num; i++) {
var index_Mine = Math.floor(Math.random() * this.tiles.length);
if(index_Mine == num_first||arr_index.lastIndexOf(index_Mine)>-1){//如果是屬于第一次點(diǎn)擊的周圍的直接跳過在該位置布雷
num++;
continue;
}
if (this.tiles[index_Mine].getAttribute("val") == 0) {
this.tiles[index_Mine].setAttribute("val", 1);
}else {
num++;
}
}
this.showValue();
this.event()
},
6.存儲(chǔ)周圍格子的函數(shù):
store : function(num) { //傳入格子的index.
var tiles_2d = [];
var indexs = 0;
for(var i = 0;i<this.num2;i++){
tiles_2d.push([]);
for(var j = 0;j<this.num1;j++){
tiles_2d[i].push(this.tiles[indexs]);
indexs++;
}
}
var j = num % this.num1;
var i = (num - j) / this.num1;
this.arr = [];
//左上
if (i - 1 >= 0 && j - 1 >= 0) {
this.arr.push(tiles_2d[i - 1][j - 1]);
}
//正上
if (i - 1 >= 0) {
this.arr.push(tiles_2d[i - 1][j]);
}
//右上
if (i - 1 >= 0 && j + 1 <= this.num1-1) {
this.arr.push(tiles_2d[i - 1][j + 1]);
}
//左邊
if (j - 1 >= 0) {
this.arr.push(tiles_2d[i][j - 1]);
}
//右邊
if (j + 1 <= this.num1-1) {
this.arr.push(tiles_2d[i][j + 1]);
}
//左下
if (i + 1 <= this.num2-1 && j - 1 >= 0) {
this.arr.push(tiles_2d[i + 1][j - 1]);
}
//正下
if (i + 1 <= this.num2-1) {
this.arr.push(tiles_2d[i + 1][j]);
}
//右下
if (i + 1 <= this.num2-1 && j + 1 <= this.num1-1) {
this.arr.push(tiles_2d[i + 1][j + 1]);
}
},
7.showAll函數(shù):作用是如果該格子周圍沒有雷,自動(dòng)翻開周圍8個(gè)格子,然后再判斷周圍八個(gè)格子的周圍8隔格子是否有雷,利用了遞歸的方法
showAll:function(num){
if (this.tiles[num].className == "showed" && this.tiles[num].getAttribute("value") == 0){
this.store(this.tiles[num].index);
var arr2 = new Array();
arr2 = this.arr;
for (var i = 0; i < arr2.length; i++) {
if (arr2[i].className != "showed"&&arr2[i].className !='biaoji') {
if (arr2[i].getAttribute("value") == 0) {
arr2[i].className = "showed";
this.showAll(arr2[i].index);
} else {
arr2[i].className = "showed";
arr2[i].innerHTML = arr2[i].getAttribute("value");
}
}
}
}
},
8.show_zj函數(shù):主要是中鍵按鈕的作用中鍵點(diǎn)擊后的函數(shù),這里的show_zj1是鼠標(biāo)鍵按下后的顯示效果,
show_zj2函數(shù)就是
show_zj1:function(num){
this.store(this.tiles[num].index);
for (var i = 0; i < this.arr.length; i++) {
if (this.arr[i].className == "tile") {
this.arr_2.push(this.arr[i]);
this.arr[i].className = "showed";
// this.arr[i].className = "test";
}
}
},
show_zj2:function(num,zt){
var count = 0;
this.store(this.tiles[num].index);
for(var i = 0,len = this.arr_2.length;i<len;i++){
this.arr_2[i].className = 'tile'; //按下效果恢復(fù)原狀
}
this.arr_2.length = 0;
for(var i = 0;i<this.arr.length;i++){
this.arr[i].className == 'biaoji'&&count++;
}
if(zt == 1){
return false;
}
var numofmines = this.tiles[num].getAttribute("value");
if(numofmines == count){ //如果周圍雷數(shù)和周圍被標(biāo)記數(shù)相等就翻開周圍的格子
var arr = new Array(this.arr.length);
for(var i = 0;i<this.arr.length;i++){
arr[i] = this.arr[i];
}
for (var i = 0,length = arr.length; i < length; i++) {
if (arr[i].className == "tile" && arr[i].getAttribute("val") != 1) {//如果周圍格子無雷則繼續(xù)。
arr[i].className = "showed";
arr[i].innerHTML = arr[i].getAttribute("value") == 0?"":arr[i].getAttribute("value");
this.showAll(arr[i].index);
} else if (arr[i].className == "tile" && arr[i].getAttribute("val") == 1) { //如果周圍格子有雷,游戲結(jié)束
this.over(arr[i]);
this.last();
return false;
}
}
}
return true;
},
9.結(jié)束判斷:
over:function(obj){
var flag = false;
var showed = document.getElementsByClassName('showed');
var num = this.tiles.length - this.mine_num;
if(showed.length == num){ //如果被排出來的格子數(shù)等于總格子數(shù)-雷數(shù),這游戲成功結(jié)束
this.detail_statistics(1,true); //游戲統(tǒng)計(jì) ,true代表勝,false,代表失敗
alert('恭喜你獲得成功');
flag = true;
}else if(obj&&obj.getAttribute('val') == 1){ //如果被點(diǎn)擊的是雷,則炸死
this.detail_statistics(1,false);
alert('被炸死!');
flag = true;
}
return flag;
},
10.結(jié)束后的顯示函數(shù):
last:function(){
var len = this.tiles.length;
for(var i = 0;i<len;i++){
this.tiles[i].className = this.tiles[i].getAttribute('val') == 1?'boom':'showed';
if(this.tiles[i].className != 'boom'){ //
this.tiles[i].innerHTML = this.tiles[i].getAttribute('value') == 0?'':this.tiles[i].getAttribute('value');
}
}
this.obj.onclick = null;
this.obj.oncontextmenu = null;
},
11 統(tǒng)計(jì)信息:還是比較全的和windows7掃雷版的判斷項(xiàng)目是一樣的,使用的是每次結(jié)束游戲后將數(shù)據(jù)存入localStorage中,
//已玩游戲,已勝游戲,勝率,最多連勝,最多連敗,當(dāng)前連局;
detail_statistics:function(num,zt){
var time_pay = 1;
var _this = this;
if(num == 0){
this.time_dsq = setInterval(function(){
$('#time_need').text(time_pay);
_this.time_dc =time_pay;
time_pay++;
},1000);
}
else if(num == 1){
clearInterval(this.time_dsq);
if(this.type == 4){return false;}
if(localStorage.details == undefined){
localStorage.details = JSON.stringify([[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0]]); //這里存放的就是上面注釋中的六項(xiàng)數(shù)據(jù)
}
if(JSON.parse(localStorage.details) instanceof Array){
this.details = JSON.parse(localStorage.details);
}
this.details[this.type][0] += 1;
if(zt == false){
if(this.details[this.type][5]>=0){
this.details[this.type][5] = -1;
}else{
this.details[this.type][5] -= 1;
}
if(this.details[this.type][5]<this.details[this.type][4]){
this.details[this.type][4] = this.details[this.type][5];
}
this.details[this.type][2] = this.toPercent(this.details[this.type][2]/this.details[this.type][0]);
localStorage.details = JSON.stringify(this.details);
return false;
}
if(this.details[this.type][5]>=0){
this.details[this.type][5] += 1;
}else{
this.details[this.type][5] = 1;
}
if(this.details[this.type][5]>this.details[this.type][3]){
this.details[this.type][3] = this.details[this.type][5];
}
this.details[this.type][3] += 1;
this.details[this.type][2] = this.toPercent(this.details[this.type][4]/this.details[this.type][0]);
localStorage.details = JSON.stringify(this.details);
var time1 = new Date();
var time_str = time1.getFullYear()+'/'+time1.getMonth()+'/'+time1.getDate()+' '+time1.getHours()+':'+time1.getMinutes();
if(localStorage.time == undefined){
localStorage.time = JSON.stringify([[],[],[]]);
}
if(JSON.parse(localStorage.time) instanceof Array){
this.time_arr = JSON.parse(localStorage.time);
}
this.time_arr[this.type].push([this.time_dc,time_str]);
this.time_arr[this.type].sort(function(a,b){
return a[0]-b[0];
});
if(this.time_arr[this.type].length>5){
this.time_arr[this.type].pop();
}
localStorage.time = JSON.stringify(this.time_arr);
}
},
掃雷的主要部分就是這些了,還有一些小功能包括沒寫來,要看完整的可以看gitHub
之前看書學(xué)習(xí)總覺得學(xué)了就忘,感覺懂了公式卻不知道怎么用,寫完掃雷小程序覺得收獲了很多
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
JavaScript設(shè)計(jì)模式之策略模式詳解
這篇文章主要為大家詳細(xì)介紹了JavaScript設(shè)計(jì)模式之策略模式的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
微信提示 在瀏覽器打開 效果實(shí)現(xiàn)過程解析
這篇文章主要介紹了微信提示 在瀏覽器打開 效果實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
layui實(shí)現(xiàn)給某一列加點(diǎn)擊事件
今天小編就為大家分享一篇layui實(shí)現(xiàn)給某一列加點(diǎn)擊事件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-10-10
JavaScript實(shí)現(xiàn)頁面無操作倒計(jì)時(shí)退出
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)頁面無操作倒計(jì)時(shí)退出,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
詳解template標(biāo)簽用法(含vue中的用法總結(jié))
這篇文章主要介紹了template標(biāo)簽用法(含vue中的用法總結(jié)),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-01-01
解決bootstrap下拉菜單點(diǎn)擊立即隱藏bug的方法
本篇文章主要介紹了解決bootstrap下拉菜單點(diǎn)擊立即隱藏bug的方法,具有一定的參考價(jià)值,有興趣的可以了解一下2017-06-06

