實現準備
分2個.c源文件和1個.h頭文件去寫代碼
- test.c 對掃雷游戲進行測試
- game.c 掃雷游戲功能的實現
- game.h 掃雷游戲功能的聲明
掃雷游戲
1.test.c對掃雷游戲進行測試
首先我們要先把玩游戲的框架寫出來,然后一步一步去完成其功能
跟著下面的代碼的節奏走一步一步來,我會標記步驟
需要理解的地方,我都會解釋,不理解的可以私信我
//scanf和printf需要用到該頭文件
#include <stdio.h>//3
//菜單
void menu()
{printf("***********************\n");printf("*** 1. play ***\n");printf("*** 0. exit ***\n");printf("***********************\n");
}//4
//掃雷游戲的實現
//這個掃雷游戲的具體功能下面再說
void game()
{printf("掃雷游戲\n");//這里只是為了測試邏輯
}//2
void test()
{int input = 0;//用do while循環是讓其上來就打印菜單以供我們選擇do{menu();//菜單printf("請選擇:");scanf("%d", &input);switch (input){case 1:game();//掃雷游戲的實現break;case 0:printf("退出游戲\n");break;default:printf("選擇錯誤\n");break;}} while (input);//while()中放input是因為游戲退出的條件就是input為0
}//1
int main()
{test();//測試掃雷游戲return 0;
}
這樣我們就搭建了一個掃雷游戲測試框架
下面我們對代碼進行測試
接下來就是對掃雷游戲的實現
2.掃雷游戲的實現
接下來是test.c和game.c和game.h配合著實現掃雷游戲
在test.c的game()函數中
void game()
{char mine[11][11];//雷的信息char show[11][11];//展示界面
}
我們用兩個字符數組:一個表示雷的信息,另一個是展示給玩家看的界面
實現9×9的掃雷棋盤,至于為什么它們的行和列都是11排查雷的時候會解釋
因為11這個數字我們在這里會經常用到,而且為了使棋盤大小可以簡易
變換,這里我們用到了#define
在game.h中
#pragma once#include <stdio.h>#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2
這里為什么#define了ROW、ROWS和COL、COLS后面會說
那么在test.c的game()函數中
#include "game.h"void game()
{char mine[ROWS][COLS];//雷的信息char show[ROWS][COLS];//展示界面
}
在test.c中用了game.h的東西,要引用頭文件game.h
為什么用" “而不用< >
自己寫的用” " 編譯器提供的用< >
為了方便直接把這里所有要用到的庫函數的頭文件全部放在game.h中
而在test.c中只用include “game.h” 就行了
接下來是初始化棋盤
初始化棋盤InitBoard
test.c的game()中
void game()
{char mine[ROWS][COLS];//雷的信息char show[ROWS][COLS];//展示界面//初始化棋盤InitBoard(mine, ROWS, COLS, '0');//'0'InitBoard(show, ROWS, COLS, '*');//'*'
}
把字符數組mine全部初始化為字符零’0’
把字符數組show全部初始化為字符星號’*’
game.h中
//初始化棋盤
void InitBoard(char board[ROWS][COLS], int r, int c, char set);
對該函數進行聲明,set是要初始化的字符是什么
game.c中
#include "game.h"void InitBoard(char board[ROWS][COLS], int r, int c, char set)
{int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){board[i][j] = set;}}
}
運用for循環把字符數組初始化
接下來是打印棋盤
打印棋盤DisplayBoard
test.c的game()中
void game()
{char mine[ROWS][COLS];//雷的信息char show[ROWS][COLS];//展示界面//初始化棋盤InitBoard(mine, ROWS, COLS, '0');//'0'InitBoard(show, ROWS, COLS, '*');//'*'//打印棋盤DisplayBoard(show, ROW, COL);DisplayBoard(mine, ROW, COL);
}
我們只是要打印9×9的棋盤所以傳參用ROW和COL
game.h中
//打印棋盤
void DisplayBoard(char board[ROWS][COLS], int r, int c);
game.c中
void DisplayBoard(char board[ROWS][COLS], int r, int c)
{printf("------掃雷游戲-------\n");int i = 0;for (i = 0; i <= c; i++){printf("%d ", i);//列}printf("\n");for (i = 1; i <= r; i++){printf("%d ", i);//行int j = 0;for (j = 1; j <= c; j++){printf("%c ", board[i][j]);}printf("\n");}
}
我們用圖來表示如何實現
我們要知道的是打印11×11中的9×9
因為我們要實現9×9的棋盤
test.c的game()中
//打印棋盤DisplayBoard(show, ROW, COL);//DisplayBoard(mine, ROW, COL);
這個mine數組的打印只是讓我們看看是否真的初始化了
實際游戲中是不會打印它的
布置雷SetMine
test.c的game()中
void game()
{char mine[ROWS][COLS];//雷的信息char show[ROWS][COLS];//展示界面//初始化棋盤InitBoard(mine, ROWS, COLS, '0');//'0'InitBoard(show, ROWS, COLS, '*');//'*'//打印棋盤DisplayBoard(show, ROW, COL);//布置雷 //布置雷也是在9×9的棋盤中布置,所以傳ROW和COLSetMine(mine, ROW, COL);DisplayBoard(mine, ROW, COL);//這里是布置完雷打印出來看看//是否真的布置雷了,真正玩游戲的時候是不可能把雷的位置打印出來的
}
game.h中
//布置雷
void SetMine(char mine[ROWS][COLS], int r, int c);
因為我們只會在mine數組中布置,所以這里的形參數組也直接用mine了
之前是因為兩個數組都要用,所以用的board數組,board意思就是棋盤
game.c中
void SetMine(char mine[ROWS][COLS], int r, int c)
{int x = 0;int y = 0;int count = 0;while (count < EASYCOUNT){x = rand() % r + 1;y = rand() % c + 1;if (mine[x][y] == '0'){mine[x][y] = '1';count++;}}
}
x和y是布置的雷的坐標
count起到計數的作用
EASYCOUNT是布置的雷的個數
在game.h中#define了
#define EASYCOUNT 10
簡單版本是10個雷
rand()函數是生成隨機數的函數
因為要讓電腦隨機布置雷
而rand()要配合srand()和time()使用
這三個函數我的猜數字游戲里講到了這里就不講怎么用了
x = rand() % r + 1;y = rand() % c + 1;
因為要生成的坐標的范圍是(1,1) -> (9,9)
這里 r 和 c 都是9,rand() % 9生成0 ~ 8的隨機數 +1 就是1 ~ 9
在test.c的test()中
void test()
{srand((unsigned int)time(NULL));//生成一個隨機數的生成起點int input = 0;下面是之前的代碼,不展示了,占地方
在game.h中
#pragma once
#include <stdio.h>
#include <stdlib.h>//rand和srand要用的頭文件
#include <time.h>//time要用的頭文件#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2#define EASYCOUNT 10
下面的代碼也不展示了
game.c的SetMine中
if (mine[x][y] == '0'){mine[x][y] = '1';count++;}
這段代碼的意思的把mine數組中的 ‘0’ 改成 ‘1’
而 ‘0’ 表示不是雷,‘1’ 表示雷,直到布置10個雷。
下面圖片展示
排查雷FindMine
在test.c的game()中
void game()
{char mine[ROWS][COLS];//雷的信息char show[ROWS][COLS];//展示界面//初始化棋盤InitBoard(mine, ROWS, COLS, '0');//'0'InitBoard(show, ROWS, COLS, '*');//'*'//打印棋盤DisplayBoard(show, ROW, COL);//布置雷SetMine(mine, ROW, COL);DisplayBoard(mine, ROW, COL);//排查雷FindMine(mine, show, ROW, COL);
}
排查雷兩個數組都要用到
因為展示給玩家的是show數組
玩家在排查雷的時候,該函數要對照mine數組看看該坐標是不是雷
game.h中
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c);
game.c中
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c)
{int x = 0;//玩家輸入的橫坐標int y = 0;//玩家輸入的縱坐標int win = 0;//獲勝計數while (win < r * c - EASYCOUNT)//這里是有10個雷,那么獲勝的條件就是排查出71個不是雷的坐標//win == 71 時,出循環{printf("請輸入要排查的坐標:");scanf("%d%d", &x, &y);//輸入的坐標必須合法//輸入的坐標的合法性if (x >= 1 && x <= r && y >= 1 && y <= c)//合法條件{//排查過的坐標不能再次排查//判斷該坐標是否已經被排查過if (show[x][y] == '*' || show[x][y] == '!')//我們是在show數組中排查的//'*'表示還沒有排查過//'!'是標記雷,萬一標記的地方不是雷但是我們標記了//也可以排查{//判斷該坐標是否為雷if (mine[x][y] == '1')//是雷//用mine數組來看(x,y)坐標是不是雷{printf("很遺憾,你被炸死了\n");DisplayBoard(mine, r, c);//游戲結束,展示哪里是雷break;//游戲結束,跳出循環}else//不是雷{//我們知道網頁版的掃雷游戲,該坐標周圍有雷的話,會顯示雷的個數//這也就是mine和show為什么是11×11的原因//因為9×9的話,角落的坐標找周圍雷的個數會越界訪問//得到周圍8個坐標的雷的個數int count = GetMineCount(mine, x, y);show[x][y] = count + '0';//把數字轉為字符放在show數組中展示給玩家看if (count == 0){//展開一片win = UnfoldBoard(mine, show, x, y, win, r, c);}else{win++;}//得到雷得個數后進行展示DisplayBoard(show, r, c);//標記雷的位置MarkMine(show, r, c);}}else//排查過{printf("該坐標已經被排查過\n");}}else//不合法{printf("輸入的坐標不合法\n");}}if (win == r * c - EASYCOUNT)//獲勝條件{printf("恭喜你,排雷成功\n");}
}
得到周圍雷得個數GetMineCount
game.c中
static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] +mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] +mine[x + 1][y] + mine[x + 1][y + 1] - (8 * '0');
}
周圍有8個坐標
已知:
‘0’ - ‘0’ -> 0
‘1’ - ‘0’ -> 1
…
‘8’ - ‘0’ -> 8
所以把周圍8個坐標的字符全部加起來再減去8個字符零就是周圍雷的個數
展開一片UnfoldBoard
game.c中
static int UnfoldBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int win, int r, int c,)
{//遞歸條件:遞歸的坐標必須合法if(x >= 1 && x <= r && y >= 1 && y <= c){if(mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y - 1] +mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] +mine[x + 1][y] + mine[x + 1][y + 1] - (8 * '0') == 0)//達到展開條件才遞歸{win++;//勝利計數show[x][y] = ' ';//將要遞歸的坐標改為空格int i = -1;int j = -1;for (i = -1; i <= 1; i++){for (j = -1; j <= 1; j++){if(show[x+i][y+i] == '*'){win = UnfoldBoard(mine, show, x + i, y + i, win, r, c);//遞歸 返回win 勝利計數}}}}else{win++;//勝利計數int count = GetMineCount(mine, x, y);show[x][y] = count + '0';}}return win;//返回勝利計數
}
因為我們這里勝利的條件是win == 71,所以展開一片的時候必須記錄win
否則win就到不了71了
展開一片的遞歸這里沒法細講,如果不理解可以私信我
標記雷的位置MarkMine
game.c中
static void MarkMine(char show[ROWS][COLS], int r, int c)
{printf("是否標記雷的位置: 1.標記 0.不標記\n");int input = 0;scanf("%d". &input);while(input){printf("請輸入要標記的坐標:");int x = 0;int y = 0;scanf("%d %d", &x, &y);show[x][y] = '!';//'!'表示要標記的雷的位置DisplayBoard(show, r, c);//標記后展示printf("繼續標記請輸入:“1”,否則輸入“0”\n");scanf("%d", &input);}
}
到了這里整個掃雷游戲就結束了
這里我們把雷的數量設置為2來測驗一下
掃雷游戲完整代碼
test.c的代碼
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"void menu()
{printf("***********************\n");printf("*** 1. play ***\n");printf("*** 0. exit ***\n");printf("***********************\n");
}void game()
{char mine[ROWS][COLS];//雷的信息char show[ROWS][COLS];//展示界面//初始化棋盤InitBoard(mine, ROWS, COLS, '0');//'0'InitBoard(show, ROWS, COLS, '*');//'*'//打印棋盤DisplayBoard(show, ROW, COL);//布置雷SetMine(mine, ROW, COL);//DisplayBoard(mine, ROW, COL);//排查雷FindMine(mine, show, ROW, COL);
}void test()
{srand((unsigned int)time(NULL));int input = 0;do{menu();printf("請選擇:");scanf("%d", &input);switch (input){case 1:game();break;case 0:printf("退出游戲\n");break;default:printf("選擇錯誤\n");break;}} while (input);
}int main()
{test();return 0;
}
game.h的代碼
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2#define EASYCOUNT 10//初始化棋盤
void InitBoard(char board[ROWS][COLS], int r, int c, char set);//打印棋盤
void DisplayBoard(char board[ROWS][COLS], int r, int c);//布置雷
void SetMine(char mine[ROWS][COLS], int r, int c);//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c);
game.c的代碼
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"void InitBoard(char board[ROWS][COLS], int r, int c, char set)
{int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){board[i][j] = set;}}
}void DisplayBoard(char board[ROWS][COLS], int r, int c)
{printf("------掃雷游戲-------\n");int i = 0;for (i = 0; i <= c; i++){printf("%d ", i);//列}printf("\n");for (i = 1; i <= r; i++){printf("%d ", i);//行int j = 0;for (j = 1; j <= c; j++){printf("%c ", board[i][j]);}printf("\n");}
}void SetMine(char mine[ROWS][COLS], int r, int c)
{int x = 0;int y = 0;int count = 0;while (count < EASYCOUNT){x = rand() % r + 1;y = rand() % c + 1;if (mine[x][y] == '0'){mine[x][y] = '1';count++;}}
}static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] +mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] +mine[x + 1][y] + mine[x + 1][y + 1] - (8 * '0');
}static void MarkMine(char show[ROWS][COLS], int r, int c)
{printf("是否標記雷的位置: 1.標記 0.不標記\n");int input = 0;scanf("%d", &input);while (input){printf("請輸入要標記的坐標:");int x = 0;int y = 0;scanf("%d %d", &x, &y);show[x][y] = '!';//標記'!'DisplayBoard(show, r, c);printf("繼續標記請輸入:“1”,否則輸入“0”\n");scanf("%d", &input);}}static int UnfoldBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int win, int r, int c)
{//遞歸x,y范圍合法if (x >= 1 && x <= r && y >= 1 && y <= c){if (mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] +mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] +mine[x + 1][y] + mine[x + 1][y + 1] - (8 * '0') == 0){show[x][y] = ' ';win++;int i = -1;int j = -1;for (i = -1; i <= 1; i++){for (j = -1; j <= 1; j++){if (show[x + i][y + j] == '*'){win = UnfoldBoard(mine, show, x + i, y + j, win, r, c);}}}}else{win++;int count = GetMineCount(mine, x, y);show[x][y] = count + '0';}}return win;
}void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c)
{int x = 0;int y = 0;int win = 0;while (win < r * c - EASYCOUNT){printf("請輸入要排查的坐標:");scanf("%d%d", &x, &y);//輸入的坐標的合法性if (x >= 1 && x <= r && y >= 1 && y <= c){//判斷該坐標是否已經被排查過if (show[x][y] == '*' || show[x][y] == '!'){//判斷該坐標是否為雷if (mine[x][y] == '1'){printf("很遺憾,你被炸死了\n");DisplayBoard(mine, r, c);break;}else{//得到周圍8個坐標的雷的個數int count = GetMineCount(mine, x, y);show[x][y] = count + '0';//展開一片if (count == 0){win = UnfoldBoard(mine, show, x, y, win, r, c);}else{win++;}DisplayBoard(show, r, c);//標記雷的位置MarkMine(show, r, c);}}else{printf("該坐標已經被排查過\n");}}else{printf("輸入的坐標不合法\n");}}if (win == r * c - EASYCOUNT){printf("恭喜你,排雷成功\n");}
}
覺得不錯的話給個三連哦!