初学者如何用C语言编写扫雷游戏(详细)

本文详细介绍了如何设计并实现一个简单的扫雷游戏,包括棋盘结构、内容布置、排雷方法、代码编写(包括初始化棋盘、随机布置雷、打印棋盘、排查雷和游戏流程),以及解决边界问题。
摘要由CSDN通过智能技术生成
编写一个游戏之前我们先去思考,去设计一个什么样的游戏。游戏方向确定后,我们再去设计它,
设计好后我们根据相应的语言进行编写。

游戏说明:

1、游戏可以通过菜单实现继续玩或者退出游戏

2、扫雷的棋盘是9*9的格子
3、   默认随机布置10个雷
4、 可以排查雷
5、 如果位置不是雷,就显示周围有几个雷
6、 如果位置是雷,就炸死游戏结束
7、 把除10个雷之外的所有非雷都找出来,排雷成功,游戏结束

游戏设计:

(1)棋盘结构

 首先我们先分析一下,不着急写代码。

我们想象中的扫雷的棋盘将类似上面的这种,这就是我们的棋盘结构。

我们需要建立两个棋盘,一个存放雷和空位置,一个全部存放星号或其它字符留给玩家进行游戏。(此处不用过多解释吧,玩过扫雷的都懂吧)。

(2)棋盘中放入内容

有了空棋盘后我们往棋盘里添加内容。

“0”表示空位置,此处没有雷;“1”表示雷。

(3)排雷方法

棋盘的内容算是已经完成了,非常的简单。

接下来我们要设计如何“排雷”。

看上图就是我们设计“排雷”的方法

它的原理是:排查(2,5)这个坐标时,我们访问周围的一圈8个黄色位置,统计周围雷的个数是1。

周围有几个雷中间绿色位置就显示几。

注意:

经过分析我们这样的排查方法有bug,当我们排查坐标(8,6)时会出现“越界”问题,如下:

要解决此问题我们不必更换我们的排查方法,只需要把我们的棋盘增加 两行两列即可,即将9*9的棋盘改为11*11的即可。
修改结果如下:

 我们再打印棋盘的时候只需要打印中间9*9的区域即可,雷也只布置在9*9的区域中。这样就完美解决了越界问题。

代码实现:

游戏设计环节我们已经结束了,接下来我们开始代码的实现。

首先我们先定义一下:

ROW表示行,COL表示列

ROWS 和COLS表示加两行两列后

#define ROW 9 
#define COL 9 //9个棋盘格子
#define ROWS ROW+2
#define COLS COL+2 //加两行两列防止越界

 (1)建立两个空棋盘

根据游戏的设计我们先建立两个空棋盘:

两个数组分别为mine和show

char mine[ROWS][COLS] = { 0 };	//不带星号的棋盘
char show[ROWS][COLS] = { 0 };	//带星号的棋盘

 (2)棋盘赋值

有了这两个空棋盘我们要分别进行赋值:(这里用函数实现)

定义一个函数名为Initboard,对其进行传参:

Initboard(mine, ROWS, COLS, '0');//不带星号的棋盘
Initboard(show, ROWS, COLS, '*');//带星号的棋盘

 Initboard函数内部实现: (此函数功能为对数组进行赋值,set 对应的是‘0’或者‘*’

void Initboard(char arr[ROWS][COLS], int rows, int cols, char set)
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			arr[i][j] = set;
		}
	}
}

调用两次

(3)打印棋盘

赋值完毕后我们可以设计一个函数来专门进行打印棋盘名为Displayboard

代码如下:(代码中的细节就不带领读者细扣了,请读者仔细阅读一定可以看懂的,例如在棋盘周围加上坐标等

void Displayboard(char arr[ROWS][COLS], int row, int col)
{
	for (int i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
}

可实现如下两个棋盘:

(1)带星号的棋盘

(2)不带星号的棋盘

完成上述代码后棋盘的结构我们就已经解决了。

(4)布置雷

接下来我们需要对不带星号的棋盘进行雷的布置,我们默认随机布置10个雷。

首先定义EASY_COUNT 为10,表示雷的数量

#define EASY_COUNT 10	//雷的数量

随机布置雷需要用到函数srand()和time(),需要调用以下代码:

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

这两个库函数就不进行讲解了,读者可点开我的主页看我发的博客。

注意用这两个库函数需要引用相应的头文件

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

 此时我们创建一个函数用来布置雷名为Setmine

代码如下:(rand()%9的范围是0~8,再加1变为1~9)

void Setmine(char arr[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = (rand() % row) + 1;
		int y = (rand() % col) + 1;//随机生成x,y坐标
		if (arr[x][y] == '0')//判断x,y坐标处的值,若为‘0’则可放入‘1’。
		{
			arr[x][y] = '1';
			count--;
		}
	}
}

调用函数:

Setmine(mine, ROW, COL);

实现如下:

此棋盘中就有了10个‘1’,即10个雷,且每次重新生成的棋盘中雷的位置都不一样。

至此我们雷也随机布置到了不带星号的棋盘中。

(5)排查雷

现在我们要开始排雷了,用上述我们设计的排雷方法,来完成相应的代码。

首先创建一个函数名为Playboard

其代码:(此代码有点长,是此游戏的难点和核心所在,包含如果位置不是雷,就显示周围有几个雷以及如果位置是雷,就炸死游戏结束等条件。还请读者认真阅读,代码中有相应的注释,就不带读者细扣了)

void Playboard(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("请输入坐标:");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//在棋盘范围1~9内
		{
			if (show[x][y] == '*')//判断是否排查过
			{
				if (mine[x][y] == '1')
				{
					printf("很遗憾你被炸死了,游戏结束\n");
					Displayboard(mine, ROW, COL);//打印棋盘让你明白雷在哪里
					break;//跳出while循环
				}
				else
				{
					int count = Getcountboard(mine, x, y);//计算周围8个格子雷的个数
					show[x][y] = count + '0';//注意此处是赋值给在x,y处的show
					Displayboard(show, ROW, COL);//打印修改后的show棋盘
					win++;
				}
			}
			else
			{
				printf("此处已经排查过了,请重新输入\n");
			}
		}
		else
		{
			printf("输入非法,请重新输入\n");
		}
	}
	if (win == (row * col) - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
	}
}

上述函数中还引用了一个名为Getcountboard的函数,此函数是用来计算的周围8个格子的雷的个数的,代码如下:

int Getcountboard(char mine[ROWS][COLS], int x, int y)
{
	int count = 0;
	for (int i = x - 1; i <= x + 1; i++)
	{
		for (int j = y - 1; j <= y + 1; j++)//周围8个格子,也包括自身x,y处。
		{
			count += mine[i][j] - '0';//减去0的ASII码值后再相加
		}
	}
	return count;
}

上述所有代码就已经完成了扫雷游戏中的游戏实现过程,这个游戏过程我们给它放入一个名为game的函数中去:(把上述所有函数放入此函数中)

void game()
{
	char mine[ROWS][COLS] = { 0 };	//不带星号的棋盘
	char show[ROWS][COLS] = { 0 };	//带星号的棋盘
	Initboard(mine, ROWS, COLS, '0');
	Initboard(show, ROWS, COLS, '*');//给棋盘赋值
	Setmine(mine, ROW, COL);//布置雷
	//Displayboard(mine, ROW, COL);
	Displayboard(show, ROW, COL);	//展示棋盘
	Playboard(mine, show, ROW, COL);//排雷
}

这个game函数就是扫雷游戏的实现部分

(7)游戏菜单

接下来我们给这个游戏弄一个游戏菜单,相当于游戏的初始页面

编写一个名为menu的函数:

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

实现如下效果:

(8)游戏流程

有了menu()和game()这两个函数此扫雷游戏已经完成90%了,剩下10%是将这两个函数在主函数中引用即可,主函数将这两个函数串在一起,用于实现游戏的流程,主函数代码如下:

int main()
{
	srand((unsigned int)time(NULL));//调用srand库函数
	int input = 0;
	do {
		menu();
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("game over\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (input);
	

	return 0;
}

最终游戏效果演示:

代码排版:

整个游戏代码如下所示:

#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 Initboard(char arr[ROWS][COLS], int rows, int cols, char set);//棋盘赋值



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



void Setmine(char arr[ROWS][COLS], int row, int col);//布置雷



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

int Getcountboard(char mine[ROWS][COLS], int x, int y);//计算周围8个格子雷的个数

void Initboard(char arr[ROWS][COLS], int rows, int cols, char set)

{

	for (int i = 0; i < rows; i++)

	{

		for (int j = 0; j < cols; j++)

		{

			arr[i][j] = set;

		}

	}

}



void Displayboard(char arr[ROWS][COLS], int row, int col)

{

	printf("----扫雷开始----\n");

	for (int i = 0; i <= row; i++)

	{

		printf("%d ", i);

	}

	printf("\n");

	for (int i = 1; i <= row; i++)

	{

		printf("%d ", i);

		for (int j = 1; j <= col; j++)

		{

			printf("%c ", arr[i][j]);

		}

		printf("\n");

	}

}



void Setmine(char arr[ROWS][COLS], int row, int col)

{

	int count = EASY_COUNT;

	while (count)

	{

		int x = (rand() % row) + 1;

		int y = (rand() % col) + 1;

		if (arr[x][y] == '0')

		{

			arr[x][y] = '1';

			count--;

		}

	}

}

int Getcountboard(char mine[ROWS][COLS], int x, int y)

{

	int count = 0;

	for (int i = x - 1; i <= x + 1; i++)

	{

		for (int j = y - 1; j <= y + 1; j++)//周围8个格子,也包括自身x,y处。

		{

			count += mine[i][j] - '0';//减去0的ASII码值后再相加

		}

	}

	return count;

}



void Playboard(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("请输入坐标:");

		scanf("%d%d", &x, &y);

		if (x >= 1 && x <= row && y >= 1 && y <= col)//在棋盘范围1~9内

		{

			if (show[x][y] == '*')//判断是否排查过

			{

				if (mine[x][y] == '1')

				{

					printf("很遗憾你被炸死了,游戏结束\n");

					Displayboard(mine, ROW, COL);

					break;//跳出while循环

				}

				else

				{

					int count = Getcountboard(mine, x, y);

					show[x][y] = count + '0';//注意此处是赋值给在x,y处的show

					Displayboard(show, ROW, COL);

					win++;

				}

			}

			else

			{

				printf("此处已经排查过了,请重新输入\n");

			}

		}

		else

		{

			printf("输入非法,请重新输入\n");

		}

	}

	if (win == (row * col) - EASY_COUNT)

	{

		printf("恭喜你,排雷成功\n");

	}

}

void menu()

{

	printf("*****************\n");

	printf("***** 1.play ****\n");

	printf("***** 0.quit ****\n");

	printf("*****************\n");

}

void game()

{

	char mine[ROWS][COLS] = { 0 };	//不带星号的棋盘

	char show[ROWS][COLS] = { 0 };	//带星号的棋盘

	Initboard(mine, ROWS, COLS, '0');

	Initboard(show, ROWS, COLS, '*');//给棋盘赋值

	Setmine(mine, ROW, COL);//布置雷

	//Displayboard(mine, ROW, COL);

	Displayboard(show, ROW, COL);	//展示棋盘

	Playboard(mine, show, ROW, COL);//排雷



}

int main()

{

	srand((unsigned int)time(NULL));//调用srand库函数

	int input = 0;

	do {

		menu();

		printf("请输入:>");

		scanf("%d", &input);

		switch (input)

		{

		case 1:

			game();

			break;

		case 0:

			printf("game over\n");

			break;

		default:

			printf("输入错误,请重新输入\n");

		}

	} while (input);

	



	return 0;

}

建议读者做此类型的需要较长代码的题目尽量分开文件夹去做,不要都放一个文件夹中

我是用vs2022做的

我用了三个文件夹,头文件是game.h,源文件是game.c和text.c

头文件用来放引用库函数和函数声明;

源文件中game.c专门来写函数的实现;

text.c用来写主函数;

结束语:

感谢看此文章的大佬们,若此文章哪有不好的地方,还请大佬们评论区留言,感谢。

互关~~

帮忙点个赞~~

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值