C语言小游戏---扫雷

目录

1.数据的定义与函数的搭建

2.菜单打印 

3.初始化数组并打印

        1.初始化数组

        2.打印格子

4.设置雷坐标

 5.排雷模块

        1.计算周围雷的数量

        2.格子连锁展开

        3.用户排雷

6.主函数设计

8.未来展望

9.完整代码


1.数据的定义与函数的搭建

        本期我们将实现一个9X9格的扫雷游戏,因此我们可以构建两个二维数组,一个数组存储每一格是否有雷,有雷存储字符'1',没雷存储字符'0';而另一个数组则用来打印输出,当排查到时则存储周围雷的数量并打印,当没有被排查时则打印字符'*'以确保雷的隐藏性。由于我们排查时需要计算周围存在雷的数量,如果数组设置为9X9会发生数组越界的问题,为了方便计算,我们将数组设置为11X11的数组以避免数组越界。

#define COL 9            //实际列数
#define ROW 9            //实际行数
#define COLS COL+2       //为了方便后续统计雷数,将数组扩宽
#define ROWS ROW+2
#define EASY_COUNT 10    //要设置的雷数


char show[ROWS][COLS];     //用于对外打印显示
char mine[ROWS][COLS];     //用于存储雷

        我们按照模块化编程的思路将游戏的逻辑分成各个模块并封装成函数 ,如下:

//game.h声明的函数


//打印菜单
void menu();

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

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

//设置雷
void SetMine(char board[ROWS][COLS], int row, int col);

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

//统计雷的个数
int GetMineCount(char mine[ROWS][COLS], int x, int y);

//扩展显示
void Expend(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col,int x,int y,int*p);

2.菜单打印 

        一个小游戏肯定少不了一个菜单,在菜单界面,用户可以选择开始游戏或退出游戏,我们设计的菜单如下:

当用户输入1时,程序进入game()函数,开始游戏,当输入0时,结束游戏。

3.初始化数组并打印

        1.初始化数组

        当用户选择开始游戏后,在游戏开始前,我们需要对两个二维数组进行初始化,我们将存储雷的数组初始化为字符0,将用于显示的数组初始化为字符*号表示隐藏。因此我们根据外部传入的字符对数组进行初始化:

void InitBoard(char board[ROWS][COLS], int rows, int cols, char c)  //初始化数组
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			board[i][j] = c;
		}
	}
}

        2.打印格子

        通过循环遍历二维数组我们可以将每一格打印出来,打印时我们就无需再打印数组扩宽的部分,只需打印9x9的部分。而为了使棋盘更加美观,我们可以使用一些空格和符号将每一格分隔开。除此之外,我们可以将行号和列号打印出来,方便用户快速定位:

void PrintBoard(char board[ROWS][COLS], int row, int col)    //打印棋盘
{
	printf("=================扫雷===================\n");
	//打印列号
	for (int j = 0; j <= col; j++)
	{
		printf(" %d |", j);
	}
	printf("\n");
	for (int j = 0; j <= col; j++)
	{
		printf("---|");
	}
	printf("\n");
	for (int i = 1; i <=row; i++)       //打印内部实际9x9格子
	{

		//打印行号
		printf(" %d |", i);
		//打印棋盘
		for (int j = 1; j <=col; j++)
		{
			printf(" %c |", board[i][j]);
		}
		printf("\n");
		for (int j = 0; j <= col; j++)
		{
			printf("---|");
		}
		printf("\n");
	}
}

最终效果如下所示:

4.设置雷坐标

        我们可以采用控制生成随机数的方式来随机生成雷所在格子的坐标,如果生成的坐标没有被占用则将该位置改为字符'1'。以此循环直到生成一定数量的雷。

void SetMine(char board[ROWS][COLS], int row, int col)   //设置雷的坐标
{
	int count = EASY_COUNT;  //设定雷的数量
	while (count)
	{
		int x = rand() % row + 1;   //1-9
		int y = rand() % col + 1;   //1-9
		if (board[x][y]=='0')  //坐标合法
		{
			board[x][y] = '1';   //表示雷
			count--;
		}
	}
}

        我们同样可以把存储雷位置的数组打印出来:

 5.排雷模块

        1.计算周围雷的数量

        排雷是整个游戏最核心的部分。我们知道,当用户排雷时,如果选中坐标没有雷,则会显示本格旁边八格雷的数量,如下:

        由此,我们需要一个函数用于计算某一格周围雷的数量。由于我们在数组中用字符1表示雷,用字符0表示无雷,所以我们可以将某一格周围8格的字符相加然后减去8*'0'即可得到雷的数量:

int GetMineCount(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');
}

        2.格子连锁展开

        玩过扫雷的想必都知道,当我们选中某一格时,往往不只是将这一格显示出来,可能会将周围一片的格子展开。格子展开的规则如下:

该坐标不是雷

该坐标周围没有雷

该坐标没有被排查过

当选中坐标满足以上三个条件时,就会将该坐标及其周围展开,然后再次遍历该坐标周围的八个坐标直到不满足以上条件停止,达到连锁展开的效果。


我们可以通过递归的方式进行展开:

void Expend(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* p)  //格子展开
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)   //限制展开的范围,防止越界
	{
		int count = GetMineCount(mine, x, y);      //获取周围雷数
		if (count == 0)  //周围没有雷
		{
			show[x][y] = ' ';    //周围没有雷,显示空格

			for (int i = x - 1; i <= x + 1; i++)   //遍历周围
			{
				for (int j = y - 1; j <= y + 1; j++)
				{
					if (show[i][j] == '*')  //没有被排查过,防止死递归
					{
						Expend(mine, show, row, col, i, j, p);
					}
				}
			}
		}
		else
		{
			show[x][y] = count + '0';  //有雷显示雷数,以字符形式
		}
		(*p)--;    //计算游戏总展开格数
	}
}

        3.用户排雷

        有了上述两个模块,我们就可以设计排雷函数,在让用户输入排雷坐标的同时,进行恰当的展开并显示正确信息:

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int wins = row*col-EASY_COUNT;     //共需要有效排查的个数
	while (wins)
	{
		printf("您还需排查%d个位置\n", wins);
		printf("请输入要排雷的坐标:> ");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')    //有效位置
			{
				if (mine[x][y] == '1')
				{
					printf("很遗憾,您被炸死了\n");
					PrintBoard(mine, ROW, COL);      //游戏结束,将雷区打印出来
					break;
				}
				else
				{
					Expend(mine, show, row, col, x, y, &wins);
					PrintBoard(show, ROW, COL);      //将棋盘打印出来
				}
			}
			else
			{
				printf("该坐标已排查,请重新输入\n");
			}

		}
		else
		{
			printf("输入的坐标有误,请重新输入\n");
		}
	}
	if (wins == 0)
	{
		printf("恭喜你,获胜了\n");
		PrintBoard(mine, ROW, COL);      //游戏结束,将雷区打印出来
	}
}

        当用户输入的位置为雷时,就会提示用户被炸死了并将存储雷的数组显示给用户;当输入的位置合法且没有雷时就会进行递归展开并打印展开后格子信息然后进行下一次输入。我们用变量wins存储还需排查的格数以提醒用户,当wins为0时就结束游戏。总效果如下:

        递归展开

         触碰到雷,打印

          全部排查完毕

6.主函数设计

        主函数依旧采用do...while循环来实现游戏的循环。每当游戏结束后,回到打印菜单处让玩家选择是否继续游戏,使用switch选择语句根据玩家的选择进入不同的分支。如果玩家选择0退出,则跳出循环,程序结束。代码如下:

int main()
{
	srand((unsigned int)time(NULL));   //设置随机种子,以供后续使用
	int input = 0;
	do
	{
		menu();
		printf("请输入:> ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入非法,请重新输入\n");
			break;
		}

	} while (input);
	return 0;
}

         而对于game()函数,就是负责把game.c文件的各个功能模块按照一定顺序整合起来,实现一个完整的扫雷逻辑,如下:

void game()    //游戏模块,整合函数
{
	char show[ROWS][COLS];     //用于对外打印显示
	char mine[ROWS][COLS];     //用于存储雷
	InitBoard(show, ROWS, COLS, '*');   //用'*'初始化显示数组
	InitBoard(mine, ROWS, COLS, '0');   //用'0'初始化雷数组
	PrintBoard(show, ROW, COL);   //对外打印show数组,只需显示中间9X9部分
	SetMine(mine, ROW, COL);      //设置雷
	//PrintBoard(mine, ROW, COL); //打印雷的分布
	FindMine(mine, show, ROW, COL);  //排雷模块,并通过Mine数组把信息传给show()数组
}

8.未来展望

目前,此游戏的功能还相对单一,未来准备进行以下两点改进:


1.将游戏做成更精美的图形界面

2.实现标记功能,方便玩家更好地进行游戏

9.完整代码

百度网盘链接:

链接:https://pan.baidu.com/s/1RhLNtKshDAj3qqx78xEuaQ 
提取码:2003


 以上,就是本期的全部内容。

制作不易,能否点个赞再走呢qwq

  • 22
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忆梦初心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值