這篇文章主要介紹了js實作掃雷小程式的範例程式碼,小編覺得挺不錯的,現在分享js原始碼給大家,也給大家做個參考。對js有興趣的一起跟著小編過來看看吧
初學javascript,寫了一個掃雷程式練練手!
掃雷規則及功能
掃雷想必大家都不陌生,就是windows上點擊排雷的小遊戲,它的主要規則有
1.左鍵點擊顯示目前格子是否為雷,如果為雷的話,GameOver啦,如果不是雷的話,這個格子會顯示周圍八個格子內的雷數量。
2.滑鼠右鍵標記,標記可能的雷,標記了之後取消需要再次右鍵點擊該格子,左鍵無效果。
3.滑鼠中鍵(滾輪)按下後,快速掃雷(如果周圍雷數和未被標記且未被翻開的格子相等,會將這些格子一併翻開)
#主要功能基本上完全復刻了windows7掃雷的功能
掃雷github位址:掃雷github位址
掃雷演算法
1.首先我定義了一個建構子,裡面是一系列的屬性:
var mineCraft = function(num1,num2,mine_num,obj,type){ this.num1 = num1; //整个扫雷的行数 this.num2 = num2; //整个扫雷的列数 this.mine_num = mine_num; //雷的个数 this.tiles = []; //数组里面存放的是每个小格子 this.obj = obj; //扫雷放置的对象 this.flag = true; this.arr = []; this.arr_2 = []; this.time_dsq = null; this.time_dc =''; this.time_arr = [[],[],[]]; //时间统计信息 this.details = [[],[],[]]; // 游戏统计详情 this.type = type; //游戏类型:初级/中级/高级/自定义 this.buildTiles(); //创建游戏函数 };
2.在頁面上建立掃雷的介面函式buildTiles :
buildTiles:function(){ this.obj.style.width = 51*this.num1+'px'; //在传进来的对象上画整体格子,每个小格子51px大小,总大小就为个数*单个大小 this.obj.style.height = 51*this.num2+'px'; var indexOfp = 0; for(var i = 0;i<this.num2;i++){ for(var j = 0;j<this.num1;j++){ var tile = document.createElement('p'); tile.className = 'tile'; //定义小格子class tile.index = indexOfp; //为每个小格子添加索引 this.tiles[indexOfp] = tile; //将小格子存入数组中 indexOfp++; this.obj.appendChild(tile); //将小格子插入到整个扫雷界面中 } } this.obj.oncontextmenu = function(){ //取消浏览器的默认右键菜单事件 return false; } this.event(); //点击事件 },
3.綁定事件函數:
event : function(){ var _this = this; this.obj.onmouseover = function(e){ //鼠标悬停事件--- if(e.target.className == 'tile'){ e.target.className = 'tile current'; } } this.obj.onmouseout = function(e){ //鼠标移出事件-- if(e.target.className == 'tile current'){ e.target.className = 'tile'; } } this.obj.onmousedown = function(e){ //鼠标按下事件 var index = e.target.index; if(e.button == 1){ //e.button属性 左键0/中键1/右键2 event.preventDefault(); //取消默认 } _this.changeStyle(e.button,e.target,index); } this.obj.onmouseup = function(e){ //鼠标弹起事件 if(e.button == 1){ _this.changeStyle(3,e.target); } } },
4.點擊呼叫的函數:
changeStyle:function(num1,obj,num_index){ if(num1 == 0){ //是左键的话 if(this.flag){ //this.flag 是之前定义的用于判断是否为第一次点击 this.store(num_index); //store函数,存放被点击的格子周围的8个格子 this.setMineCraft(this.mine_num,this.arr,num_index); //如果是第一次点击 即调用布雷函数 更改flag状态 this.flag = false; this.detail_statistics(0,false); //开始信息统计函数 } if(obj.className != 'tile'&&obj.className !='tile current'){//如果不是第一次点击,被点击的格子不是未点击状态,无效 return false; } if(obj.getAttribute('val') == 0){ //如果不是雷。改为翻开状态 obj.className = "showed"; obj.innerHTML = obj.getAttribute('value') == 0?'':obj.getAttribute('value'); //显示周围雷数 this.showAll(obj.index); //递归函数判断周围格子的情况,就是扫雷游戏上一点开会出现一片的那种 } if(this.over(obj)){ //判断游戏是否结束 this.last(); } } if(num1 == 2){ //右键标记事件 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){ //鼠标弹起事件 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()){ //弹起判断是否结束 this.last(); } } },
5.布雷:我之前的布雷是在頁面加載在buildTiles()的時候布雷的,但是這樣會導致有可能你電機的第一個格子就是雷(遊戲性不強),後來修改到第一次點擊完成之後布雷(確保第一下點的不是雷),避開直接炸死的現象.所以把調用放在後面的event後觸發的changeStyle函數中
setMineCraft:function(num,arr_first,num_first){ //雷的个数、最开始被点击的格子周围的八个、被点击的那个格子 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){//如果是属于第一次点击的周围的直接跳过在该位置布雷 num++; continue; } if (this.tiles[index_Mine].getAttribute("val") == 0) { this.tiles[index_Mine].setAttribute("val", 1); }else { num++; } } this.showValue(); this.event() },
6.儲存周圍格子的函數:
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函數:作用是如果該格子周圍沒有雷,自動翻開周圍8個格子,然後再判斷周圍八個格子的周圍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函數:主要是中鍵按鈕的作用中鍵點擊後的函數,這裡的show_zj1是滑鼠鍵按下後的顯示效果,
show_zj2函數就是
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'; //按下效果恢复原状 } 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){ //如果周围雷数和周围被标记数相等就翻开周围的格子 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) {//如果周围格子无雷则继续。 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) { //如果周围格子有雷,游戏结束 this.over(arr[i]); this.last(); return false; } } } return true; },
9.結束判斷:
over:function(obj){ var flag = false; var showed = document.getElementsByClassName('showed'); var num = this.tiles.length - this.mine_num; if(showed.length == num){ //如果被排出来的格子数等于总格子数-雷数,这游戏成功结束 this.detail_statistics(1,true); //游戏统计 ,true代表胜,false,代表失败 alert('恭喜你获得成功'); flag = true; }else if(obj&&obj.getAttribute('val') == 1){ //如果被点击的是雷,则炸死 this.detail_statistics(1,false); alert('被炸死!'); flag = true; } return flag; },
#10.結束後的顯示函數:
##
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 統計資料:還是比較全的和windows7掃雷版的判斷項目是一樣的,使用的是每次結束遊戲後將資料存入localStorage中,
//已玩游戏,已胜游戏,胜率,最多连胜,最多连败,当前连局; 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]]); //这里存放的就是上面注释中的六项数据 } 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之前看書學習總覺得學了就忘,感覺懂了公式卻不知道怎麼用,寫完掃雷小程式覺得收穫了很多
以上是js實作掃雷小程式的範例程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!