C语言——简易扫雷
最終效果:
目錄
1、總體邏輯
2、地圖布局
?3、埋雷
?4、打印地圖
5、掃雷
5.1、將每個雷的位置顯示到當前地圖上
5.2、計算出當前地圖剩余的非雷數
5.3、計算坐標周圍雷的個數
5.4、如果周圍沒有雷顯示空白并且向外擴展
6、總體
1、總體邏輯
掃雷的地圖以9x9的大小為例,需要一個二維數組來當地圖,并且要在這個地圖上進行埋雷、掃雷操作,還要將周圍的雷數顯示出來,所以僅用一個二維數組是不夠的,需要用兩個:
- 埋雷地圖:雷層,埋在土的下面
- 掃雷地圖:土層,鋪在雷的上面
- 同時掃雷地圖也就是展示出來的地圖
在掃雷的過程中:
- 如果掃到雷:游戲結束
- 如果不是雷:顯示周圍八個格子的雷數
- 如果周圍0個雷,那就繼續判斷周圍的周圍,直到周圍出現雷
2、地圖布局
以9x9的地圖為例,就需要兩個9x9大小的二維數組來作為地圖,但是想到在掃雷的過程中會對一個坐標的周圍其他八個坐標進行掃描,如果那個坐標是邊界上的坐標的話,那掃面周圍的坐標的話就需要進行另外的判斷,如圖:
?假如要輸入的坐標是(1,1),也就是左上角第一個格子,這時需要判斷他的周圍格子是否是雷,但是他的上方和左方都已經超過了邊界,所以就要對他進行單獨的判斷來保證坐標不會出現非法的情況,但是這種邊界的格子很多,每個都這樣判斷很麻煩,所以把地圖在原來的基礎上擴大一圈,只操作原來大小的地圖就可以了:
//地圖布局 #define ROW 9 //實際的雷區范圍 #define COL 9 #define ROWS ROW+2 //實際的數組范圍 #define COLS COL+2//雷數 #define COUNT 10char mine[ROWS][COLS]; //埋雷地圖、雷層 char show[ROWS][COLS]; //掃雷地圖、土層 memset(mine, ' ', sizeof(mine)); //初始化,空格表示沒有雷 memset(show, '.', sizeof(show)); //初始化,. 表示沒有被排查過?3、埋雷
埋雷的操作用生成隨機數來實現,要注意的是判斷生成的隨機數的范圍,不能超出了實際雷區大小的范圍,還有生成的坐標位置是否已經布置過雷了,用 * 來表示雷:
// 布置雷 void setMap(char mine[ROWS][COLS]) {int count = COUNT; //總雷數srand((unsigned)time(NULL)); //設置隨機數種子while (count) {int x = 1 + rand() % ROW; //范圍公式:1+(0--ROW-1) int y = 1 + rand() % COL;if (mine[x][y] == ' ') { //沒有布置雷的地方mine[x][y] = '*';count--;}} }?4、打印地圖
打印出的地圖是不知道雷的位置的圖,就是顯示的是”土層“,具體的雷層不會顯示出來,為了方便用戶輸入坐標,在地圖的最上面和最左邊打印出列號和行號:
// 打印界面 void print(char show[ROWS][COLS]) {// 這一部分是打印行數printf(" ");for (int i = 1; i <= ROW; i++)printf(" %d", i);printf("\n ");for (int i = 1; i <= ROW; i++)printf("--");printf("\n");//這一部分才是打印列數地圖for (int i = 1; i <= ROW; i++) {printf("%d|", i);for (int j = 1; j <= COL; j++) {printf("%c ", show[i][j]);}printf("\n");} }5、掃雷
掃雷通過用戶輸入坐標,來判斷該坐標是否是雷:
- 是雷:游戲結束、用戶被炸死
- 踩到雷的情況下,將每個雷的位置顯示到當前的掃雷地圖上
- 非雷:得到旁邊八個坐標的含雷數
- 如果為0:就繼續判斷周圍的周圍的含雷數,遞歸至碰到雷
- 如果非0:就顯示出雷的個數
就是如下圖的情況:
另外掃雷的過程中還需要判斷是否已經把雷全部掃出來了,就是判斷掃雷地圖上的 已掃出的 非雷的 數量,如果數量等于了 總格子數 - 雷數,那么游戲就可以結束了,并且將每個雷的位置顯示到當前的掃雷地圖上。
5.1、將每個雷的位置顯示到當前地圖上
就直接遍歷埋雷地圖,把雷的坐標拿到掃雷地圖,然后將掃雷地圖該坐標的符號改為雷的符號:
// 把展示界面的雷顯示出來 void show_mine(char mine[ROWS][COLS], char show[ROWS][COLS]) {for (int i = 1; i <= ROW; i++) {for (int j = 1; j <= COL; j++) {if (mine[i][j] == '*')show[i][j] = mine[i][j];}} }5.2、計算出當前地圖剩余的非雷數
還是遍歷,每次從頭遍歷 掃雷地圖上 實際雷區的 已被掃的區域:
// 計算全圖空白數 int get_blank_num(char show[ROWS][COLS]) {int count = 0;for (int i = 1; i <= ROW; i++) {for (int j = 1; j <= COL; j++) {if (show[i][j] != '.') //不等于 . 就表示掃過了count++;}}return count; }5.3、計算坐標周圍雷的個數
因為9x9的雷區范圍實際數組大小是11x11的,所以掃描哪一個坐標的周圍其他八個坐標都不用擔心坐標非法的問題,直接遍歷周圍就可以了:
// 計算周圍雷的個數 int get_mine_num(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++) {if (mine[i][j] == '*')count++;}}return count; }從x-1 和 y-1 開始遍歷到 x+1 和 y+1 是因為周圍八個坐標就在這個范圍內。
5.4、如果周圍沒有雷顯示空白并且向外擴展
如果一個坐標不是雷,且周圍也沒有雷,那應該往周圍的周圍擴展,判斷周圍的周圍是否有雷、有多少個雷,達到下圖效果:
需要注意的就是,遞歸的結束條件:
- 如果碰到雷,就返回
- 如果周圍的坐標已經被掃過了,那么不再遞歸進去該坐標
- 坐標也要合法
6、總體
整個掃雷的具體功能就是這些,有些功能并沒有講究代碼的美觀,交互和邏輯為了方便也就放在一起了,以下是全部代碼:
主程序文件
#define _CRT_SECURE_NO_WARNINGS 1 #include"game_head.h"// 菜單函數 void menu() {printf("***********************\n");printf("****** 1、play ******\n");printf("****** 0、exit ******\n");printf("***********************\n"); } int main(){system("title 掃雷2.0");int chose = 0;do {menu();printf("請輸入:>");scanf("%d", &chose);switch (chose) {case 1://進入游戲system("cls");game();break;case 0:printf("退出游戲!\n");return 0;default:printf("輸入錯誤!\n");}} while (chose);return 0; }程序頭文件
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<time.h> #include<stdlib.h> #include<string.h>//地圖布局 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 //雷數 #define COUNT 10//聲明 void game();程序功能文件
#include"game_head.h"// 打印界面 void print(char show[ROWS][COLS]) {// 這一部分是打印行數printf(" ");for (int i = 1; i <= ROW; i++)printf(" %d", i);printf("\n ");for (int i = 1; i <= ROW; i++)printf("--");printf("\n");//這一部分才是打印列數地圖for (int i = 1; i <= ROW; i++) {printf("%d|", i);for (int j = 1; j <= COL; j++) {printf("%c ", show[i][j]);}printf("\n");} } // 布置雷 void setMap(char mine[ROWS][COLS]) {int count = COUNT; //總雷數srand((unsigned)time(NULL));while (count) {int x = 1 + rand() % ROW;//1+(0--ROW-1) int y = 1 + rand() % COL;if (mine[x][y] == ' ') {mine[x][y] = '*';count--;}} } // 計算周圍雷的個數 int get_mine_num(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++) {if (mine[i][j] == '*')count++;}}return count; } // 計算全圖空白數 int get_blank_num(char show[ROWS][COLS]) {int count = 0;for (int i = 1; i <= ROW; i++) {for (int j = 1; j <= COL; j++) {if (show[i][j] != '.')count++;}}return count; } //掃雷具體 void sweep2(char show[ROWS][COLS],char mine[ROWS][COLS], int x, int y) {if (mine[x][y] == '*') return; // 是雷就返回if (x > 0 && x <= ROW && y > 0 && y <= COL) { // 坐標合法int mine_num = get_mine_num(mine, x, y); //得到周圍雷數if (mine_num == 0) { //如果周圍沒有雷,那就要顯示出周圍的空白,用遞歸,要判斷周圍的周圍坐標是否合法、是否已經被掃過了show[x][y] = ' ';if (show[x][y - 1] != ' ' && y - 1 > 0)sweep2(show, mine, x, y - 1);if (show[x][y + 1] != ' ' && y + 1 <= COL)sweep2(show, mine, x, y + 1);if (show[x - 1][y - 1] != ' ' && x - 1 > 0 && y - 1 > 0)sweep2(show, mine, x - 1, y - 1);if (show[x - 1][y] != ' ' && x - 1 > 0)sweep2(show, mine, x - 1, y);if (show[x - 1][y + 1] != ' ' && x - 1 > 0 && y + 1 <= COL)sweep2(show, mine, x - 1, y + 1);if (show[x + 1][y - 1] != ' ' && x + 1 <= ROW && y - 1 > 0)sweep2(show, mine, x + 1, y - 1);if (show[x + 1][y] != ' ' && x + 1 <= ROW)sweep2(show, mine, x + 1, y);if (show[x + 1][y + 1] != ' ' && x + 1 <= ROW && y + 1 <= COL)sweep2(show, mine, x + 1, y + 1);}else {show[x][y] = mine_num + '0';}} } // 把展示界面的雷顯示出來 void show_mine(char mine[ROWS][COLS], char show[ROWS][COLS]) {for (int i = 1; i <= ROW; i++) {for (int j = 1; j <= COL; j++) {if (mine[i][j] == '*')show[i][j] = mine[i][j];}} } // 掃雷 void sweep(char show[ROWS][COLS],char mine[ROWS][COLS]) {int x = 0;int y = 0;int blank_num = 0;while (blank_num != ROW * COL - COUNT) {print(show);printf("輸入坐標:>");scanf("%d%d", &x, &y);if (x > 0 && x <=ROW && y > 0 && y <= COL) {//判斷是不是雷if (mine[x][y] == '*') {system("cls");show_mine(mine, show);printf(" 你輸入的坐標:(%d,%d)\n", x, y);print(show);printf("很遺憾,你掛了!\n");return;}else {//判斷周圍的雷sweep2(show, mine, x, y);system("cls");printf(" 你輸入的坐標:(%d,%d)\n", x, y);}blank_num = get_blank_num(show);}else {system("cls");printf(" (%d,%d)坐標非法!\n",x,y);}}if (blank_num == ROW * COL - COUNT) { // 掃完所有的雷了show_mine(mine, show);system("cls");print(show);printf("恭喜你,挑戰成功!\n");}} // 進入游戲 void game() {//定義兩個數組,一個是展示的界面,一個是存雷的界面char mine[ROWS][COLS]; // 存雷char show[ROWS][COLS]; //展示memset(mine, ' ', sizeof(mine)); //初始化,空格表示沒有雷memset(show, '.', sizeof(show)); //初始化,. 表示沒有被排查過//布置雷setMap(mine);//掃雷sweep(show, mine); }總結