C語言實(shí)現(xiàn)經(jīng)典windows游戲掃雷的示例代碼
1. 前言
大家好,我是努力學(xué)習(xí)游泳的魚。今天我們會用C語言實(shí)現(xiàn)一個經(jīng)典的windows小游戲:掃雷。掃雷是一款單機(jī)小游戲,我上中學(xué)時特喜歡在電腦課上玩,研究應(yīng)對各種情況的思路,每次通關(guān)最高難度的關(guān)卡都會開心好一陣?,F(xiàn)在學(xué)會了C語言,總算可以自己實(shí)現(xiàn)掃雷了。話不多說,咱們開始吧。
2. 準(zhǔn)備工作
我們新建一個項(xiàng)目,并創(chuàng)建三個文件:
- test.c - 負(fù)責(zé)測試游戲代碼。
- game.c - 負(fù)責(zé)游戲功能的具體實(shí)現(xiàn)。
- game.h - 負(fù)責(zé)頭文件的包含,符號的定義,函數(shù)的聲明。
在test.c和game.c里都要#include "game.h"
測試游戲時,玩一把肯定不過癮,會想要再來一把,這就需要用到do while循環(huán)。
void menu()
{
printf("****************************\n");
printf("******** 1. play *******\n");
printf("******** 0. exit *******\n");
printf("****************************\n");
}
int main()
{
int input = 0;
do
{
menu();
printf("請選擇(1/0):>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戲\n");
break;
default:
printf("選擇錯誤,重新選擇!\n");
break;
}
} while (input);
return 0;
}
3. 設(shè)計(jì)思路
接下來講解掃雷游戲(game函數(shù))實(shí)現(xiàn)的思路。
1.布置雷 - 10個。
2.掃雷 - 玩法如下:
輸入坐標(biāo),此時分兩種情況:
- 是雷 – 就被炸了,游戲結(jié)束。
- 不是雷 – 就告訴你這個坐標(biāo)周圍8個坐標(biāo)上總共有多少個雷。
直到把所有非雷的位置全部都找出來,游戲結(jié)束,掃雷成功。
我們假設(shè)掃雷的棋盤是9×9的。我們布置雷的信息要想全部存起來,就需要使用9×9的二維數(shù)組。
怎么布置雷呢?假設(shè)要布置10個雷,我們就隨機(jī)生成10個坐標(biāo),把數(shù)組的這10個位置都置成1,其余位置存儲0。實(shí)際排查的時候,只需要顯示周圍8個坐標(biāo)有幾個1就行了。
但是這樣設(shè)計(jì)有一個問題,假設(shè)有一個位置周圍只有1個雷,那就顯示1。我們?nèi)绾闻袛噙@個1是表示雷的1,還是顯示周圍有一個雷的1呢?這就有歧義了。
如何解決這個問題呢?我們可以再搞一個一樣大的數(shù)組。兩個數(shù)組,一個放布置好的雷的信息,另一個放排查出的雷的信息。對于后者,如果某個位置沒有排查過,就存儲星號,以保持神秘感;如果排查過了,并且不是雷,就存儲周圍雷的個數(shù)。由于星號是一個字符,為了保持類型的統(tǒng)一,雷的個數(shù)也要用數(shù)字字符來存儲(如某位置周圍有3個雷,就存儲字符3),那存儲排查出的雷的信息的數(shù)組就是一個9×9的char類型的數(shù)組。還是為了保持類型的統(tǒng)一,存儲雷的信息的數(shù)組中,我們用字符0表示非雷,字符1表示雷,該數(shù)組也是一個9×9的char類型的數(shù)組。
階段總結(jié)一下:
- char mine[9][9] 負(fù)責(zé)存儲布置好的雷的信息,字符1表示雷,字符0表示非雷。
- char show[9][9] 負(fù)責(zé)存儲排查出的雷的信息,星號表示未排查,數(shù)字字符表示已排查。
但是,這樣設(shè)計(jì)仍然有問題。如果我們要排查數(shù)組邊上或角上的位置,我們需要訪問該位置周圍的8個位置,就有可能越界訪問了。
如何解決這個問題呢?我們可以把存放雷的信息的數(shù)組開大一圈,這樣訪問時,排查邊上或角上的數(shù)據(jù)就不會越界了。為了保持兩個數(shù)組的一一對應(yīng),另一個數(shù)組也開大一圈。經(jīng)過以上的分析,如果實(shí)際使用的棋盤大小是9×9的,兩個數(shù)組就應(yīng)該定義為11×11的。
4. 定義數(shù)組
為了以后修改這點(diǎn)方便,我們定義幾個宏,ROW和COL為實(shí)際使用的大?。?×9),ROWS和COLS為實(shí)際定義數(shù)組的大?。?1×11)。
#define ROW 9 #define COL 9 #define ROWS (ROW + 2) #define COLS (COL + 2)
接著定義兩個數(shù)組。
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
5. 初始化
mine數(shù)組由于要存儲雷的信息,沒布置雷前,我們想把這個數(shù)組全部初始化成0。show數(shù)組用于存放排查出來的雷的信息,一開始應(yīng)全部初始化成星號。所以我們需要一個初始化函數(shù)。由于兩個數(shù)組初始化的內(nèi)容不一樣,所以我們設(shè)計(jì)函數(shù)時,需要把初始化的內(nèi)容當(dāng)做參數(shù)傳過去。
init_board(mine, ROWS, COLS, '0'); init_board(show, ROWS, COLS, '*');
具體的實(shí)現(xiàn),只需遍歷數(shù)組就行了。
void init_board(char arr[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (; i < rows; ++i)
{
int j = 0;
for (; j < cols; ++j)
{
arr[i][j] = set;
}
}
}
6. 打印
對兩個數(shù)組進(jìn)行初始化后,我們想把它們打印出來看看。
初始化時,我們需要初始化整個數(shù)組(11×11),但是打印以及后面的操作,我們基本只關(guān)心中間的9×9,周圍的一圈只是為了防止越界。
show_board(mine, ROW, COL); show_board(show, ROW, COL);
具體的實(shí)現(xiàn),也是遍歷數(shù)組除去最外面一圈的元素,那下標(biāo)應(yīng)從1開始,最大是row或col。
void show_board(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
for (i = 1; i <= row; ++i)
{
int j = 0;
for (j = 1; j <= col; ++j)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
打印出來效果如下:

但是這樣打印不夠完美,我們每次還要去數(shù)某個位置是第幾行第幾列,所以最好把行標(biāo)和列標(biāo)也打印出來。
每次打印一行前,我們都打印下行號printf("%d ", i);
在所有信息打印前,我們把列標(biāo)打印出來。
for (i = 0; i <= col; ++i)
{
printf("%d ", i);
}
printf("\n");
當(dāng)然,我們可以在打印的最前和最后加上分割行。printf("------------掃雷------------\n");
下面是打印函數(shù)完整的代碼。
void show_board(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
printf("------------掃雷------------\n");
for (i = 0; i <= col; ++i)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; ++i)
{
printf("%d ", i);
int j = 0;
for (j = 1; j <= col; ++j)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
printf("------------掃雷------------\n");
}
打印效果如下:

7. 布置雷
我們需要在mine數(shù)組里布置雷。set_mine(mine, ROW, COL);
假設(shè)雷的個數(shù)是EASY_COUNT。#define EASY_COUNT 10
具體的實(shí)現(xiàn),我們需要寫一個循環(huán),每次隨機(jī)生成一個坐標(biāo),如果這個位置不是雷,就在這個位置放雷,知道把所有的雷放完為止。
void set_mine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
int x = 0;
int y = 0;
while (count)
{
x = rand() % row + 1;
y = rand() % col + 1;
if ('0' == mine[x][y])
{
mine[x][y] = '1'; // 布置雷
--count;
}
}
}不要忘記在調(diào)用rand函數(shù)之前要調(diào)用srand函數(shù),并給srand函數(shù)傳遞用time函數(shù)生成的時間戳。srand((unsigned int)time(NULL));rand函數(shù)和srand函數(shù)需要引用頭文件stdlib.h,time函數(shù)需要引用頭文件time.h。
我們可以把生成雷的信息打印出來。show_board(mine, ROW, COL);

8. 排查雷
布置好雷后,就開始排查雷。排查雷需要同時操作兩個數(shù)組。find_mine(mine, show, ROW, COL);
排查雷時,可以通過一個循環(huán)反復(fù)獲取坐標(biāo),并對坐標(biāo)進(jìn)行判斷。
- 首先判斷坐標(biāo)的合法性,橫縱坐標(biāo)都必須在1到row(col)之間。
- 如果坐標(biāo)合法,再看這個坐標(biāo)有沒有排查過,如果show數(shù)組在該位置還是星號,說明沒有排查過。
- 如果坐標(biāo)還沒排查過,再看是不是雷,是雷的話游戲結(jié)束,不是雷的話就顯示該坐標(biāo)周圍有幾個雷。
循環(huán)會在兩種情況下結(jié)束,
一種是踩到雷了,直接break出去,
另一種是找到所有非雷的位置,就排雷成功了。總共有row×col個位置,一共有EASY_COUNT個雷,那非雷的位置個數(shù)就是兩者相減。判斷是否排雷成功,可以在循環(huán)條件中判斷。
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EASY_COUNT)
{
printf("請輸入要排查的坐標(biāo):>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if ('*' == show[x][y])
{
if ('1' == mine[x][y])
{
printf("很遺憾,你被炸死了\n");
show_board(mine, row, col);
break;
}
else
{
int count = get_mine_count(mine, x, y);
show[x][y] = count + '0';
show_board(show, row, col);
++win;
}
}
else
{
printf("該坐標(biāo)已被排查\n");
}
}
else
{
printf("坐標(biāo)非法,重新輸入\n");
}
}
if (row * col - EASY_COUNT == win)
{
printf("恭喜你,排雷成功\n");
show_board(mine, row, col);
}
}我們用get_mine_count函數(shù)來獲取某個位置(坐標(biāo)為x,y)周圍8個坐標(biāo)雷的個數(shù)。由于字符1的ASCII碼值減去字符0的ASCII碼值是1,而mine數(shù)組里存放的就是字符1和字符0。所以我們只需要把mine數(shù)組中,該位置周圍八個坐標(biāo)存儲的字符加起來,再減去字符0的ASCII碼值的八倍,就能算出一共有多少個雷了。由于get_mine_count函數(shù)只在fine_mine函數(shù)中使用,所以加上static。
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y]
+ mine[x - 1][y - 1]
+ mine[x][y - 1]
+ mine[x + 1][y - 1]
+ mine[x + 1][y]
+ mine[x + 1][y + 1]
+ mine[x][y + 1]
+ mine[x - 1][y + 1] - 8 * '0';
}
到此為止,整個掃雷游戲就寫完啦。
9. 完整代碼
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 EASY_COUNT 10 // 初始化 void init_board(char arr[ROWS][COLS], int rows, int cols, char set); // 打印 void show_board(char arr[ROWS][COLS], int row, int col); // 布置雷 void set_mine(char mine[ROWS][COLS], int row, int col); // 排查雷 void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void init_board(char arr[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (; i < rows; ++i)
{
int j = 0;
for (; j < cols; ++j)
{
arr[i][j] = set;
}
}
}
void show_board(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
printf("------------掃雷------------\n");
for (i = 0; i <= col; ++i)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; ++i)
{
printf("%d ", i);
int j = 0;
for (j = 1; j <= col; ++j)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
printf("------------掃雷------------\n");
}
void set_mine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
int x = 0;
int y = 0;
while (count)
{
x = rand() % row + 1;
y = rand() % col + 1;
if ('0' == mine[x][y])
{
mine[x][y] = '1'; // 布置雷
--count;
}
}
}
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y]
+ mine[x - 1][y - 1]
+ mine[x][y - 1]
+ mine[x + 1][y - 1]
+ mine[x + 1][y]
+ mine[x + 1][y + 1]
+ mine[x][y + 1]
+ mine[x - 1][y + 1] - 8 * '0';
}
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EASY_COUNT)
{
printf("請輸入要排查的坐標(biāo):>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if ('*' == show[x][y])
{
if ('1' == mine[x][y])
{
printf("很遺憾,你被炸死了\n");
show_board(mine, row, col);
break;
}
else
{
int count = get_mine_count(mine, x, y);
show[x][y] = count + '0';
show_board(show, row, col);
++win;
}
}
else
{
printf("該坐標(biāo)已被排查\n");
}
}
else
{
printf("坐標(biāo)非法,重新輸入\n");
}
}
if (row * col - EASY_COUNT == win)
{
printf("恭喜你,排雷成功\n");
show_board(mine, row, col);
}
}
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()
{
// 掃雷游戲的具體實(shí)現(xiàn)
// 存儲布置好的雷的信息
char mine[ROWS][COLS] = { 0 };
// 存放排查出來的雷的信息
char show[ROWS][COLS] = { 0 };
// 初始化棋盤
init_board(mine, ROWS, COLS, '0');
init_board(show, ROWS, COLS, '*');
// 打印棋盤
//show_board(mine, ROW, COL);
// 布置雷
set_mine(mine, ROW, COL);
show_board(show, ROW, COL);
// 排查雷
find_mine(mine, show, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("請選擇(1/0):>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戲\n");
break;
default:
printf("選擇錯誤,重新選擇!\n");
break;
}
} while (input);
return 0;
}
以上就是C語言實(shí)現(xiàn)經(jīng)典windows游戲掃雷的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于C語言掃雷游戲的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語言連續(xù)生成多個隨機(jī)數(shù)實(shí)現(xiàn)可限制范圍
這篇文章主要介紹了C語言連續(xù)生成多個隨機(jī)數(shù)實(shí)現(xiàn)可限制范圍,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
C++數(shù)據(jù)結(jié)構(gòu)關(guān)于棧迷宮求解示例
這篇文章主要為大家介紹了C++數(shù)據(jù)結(jié)構(gòu)關(guān)于棧的迷宮求解示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2021-11-11
C語言實(shí)現(xiàn)3*3數(shù)組對角線之和示例
今天小編就為大家分享一篇C語言實(shí)現(xiàn)3*3數(shù)組對角線之和示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12
C語言使用單鏈表實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語言使用單鏈表實(shí)現(xiàn)學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11
C語言中數(shù)據(jù)結(jié)構(gòu)之鏈?zhǔn)交鶖?shù)排序
這篇文章主要介紹了C語言中數(shù)據(jù)結(jié)構(gòu)之鏈?zhǔn)交鶖?shù)排序的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09

