如何用C语言实现简单的扫雷游戏

打开vs,新建两个c++文件,分别是test.c和game.c,再新建一个头文件game.h。

其中test.c实现主函数,game.h放函数的声明和一些宏定义,game.h放函数的实现

让我们开始吧!

先把源码拿出来供大家参考,详细解释在下面

test.c

#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');
	InitBoard(show, ROWS, COLS,'*');

	//打印棋盘
	//DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);
	//布置雷
	SetMine(mine, ROW, COL, EASY_CONUT);
	//DisplayBoard(mine, ROW, COL);

	//排查雷
	FineMine(mine, show, ROW, COL);
}

int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;


		}
	} while (input);
	return 0;
}

game.c

#include"game.h"

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("-------------扫雷游戏-------------\n");
	for (i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("-------------扫雷游戏-------------\n");
}

void SetMine(char mine[ROWS][COLS], int row, int col, int count)
{
	while (count)
	{
		//1 - 9
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			//x,y坐标处没有雷
			mine[x][y] = '1';
			count--;
		}
	}
}

//统计mine数组的x,y周围有几个雷
int GetMine(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 FineMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
	int win = 0;
	while (win<row*col - EASY_CONUT)
	{
		printf("请输入要排查的坐标:>");
		int x = 0;
		int y = 0;
		scanf("%d%d", &x, &y);

		//1.坐标合法性
		if (x >= 1 && x <= row&&y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, row, col);
				break;
			}
			else
			{
				//2.该坐标是不是雷?不是雷,统计雷的个数
				int count = GetMine(mine, x, y);
				show[x][y] = count + '0';//存放数字字符
				DisplayBoard(show, row, col);
				win++;
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
	if (win == row*col - EASY_CONUT)
	{
		printf("恭喜你,排雷成功!\n");
		DisplayBoard(show, row, col);
	}
}

game.h

#include<stdio.h>
#include<time.h>
#include<stdlib.h>

#define EASY_CONUT 10

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

//初始化棋盘
void InitBoard(char board[ROWS][COLS],int rows,int cols,char set);

//打印棋盘
void DisplayBoard(char board[ROWS][COLS],int row,int col);

//mine - 存放雷的信息
//count - 布置的雷的个数
void SetMine(char mine[ROWS][COLS],int row,int col,int count);

//排雷
void FineMine(char mine[ROWS][COLS],
	          char show[ROWS][COLS],
			  int row,
			  int col);

统计mine数组的x,y周围有几个雷
int GetMine(char mine[ROWS][COLS], int x, int y);

-----------------------------------------

游戏玩一次不够可以一直玩,所以首先我们要在主函数里面写一个do-while循环,使我们有个入口进入游戏,完整代码如下

	int input = 0;
	do
	{
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;


		}
	} while (input);

这里我们将游戏菜单封装成一个menu函数,menu函数的代码如下

void menu()
{
	printf("************************\n");
	printf("*******  1.play  *******\n");
	printf("*******  0.exit  *******\n");
	printf("************************\n");
}

选择1后我们可以进入游戏,定义一个game()函数,而最开始我们应该打印两个扫雷棋盘,一个是全是*的游戏棋盘,是我们之后用来玩的,另一个是由字符0组成的棋盘,是方便我们观察雷是否布置成功和雷是如何布置的。

这里遇到了一个问题,当我们在排棋盘边界上的雷的时侯,例如排左上角第一个雷,只能扫描出周围三个格子的情况,对于边界外的情况其实是未知的,因此我们在边上还需要一圈格子

我们以9×9的扫雷举例,我们应该创建一个11×11的棋盘,我们初始化11×11的棋盘,但我们显示在程序中是一个9×9的棋盘,这就完美地解决了上面的问题,当我们创建扫雷棋盘的时候,我们需要定义两个字符二维数组,一个用来玩,一个用来布置雷

用来玩的棋盘为show,布置雷的棋盘为mine

	//创建棋盘对应的数组
	char mine[ROWS][COLS];//存放布置好的雷的信息
	char show[ROWS][COLS];//存放排查出的雷的信息

为了方便以后改动棋盘的尺寸而不仅仅是玩9×9的扫雷,我们先在game.h里写一下宏定义

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

回到game函数里,此时要初始化棋盘,为了方便之后的操作,我们把我们mine棋盘初始化为字符0,之后布置雷的时候把有雷的地方改成字符1

而show棋盘就是要玩的棋盘,我们玩的时候不知道雷的具体布置情况,所以我们把show棋盘初始化为字符*

具体代码如下

    

	//初始化棋盘
	InitBoard(mine, ROWS, COLS,'0');
	InitBoard(show, ROWS, COLS,'*');
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;
		}
	}
}

初始化完成后我们可以先打印出扫雷棋盘看看,因此定义一个显示棋盘的函数DisplayBoard(),注意我们打印的时候应该传9进去而不是11.

具体代码如下(为了方便看出棋盘坐标,我在以下的代码加入了一个简易的二维坐标系)

	//打印棋盘
	DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("-------------扫雷游戏-------------\n");
	for (i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("-------------扫雷游戏-------------\n");
}

完成打印棋盘的函数后,我们可以编译运行一下看看效果,效果如下图

完成棋盘的打印后,我们开始布置雷,定义一个SetMine()函数,EASY_COUNT为我们设置的雷的个数,在头文件中定义

	//布置雷
	SetMine(mine, ROW, COL, EASY_CONUT);

函数代码如下

void SetMine(char mine[ROWS][COLS], int row, int col, int count)
{
	while (count)
	{
		//1 - 9
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			//x,y坐标处没有雷
			mine[x][y] = '1';
			count--;
		}
	}
}

其中x,y是设置的布置的雷坐标,布置的雷是随机布置,因此我们这里用了库函数rand(),但它的返回值是一个伪随机值,不够随机,因此我们应在main()函数的最开头加上这么一句,以实现真正的随机,

srand((unsigned int)time(NULL));

在头文件中我们可以修改宏定义来调整布置的雷的参数,这里以布置10个雷为例,布置雷的时候我们还要进行一下判断,该格子不是雷(字符0)才能布置雷,

设置一个while循环,当成功布置一个雷,count--;

当雷布置完后,count=0,跳出循环

随机的布置完雷后我们可以再调用一下DisplayBoard函数来看看布置雷完后的效果

	//布置雷
	SetMine(mine, ROW, COL, EASY_CONUT);
	DisplayBoard(mine, ROW, COL);

可以看到其中的1就是我们随机布置的雷

布置完雷之后的下一步就要开始排雷了,这里我们再定义一个排雷函数FineMine函数

	//排查雷
	FineMine(mine, show, ROW, COL);
//排雷
void FineMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
	int win = 0;
	while (win<row*col - EASY_CONUT)
	{
		printf("请输入要排查的坐标:>");
		int x = 0;
		int y = 0;
		scanf("%d%d", &x, &y);

		//1.坐标合法性
		if (x >= 1 && x <= row&&y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, row, col);
				break;
			}
			else
			{
				//2.该坐标是不是雷?不是雷,统计雷的个数
				int count = GetMine(mine, x, y);
				show[x][y] = count + '0';//存放数字字符
				DisplayBoard(show, row, col);
				win++;
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
	if (win == row*col - EASY_CONUT)
	{
		printf("恭喜你,排雷成功!\n");
		DisplayBoard(show, row, col);
	}
}

思路:输入你认为不是雷的坐标,输入坐标后,要进行判断,

判断该坐标的合法性(以9×9为例,判断输入的坐标范围是否在9×9的范围内),若坐标非法,则提示重新输入,然后循环,输入。

若坐标合法,则判断该坐标是否是雷,若是雷,则游戏结束,展示整个雷盘并跳出循环。

若该坐标不是雷,则需要统计雷的个数,并将周围雷的个数展示在该坐标上

这里再定义一个用来统计雷的函数GetMine()

//统计mine数组的x,y周围有几个雷
int GetMine(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';
}

思路:在前面初始化棋盘和布置棋盘的时候,我们采用的方法是此坐标不是雷,则为字符0,此坐标是雷,则为字符1

这种方法的好处在于方便我们这一步的统计雷,我们可以计算坐标x,y周围8个格子里的字符数字之和,然后减掉8×字符0,返回的值便是该坐标周围8个格子的藏雷数

但是这是棋盘是一个字符数组,我们再把返回的值加上字符0,便得到该值的数字字符,然后我们可以调用DisplayBoard函数来展示棋盘,每输入一次坐标展示一次棋盘,形成一个循环。

那么问题又来了,循环什么时候停止呢,

我写的这个扫雷程序,赢的规则是把棋盘上所有不是雷的坐标都以输入的方式排查出来,换句话说,就是当你输入的坐标个数等于布置的雷的个数,就说明排雷成功,

因此这里我们可以初始化一个变量win=0,每成功输入一个非雷的坐标,win++,

而循环的判断条件便是win的值和(棋盘格子总数-布置的雷的个数)的值进行比较,当win<总数-雷的个数时,说明雷还没排完,循环继续,

当最后一次完成排雷,两值相等,排雷完毕,跳出循环,进行一次两个值的比较判断(因为跳出循环也有可能是被雷炸死,而被雷炸死时两值不相等,所以有必要进行值的比较判断),

若两值相等,便显示排雷成功,然后再调用DisplayBoard函数展示一下你成功排完雷的棋盘。

-----------------------------分割线-----------------------------

一切就绪后,我们再把game函数中的DisplayBoard(mine, ROW, COL)和注释掉,如下:

void game()
{
	//创建棋盘对应的数组
	char mine[ROWS][COLS];//存放布置好的雷的信息
	char show[ROWS][COLS];//存放排查出的雷的信息
	//初始化棋盘
	InitBoard(mine, ROWS, COLS,'0');
	InitBoard(show, ROWS, COLS,'*');

	//打印棋盘
	//DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);
	//布置雷
	SetMine(mine, ROW, COL, EASY_CONUT);
	//DisplayBoard(mine, ROW, COL);

	//排查雷
	FineMine(mine, show, ROW, COL);
}

这样布置雷的棋盘就不会显示出来了,我们就可以开始玩了

为了方便演示效果,我把雷的个数改成了80个

编译运行结果如下

我们可以看到,81个格子中只有坐标(6,5)不是雷,这便是扫雷游戏啦!

新人创作,若有意见和建议可在评论区提出,有不明白的地方也可以私信我探讨

猜你喜欢

转载自blog.csdn.net/qq_52454367/article/details/113172157