這次打算以掃雷遊戲,做一個Qt界面設計的總結過程
這第一篇以介紹掃雷的算法,實現一個控制檯版本爲起點
先來查看掃雷遊戲的程序設計,玩法就不介紹了:1、首先便是要隨機生成地雷,可以利用rand生成
這裏以一個簡單的9*9的一維數組模擬2維數組作爲存儲結構,以簡單的幾個數字來標記每個方塊的狀態-1表示有雷0-8表示周圍八個方塊的雷數情況10表示右鍵點擊標記的真雷11表示右鍵點擊標記的假雷,也就是猜錯了,其實這不是雷12表示左鍵點擊的無雷區這就是用來表示方塊數據的數據結構
2、每局開始之前,先遍歷一邊所有的方塊,將雷數緩存下來,這樣,之後再使用時可以減少計算,當地圖生成之後,這個就可以計算了
3、關於繪製圖像,即以左鍵點擊和右鍵點擊來決定繪製的圖片是怎樣的,不過作爲控制檯程序,這裏就是以下方式顯示
"*"表示未被點擊狀態
"F" 表示右鍵標記狀態,即假定這是個雷
" " 空格表示左鍵點擊,且此處無雷
4、關於左鍵點擊之後,即應該遍歷判斷點擊區域周圍的八個方向,若該方向被點擊或有雷則停止,若遍歷的方塊的雷數仍然爲0,則繼續遞歸判斷,直到不滿足條件,或超出邊界。
這裏關於掃雷的大體設計即介紹完畢,接下來以代碼演示看一下:
1、創建雷區
// 初始化8個雷
int map[9 * 9] = {0};
int minesTotal = 8;
for (int i = 0; i < minesTotal; ++i)
{
map[i] = -1;
}
// 此函數由STL算法提供,可用於將數組亂序
random_shuffle(map, map + 9 * 9);
通過上述代碼,即構建好雷區,這裏利用random_shuffle來生成隨機地圖
2、迭代整個地圖,來緩存每個方塊存儲的雷數
// 預先計算,緩存雷數
for (int i = 0; i < 9 * 9; ++i)
{
int row = i / 9;
int col = i % 9;
countMine(row, col, map);
}
現在查k難countMine函數,這裏先看一個最直接簡單的方式
int toIndex(int row, int col)
{
return row * 9 + col;
}
// 這裏用最直接的方式,8個if,每個都計算一個鄰近方塊的有雷情況
void countMine(int row, int col, int *map)
{
if (map[toIndex(row, col)] == -1)
return;
int nums = 0;
if (row-1 >= 0 && col-1 >=0 && map[toIndex(row-1, col-1)] == -1)
++nums;
if (row-1 >= 0 && map[toIndex(row-1, col)] == -1)
++nums;
if (row-1 >= 0 && col+1 <=8 && map[toIndex(row-1, col+1)] == -1)
++nums;
if (col-1 >=0 && map[toIndex(row, col-1)] == -1)
++nums;
if (col+1 <=8 && map[toIndex(row, col+1)] == -1)
++nums;
if (row+1 <= 8 && col-1 >=0 && map[toIndex(row+1, col-1)] == -1)
++nums;
if (row+1 <= 8 && map[toIndex(row+1, col)] == -1)
++nums;
if (row+1 <= 8 && col+1 <=8 && map[toIndex(row+1, col+1)] == -1)
++nums;
map[toIndex(row, col)] = nums;
}
哈哈,這種方式,不動腦子哦,下面用循環改寫下
bool isMine(int row, int col, int *map)
{
int index = toIndex(row, col);
return (map[index] == -1)
}
bool isValidIndex(int row, int col)
{
if (0 <= row && row < 9 && 0 <= col && col < 9)
return true;
return false;
}
void countMine(int row, int col, int *map)
{
if (isMine(row, col, map))
return;
int minesCount = 0;
for (int aroundRow = row - 1; aroundRow <= row + 1; ++aroundRow)
{
for (int aroundCol = col - 1; aroundCol <= col + 1; ++aroundCol)
{
// 判斷鄰近周圍8個方塊的是否有雷,來統計本方塊的雷數
bool bvalidIndex = isValidIndex(aroundRow, aroundCol);
bool bSameBlock = (row == aroundRow && col == aroundCol);
bool bMine = isMine(aroundRow, aroundCol);
if (bvalidIndex && !bSameBlock && isMine)
++minesCount;
}
}
map[toIndex(row, col)] = minesCount;
}
利用循環,改寫上述重複的if判斷
3、處理完成之後,應該遊戲開始,這裏即以一個死循環開始,滿足勝利或失敗條件即退出
while (true)
{
// 輸出歡迎語
cout << "\ninput the cordinate (like 5 5, & f for flag, e for empty)\n";
// 獲取輸入
int row, col;
char ch;
cin >> row >> col >> ch;
// 檢查此次的輸入,應該達到的顯示效果
if (checkMap(row - 1, col - 1, ch, map))
{
// 這裏不小心點到雷即退出遊戲,這裏滿足失敗條件
cout << "\n You Lose !!!!!!!!!!!!!!!!\n\n";
break;
}
if (minesRemain == 0)
{
// 這裏滿足勝利條件
cout << "\n You Win !!!!!!!!!!!!!!!!!\n\n";
break;
}
// 顯示打印地圖,此輪結束,繼續下輪
showMap(map);
}
可以看到,整個遊戲的邏輯判斷,即位於checkMap中
checkMap很簡單,只是依據輸入,進行判斷,邏輯沒有複雜的,唯一就是在點擊無雷區時,需要判斷是否需要展開周圍雷區
// 返回真表示點到雷,掛掉
// 假則表示這次點擊正確
bool checkMap(int row, int col, char ch, int *map)
{
bool status = false;
if (isMine(row, col, map))
{
if (ch == 'f')
{
map[toIndex(row, col)] = 10;
--minesRemain;
}
else if (ch == 'e')
{
status = true;
}
}
else
{
if (ch == 'f')
{
map[toIndex(row, col)] = 11;
}
else if (ch == 'e')
{
map[toIndex(row, col)] = 12;
checkAround(row, col, map);
}
}
return status;
}
checkAround的實現,就如所countMines中一樣,這裏直接利用循環,遍歷鄰近8個方塊,然後繼續判斷是否需要進行遞歸
整個函數只有在該方塊的雷數爲0的情況下才需要進行遞歸判斷,若雷數不爲0,則不需要再去判斷周圍雷區情況。
void checkAround(int row, int col, int *map)
{
if (map[toIndex(row, col)] == 0)
{
for (int aroundRow = row - 1; aroundRow <= row + 1; ++aroundRow)
{
for (int aroundCol = col - 1; aroundCol <= col + 1; ++aroundCol)
{
bool bvalidIndex = isValidIndex(aroundRow, aroundCol);
bool bSameBlock = (row == aroundRow && col == aroundCol);
if (bvalidIndex && !bSameBlock)
{
if (map[toIndex(aroundRow, aroundCol)] == 12)
continue;
// 設置此處被點擊,以後遍歷直接跳過
map[toIndex(aroundRow, aroundCol)]= 12;
checkAround(aroundRow, aroundCol, map);
}
}
}
}
}
至此,遊戲主體和邏輯已經介紹完畢,之後的打印函數,就不在顯示了。
下面給出,控制檯版本的代碼,這部分代碼已經把上述封裝成類,有興趣的可以前去下載。
http://download.csdn.net/detail/satanzw/5825767