掃雷

在以前的win7系統(tǒng)中,自帶的小游戲中,有一款游戲叫掃雷,效果如下圖:

掃雷

html中的結(jié)構(gòu)代碼如下:

<select name="mineNum">
    <option value="0">請(qǐng)選擇雷的數(shù)量</option>
    <option value="10-8">10個(gè)雷</option>
    <option value="40-16">40個(gè)雷</option>
    <option value="99-32">99個(gè)雷</option>
</select>
剩余雷數(shù)量:<span class="mineNum">0</span>

js邏輯代碼中,預(yù)置兩個(gè)工具函數(shù):

// 設(shè)置樣式的函數(shù)
function setStyle(ele, styleObj){
    for(var attr in styleObj){
        ele.style[attr] = styleObj[attr];
    }
}
// 獲取隨機(jī)數(shù)的函數(shù)
function getRandom(a,b=0){
    var max = a;
    var min = b;
    if(a<b){
        max = b;
        min = a;
    }
    return Math.floor(Math.random() * (max - min)) + min;
}

首先要根據(jù)選中的雷的數(shù)量,來(lái)創(chuàng)建小盒子:

// 獲取標(biāo)簽
var mineSelect = document.querySelector('[name="mineNum"]');
var mineNumBox = document.querySelector('.mineNum');
// 獲取掃雷游戲需要的列的數(shù)量
mineSelect.onchange = function(){
    // 獲取當(dāng)前選中的option的value
    var value = this.value;
    if(value === '0'){
        alert("請(qǐng)選擇雷的數(shù)量")
        return false;
    }
    // 從中獲取到類的數(shù)量和掃雷需要的列的數(shù)量
    var mineNum = +value.split('-')[0]
    var col = +value.split('-')[1]
    // 如果頁(yè)面中已經(jīng)有掃雷游戲了,就將之前的刪除
    var mine = document.querySelector('.mine');
    if(mine){
        document.body.removeChild(mine)
    }
    // 調(diào)用創(chuàng)建掃雷游戲的函數(shù)
    createMine(mineNum,col)
}
// 創(chuàng)建掃雷游戲的函數(shù)
function createMine(mineNum,col){
    // 根據(jù)列的數(shù)量創(chuàng)建大盒子
    var box = document.createElement('div')
    // 設(shè)置類名
    box.className = 'mine';
    document.body.appendChild(box)
    // 不能選中box中的內(nèi)容
    box.onselectstart = function(){
        return false
    }
    // 設(shè)置樣式
    setStyle(box,{
        border:"3px solid #00f",
        position:"relative",
        width:col*20 + 'px',
        height:col*20 + 'px',
    })
    // 在大盒子中創(chuàng)建小盒子
    for(var i=0;i<Math.pow(col,2);i++){
        var div = document.createElement('div')
        box.appendChild(div)
        setStyle(div,{
            width:"18px",
            height:"18px",
            border:"1px solid #fff",
            position:"absolute",
            left:i%col*20+'px',
            top:Math.floor(i/col)*20 + 'px',
            backgroundColor:"#aaa"
        })
    }
    // 調(diào)用隨機(jī)設(shè)置雷的函數(shù)
    var indexArr = setMine(box,mineNum,col)
    // 調(diào)用計(jì)算雷數(shù)量的函數(shù)
    countMine(box,col)
    // 點(diǎn)擊小div開(kāi)始掃
    clearance(box,col,indexArr)
}

設(shè)置雷的函數(shù)如下:

// 設(shè)置隨機(jī)雷的函數(shù)
function setMine(ele,num,col){
    // 在ele中創(chuàng)建num個(gè)雷
    // 定義數(shù)組用來(lái)存放是雷的小div的下標(biāo)
    var indexArr = []
    for(var i=0;i<num;i++){
        // 獲取隨機(jī)下標(biāo)
        var randomIndex = getRandom(ele.children.length)
        // 判斷隨機(jī)下標(biāo)是否在數(shù)組中
        var index = indexArr.indexOf(randomIndex)
        // 如果這個(gè)隨機(jī)下標(biāo)不在數(shù)組中,就將這個(gè)隨機(jī)下標(biāo)放當(dāng)數(shù)組中
        if(index<0){
            indexArr.push(randomIndex)
        }else{
            // 這次循環(huán)作廢 - 重新創(chuàng)建隨機(jī)下標(biāo) - 要保證數(shù)組中隨機(jī)下標(biāo)的個(gè)數(shù)一定是雷的數(shù)量
            i--
        }
    }
    // indexArr中存放的是所有 是雷的div的下標(biāo)
    // 給所有是雷的div做特殊的標(biāo)記
    for(var i=0;i<indexArr.length;i++){
        ele.children[indexArr[i]].mine = true;
        // ele.children[indexArr[i]].style.backgroundColor = 'red';
    }
    // 更改頁(yè)面中雷的數(shù)量
    mineNumBox.innerText = num
    return indexArr
}

根據(jù)設(shè)置好的雷,給每個(gè)小div計(jì)算周圍雷的數(shù)量:

// 計(jì)算雷數(shù)量的函數(shù)
function countMine(ele,col){
    // 遍歷每個(gè)小div,計(jì)算周圍雷的數(shù)量
    for(var i=0;i<ele.children.length;i++){
        // 如果當(dāng)前div是雷就跳過(guò)
        if(ele.children[i].mine){
            continue
        }
        // 獲取周圍所有div的下標(biāo)的數(shù)組
        var arr = getIndexArr(i,col);
        // 定義當(dāng)前div周圍雷的數(shù)量變量
        var num = 0
        // 遍歷周圍的div計(jì)算
        for(var j=0;j<arr.length;j++){
            if(ele.children[i+arr[j]].mine){
                num++
            }
        }
        ele.children[i].num = num;
        // ele.children[i].innerText = num;
    }
}

其中獲取周圍div的下標(biāo)數(shù)組的代碼如下:

// 獲取每個(gè)div周圍的div下標(biāo)的數(shù)組
function getIndexArr(index,col){
    // 定義周圍的div下標(biāo)的數(shù)組
    var arr = [1,-1,col,-col,col+1,col-1,-col-1,-col+1];
    if(index<col){
        arr = [-1,1,col,col-1,col+1];
    }
    // 如果是最后1行,周圍只有5個(gè)div - arr數(shù)組中就應(yīng)該只有5個(gè)下標(biāo)
    if(Math.floor(index/col) === col-1){
        arr = [-1,1,-col,-col-1,-col+1];
    }
    // 如果是第1列,周圍只有5個(gè)div - arr數(shù)組中就應(yīng)該只有5個(gè)下標(biāo)
    if(index%col === 0){
        arr = [col,col+1,1,-col,-col+1];
    }
    // 如果是最后1列,周圍只有5個(gè)div - arr數(shù)組中就應(yīng)該只有5個(gè)下標(biāo)
    if((index+1)%col === 0){
        arr = [-col-1,-col,-1,col-1,col];
    }
    // 如果是左上角的div,周圍只有3個(gè)div - arr數(shù)組中就應(yīng)該只有3個(gè)下標(biāo)
    if(index===0){
        arr = [1,col,col+1];
    }
    // 如果是左下角的div,周圍只有3個(gè)div - arr數(shù)組中就應(yīng)該只有3個(gè)下標(biāo)
    if(index===(col-1)*col){
        arr = [1,-col,-col+1];
    }
    // 如果是右下角的div,周圍只有3個(gè)div - arr數(shù)組中就應(yīng)該只有3個(gè)下標(biāo)
    if(index===col*col-1){
        arr = [-1,-col,-col-1];
    }
    // 如果是右上角的div,周圍只有3個(gè)div - arr數(shù)組中就應(yīng)該只有3個(gè)下標(biāo)
    if(index===col-1){
        arr = [-1,col,col-1];
    }
    return arr
}

再下來(lái)就可以開(kāi)始玩游戲了,開(kāi)始掃雷的代碼如下:

// 點(diǎn)擊小div開(kāi)始掃的函數(shù)
function clearance(ele,col,indexArr){
    // 遍歷所有小div綁定事件
    for(let i=0;i<ele.children.length;i++){
        // 單擊事件 - 如果不是雷,就將周圍雷的數(shù)量顯示出來(lái) - 如果周圍沒(méi)有雷就不顯示數(shù)量,繼續(xù)將周圍的div點(diǎn)開(kāi)
        ele.children[i].onclick = function(){
            if(!this.mine){
                // 如果點(diǎn)擊的不是雷,就打開(kāi)
                openNow(this,i,col,ele)
            }else{
                alert("GAME OVER");
                // 如果點(diǎn)擊的div是雷,就將所有類引爆,并結(jié)束游戲
                for(var j=0;j<indexArr.length;j++){
                    ele.children[indexArr[j]].style.backgroundColor = 'red';
                }
            }
        }
        // 右擊事件,標(biāo)記雷
        ele.children[i].oncontextmenu = function(){
            // 將雷標(biāo)紅
            this.style.backgroundColor = 'red';
            mineNumBox.innerText = mineNumBox.innerText-1
            // 阻止默認(rèn)行為
            return false;
        }
    }
}

打開(kāi)當(dāng)前div的函數(shù)如下:

// 打開(kāi)當(dāng)前小div,設(shè)置不同的背景顏色,并判斷是否需要遞歸打開(kāi)
function openNow(nowEle,i,col,ele){
    // 給已經(jīng)打開(kāi)的div做標(biāo)記
    nowEle.open = true;
    // 設(shè)置打開(kāi)的div的背景顏色
    nowEle.style.backgroundColor = '#eee';
    // 如果數(shù)量是0就繼續(xù)打開(kāi)
    if(nowEle.num === 0){
        // 繼續(xù)打開(kāi)周圍的div
        open(ele,i,col)
    }else{
        // 如果數(shù)量不是0就顯示數(shù)量
        nowEle.innerText = nowEle.num;
        nowEle.style.textAlign = 'center'
        nowEle.style.lineHeight = '20px'
        nowEle.style.fontSize = '12px'
        nowEle.style.color = '#666'
    }
}

如果當(dāng)前div周圍雷的數(shù)量為0,就將周圍的div也打開(kāi),需要遞歸,函數(shù)如下:

// 繼續(xù)打開(kāi)周圍div的函數(shù)
function open(ele,index,col){
    // 遍歷周圍的div - 如果雷的數(shù)量不是0,就顯示數(shù)量,如果雷的數(shù)量是雷就繼續(xù)遞歸打開(kāi)
    var arr = getIndexArr(index,col);
    // 遍歷數(shù)組,判斷周圍div是否打開(kāi)
    for(var i=0;i<arr.length;i++){
        if(ele.children[index+arr[i]].open){
            continue;
        }else{
            openNow(ele.children[index+arr[i]],index+arr[i],col,ele)
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 我在編程教學(xué)方面不是專家,但當(dāng)我想更好掌握某一樣?xùn)|西時(shí),會(huì)試著找出讓自己樂(lè)在其中的方法。比方說(shuō),當(dāng)我想在shell...
    老率的IT私房菜閱讀 188評(píng)論 0 0
  • 前篇: web版掃雷開(kāi)發(fā)小記(1)web版掃雷開(kāi)發(fā)小記(2)web版掃雷開(kāi)發(fā)小記(3)web版掃雷開(kāi)發(fā)小記(4) ...
    franose閱讀 643評(píng)論 0 0
  • 一、 掃雷游戲?qū)崿F(xiàn)核心思路解析 數(shù)據(jù)和視圖盡量分離。采用面向?qū)ο蟮膶?shí)現(xiàn)設(shè)計(jì)數(shù)據(jù)模塊。格子作為一類對(duì)象,雷場(chǎng)作為一類...
    游戲開(kāi)發(fā)大表哥閱讀 590評(píng)論 0 0
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者,不喜歡去冒險(xiǎn),但是人生放棄了冒險(xiǎn),也就放棄了無(wú)數(shù)的可能。 ...
    yichen大刀閱讀 7,866評(píng)論 0 4
  • 公元:2019年11月28日19時(shí)42分農(nóng)歷:二零一九年 十一月 初三日 戌時(shí)干支:己亥乙亥己巳甲戌當(dāng)月節(jié)氣:立冬...
    石放閱讀 7,454評(píng)論 0 2

友情鏈接更多精彩內(nèi)容