1. 程式人生 > >C初階-掃雷

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()放入測試即可。但是不要忘了隨機種子,否則掃雷的佈置雷數是沒有變化的。