掃雷小程序的js實現

初學javascript,寫了一個掃雷程序練練手!javascript

掃雷規則及功能

掃雷想必你們都不陌生,就是windows上點擊排雷的小遊戲,它的主要規則有java

1.左鍵點擊顯示當前格子是否爲雷,若是爲雷的話,GameOver啦,若是不是雷的話,這個格子會顯示周圍八個格子內的雷數量。
2.鼠標右鍵標記,標記可能的雷,標記了以後取消須要再次右鍵點擊該格子,左鍵無效果。
3.鼠標中鍵(滾輪)按下後,快捷掃雷(若是周圍雷數和未被標記且未被翻開的格子相等,會將這些格子一併翻開)
主要功能基本徹底復刻了windows7掃雷的功能

掃雷github地址:
掃雷github地址git

掃雷算法

  • 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 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;          //爲每一個小格子添加索引
                    this.tiles[indexOfdiv] = tile;    //將小格子存入數組中
                    indexOfdiv++;
                    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);
           
            }
        },

掃雷的主要部分就是這些了,還有一些小功能包括沒寫來,要看完整的能夠看gitHubgithub

以前看書學習總以爲學了就忘,感受懂了公式殊不知道怎麼用,寫完掃雷小程序以爲收穫了不少算法

相關文章
相關標籤/搜索