前篇:
web版掃雷開發(fā)小記(1)
web版掃雷開發(fā)小記(2)
web版掃雷開發(fā)小記(3)
web版掃雷開發(fā)小記(4)
完整代碼:我的Github
在線試玩:點(diǎn)擊
上一節(jié)完成了數(shù)字的顯示,這次主要完成一個功能,即當(dāng)當(dāng)前方塊周圍沒有雷(roundMines
為0)時修噪,自動挖開周邊的雷。
思路分析
首先先弄明白掃雷中的這一游戲規(guī)則辫封。
當(dāng)用戶點(diǎn)擊到一個方塊砖第,而它的周圍沒有雷時荷腊,就會挖開它周圍的方塊绍移,而當(dāng)它的周邊共計(jì)8個方塊中悄窃,有一個方塊也是周圍沒有雷的話,又會繼續(xù)挖開這個方塊周圍的方塊蹂窖。如下圖:
很容易想到轧抗,這是一個遞歸的思想,設(shè)計(jì)一個cleanArea()
函數(shù)恼策,遍歷某方塊周圍的八個方塊鸦致,當(dāng)其中一個方塊的roundMines
屬性為0時潮剪,則遞歸調(diào)用cleanArea()
函數(shù)涣楷,直到該區(qū)域方塊都被挖開為止。
(這里的實(shí)現(xiàn)搜羅了下其他人的想法抗碰,還可以用BFS實(shí)現(xiàn)狮斗,畢竟遞歸太低效了。不過我算法不咋樣弧蝇,還是先用著遞歸吧碳褒,有時間了來研究研究BFS)
函數(shù)實(shí)現(xiàn)
cleanArea()
函數(shù)接收兩個參數(shù),當(dāng)前點(diǎn)擊方塊的列columns
和行rows
看疗。
通過兩層for
循環(huán)嵌套沙峻,遍歷當(dāng)前方塊周邊的8個方塊,將周邊方塊的行列數(shù)push到定義的一個數(shù)組中两芳,然后通過讀取該數(shù)組的值摔寨,對方塊進(jìn)行clearRect()
。
添加isClicked屬性
然而按照這個思路實(shí)現(xiàn)起來有諸多問題怖辆,最主要的一個問題就是循環(huán)會遍歷到該方塊本身是复。
例如,當(dāng)前方塊行為12竖螃,列為15淑廊,它的roundMines為0,那么函數(shù)就會遍歷周邊的方塊特咆,當(dāng)我遍歷到比如行11季惩,列15的方塊時,發(fā)現(xiàn)它的roundMines為0,那么遞歸函數(shù)調(diào)用画拾,以行11关摇,列15為當(dāng)前方塊,開始遍歷碾阁,其他方塊暫且不論输虱,當(dāng)檢查到行12,列15的方塊時(是的兩個方塊互為周邊塊)脂凶。發(fā)現(xiàn)也是0宪睹,好,又是一個遞歸調(diào)用蚕钦。于是兩個遞歸互相調(diào)用亭病,永無止境……
這是我最開始寫的:
function cleanArea(columns, rows, mineObject) {
for (var k = -1; k < 2; k++) {
for (var l = -1; l < 2; l++) {
if (columns + l > -1 && rows + k > -1 && columns + l < 30 && rows + k < 16 ) {
mineObject.cleanArr.push({
columns: columns + l,
rows: rows + k
});
if (mineObject.mines[columns + l][rows + k].roundMines === 0) {
mineObject.cleanArea(columns + l, rows + k, mineObject);
}
}
}
}
}
這個問題解決起來也簡單,添加一個條件判斷語句就好嘶居。例如這里兩個范圍-1到1的變量l和k罪帖,代表行和列的變化,那么當(dāng)兩個變量都為0時邮屁,就代表是當(dāng)前塊整袁。
但是當(dāng)我添加一個(l!==0)&&(k!==0)
的條件時,程序每次在其中一個變量為0時就會判斷為true佑吝。百思不得其解坐昙,自認(rèn)這個判斷寫得也沒有問題,搜索了一下大家也都是這么寫得芋忿。沒辦法炸客,只有擱置。
同時也感謝這個bug戈钢,讓我思考到了更為便捷且語義更加明確的方法:
在之前定義的Mineblock
類中痹仙,再添加一個isClicked屬性,初始值為false殉了。對于每個方塊开仰,當(dāng)被點(diǎn)擊到時isClicked更改為true。對于上述方法也同樣如此宣渗,當(dāng)我在將它push到定義的數(shù)組中時抖所,同時更改isClicked屬性。
那么在我判斷中痕囱,我只需要加入isClicked===false
的判斷田轧,就可以規(guī)避掉所有或被點(diǎn)擊或被挖開的方塊。
代碼如下:
function cleanArea(columns, rows, mineObject) {
for (var k = -1; k < 2; k++) {
for (var l = -1; l < 2; l++) {
if (columns + l > -1 && rows + k > -1 && columns + l < 30 && rows + k < 16 && mineObject.mines[columns + l][rows + k].isClicked === false) {
mineObject.cleanArr.push({
columns: columns + l,
rows: rows + k
});
mineObject.mines[columns + l][rows + k].isClicked = true;
if (mineObject.mines[columns + l][rows + k].roundMines === 0) {
mineObject.cleanArea(columns + l, rows + k, mineObject);
}
}
}
}
}
效果可以看上面的截圖
添加游戲成功條件
游戲失敗的條件很簡單鞍恢,點(diǎn)擊到一個雷方塊時就結(jié)束傻粘。游戲成功稍微麻煩一點(diǎn)每窖,概念上很簡單,所有不是雷的方塊都被點(diǎn)開時游戲就成功了弦悉。但是怎么做呢窒典?
最開始的想法很蠢,每次點(diǎn)擊或者挖開一個方塊時稽莉,都調(diào)用一個函數(shù)瀑志。這個函數(shù)的作用是什么呢?遍歷當(dāng)前所有isMined===false
的對象污秆,查看它的isClicked
屬性是否等于true劈猪。若所有方塊都已經(jīng)被挖開,那么游戲成功良拼。
聽起來思路其實(shí)很清晰战得,但這個方法不用說,太不節(jié)約了庸推。
最后決定給MineSweeping對象添加一個屬性count常侦,作用很簡單,每點(diǎn)開或者挖開一個方塊時贬媒,count數(shù)都+1聋亡,當(dāng)count數(shù)達(dá)到480-99(高級掃雷)時,即游戲成功掖蛤。