?
想必屏幕前的你,肯定玩過windows系統(tǒng)自帶的那個游戲,掃雷
回想當(dāng)年,我根本沒看懂這個游戲是怎么玩的
比起掃雷,三維彈球?qū)ξ腋形?/p>
跑題了
本篇博客就讓我們一起來試試,如何通過C語言代碼,制作出一個“掃雷游戲se”
1.游戲程序主函數(shù)
在編寫這類游戲代碼時,我們要用到的主函數(shù)基本是一致的
掃雷游戲的主函數(shù)和猜數(shù)字游戲的主函數(shù)相差很小
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
void menu()//簡易目錄
{
printf("*************************** ");
printf("**** 1. play 0. exit***** ");
printf("*************************** ");
}
int main()
{
int input = 0;
do
{
menu();
printf("請選擇:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();//實現(xiàn)游戲的函數(shù)
break;
case 0:
printf("退出游戲 ");
break;
default:
printf("輸入錯誤 ");
break;
}
} while (input);
return 0;
}
2.游戲?qū)崿F(xiàn)原理
想寫好一串代碼,首先我們要知道掃雷游戲需要通過什么方式來實現(xiàn)
我們需要一個9x9的棋盤,用于生成我們的雷以及玩家的游玩
在c語言中當(dāng)然無法直接產(chǎn)生這樣的畫面
但我們可以同符號*或者#來代替網(wǎng)格,用1和0來表示有無雷
如果我們只生成一個棋盤,那1和0會直接顯示出來,達不到隱藏的效果
所以我們需要用二維數(shù)組生成兩個棋盤,一個用于存放雷,一個用于玩家的游玩
- ?
- ?
char mine[ROWS][COLS];//雷區(qū)布置
char show[ROWS][COLS];//玩家看到的界面
掃雷游戲我們使用頭文件+源文件的形式撰寫代碼
這樣寫代碼的優(yōu)點在于后續(xù)我們可以直接通過更改.h文件中的數(shù)組,從而更改我們的格子大小
如: 改成12x12的游玩界面,改變雷區(qū)布雷個數(shù)等等
所以我們需要在game.h中定義這些符號
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
同時我們要在主函數(shù)的最上面引用這個自己寫的頭文件
只要把庫函數(shù)頭文件放入game.h文件,在其他源文件中只需引用game.h
不需要再次引用
- ?
include 'game.h'
棋盤大小為什么需要11x11?
你可能注意到了,在生成數(shù)組的時候,我使用了ROWS,其值為ROW+2
我們最終展示的只是9x9的游戲界面,但生成的棋盤其實是11x11的
這是因為我們需要在mine數(shù)組中實現(xiàn)掃描雷區(qū)的操作
玩過掃雷游戲的你肯定知道:在你點擊一個格子的時候,如果這個格子不是雷
它會顯示一個數(shù)字,告訴你它周圍的8個格子中有幾顆雷
如圖所示:
在C語言中,我們可以用函數(shù)統(tǒng)計周圍8個格子中雷’1’的個數(shù)
但是如果你來到邊緣,那就出現(xiàn)問題了
如果我們想統(tǒng)計邊緣的格子周邊有幾顆雷,就會遇到這種溢出數(shù)組的情況
此時代碼會報錯
為了避免這個問題,我們可以在原來9x9的基礎(chǔ)上在周圍加一圈空白的格子
也就是代碼所示的ROW(行)COL(列)都要+2的情況
- ?
- ?
- ?
- ?
- ?
游戲過程
這里簡單梳理一下我們的游戲過程
(1)玩家選擇開始游戲
(2)生成兩個棋盤,一個放置雷掃描雷,一個向玩家展示游戲界面
(3)玩家輸入坐標,選擇排雷位置
(4)有雷–>玩家被炸死,游戲結(jié)束;無雷–>顯示周邊有幾顆雷,游戲繼續(xù)
(5)所有雷被排出,游戲勝利
?
3.游戲代碼實現(xiàn)
接下來就進入我們的游戲代碼部分
3-1.初始化和打印
- ?
- ?
- ?
- ?
- ?
- ?
- ?
//初始化掃雷
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//打印掃雷
DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
我們需要初始化兩個棋盤,其中雷區(qū)初始化為0(0代表無雷),展示區(qū)初始化為’*’,用代替界面
同時我們打印這兩個棋盤,查看初始化效果
因為這是我們的自定義函數(shù),所以需要在.h文件中定義函數(shù),在另外一個.c文件中包含函數(shù)的實現(xiàn)
- ?
- ?
- ?
- ?
//初始化棋盤
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印
void DisplayBoard(char board[ROWS][COLS], int row, int col);
初始化函數(shù)和打印函數(shù)比較簡單,使用for語句達成我們的需求
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("------掃雷游戲------ ");
//打印列號
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf(" ");
for (i = 1; i <= row; i++)//只打印中心的99方格
{
printf("%d ", i);//打印行號
for (j = 1; j <= col; j++)//只打印中心的99方格
{
printf("%c ", board[i][j]);
}
printf(" ");
}
printf("-------------------- ");
}
需要注意的是我們的最后打印棋盤的時候是從i=1開始的,這樣就能避開添加的空白邊緣區(qū)域,只打印中心的99方格
同時我們添加了列號和行號,這樣能讓玩家清除的知道自己應(yīng)該輸入什么坐標
3-2.布置雷區(qū)
- ?
- ?
//布置雷
SetMine(mine, ROW, COL);
同樣的,我們需要在game.h中定義這個函數(shù)
- ?
- ?
//布置地雷
void SetMine(char mine[ROWS][COLS], int row, int col);
在game.c中寫入自定義函數(shù)的實現(xiàn)
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
//放置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] =='0')
{
mine[x][y] = '1';
count--;
}
}
}
這里面出現(xiàn)了一個前面沒有提到的變量,EASY_COUNT
本來這個位置只是個10
但如果我們想更改布雷個數(shù),那每次都需要更改這里的10,后面的代碼中也需要更改,非常麻煩
所以我們改為使用一個自定義變量,在game.h中定義這個變量的值
- ?
這個值就代表我們布置雷的個數(shù)了
3-3.玩家排查雷
- ?
- ?
- ?
- ?
- ?
//在主函數(shù)中引用這個函數(shù)
FindMine(mine,show, ROW, COL);//需要把mine數(shù)組中排查的雷放入show
//在game.h中定義這個函數(shù)
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col);
因為我們需要把mine數(shù)組中排查出的雷的個數(shù)放入show數(shù)組中打印出來
所以這里我們需要把兩個數(shù)組都傳送過去
1.輸入排查的坐標
2.檢查坐標處是不是雷
? ? ? ? ·是雷 -boom!炸死 -游戲結(jié)束
? ? ? ? ·不是雷 -統(tǒng)計坐標周圍有幾個雷-存儲排雷的信息到show數(shù)組,游戲繼續(xù)
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("請輸入排雷坐標:> ");
scanf("%d%d", &x, &y);
//判斷坐標是否正確
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遺憾,你被炸死了 ");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
//不是雷的情況下,統(tǒng)計坐標周圍有幾個雷
int count = get_mine_count(mine, x, y);
show[x][y] = count + '0';
}
}
else
{
printf("坐標錯誤,請重新輸入 ");
}
}
}
注意,這里面我們需要添加一個代碼來判斷坐標合法性
我們的棋盤是9x9,玩家要是輸入一個(99,99)的坐標,那肯定不在數(shù)組中的,是無效的
我們需要提醒玩家他輸錯了
‘0’的作用
- ?
show[x][y] = count + '0';
你可能會對這行代碼感到疑惑
為什么要在count后面+上一個‘0’?
這里就和我們ascii碼表有關(guān)了
因為我們初始化數(shù)組和布置雷的時候,我們給數(shù)組傳入的都是1和0這兩個符號,并不是數(shù)字!
但是在show數(shù)組中我們需要給玩家顯示一個數(shù)字的字符
這里面我們提供的是1的字符,并不是1它本身
而我們在計算周邊雷的個數(shù)的時候,傳回來的是一個具體的數(shù)字
觀察表格,你會發(fā)現(xiàn)數(shù)字和對應(yīng)的字符中間,都差了48
而48恰好是字符’0’對應(yīng)的ASCII碼值
所以我們需要用count加上字符’0’,以此在界面中向玩家展示周邊8格有幾顆雷
3-4. 系統(tǒng)掃描雷
如何把玩家選擇的格子周邊的雷掃描出來呢?
設(shè)玩家的選擇的坐標為x和y
我們只需要把這些坐標全部在二維數(shù)組中鍵入,就能逐個掃描出雷的個數(shù)
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
//統(tǒng)計雷的個數(shù)
static int get_mine_count(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';
}
這里因為我們掃描出來的也是‘1’的字符,系統(tǒng)中是字符1的ascii碼值49
所以我們需要減去8個字符‘0’,這樣就能得到雷的個數(shù)的數(shù)字
(然后在之前的那個函數(shù)中接受,count+‘0’,在show數(shù)組中顯示)
這個函數(shù)是在玩家排查雷的函數(shù)之前的
因為在主函數(shù)中我們不需要使用這個自定義函數(shù),所以不需要在game.h中定義
我們想讓它只在game.c中生效,所以用static修飾它
static的作用
1.修飾局部變量
2.修飾全局變量
3.修飾函數(shù)
上面的代碼其實還少了一個東西
我們需要判斷玩家什么時候勝利——雷區(qū)的0(無雷方塊)全部被玩家找出,玩家就勝利了
- ?
- ?
int win = 0;
while (win< row * col - EASY_COUNT)
這里我們需要更改的是whlie函數(shù)
其中 row * col - EASY_COUNT 指方格總數(shù)減去雷的個數(shù),得到的是無雷方塊的個數(shù)
玩家每成功排除一個無雷方塊,win就會加一個數(shù)字
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
else
{
//不是雷的情況下,統(tǒng)計坐標周圍有幾個雷
int count = get_mine_count(mine, x, y);
show[x][y] = count + '0';
//顯示排查出來的信息
DisplayBoard(show, ROW, COL);
win++;
}
當(dāng)win達到無雷方塊個數(shù)的時候,whlie循環(huán)就會停止
隨后我們判斷玩家是否勝利,如果勝利,就打印棋盤,讓玩家知道雷的位置
- ?
- ?
- ?
- ?
- ?
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,游戲勝利! ");
DisplayBoard(mine, ROW, COL);
}
這里必須要判斷,因為你失敗了也是會跳出循環(huán)的!
到此,我們的掃雷代碼就是完成了
4.查看結(jié)果
這里我作弊,將雷的個數(shù)設(shè)置為80并打印出布置雷之后的棋盤
輸入最后一個雷的位置,系統(tǒng)提示我們游戲勝利
輸入雷的坐標后,也會提示你被炸死了
到這里我們可以確認代碼是編寫成功了!
評論
查看更多