《扫雷》游戏递归算法分析
掃雷游戲中在點擊空白塊的時候,如果遇到了數(shù)字為空的空白塊那么他會自動的幫助點擊空板塊周圍的所有空白塊。那么在自動點開的空白塊中還是遇到了數(shù)字為空的空白塊,他就又會幫助點擊其周圍的空白塊。
效果如下圖:
為了方便理解我將空白的方塊以數(shù)字0標(biāo)識。
那么可以設(shè)計遞歸函數(shù)。如下:
結(jié)構(gòu)體如下:
struct Block {int x; // 格子的橫坐標(biāo)int y; // 格子的縱坐標(biāo) };enum LeiStatus { isNum = 0, // 該空白格為數(shù)字 isLei // 該空白格為雷};enum ShowStatus {hideNum = 0,showNum,isOk, // 玩家確認(rèn)這個是雷,即給這個格子標(biāo)上小旗isAsk // 不確定這個是什么,給這個格子標(biāo)上問號};/*** @brief The Cell 每一個格子的結(jié)構(gòu)體*/ struct Cell : Block {LeiStatus leiStatus;ShowStatus showStatus;int num; // 表示每個位置的上下左右八個位置的雷的數(shù)據(jù),如果雷的狀態(tài)是Cell(){leiStatus = isNum;num = 0;showStatus = hideNum;} };當(dāng)前格子的周圍格子的標(biāo)識如下圖:
x,y為基礎(chǔ)坐標(biāo),當(dāng)自動點擊x-1,y-1的時候,又會以x-1,y-1作為新的基礎(chǔ)坐標(biāo)進(jìn)行計算。因此可以寫出一下算法。
我的空白格存儲沒有使用二維數(shù)組而是使用一位數(shù)組來表示二維數(shù)組,算法如下:
m_max_block_x 表示橫向坐標(biāo)的類的最大數(shù)字。
int QGameWidget::getIndex(int block_x, int block_y) {return block_x * m_max_block_x + block_y; }第一版本
第一版本比較容易理解,將所有的格子都傳遞一遍讓其進(jìn)行更新。
void QGameWidget::updateLei(int block_x, int block_y) {m_cells[getIndex(block_x, block_y)].showStatus = showNum;if (m_cells[getIndex(block_x, block_y)].num != 0) {return;}// 右上角if (block_x - 1 >= 0 && block_y - 1 >= 0 &&m_cells[getIndex(block_x - 1, block_y - 1)].showStatus == hideNum) {m_cells[getIndex(block_x - 1, block_y - 1)].showStatus = showNum;if (m_cells[getIndex(block_x - 1, block_y - 1)].num == 0)updateLei(block_x - 1, block_y - 1);}// 上if (block_y - 1 >= 0 &&m_cells[getIndex(block_x, block_y - 1)].showStatus == hideNum) {m_cells[getIndex(block_x, block_y - 1)].showStatus = showNum;if (m_cells[getIndex(block_x, block_y - 1)].num == 0)updateLei(block_x, block_y - 1);}// 左上if (block_x + 1 < m_max_block_x && block_y - 1 >= 0 &&m_cells[getIndex(block_x + 1, block_y - 1)].showStatus == hideNum) {m_cells[getIndex(block_x + 1, block_y - 1)].showStatus = showNum;if (m_cells[getIndex(block_x + 1, block_y - 1)].num == 0)updateLei(block_x + 1, block_y - 1);}// 左if (block_x + 1 < m_max_block_x &&m_cells[getIndex(block_x + 1, block_y)].showStatus == hideNum) {m_cells[getIndex(block_x + 1, block_y)].showStatus = showNum;if (m_cells[getIndex(block_x + 1, block_y)].num == 0)updateLei(block_x + 1, block_y);}// 右下if (block_x + 1 < m_max_block_x && block_y + 1 < m_max_block_y &&m_cells[getIndex(block_x + 1, block_y + 1)].showStatus == hideNum) {m_cells[getIndex(block_x + 1, block_y + 1)].showStatus = showNum;if (m_cells[getIndex(block_x + 1, block_y + 1)].num == 0)updateLei(block_x + 1, block_y + 1);}// 下if (block_y + 1 < m_max_block_y &&m_cells[getIndex(block_x, block_y + 1)].showStatus == hideNum) {m_cells[getIndex(block_x, block_y + 1)].showStatus = showNum;if (m_cells[getIndex(block_x, block_y + 1)].num == 0)updateLei(block_x, block_y + 1);}// 左下if (block_x - 1 >= 0 && block_y + 1 < m_max_block_y &&m_cells[getIndex(block_x - 1, block_y + 1)].showStatus == hideNum) {m_cells[getIndex(block_x - 1, block_y + 1)].showStatus = showNum;if (m_cells[getIndex(block_x - 1, block_y + 1)].num == 0)updateLei(block_x - 1, block_y + 1);}// 右if (block_x - 1 >= 0 &&m_cells[getIndex(block_x - 1, block_y)].showStatus == hideNum) {m_cells[getIndex(block_x - 1, block_y)].showStatus = showNum;if (m_cells[getIndex(block_x - 1, block_y)].num == 0)updateLei(block_x - 1, block_y);}return; }?第二版
可以看到每一個便宜的格子在計算之前都對其進(jìn)行合理性的處理。
例如(x-1,y-1)左上角的坐標(biāo),他的值是不能在游戲設(shè)定的范圍意外的。如下圖:紅色部分為異常部分因此需要將其剔除。那么我們在這個地方是可以優(yōu)化的。因為我使用的是一位數(shù)組轉(zhuǎn)代替二維數(shù)組的方式,因此我只需要對一維數(shù)組進(jìn)行判斷就行,我們可以放在函數(shù)內(nèi)部進(jìn)行。代碼可以這樣寫
int QGameWidget::getIndex(int block_x, int block_y) {if(block_x < 0 || block_y < 0 )return -1;if(block_x >= m_max_block_x || block_y >= m_max_block_y ) return -2;return block_x * m_max_block_x + block_y; } void QGameWidget::updateLei(int block_x, int block_y) {if (getIndex(block_x, block_y) < 0 ||getIndex(block_x, block_y) >= m_max_block_x * m_max_block_y) {return;}// 為避免死循環(huán),因此這里需要判斷該方塊是否已經(jīng)被點擊,如果沒被點擊就設(shè)置其狀態(tài)位點擊狀態(tài)。if (m_cells[getIndex(block_x, block_y)].showStatus == hideNum) {m_cells[getIndex(block_x, block_y)].showStatus = showNum;if (m_cells[getIndex(block_x, block_y)].num == 0) {updateLei(block_x - 1, block_y - 1);updateLei(block_x, block_y - 1);updateLei(block_x + 1, block_y - 1);updateLei(block_x + 1, block_y);updateLei(block_x + 1, block_y + 1);updateLei(block_x, block_y + 1);updateLei(block_x - 1, block_y + 1);updateLei(block_x - 1, block_y);}}return; }?第三版
第三版是發(fā)現(xiàn)這個-1.+1啥的很容易寫錯因此可以這樣寫。
void QGameWidget::updateLei(int block_x, int block_y) {if (getIndex(block_x, block_y) < 0 ||getIndex(block_x, block_y) >= m_max_block_x * m_max_block_y) {return;}if (m_cells[getIndex(block_x, block_y)].showStatus == hideNum) {m_cells[getIndex(block_x, block_y)].showStatus = showNum;if (m_cells[getIndex(block_x, block_y)].num == 0) {for(int i = - 1; i <=1;i++){for(int j =-1;j<=1; j++){if(!(i == 0&& j ==0))updateLei(block_x + i, block_y + j);}}}}return; }第四版
由于getIndex頻繁被調(diào)用因此可以修改為第一次調(diào)用之后復(fù)用,這樣雖然浪費了一點控件,但是節(jié)約了很多的算法時間。
代碼如下:
void QGameWidget::updateLei(int block_x, int block_y) {// 計算坐標(biāo)對應(yīng)的索引int index = getIndex(block_x, block_y);if (index < 0 ||index >= m_max_block_x * m_max_block_y) {return;}if (m_cells[index].showStatus == hideNum) {m_cells[index].showStatus = showNum;if (m_cells[index].num == 0) {for(int i = - 1; i <=1;i++){for(int j =-1;j<=1; j++){if(!(i == 0&& j ==0))updateLei(block_x + i, block_y + j);}}}}return; }隨著代碼的一步一步優(yōu)化最終得到算法函數(shù)。
C++實現(xiàn)《掃雷》游戲(入門經(jīng)典)_啊淵的博客-CSDN博客
總結(jié)
以上是生活随笔為你收集整理的《扫雷》游戏递归算法分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 7.腾讯微博Android客户端开发——
- 下一篇: Docker( 八)docker的安全以