C语言扫雷游戏实现方法详解(包含递归,变色,记录时间等)
作者:zore_c
前言:
本期需要用到大量自定义函数
可以很好的练习函数
代码内均有注释可查看
最终代码放在结尾
一、扫雷游戏简介
胜利条件:
你需要在不点错雷的情况下尽可能快的将所有的雷都标记出来,
如果你点错,就得重新开始,所以扫雷也有一定的运气成分。
如何操作:
可翻开这一格,也可标记地雷
你点出了一个数字,
是1,就说明它周围的8的格子里有1个雷,
是2就有两个雷,是3就有三个雷···以此类推。
但如果你标记错了雷,那就会"boom!"一切重新开始。

OK,知道了游戏规则,那么该如何设计游戏呢?
二、扫雷游戏的设计思路
1.创建游戏菜单。
2.初始化棋盘。
3.随机布置雷。
4.打印出棋盘。
5.操作:排查雷,标记雷,或取消标记雷。
7.循环,直到游戏结束。
8.当然其中的细节处还有很多,带着你一步一步实现。
三、代码的逐步实现
1.创建头文件以及源文件
在我们写代码时,若代码很长很复杂
可以设置多个文件来拆分代码
我这里创造了三个文件:
头文件—game.h
用来放函数的声明和一些库函数的头文件
源文件–game.c
用来放函数的定义
(游戏代码的主体)
源文件–main.c
用来放主函数
这样条理就很清晰也不会乱

当然,当声明写在game.h中时,不要忘记在源文件调用game.h
在源文件顶部写上#include“game.h"就行啦
如下
#include"game.h"
2.创建主函数main
写出创建游戏主体
这里我用了一个do while循环来判断是否继续游戏
int main()
{
do
{
char x = 0;
menu();
//打印菜单
scanf("%d", &g);
switch (g)
{
case 1:
game();
//进入游戏
break;
case 0:
printf("退出\n");
//退出游戏
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (g);
return 0;
}
3.创建游戏菜单
这没什么好说的,创建一个菜单
当然大家也可以设计出更好看的菜单
void menu()//打印菜单
{
printf("**********************\n");
printf("****** 扫 雷 ******\n");
printf("**********************\n");
printf("***** 1.开 始 *****\n");
printf("***** 0.退 出 *****\n");
printf("**********************\n");
printf("请输入 1 或 2 \n\n");
printf("请输入:>> ");
}
4、初始化棋盘
首先,扫雷是一个9*9的矩形
那我们可以想到用二维数组来放置雷
用” * “来表示雷,用” 0 “来表示非雷
排查雷的时候就判断周围有几个雷就显示数字几
但是有一个问题,如果我要排查边框的雷的时候,如下:
此时就会出现数组的越界访问的问题
故为了防止越界,我们在设计的时候,需给数组扩大一圈
雷布置在中间的9 * 9的格子内,但是周围扩大一圈,这样就解决了越界的问题。
所以我们将存放数据的数组创建成11 * 11最合适

还有,我们需要将排查出的雷的数量信息记录存储,并打印出来作为排雷的重要参考信息。但是雷的个数信息存放在哪里呢?如果存放在布置雷的数组中,这样雷的信息和雷的个数信息就会产生混淆和打印上的困难。
所以,我们需要设置两个棋盘,给⼀个棋盘(mine)存放布置好的雷的信息,再给另外⼀个棋盘(show)存放排查出的雷的信息。这样就互不干扰了,把雷布置到mine数组,在mine数组中排查雷,排查出雷的数据存放在show数组,并且打印出来。
还有一点,这两个数组既要存放字符" * ",又要存放数字
故我们可以设置字符数组来存放信息
OK,现在就可以开始写代码了
在game.h头文件写到:
#define ROW 9 #define COL 9 //定义9*9小边框 #define ROWS ROW+2 #define COLS COL+2 //定义11*11大边框 void SetBoard(char board[ROWS][COLS], int rows, int cols, char set); //声明SetBoard函数(初始化棋盘)
在game.c源文件中写到:
void SetBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
在main.c源文件中写到:
char mine[ROWS][COLS];//放置雷 char show[ROWS][COLS];//展示信息 //定义数组大小 SetBoard(mine, ROWS, COLS, '0');//放置雷 SetBoard(show, ROWS, COLS, '*');//展示信息 //初始化数组,传入要初始化的值
5、放置雷(随机数的应用)
这里需要注意,每一把游戏的雷位置是随机的
故我们要在这里设置一个随机数来随机摆放雷
要使用到rand函数,头文件为<stdlib.d>
rand的具体使用方法详见【C语言】rand函数的应用(随机数的生成)
还可以去c++官网查看:https://legacy.cplusplus.com/
因为是在 9 * 9 的框中放置雷
故我们将 i 和 j 设置成0到8的随机数就可以了
还要注意,已经放置雷的格子就不能放雷了
创建自定义函数SetMine来放置雷
在game.h头文件写到:
#define EASY_COUNT 10 //设置雷的个数(10个) void SetMine(char mine[ROWS][COLS], int row, int col); //声明SetMine函数(放置雷)
在game.c源文件中写到:
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
//规定放置雷的个数
srand((unsigned int)time(NULL));
//生成随机数
while (count)
{
int i = rand() % row + 1;//生成1-9随机数
int j = rand() % col + 1;//生成1-9随机数
if (mine[i][j] != '*')//若此处没雷就放置雷
{
mine[i][j] = '*';
count--;
}
}
}
在main.c源文件中写到:
SetMine(mine, ROW, COL); //放置雷
6、打印棋盘(设置多彩颜色)
为了使棋盘更加美观,我们还可以增加颜色元素
如下,1,2,3,4的颜色各不相同
创建自定义函数DisplayBoard来打印棋盘

这里我们用到了颜色定义函数:
这样可以同时设置多种颜色
对于不同数字展现不同颜色
void setColor(int color)
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, color);
}
void red() { setColor(12); }
void green() { setColor(10); }
void yellow() { setColor(14); }
void blue() { setColor(9); }
void purple() { setColor(13); }
void cyan() { setColor(11); }
void white() { setColor(15); }
void reset() { setColor(7); }
在game.h头文件写到:
void DisplayBoard(char board[ROWS][COLS], int row, int col); //声明DisplayBoard函数(打印棋盘)
在game.c源文件中写到:
void setColor(int color)
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, color);
}
void red() { setColor(12); }
void green() { setColor(10); }
void yellow() { setColor(14); }
void blue() { setColor(9); }
void purple() { setColor(13); }
void cyan() { setColor(11); }
void white() { setColor(15); }
void reset() { setColor(7); }
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("-------扫雷--------\n");
printf(" ");
int i = 1;
for (i = 1; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
int j = 1;
for (j = 1; j <= col; j++)
{
switch (board[i][j])
{
case '0':
cyan();
break;
case '1':
blue();
break;
case '2':
yellow();
break;
case '3':
purple();
break;
case '4':
case '5':
case '6':
case '7':
case '8':
case 'F':
red();
break;
}
printf("%c ", board[i][j]);
reset();
}
printf("\n");
}
}
在main.c源文件中写到:
DisplayBoard(show, ROW, COL); //打印棋盘
打印颜色后的运行结果:
这样就有了多种颜色,使棋盘更加美观

7、正式开始扫雷游戏
( 1 ) 规则
首先我们要打印出我们的规则,让玩家能更好的操作
可以排查雷,可以标记雷,也可以取消标记雷
如下:
printf("1·排查地雷:请输入要排查的坐标例如‘2 4',第二行第四列\n");
printf("2·标记地雷:输入要标记雷的坐标例如‘2+4',注意要加'+'\n");
printf("3·取消标记:输入要取消标记雷的坐标例如‘2-4',注意要加'-'\n");
printf("请输入:");
( 2 ) 踩雷时
当排雷时,若踩雷了,要退出游戏,
并且打印出雷的位置,至少让玩家知道自己是怎么死的
创建自定义函数DisplayBoard2来打印死亡后的棋盘
if (mine[x][y] == '*')
{
printf("\a很遗憾,\a您踩雷了,\a游戏结束\a\n");
DisplayBoard2(mine, ROW, COL);
break;
}
而且,为了更好区分出雷,雷我用了红色颜色
void DisplayBoard2(char board[ROWS][COLS], int row, int col)
{
printf("-------扫雷--------\n");
printf(" ");
int i = 1;
for (i = 1; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
int j = 1;
for (j = 1; j <= col; j++)
{
if (board[i][j] == '*')
{
red();
}
printf("%c ", board[i][j]);
reset();
}
printf("\n");
}
}
运行结果:

( 3 ) 没踩雷时
若没踩雷,就在格子上统计出周围雷的个数
这里我用到了暴力统计
发现一个雷就+1
并且在棋盘上打印出信息
创建自定义函数GetMineCount来获取周围雷的个数
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
int i, j;
int count = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (mine[i][j] == '*')
{
count++;
}
}
}
return count;
}
int count = GetMineCount(mine, x, y); show[x][y] = count + '0';
( 4 ) 四周没雷时(函数递归)
在我们排雷时,若周围9格均没雷
就会返回0,但是此时周围可以确定没雷
再一一输入排查就太浪费时间了
而在传统扫雷中,周围没雷就会 ” 炸 “ 开一片
如下:

为了达到这种效果,我们可以用到函数归递
周围没雷时,自动排除周围,若周围还是0(没雷),就再自动排除周围
以此往复,知道没有可以自动排除的区块
创建自定义函数Chain来炸开周围格子
代码实现:
if (count == 0) Chain(mine, show, x, y, ROWS); //归递ROWS(边长)次
void Chain(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int g)
{
int i = x - 1, j = y - 1;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (show[i][j] == '*' && i >= 1 && i <= ROW && j >= 1 && j <= COL)
//1.当show[i][j]=="*"时再进入防止死循环 2.设置边界防止越界访问
{
show[i][j] = GetMineCount(mine, i, j) + '0';
if (show[i][j] == '0' && g)//当g==0时退出
{
g--;
Chain(mine, show, i, j, g);
//设置归递g次,这里设置成棋盘边长
}
}
}
}
}
最后再打印出操作完后的棋盘
(上面提到了就不赘述了)
( 5 ) 标记和取消标记
我这里用 x + y 表示标记雷,用 x - y 表示取消标记雷
标记就把格子改成红色F,取消就复原就行了
创建自定义函数Sign来标记雷
创建自定义函数Unsign来取消标记雷
else if (s == '+' && x >= 1 && x <= row && y >= 1 && y <= col) Sign(show, x, y); else if (s == '-' && x >= 1 && x <= row && y >= 1 && y <= col) Unsign(show, x, y);
void Sign(char show[ROWS][COLS], int x, int y)
{
if (show[x][y] == '*')
show[x][y] = 'F';
DisplayBoard(show, ROW, COL);
}
void Unsign(char show[ROWS][COLS], int x, int y)
{
if (show[x][y] == 'F')
show[x][y] = '*';
DisplayBoard(show, ROW, COL);
}
( 6 ) 游戏结束(所有雷已找出)
当排查了除了雷的所有格子时,游戏结束(算上自动排查)
创建自定义函数Judge判断是否扫完雷
若扫完雷时,返回0,否则返回1。
flag = Judge(show, ROW, COL);
int Judge(char show[ROWS][COLS], int row, int col)
{
int i, j, count = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (show[i][j] != '*')
{
count++;//逐个计数
if (count == row * col - EASY_COUNT)
//当所有除了雷的所有格子扫完时
return 0;
}
}
}
return 1;
}
if (flag == 0)
{
printf("恭喜你扫雷成功!!!\n");
}
8、记录游戏时间
以上 7、正式开始扫雷游戏的所有代码是整个游戏过程
我们把他们合到一起(不包含自定义函数)
创建自定义函数FindMine表示整个游戏过程
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
char s;
int x, y, flag = 1;
while (flag)
{
printf("1·排查地雷:请输入要排查的坐标例如‘2 4',第二行第四列\n");
printf("2·标记地雷:输入要标记雷的坐标例如‘2+4',注意要加'+'\n");
printf("3·取消标记:输入要取消标记雷的坐标例如‘2-4',注意要加'-'\n");
printf("请输入:");
scanf("%d%c%d", &x, &s, &y);
printf("\n\n");
if (s == ' ' && x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '*')
{
printf("\a很遗憾,\a您踩雷了,\a游戏结束\a\n");
DisplayBoard2(mine, ROW, COL);
break;
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
if (count == 0)
Chain(mine, show, x, y, ROWS);
DisplayBoard(show, ROW, COL);
flag = Judge(show, ROW, COL);
}
}
else if (s == '+' && x >= 1 && x <= row && y >= 1 && y <= col)
Sign(show, x, y);
else if (s == '-' && x >= 1 && x <= row && y >= 1 && y <= col)
Unsign(show, x, y);
else
{
printf("输入错误,坐标范围X(1-9) Y(1-9),请重新输入\n");
}
}
if (flag == 0)
{
printf("恭喜你扫雷成功!!!\n");
}
}
若要记录整个游戏时间,就是自定义函数FindMine这整个代码时间
可以在主函数中写:
clock_t start = clock();//记录游戏时间
{
FindMine(mine, show, ROW, COL);
}
double time = (double)(clock() - start) / CLOCKS_PER_SEC;
printf("您一共用时: %.2f 秒\n\n\n", time);
这样就可以记下游戏时间了,精度为0.01秒
注意:记时代码要包含头文件<windows.h>
运行结果:(虽然这里没成功扫雷)

四、扫雷游戏完整代码
在game.h头文件写到:
(头文件的声明)
(以及用来放函数的声明和一些库函数的头文件)
#pragma once #include <stdio.h> #include <time.h> #include <stdlib.h> #include <string.h> #include <windows.h> #define ROW 9 #define COL 9 //定义9*9小边框 #define ROWS ROW+2 #define COLS COL+2 //定义11*11大边框 #define EASY_COUNT 10 //设置雷的个数 void SetBoard(char board[ROWS][COLS], int rows, int cols, char set); //声明SetBoard函数(初始化棋盘) void SetMine(char mine[ROWS][COLS], int row, int col); //声明SetMine(函数(放置雷) void DisplayBoard(char board[ROWS][COLS], int row, int col); //声明DisplayBoard函数(打印棋盘) void DisplayBoard2(char board[ROWS][COLS], int row, int col); //声明DisplayBoard2函数(打印失败棋盘) void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); //声明函数FindMine(整个游戏过程)
在game.c源文件中写到:
(游戏主要代码)
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void setColor(int color)
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, color);
}
void red() { setColor(12); }
void green() { setColor(10); }
void yellow() { setColor(14); }
void blue() { setColor(9); }
void purple() { setColor(13); }
void cyan() { setColor(11); }
void white() { setColor(15); }
void reset() { setColor(7); }
void menu()//打印菜单
{
printf("**********************\n");
printf("****** 扫 雷 ******\n");
printf("**********************\n");
printf("***** 1.开 始 *****\n");
printf("***** 0.退 出 *****\n");
printf("**********************\n");
printf("请输入 1 或 2 \n\n");
printf("请输入:>> ");
}
void SetBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
//规定放置雷的个数
srand((unsigned int)time(NULL));
//生成随机数
while (count)
{
int i = rand() % row + 1;//生成1-9随机数
int j = rand() % col + 1;//生成1-9随机数
if (mine[i][j] != '*')//若此处没雷就放置雷
{
mine[i][j] = '*';
count--;
}
}
}
void DisplayBoard2(char board[ROWS][COLS], int row, int col)
{
printf("-------扫雷--------\n");
printf(" ");
int i = 1;
for (i = 1; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
int j = 1;
for (j = 1; j <= col; j++)
{
if (board[i][j] == '*')
{
red();
}
printf("%c ", board[i][j]);
reset();
}
printf("\n");
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("-------扫雷--------\n");
printf(" ");
int i = 1;
for (i = 1; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
int j = 1;
for (j = 1; j <= col; j++)
{
switch (board[i][j])
{
case '0':
cyan();
break;
case '1':
blue();
break;
case '2':
yellow();
break;
case '3':
purple();
break;
case '4':
case '5':
case '6':
case '7':
case '8':
case 'F':
red();
break;
}
printf("%c ", board[i][j]);
reset();
}
printf("\n");
}
}
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
int i, j;
int count = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (mine[i][j] == '*')
{
count++;
}
}
}
return count;
}
void Chain(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int g)
{
int i = x - 1, j = y - 1;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (show[i][j] == '*' && i >= 1 && i <= ROW && j >= 1 && j <= COL)
{
show[i][j] = GetMineCount(mine, i, j) + '0';
if (show[i][j] == '0' && g)
{
g--;
Chain(mine, show, i, j, g);
}
}
}
}
}
int Judge(char show[ROWS][COLS], int row, int col)
{
int i, j, count = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (show[i][j] != '*')
{
count++;
if (count == row * col - EASY_COUNT)
return 0;
}
}
}
return 1;
}
void Sign(char show[ROWS][COLS], int x, int y)
{
if (show[x][y] == '*')
show[x][y] = 'F';
DisplayBoard(show, ROW, COL);
}
void Unsign(char show[ROWS][COLS], int x, int y)
{
if (show[x][y] == 'F')
show[x][y] = '*';
DisplayBoard(show, ROW, COL);
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
char s;
int x, y, flag = 1;
while (flag)
{
printf("1·排查地雷:请输入要排查的坐标例如‘2 4',第二行第四列\n");
printf("2·标记地雷:输入要标记雷的坐标例如‘2+4',注意要加'+'\n");
printf("3·取消标记:输入要取消标记雷的坐标例如‘2-4',注意要加'-'\n");
printf("请输入:");
scanf("%d%c%d", &x, &s, &y);
printf("\n\n");
if (s == ' ' && x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '*')
{
printf("\a很遗憾,\a您踩雷了,\a游戏结束\a\n");
DisplayBoard2(mine, ROW, COL);
break;
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
if (count == 0)
Chain(mine, show, x, y, ROWS);
DisplayBoard(show, ROW, COL);
flag = Judge(show, ROW, COL);
}
}
else if (s == '+' && x >= 1 && x <= row && y >= 1 && y <= col)
Sign(show, x, y);
else if (s == '-' && x >= 1 && x <= row && y >= 1 && y <= col)
Unsign(show, x, y);
else
{
printf("输入错误,坐标范围X(1-9) Y(1-9),请重新输入\n");
}
}
if (flag == 0)
{
printf("恭喜你扫雷成功!!!\n");
}
}
在main.c源文件中写到:
(游戏主体框架)
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
int g;
void game()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
//定义数组大小
SetBoard(mine, ROWS, COLS, '0');//放置雷
SetBoard(show, ROWS, COLS, '*');//展示信息
//初始化数组,传入要初始化的值
SetMine(mine, ROW, COL);
//放置雷
DisplayBoard(show, ROW, COL);
//打印棋盘
/*DisplayBoard(mine, ROW, COL); */
clock_t start = clock();//记录游戏时间
{
FindMine(mine, show, ROW, COL);
}
double time = (double)(clock() - start) / CLOCKS_PER_SEC;
printf("您一共用时: %.2f 秒\n\n\n", time);
}
int main()
{
do
{
char x = 0;
menu();
//打印菜单
scanf("%d", &g);
switch (g)
{
case 1:
game();
//进入游戏
break;
case 0:
printf("退出\n");
//退出游戏
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (g);
return 0;
}
五、结语
OK,本期的扫雷代码到这里就结束了
大家可自己游玩,也可改进我的代码
本期资料来自于:

到此这篇关于C语言扫雷游戏实现的文章就介绍到这了,更多相关C语言扫雷游戏内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
