C初階-掃雷
上次完成了三子棋的做法,利用了二維陣列。掃雷也是一樣,需要我們利用二維陣列去完成。掃雷有所不同的地方在於,我們需要兩個二維陣列。一個用來展示棋盤,另外一個用於展示我們的下雷過程,因為每一個點選有可能是雷或者不是雷。
關於掃雷的程式設計思想我們與三子棋相比是有一些不同的地方。但歸途同屬,我們只需要想清楚每一個步驟就可以。
還是先建立一個工程,並且我還是習慣於一個頭檔案定義函式,一個原始檔測試。
我們首先設定一個選擇功能,讓我們在看的時候,比較美觀。畢竟好看的事務會給別人留下的印象也比較深。
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void Start(){
int choice == Menu();
while(1){
if(choice == 0){
break;
}
Game();
}
}
int Menu(){
printf("====================\n");
printf(" 1.開始遊戲\n");
printf(" 2.退出遊戲\n");
printf("====================\n") ;
int choice = 0;
scanf("%d",&choice);
return choice;
}
基本開始樣式就ok了。之後我們開始需要對每個關鍵步驟寫程式碼了。
我們先想象一下掃雷的規則與步驟。
1.我們首先得有一個地圖的樣式,然後初始化它最初的樣子
2.列印這個棋盤。(這兩步與三子棋的思路比較像)
3.進行點選,之後判斷點選的情況。
4.判定是否踩雷,如果踩雷,GG。
5.如果沒踩雷,判定是否掀開了所有格子。
6.如果沒有掀開所有格子。那麼更新周圍有幾個雷。顯示到地圖上。
3-6這是一個迴圈的過程,所以我們將用while來判斷什麼時候勝利或者失敗。
#define MAX_ROW 10
#define MAX_COL 10
#define DEFAULT_LEIZI_COUNT 10
void Game(){
char show_map[MAX_ROW + 2][MAX_COL + 2];
char mine_map[MAX_ROW + 2][MAX_COL + 2];
Init_map();
Print_map();
while(1){
int count = 0;
int row,col;
printf("請輸入座標,例如1 2\n");
scanf("%d",&row,&col);
if(row <= 0 || row > MAX_ROW || col <= 0 || col > MAX_COL){
printf("請重新輸入座標\n");
continue;
}
if(mine_map[row][col] == '1'){
printf("遊戲結束,此處有雷\n");
Print_map();
break;
}
if (count==DEFAULT_LEIZI_COUNT + 1){
printf("掃雷成功\n");
Print_map(mine_map);
break;
}
for (int i = 1; i <= MAX_ROW; i++){
for (int j = 1; j <= MAX_COL; j++){
if (show_map[i][j] == '*'){
count++;
}
}
}
if (count == DEFAULT_LEIZI_COUNT){
printf("掃雷成功\n");
Print_map(mine_map);
break;
}
Updateshowmap();
Showaround();
Print_map();
}
}
這就是上面描述的一個基本的流程。也就是掃雷對輸贏的判斷規則。接著對每一個函式去完善。函式內參數我是沒有直接新增進去的。我是都定義完成之後,才慢慢新增進去的。
首先是對我們地圖的一個初始化
void Init_map(char show_map[MAX_ROW + 2][MAX_COL + 2];
char mine_map[MAX_ROW + 2][MAX_COL + 2]){
//在這裡就要解釋一下,我們為什麼要加上一個2了。因為我們要保證我們需要一個邊框
//大多數人的潛意識中喜歡輸入都是1 1,這樣我們定義一個邊框,我們就可以將其上下包圍這些地圖
//那麼這個地圖將是從1 1到 10 10的所有點了。
for(int row = 0;row < MAX_ROW;row++){
for(int col = 0;col <MAX_COL;col++){
show_map[row][col] = '*';
}
}
for(int row = 0;row < MAX_ROW;row++){
for(int col = 0;col <MAX_COL;col++){
mine_map[row][col] = '*';
}
}
int mine_count = DEFAULT_LEIZI_COUNT;
while(mine_count > 0){
int row = rand() % 10 + 1;
int col = rand() % 10 + 1;
if(mine[row][col] == '1'){
continue;
}
mine[row][col] = '1';
mine_count--;
}
}
初始化完成之後,我們開始列印地圖
void Print_map((char map[MAX_ROW + 2][MAX_COL + 2],int row,int col){
printf(" ");
for(int i = 1;i < MAX_ROW + 2;i++){
printf("%d ",row);
}
printf("\n");
for(int i = 1;i < MAX_ROW +2 ;i++){
printf("---\n");
}
for(int row = 1;row < MAX_ROW + 2;row++){
printf("%02d |\n",row);
for(int col = 1;col < MAX_COL + 2;col++){
printf("%c ",map[row][col]);
}
printf("\n");
}
}
列印完地圖之後,就是掃雷中最關鍵的一個部分了。判斷周圍雷的個數,然後將它列印到地圖中。
void Updateshowmap(char show_map[MAX_ROW + 2][MAX_COL + 2];
char mine_map[MAX_ROW + 2][MAX_COL + 2],int row,int col){
int leizi_num = (mine_map[row][col + 1] - '0')+
(mine_map[row][col - 1] - '0')+
(mine_map[row + 1][col + 1] - '0')+
(mine_map[row + 1][col] - '0')+
(mine_map[row + 1][col - 1] - '0')+
(mine_map[row - 1][col + 1] - '0')+
(mine_map[row - 1][col] - '0')+
(mine_map[row - 1][col - 1] - '0');
show_map[row][col] = leizi_num + '0';
return show_map[row][col];
}
在這裡我是將周圍八個格子是否有雷的情況統計下來,然後將其賦給中間的格子,這樣我們就可以知道周圍有幾個雷了。
我們都知道掃雷的規則,當你點開一部分後,如果周圍沒雷的話,我們就將會點開空白區域。所以我們要寫一個遞迴來去不斷的呼叫。
void Showaround(char show_map[MAX_ROW + 2][MAX_COL + 2],
char mine_map[MAX_ROW + 2][MAX_COL + 2], int row, int col){
if (mine_map[row][col] == '0' && show_map[row][col] == '*'){
show_map[row][col] = Updateshowmap(show_map, mine_map, row, col);
}
if (mine_map[row][col - 1] == '0' && show_map[row][col - 1]=='*'){
show_map[row][col - 1] = Updateshowmap(show_map, mine_map, row, col - 1);
if (Updateshowmap(show_map, mine_map, row, col) == '0'){
Showaround(show_map, mine_map, row, col - 1);
}
}
if (mine_map[row + 1][col] == '0' && show_map[row + 1][col] == '*'){
show_map[row + 1][col] = Updateshowmap(show_map, mine_map, row + 1, col);
if (Updateshowmap(show_map, mine_map, row + 1, col) == '0'){
Showaround(show_map, mine_map, row + 1, col);
}
}
if (mine_map[row - 1][col] == '0' && show_map[row - 1][col] == '*'){
show_map[row - 1][col] = Updateshowmap(show_map, mine_map, row - 1, col);
if (Updateshowmap(show_map, mine_map, row - 1, col) == '0'){
Showaround(show_map, mine_map, row - 1, col);
}
}
if (mine_map[row][col + 1] == '0' && show_map[row][col + 1] == '*'){
show_map[row][col + 1] = Updateshowmap(show_map, mine_map, row, col + 1);
if (Updateshowmap(show_map, mine_map, row, col + 1) == '0'){
Showaround(show_map, mine_map, row, col + 1);
}
}
}
我覺得掃雷對周圍的判斷是根據上下左右來進行的。如果周圍沒雷,那麼它就是相當於空白的。當分成八個方向去判斷時,那麼就將產生一鍵排雷的這種奇葩操作。
最後就是一個原始檔了。將Start()放入測試即可。但是不要忘了隨機種子,否則掃雷的佈置雷數是沒有變化的。