【愚公系列】2022年03月 微信小程序-視圖容器
859
2022-05-28
掃雷的介紹
掃雷就是要把所有非地雷的格子揭開即勝利;踩到地雷格子就算失敗。 游戲主區域由很多個方格組成。 使用鼠標左鍵隨機點擊一個方格,方格即被打開并顯示出方格中的數字;方格中數字則表示其周圍的8個方格隱藏了幾顆雷。
部分效果展示
游戲實現
開始準備
開始界面
我們可以首先打印菜單,提醒玩家是否開始游戲,通過while循環控制,可以達到一次玩完不過癮還能接著玩的目的
int main() { int input = 0; do { menu(); printf("請選擇:>"); scanf("%d", &input); switch (input) { case 1: printf("游戲開始!\n"); game();//封裝的游戲主體,后文介紹 break; case 0: printf("退出游戲\n"); system("pause"); break; default: printf("選擇錯誤,重新選擇!\n"); break; } } while (input); return 0; }
菜單函數如下
void menu() { printf("*********************************\n"); printf("*****歡迎游玩掃雷游戲!**********\n"); printf("*****made by 東條希爾薇**********\n"); printf("*******1. play***************\n"); printf("*******0. exit***************\n"); printf("*********************************\n"); }
打印效果如下
初始化棋盤
我們在這里需要定義兩個二維數組,一個數組用于展示給玩家,并儲存排雷信息,一個數組用于在后臺隨機生成雷并儲存。用兩個數組儲存可以有效的防止排雷過程中產生的歧義(例如,把雷設為‘1’,那么就不清楚‘1’到底是給玩家的提示還是放置的雷)。
當然,我們最好將雷設為‘1’,至于為什么這樣設置,見下文
void make_board(char board[ROWS][COLS], int rows, int cols, char set) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { board[i][j] = set; } } }
在初始化的時候
char show[ROWS][COLS];//ROWS已經在#define中定義為11,ROW,已定義為9 char mine[ROWS][COLS]; make_board(show, ROWS, COLS, '*');//將*設置為不知道信息的區域 make_board(mine, ROWS, COLS, '0');//0來初始化,設為非雷區
我們模擬的是簡單模式的99,那為什么要把數組初始化為1111的呢?
為了防止數組越界
畫圖示意
打印棋盤
我們只需要打印中間的9*9區域即可,而且只需要打印show數組,存儲雷的數組不需要顯示給玩家
void display_board(char board[ROWS][COLS], int rows, int cols) { int i = 0; int j = 0; for (i = 0; i < rows-1; i++)//打印行數,方便玩家操作 { printf("%d ", i); } printf("\n"); for (i = 1; i < rows - 1; i++) { printf("%d ", i);//打印列數 for (j = 1; j < cols - 1; j++) { printf("%c ", board[i][j]); } printf("\n"); } }
效果如下
隨機生成雷
我們可以使用stdlib.h中的rand函數來隨機生成雷的坐標,為了能保證每次游戲的隨機生成結果不同,可以在main函數中使用srand和time函數。
void set_mine(char board[ROWS][COLS], int row, int col) { int count = COUNT;//COUNT已用#define定義,方便后期維護,使用簡單難道的雷數 while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (board[x][y] != '1') { board[x][y] = '1'; count--;//只有在坐標沒被占用時才會放置,防止最后生成的雷數小于10個 } } }
準備工作分界線
排雷過程
排雷函數主體
需要玩家選擇排雷的坐標,并將該坐標周圍的信息反饋給玩家,當然,玩家可能會不慎選擇不合法的坐標,所以可以通過循環讓玩家反復選擇,直到選擇到合法的坐標為止
void find_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int count1 = 0; while (1) { printf("請選擇要排的坐標\n"); scanf("%d%d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col)//合法的坐標范圍 { if (mine[x][y] == '1') { printf("很遺憾,你被炸死了!\n");//玩家踩到雷了 display_board(mine, ROWS, COLS); system("pause"); break; } else { if (show[x][y] != '*') { printf("坐標被占用!重新輸入\n");//玩家輸入的坐標已經被開過了 } else { open_board(mine,show, ROW, COL, x, y);//后文會提到的信息函數 display_board(show, ROWS, COLS);//將每次更新的信息呈現給玩家 printf("你是否需要標記?1是 or 0否\n");//后文將會提到的標記函數 int ret = 0; scanf("%d", &ret); if (ret == 1) { printf("開始標記\n"); count1 = flag(mine, show, row, col); if (count1 == COUNT) { printf("恭喜,你贏了!\n"); break; } } else printf("繼續\n"); } } } else { printf("坐標非法!重新輸入!\n");//輸入坐標超出棋盤范圍 } } }
信息函數
這里我們使用了函數的遞歸,可以實現以下的效果,能有效防止反復操作的問題
void open_board(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col,int x,int y) { if (x >= 1 && x <= row && y >= 1 && y <= col) { if (show[x][y] == ' ') { return;//遞歸的停止條件 } else if (count_mine(mine, x, y) != 0)//判斷周圍是否有雷,不用直接遍歷判斷,有就呈現信息 { show[x][y] = count_mine(mine, x, y) + '0';//后文提到的計算雷數的函數 } else { show[x][y] = ' ';//標記,防止重復調用,造成遞歸死循環 for (int i = x - 1; i <= x + 1; i++) { for (int j = y - 1; j <= y + 1; j++) { open_board(mine, show, row, col, i, j);//查看以該坐標中心的周圍8格區域 } } } } }
計算周圍雷數的函數
這里將‘1’設為雷的優越性就體現出來了。
可以直接把‘1’減去字符0,將其轉化為數字相加,在呈現信息的時候再加上‘0’轉化為字符。
int count_mine(char mine[ROWS][COLS], int x, int y) { int count = 0; for (int i = x - 1; i <= x + 1; i++) { for (int j = y - 1; j <= y + 1; j++) { count += (mine[i][j] - '0'); } } return count; }
呈上效果圖
雷區標記函數
我們可以模擬掃雷游戲中的標記功能,供玩家標記他們認為有可能有雷的坐標,當然,如果標記錯了,可以隨時取消
int flag(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int input = 0; int count = 0; int x = 0; int y = 0; do { printf("請標記你認為的雷的位置,輸入已經標記的坐標已取消\n"); scanf("%d%d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col)//判斷坐標是否合法 { if (show[x][y] == '*') { show[x][y] = '$';//用$作為旗幟標記 for (int i = 1; i <= row; i++) { for (int j = 1; j <= col; j++) { if (show[i][j] == '$' && mine[i][j] == '1') { count++;//下文提到的判斷輸贏的方式 } } } display_board(show, ROWS, COLS);//每次標記完后打印棋盤 } else if (show[x][y] == '$') { show[x][y] = '*';//實現取消標記的功能 } else { printf("坐標被占用!\n");//坐標已經被開過或者已經被標記過 } } else { printf("坐標非法,重新輸入!\n"); } printf("是否繼續?1是 or 0否\n"); scanf("%d", &input); } while (input);//可以讓玩家一直標記,直到玩家主動停止標記 return count; }
判斷輸贏
我們上文中的flag函數有個返回值,返回show數組中的旗幟$和mine數組中的雷重合的個數,判斷,如果重合個數等于了COUNT就判斷贏了,如果踩到雷,就判斷輸了
count1 = flag(mine, show, row, col); if (count1 == COUNT) { printf("恭喜,你贏了!\n"); break; } if (mine[x][y] == '1') { printf("很遺憾,你被炸死了!\n"); display_board(mine, ROWS, COLS); system("pause"); break; }
為了驗證我們邏輯的正確性,將雷數設置為1,來測試一下我們的小游戲
完整代碼實現
main.c文件
#include"game.h" void game() { char show[ROWS][COLS]; char mine[ROWS][COLS]; make_board(show, ROWS, COLS, '*'); make_board(mine, ROWS, COLS, '0'); set_mine(mine, ROW, COL); display_board(show, ROWS, COLS); //display_board(mine, ROWS, COLS); find_mine(show, mine, ROW, COL); } int main() { srand((unsigned int)time(NULL)); int input = 0; do { menu(); printf("請選擇:>"); scanf("%d", &input); switch (input) { case 1: printf("游戲開始!\n"); game(); break; case 0: printf("退出游戲\n"); system("pause"); break; default: printf("選擇錯誤,重新選擇!\n"); break; } } while (input); return 0; }
game.c文件
#include"game.h" void menu() { printf("*********************************\n"); printf("*****歡迎游玩掃雷游戲!**********\n"); printf("*****made by 東條希爾薇**********\n"); printf("*******1. play***************\n"); printf("*******0. exit***************\n"); printf("*********************************\n"); } void make_board(char board[ROWS][COLS], int rows, int cols, char set) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { board[i][j] = set; } } } void display_board(char board[ROWS][COLS], int rows, int cols) { int i = 0; int j = 0; for (i = 0; i < rows-1; i++) { printf("%d ", i); } printf("\n"); for (i = 1; i < rows - 1; i++) { printf("%d ", i); for (j = 1; j < cols - 1; j++) { printf("%c ", board[i][j]); } printf("\n"); } } void set_mine(char board[ROWS][COLS], int row, int col) { int count = COUNT; while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (board[x][y] != '1') { board[x][y] = '1'; count--; } } } void find_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int count1 = 0; while (1) { printf("請選擇要排的坐標\n"); scanf("%d%d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == '1') { printf("很遺憾,你被炸死了!\n"); display_board(mine, ROWS, COLS); system("pause"); break; } else { if (show[x][y] != '*') { printf("坐標被占用!重新輸入\n"); } else { open_board(mine,show, ROW, COL, x, y); display_board(show, ROWS, COLS); printf("你是否需要標記?1是 or 0否\n"); int ret = 0; scanf("%d", &ret); if (ret == 1) { printf("開始標記\n"); count1 = flag(mine, show, row, col); if (count1 == COUNT) { printf("恭喜,你贏了!\n"); break; } } else printf("繼續\n"); } } } else { printf("坐標非法!重新輸入!\n"); } } } int count_mine(char mine[ROWS][COLS], int x, int y) { int count = 0; for (int i = x - 1; i <= x + 1; i++) { for (int j = y - 1; j <= y + 1; j++) { count += (mine[i][j] - '0'); } } return count; } int flag(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int input = 0; int count = 0; int x = 0; int y = 0; do { printf("請標記你認為的雷的位置,輸入已經標記的坐標已取消\n"); scanf("%d%d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (show[x][y] == '*') { show[x][y] = '$'; for (int i = 1; i <= row; i++) { for (int j = 1; j <= col; j++) { if (show[i][j] == '$' && mine[i][j] == '1') { count++; } } } display_board(show, ROWS, COLS); } else if (show[x][y] == '$') { show[x][y] = '*'; } else { printf("坐標被占用!\n"); } } else { printf("坐標非法,重新輸入!\n"); } printf("是否繼續?1是 or 0否\n"); scanf("%d", &input); } while (input); return count; } void open_board(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col,int x,int y) { if (x >= 1 && x <= row && y >= 1 && y <= col) { if (show[x][y] == ' ') { return; } else if (count_mine(mine, x, y) != 0)//判斷周圍是否有雷,不用直接遍歷判斷 { show[x][y] = count_mine(mine, x, y) + '0'; } else { show[x][y] = ' ';//標記,防止重復調用 for (int i = x - 1; i <= x + 1; i++) { for (int j = y - 1; j <= y + 1; j++) { open_board(mine, show, row, col, i, j); } } } } }
game.h文件
#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include
C 語言
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。