C語言實(shí)現(xiàn)掃雷游戲詳解
本文實(shí)例為大家分享了C語言實(shí)現(xiàn)掃雷游戲的具體代碼,供大家參考,具體內(nèi)容如下
功能設(shè)計
掃雷大家應(yīng)該都玩過,這是一個十分經(jīng)典的游戲,今天我將給大家講解如何用C語言實(shí)現(xiàn)掃雷,我的這個掃雷有如下幾個功能:
1、顯示該點(diǎn)周圍雷的個數(shù)
2、第一次下子,不炸死
3、坐標(biāo)周圍沒雷,可以實(shí)現(xiàn)展開
4、游戲結(jié)束后展示玩家用時
效果展示
話不多說,先附上效果圖:



設(shè)計思路
我們只要輸入坐標(biāo)就可以掃雷了,是不是很有趣?
其實(shí)要想實(shí)現(xiàn)這也不難,我們要用幾個算法模塊來模擬游戲規(guī)則,需要用函數(shù)來調(diào)用各個模塊使游戲跑起來。
那么第一步我們就要構(gòu)思一個棋盤,看見上面第一張圖沒,在開始游戲的界面我打印了兩個棋盤,有0和1的棋盤是給我們設(shè)計者看的,它可以顯示出當(dāng)前雷的真實(shí)分布,這有利于我們測試代碼的正確性,而全是 * 的棋盤是給玩家掃雷用的。
那我們就需要用二維數(shù)組來打印兩個棋盤,假如我們要打印10X10的棋盤,那我們的二維數(shù)組元素也要為10X10個嗎?,不能,因?yàn)槲覀冊谠O(shè)計算法時需要統(tǒng)計坐標(biāo)周圍8個方位雷的個數(shù),假如要統(tǒng)計邊界坐標(biāo)周圍雷的個數(shù),那么就會有數(shù)組越界的問題,那我們就要在10X10的邊界多上一圈元素,也就要定義12X12的數(shù)組元素,這些元素我們不要打印出來,心里有數(shù)就行,如下圖:
#define row 12 #define col 12 show_mine[row][col];//玩家數(shù)組 real_mine[row][col];//設(shè)計者數(shù)組

我們在一個項目里建立三個源文件,如下:

1、我們打印設(shè)計者棋盤要用數(shù)組real_mine,打印玩家棋盤要用數(shù)組show_mine,兩個數(shù)組在開始必須要初始化,在設(shè)計者棋盤中字符0代表不是雷,字符1代表雷,先初始化兩個數(shù)組
代碼如下:
void init_mine()//初始化兩個雷陣
{
int i = 0;
int j = 0;
for (int i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
show_mine[i][j] = '*';
real_mine[i][j] = '0';
}
}
}
2、打印兩個雷陣(不要忘了打印橫豎序號以便確定坐標(biāo))
void print_player()//打印玩家棋盤
{
int i = 0;
int j = 0;
printf("0 ");
for (i = 1; i <row-1; i++)
{
printf("%d ", i);//打印橫標(biāo)(0--10)
}
printf("\n");
for (i = 1; i <row-2; i++)//打印豎標(biāo)(1--10)
{
printf("%d ", i);
for (j = 1; j < col-1; j++)
{
printf("%c ", show_mine[i][j]);//玩家棋盤數(shù)組
}
printf("\n");
}
printf("10 ");//開始打印最后一行
for (i = 1; i < row-1; i++)
{
printf("%c ", show_mine[10][i]);
}
printf("\n");
}
void print_mine()//打印設(shè)計者棋盤
{
int i = 0;
int j = 0;
printf("0 ");
for (i = 1; i <row - 1; i++)
{
printf("%d ", i);//打印橫標(biāo)(0--10)
}
printf("\n");
for (i = 1; i <row - 2; i++)//打印豎標(biāo)(1--10)
{
printf("%d ", i);
for (j = 1; j < col - 1; j++)
{
printf("%c ", real_mine[i][j]);
}
printf("\n");
}
printf("10 ");//開始打印最后一行
for (i = 1; i < row - 1; i++)
{
printf("%c ", real_mine[10][i]);
}
printf("\n");
}
3、我們在每一次玩的時候設(shè)計者棋盤中的雷分布都必須不相同,使用隨機(jī)數(shù)生成橫豎坐標(biāo)確定布雷坐標(biāo),代碼如下:
void set_mine()//給設(shè)計者棋盤布雷
{
int x = 0;
int y = 0;
int count = COUNT;//雷總數(shù)
while (count)//雷布完后跳出循環(huán)
{
int x = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷
int y = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷
if (real_mine[x][y] == '0')//找不是雷的地方布雷
{
real_mine[x][y] = '1';
count--;
}
}
}
4、為了統(tǒng)計當(dāng)前坐標(biāo)周圍雷的個數(shù),定義一個函數(shù)實(shí)現(xiàn):
int count_mine(int x, int y)//檢測周圍8個區(qū)域雷的個數(shù)
{
int count = 0;
if (real_mine[x - 1][y - 1] == '1')
count++;
if (real_mine[x - 1][y] == '1')
count++;
if (real_mine[x - 1][y + 1] == '1')
count++;
if (real_mine[x][y - 1] == '1')
count++;
if (real_mine[x][y + 1] == '1')
count++;
if (real_mine[x + 1][y - 1] == '1')
count++;
if (real_mine[x + 1][y] == '1')
count++;
if (real_mine[x + 1][y + 1] == '1')
count++;
return count;
}
5、為了確保第一次不被雷炸死,我們需要定義個函數(shù)來實(shí)現(xiàn),如果第一次選到雷就將這顆雷放在其他不是雷的地方,代碼如下:
void safe_mine()//避免第一次炸死
{
int x = 0;
int y = 0;
char ch = 0;
int count = 0;
int ret = 1;
printf("輸入坐標(biāo)掃雷\n");
while (1)
{
scanf("%d%d", &x, &y);//只能輸入1到10,輸入錯誤重新輸入
if ((x >= 1 && x <= 10) && (y >= 1 && y <= 10))//判斷輸入坐標(biāo)是否有誤
{
if (real_mine[x][y] == '1')//第一次踩到雷后補(bǔ)救
{
real_mine[x][y] = '0';
char ch = count_mine(x, y);
show_mine[x][y] = ch + '0';//數(shù)字對應(yīng)的ASCII值和數(shù)字字符對應(yīng)的ASCII值相差48,即'0'的ASCII值
open_mine(x, y);
while (ret)//在其余有空的地方設(shè)置一個雷
{
int x = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷
int y = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷
if (real_mine[x][y] == '0')//找不是雷的地方布雷
{
real_mine[x][y] = '1';
ret--;
break;
}
}break;//跳出此函數(shù)
}
if (real_mine[x][y] == '0')
{
char ch = count_mine(x, y);
show_mine[x][y] = ch + '0';//數(shù)字對應(yīng)的ASCII值和數(shù)字字符對應(yīng)的ASCII值相差48,即'0'的ASCII值
open_mine(x, y);
break;
}
}
else//坐標(biāo)錯誤
{
printf("輸入錯誤重新輸入\n");
}
}
}
6、為了實(shí)現(xiàn)展開功能,需要展開函數(shù)模塊(展開的坐標(biāo)還要顯示其坐標(biāo)周圍的雷數(shù)),如下:
void open_mine(int x, int y)//坐標(biāo)周圍展開函數(shù)
{
if (real_mine[x - 1][y - 1]== '0')
{
show_mine[x - 1][y - 1] = count_mine(x - 1, y - 1) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x - 1][y] == '0')
{
show_mine[x - 1][y] = count_mine(x - 1, y) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x - 1][y + 1] == '0')
{
show_mine[x - 1][y + 1] = count_mine(x - 1, y + 1) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x][y - 1] == '0')
{
show_mine[x][y - 1] = count_mine(x, y - 1) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x][y + 1] == '0')
{
show_mine[x][y + 1] = count_mine(x, y + 1) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x + 1][y - 1] == '0')
{
show_mine[x + 1][y - 1] = count_mine(x + 1, y - 1) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x + 1][y] == '0')
{
show_mine[x + 1][y] = count_mine(x + 1, y) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x + 1][y + 1] == '0')
{
show_mine[x + 1][y + 1] = count_mine(x + 1, y + 1) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
}
7、掃雷函數(shù)是一個重要的模塊,代碼如下:
int sweep_mine()//掃雷函數(shù),踩到雷返回1,沒有踩到雷返回0
{
int x = 0;
int y = 0;
int count = 0;
printf("輸入坐標(biāo)掃雷\n");
scanf("%d%d", &x, &y);//只能輸入1到10
if ((x >= 1 && x <= 10) && (y >= 1 && y <= 10))//判斷輸入坐標(biāo)是否有誤,輸入錯誤重新輸入
{
if (real_mine[x][y] == '0')//沒踩到雷
{
char ch = count_mine(x,y);
show_mine[x][y] = ch+'0';//數(shù)字對應(yīng)的ASCII值和數(shù)字字符對應(yīng)的ASCII值相差48,即'0'的ASCII值
open_mine(x, y);
if (count_show_mine() == COUNT)//判斷剩余未知區(qū)域的個數(shù),個數(shù)為雷數(shù)時玩家贏
{
print_mine();
printf("玩家贏!\n\n");
return 0;
}
}
else if (real_mine[x][y]=='1')//踩到雷
{
return 1;
}
}
else
{
printf("輸入錯誤重新輸入\n");
}
return 0;//沒踩到雷
}
到最后需要確定游戲勝利的條件,我們要統(tǒng)計當(dāng)前狀態(tài)玩家棋盤中顯示的剩余 * 的個數(shù),如果個數(shù)等于總雷數(shù)時說明掃雷完成,游戲勝利,定義一個函數(shù)實(shí)現(xiàn):
int count_show_mine()//判斷剩余未知區(qū)域的個數(shù),個數(shù)為雷數(shù)時玩家贏
{
int count = 0;
int i = 0;
int j = 0;
for (i = 1; i <= row - 2; i++)
{
for (j = 1; j <= col - 2; j++)
{
if (show_mine[i][j] == '*')
{
count++;
}
}
}
return count;
}
我們將以上函數(shù)的定義放在 game.c 文件中
以上就是我們要實(shí)現(xiàn)掃雷的模塊,要想把這些模塊整合起來運(yùn)行,就需要一個游戲執(zhí)行函數(shù)來調(diào)用這些模塊,定義個game()函數(shù)實(shí)現(xiàn),代碼如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
double start, finish;
void game()
{
int ret = 0;
init_mine();//初始化玩家棋盤和設(shè)計者棋盤
set_mine();//給設(shè)計者棋盤布雷
print_mine();//打印設(shè)計者棋盤(可不打印)
printf("\n");
print_player();//打印玩家棋盤
start = clock();
safe_mine();//避免第一次被炸死
if (count_show_mine() == COUNT)//一步就贏的情況
{
print_mine();
printf("玩家贏!\n\n");
return ;
}print_player();打印玩家棋盤
while (1)//循環(huán)掃雷
{
int ret=sweep_mine();//掃雷,踩到雷返回1,沒有踩到雷返回0
if (count_show_mine() == COUNT)//若玩家棋盤的'*'個數(shù)為雷數(shù)時,掃雷完成,游戲勝利
{
print_mine();//打印設(shè)計者棋盤
printf("玩家贏!\n\n");
finish = clock();//取結(jié)束時間
printf("用時%d 秒\n",(int) (finish - start) / CLOCKS_PER_SEC);
break;
}
if (ret)//判斷是否踩到雷
{
printf("被雷炸死\t");
finish = clock();//取結(jié)束時間
printf("用時%d 秒\n", (int)(finish - start) / CLOCKS_PER_SEC);
print_mine();//打印設(shè)計者雷陣查看雷的分布
break;
}print_player();//打印玩家棋盤
}
}
int main()
{
srand((unsigned int)time(NULL));//產(chǎn)生隨機(jī)數(shù)生成器
int input = 0;
muen();//菜單
do
{
scanf("%d", &input);
switch (input)
{
case 1:game();
break;
case 0:exit(1);//退出游戲
break;
default:
printf("輸入錯誤,重新輸入\n");
break;
}
muen();
printf("contiue?\n");
} while (1);//循環(huán)玩游戲
system("pause");
return 0;
}
在頭文件game.h中聲明各種函數(shù):
game.h
#ifndef __GAME_H__ #define __GAME__H__ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<time.h> #define row 12 #define col 12 #define COUNT 10//棋盤中雷的總數(shù) extern char show_mine[row][col];//展示數(shù)組 extern char real_mine[row][col];//布雷數(shù)組 void muen();//菜單函數(shù) void init_mine();//初始化數(shù)組函數(shù) void set_mine();//布雷函數(shù) int count_mine();//統(tǒng)計周圍雷的個數(shù) void print_player();//打印玩家棋盤 void print_mine();//打印設(shè)計者棋盤 int sweep_mine();//掃雷函數(shù) void safe_mine();//避免第一次被雷炸死的函數(shù) void open_mine(int x, int y);//展開函數(shù) int count_show_mine(); ///判斷玩家棋盤剩余未知區(qū)域的個數(shù) #endif //__GAME_H__
將上面這個函數(shù)放在main.c文件中
以上我們便完成了掃雷的所有C語言代碼,接下來我們測驗(yàn)一下:
1、檢測第一次是否能不被炸死
2、檢測周圍沒雷可以展開

總結(jié)
測試顯示我們的代碼沒有問題,我們總結(jié)一下,這個程序的難點(diǎn)的就是如何實(shí)現(xiàn)展開和和保證第一次不被炸死,如果你仔細(xì)理解以上的程序,你就會明白其中的原理,包括為什么要在數(shù)組元素周圍多一圈元素,這些模塊都是游戲的核心,等你掌握了這個程序,你的邏輯思維能力就會有很大的提高!
附game.c文件源碼:
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
char show_mine[row][col] = { 0 };
char real_mine[row][col] = { 0 };
void muen()
{
printf("*******************************\n");
printf("*****1.play 0.exit*******\n");
printf("*******************************\n");
}
void init_mine()//初始化兩個棋盤
{
int i = 0;
int j = 0;
for (int i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
show_mine[i][j] = '*';
real_mine[i][j] = '0';
}
}
}
void print_player()//打印玩家棋盤
{
int i = 0;
int j = 0;
printf("0 ");
for (i = 1; i <row-1; i++)
{
printf("%d ", i);//打印橫標(biāo)(0--10)
}
printf("\n");
for (i = 1; i <row-2; i++)//打印豎標(biāo)(1--10)
{
printf("%d ", i);
for (j = 1; j < col-1; j++)
{
printf("%c ", show_mine[i][j]);//玩家棋盤數(shù)組
}
printf("\n");
}
printf("10 ");//開始打印最后一行
for (i = 1; i < row-1; i++)
{
printf("%c ", show_mine[10][i]);
}
printf("\n");
}
void print_mine()//打印設(shè)計者棋盤
{
int i = 0;
int j = 0;
printf("0 ");
for (i = 1; i <row - 1; i++)
{
printf("%d ", i);//打印橫標(biāo)(0--10)
}
printf("\n");
for (i = 1; i <row - 2; i++)//打印豎標(biāo)(1--10)
{
printf("%d ", i);
for (j = 1; j < col - 1; j++)
{
printf("%c ", real_mine[i][j]);
}
printf("\n");
}
printf("10 ");//開始打印最后一行
for (i = 1; i < row - 1; i++)
{
printf("%c ", real_mine[10][i]);
}
printf("\n");
}
void set_mine()//給設(shè)計者棋盤布雷
{
int x = 0;
int y = 0;
int count = COUNT;//雷總數(shù)
while (count)//雷布完后跳出循環(huán)
{
int x = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷
int y = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷
if (real_mine[x][y] == '0')//找不是雷的地方布雷
{
real_mine[x][y] = '1';
count--;
}
}
}
int count_mine(int x, int y)//檢測周圍8個區(qū)域雷的個數(shù)
{
int count = 0;
if (real_mine[x - 1][y - 1] == '1')
count++;
if (real_mine[x - 1][y] == '1')
count++;
if (real_mine[x - 1][y + 1] == '1')
count++;
if (real_mine[x][y - 1] == '1')
count++;
if (real_mine[x][y + 1] == '1')
count++;
if (real_mine[x + 1][y - 1] == '1')
count++;
if (real_mine[x + 1][y] == '1')
count++;
if (real_mine[x + 1][y + 1] == '1')
count++;
return count;
}
void safe_mine()//避免第一次炸死
{
int x = 0;
int y = 0;
char ch = 0;
int count = 0;
int ret = 1;
printf("輸入坐標(biāo)掃雷\n");
while (1)
{
scanf("%d%d", &x, &y);//只能輸入1到10,輸入錯誤重新輸入
if ((x >= 1 && x <= 10) && (y >= 1 && y <= 10))//判斷輸入坐標(biāo)是否有誤
{
if (real_mine[x][y] == '1')//第一次踩到雷后補(bǔ)救
{
real_mine[x][y] = '0';
char ch = count_mine(x, y);
show_mine[x][y] = ch + '0';//數(shù)字對應(yīng)的ASCII值和數(shù)字字符對應(yīng)的ASCII值相差48,即'0'的ASCII值
open_mine(x, y);
while (ret)//在其余有空的地方設(shè)置一個雷
{
int x = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷
int y = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷
if (real_mine[x][y] == '0')//找不是雷的地方布雷
{
real_mine[x][y] = '1';
ret--;
break;
}
}break;//跳出此函數(shù)
}
if (real_mine[x][y] == '0')
{
char ch = count_mine(x, y);
show_mine[x][y] = ch + '0';//數(shù)字對應(yīng)的ASCII值和數(shù)字字符對應(yīng)的ASCII值相差48,即'0'的ASCII值
open_mine(x, y);
break;
}
}
else//坐標(biāo)錯誤
{
printf("輸入錯誤重新輸入\n");
}
}
}
int sweep_mine()//掃雷函數(shù),踩到雷返回1,沒有踩到雷返回0
{
int x = 0;
int y = 0;
int count = 0;
printf("輸入坐標(biāo)掃雷\n");
scanf("%d%d", &x, &y);//只能輸入1到10
if ((x >= 1 && x <= 10) && (y >= 1 && y <= 10))//判斷輸入坐標(biāo)是否有誤,輸入錯誤重新輸入
{
if (real_mine[x][y] == '0')//沒踩到雷
{
char ch = count_mine(x,y);
show_mine[x][y] = ch+'0';//數(shù)字對應(yīng)的ASCII值和數(shù)字字符對應(yīng)的ASCII值相差48,即'0'的ASCII值
open_mine(x, y);
if (count_show_mine() == COUNT)//判斷剩余未知區(qū)域的個數(shù),個數(shù)為雷數(shù)時玩家贏
{
print_mine();
printf("玩家贏!\n\n");
return 0;
}
}
else if (real_mine[x][y]=='1')//踩到雷
{
return 1;
}
}
else
{
printf("輸入錯誤重新輸入\n");
}
return 0;//沒踩到雷
}
void open_mine(int x, int y)//坐標(biāo)周圍展開函數(shù)
{
if (real_mine[x - 1][y - 1]== '0')
{
show_mine[x - 1][y - 1] = count_mine(x - 1, y - 1) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x - 1][y] == '0')
{
show_mine[x - 1][y] = count_mine(x - 1, y) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x - 1][y + 1] == '0')
{
show_mine[x - 1][y + 1] = count_mine(x - 1, y + 1) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x][y - 1] == '0')
{
show_mine[x][y - 1] = count_mine(x, y - 1) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x][y + 1] == '0')
{
show_mine[x][y + 1] = count_mine(x, y + 1) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x + 1][y - 1] == '0')
{
show_mine[x + 1][y - 1] = count_mine(x + 1, y - 1) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x + 1][y] == '0')
{
show_mine[x + 1][y] = count_mine(x + 1, y) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
if (real_mine[x + 1][y + 1] == '0')
{
show_mine[x + 1][y + 1] = count_mine(x + 1, y + 1) + '0';//顯示該坐標(biāo)周圍雷數(shù)
}
}
int count_show_mine()//判斷剩余未知區(qū)域的個數(shù),個數(shù)為雷數(shù)時玩家贏
{
int count = 0;
int i = 0;
int j = 0;
for (i = 1; i <= row - 2; i++)
{
for (j = 1; j <= col - 2; j++)
{
if (show_mine[i][j] == '*')
{
count++;
}
}
}
return count;
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Ubuntu 20.04 下安裝配置 VScode 的 C/C++ 開發(fā)環(huán)境(圖文教程)
這篇文章主要介紹了Ubuntu 20.04 下安裝配置 VScode 的 C/C++ 開發(fā)環(huán)境,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05

