C语言扫雷完整版——长文 详细分享

一.游戏玩法和基本思路介绍

首先是关于游戏玩法:输入坐标进行排雷,如果所输入坐标不是雷,那么就会告诉我们周围8个格子中地雷的总数。如果周围8个格子都没有地雷,那就自动扩大安全区域。效果如下。而如果能够成功排完全部地雷,那就胜利
在这里插入图片描述

二.游戏实现和步骤介绍

1.准备阶段

步骤讲解

游戏开始之前,我们需要简略地制作一下游戏封面,效果图如下在这里插入图片描述这个功能的实现并不复杂,直接上代码

步骤代码

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

int main()
{
	int input = 0;
	do
	{
		menu();  //菜单函数
		scanf("%d", &input);
		switch (input)
		{
			case 1: game();
				break;
			case 0:
				printf("退出游戏\n");
				break;
			default :printf("输入数值无效\n");
				break;
		}
	} while (input);   //输入1则进入游戏,输入0则直接退出,否则便会一直循环
}

2.设计和初始化棋盘

步骤讲解

1.首先我们需要设计像下面这样9×9的棋盘,并且我们需要一个棋盘来记录地图上地雷的位置信息,另一个棋盘是专门给用户游玩的棋盘,也就是展示棋盘。

2.其次,考虑到我们需要实现统计周围地雷数量的函数,而当统计的位置在地图的边角的时候,统计起来就会很麻烦,也许要分情况,也许会越界访问。
在这里插入图片描述3.为了解决2问题,我们可以考虑直接将棋盘扩大一圈,也就是11*11。但是!我们的主要功能都是在两个棋盘的9×9区域实现的

所以我们先设计一些常量,并定义上述两个棋盘

//11×11
#define ROWS 11
#define COLS 11
//9×9
#define ROW 9
#define COL 9

char mine[ROWS][COLS] = { 0 };   //扫雷信息的棋盘
char show[ROWS][COLS] = { 0 };   //展示棋盘,即游戏棋盘

诶那这完事之后,我们就要开始对这两个棋盘动手动脚了(初始化)

4.首先我们先初始化一下扫雷信息的棋盘(mine数组),为了再次方便后续统计周围区域地雷数量,我们可以把字符1表示有地雷,而字符0表示没有地雷,这样子的话,周围有多少个字符1,就能够代表有多少个地雷,所以我们只需要两个循环把他全部初始化成字符0即可

5.然后就轮到了展示棋盘(show数组),因为是展示给用户看的,所以我们全部初始化成星号 * 就行,也就是两个循环全部初始化成星号

6.这时候眼尖的小伙伴就发现了,这两个棋盘的初始化都是两个循环直接全部赋值成某个字符,那么我们就可以直接把这个字符当做参数传入函数,这样就能一个函数初始化两个棋盘
在这里插入图片描述

步骤代码

//函数实现
void InitBoard(char(*arr)[COLS],int row,int col,char set)
{//                                  11      11     
	int i = 0, j = 0;
	for (i = 0; i < row; i++)   //11×11的棋盘一起初始化
	{
		for (j = 0; j < col; j++)
		{
			arr[i][j] = set;  //这个set就是我们想要初始化的字符
		}
	}
}
//函数体
InitBoard(mine, ROWS, COLS, '0'); //初始化棋盘
InitBoard(show, ROWS, COLS, '*');

3.打印棋盘

步骤讲解

1.由于是要给用户玩的,所以我们在打印的时候打印中间9×9的棋盘就可以。

2.其次,为了后续用户方便输入坐标,我们还需要在棋盘上打印一下行和列。效果如下
在这里插入图片描述

步骤代码

void ShowBoard(char (*arr)[COLS],int row,int col)
{//                                   9       9
	int i = 0, j = 0;
	//打印列的序号
	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 ", arr[i][j]);
		}
		printf("\n");
	}
}

4.布置雷

步骤讲解

1.接下来我们就要对mine数组(地雷信息数组)放点地雷进去了,我们先定义一个常量来表示我们要安放的地雷数量

#define MINE_NUM  10 //雷的个数

2.然后我们就要将这些地雷随机放到9×9的棋盘中,我们可以整个while循环,定义一个conut=MINE_NUM,然后每放一个地雷,conut就减一

步骤代码

void SetMine(char(*arr)[COLS], int row, int col)
{ //                                9        9
	srand((unsigned int)time(NULL));//随机布置雷
	int count = MINE_NUM;   //要布置的雷的数量
	while (count)
	{//                   9
		int x = rand() % ROW + 1;  
		int y = rand() % COL + 1;
		if (arr[x][y] == '0')    //如果是字符0,代表没有地雷,让他变成字符1即可
		{
			arr[x][y] = '1';
			count--;
		}
	}
}

5.排雷

前菜

1.前菜:既然是扫雷,那就会有胜利的时候,也就是我们要实现胜利的功能,我们可以定义一个全局变量int win = 0;来表示我们安全区域的个数,那么就会有:win+雷的总个数=总格子数(81),而我们不断排雷的过程就是个循环。 所以我们可以先得出这个框架

void ClearMine(char(*mine)[COLS], char(*show)[COLS], int row, int col)
{ //这一步是在9×9的棋盘中操作的                             9        9
	int x = 0, y = 0;
	int sum = ROW * COL;//总格子数81
	while (win + MINE_NUM < sum)   //如果雷的数量和没踩到雷的数量=81,那就胜利
	{
		printf("请输入扫雷坐标(x,y)\n");
		scanf("%d%d", &x, &y);
		……
	}
}

2.而用户输入的坐标需要判断合法性 if ((x >= 1 && x <= row) && (y >= 1 && y <= col))

3.如果输入的坐标是雷(字符1),也就简单了,打印一句游戏结束,就直接return

4.相反,如果不是雷,我们就要获取周围地雷数量,并更新展示棋盘(show)。

获取周边地雷数

5.为了获取坐标所在九宫格的地雷数量,我们一个循环就可以实现,我们定义int cnt=0;来统计雷的数量,由于坐标本身不是雷(即字符0),所以我们循环只需要cnt+=mine[x][y]-'0' 即完成任务。

6.得到周围地雷数量后我们还要同步更新show[x][y]

获取周边地雷数代码

int GetAroundNum(char(*mine)[COLS], int x, int y)
{
	int cnt = 0;
	int i = 0, j = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			cnt += mine[i][j] - '0';
		}
	}
	return cnt;
}

步骤完整代码

void ClearMine(char(*mine)[COLS], char(*show)[COLS], int row, int col)
{ //是在9×9的棋盘上操作的                                  9        9
	int x = 0, y = 0;
	int sum = ROW * COL;//总格子数
	while (win + MINE_NUM < sum)   //如果雷的数量和没踩到雷的数量=81,那就胜利
	{
		printf("请输入扫雷坐标(x,y)\n");
		scanf("%d%d", &x, &y);
		if ((x >= 1 && x <= row) && (y >= 1 && y <= col))//坐标合法
		{
			if (mine[x][y] == '1')
			{
				printf("踩雷!\n游戏结束!\n");
				/*ShowBoard(mine, row, col);
				system("pause");
				system("cls");锦上添花部分,可加可不加*/
				return;
			}
			else
			{
				int num = GetAroundNum(mine, x, y);//得到周边地雷数量
				win++;  //是安全区域,win++
				if (num==0)//如果周围的地雷数量为0
				{
					Expand(mine, show, x, y);//那就开始拓展,下面有详讲
				}
				else
				{
					show[x][y] = num + '0';  //更新show数组
				}
				ShowBoard(show, row, col);
			}
		}
		else
		{
			printf("坐标输入值非法\n");
		}
	}
	printf("排雷成功!\n");   //如果能够完成这个循环,那就排雷成功
	/*system("pause");
	system("cls");可加可不加*/
}
//获得周围地雷个数
int GetAroundNum(char(*mine)[COLS], int x, int y)
{//                                    x和y即坐标
	int cnt = 0;
	int i = 0, j = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			cnt += mine[i][j] - '0'; //减去'0'得到整数
		}
	}
	return cnt;
}

6.递归拓展安全区域

步骤讲解

1.如果周围存在地雷,那就让show[x][y]等于地雷数量对应的字符即可(比如周围有3个雷,就要让show[x][y]=‘3’,实现方法也很简单,让3+‘0’就好)

2.如果周围没有地雷,那就要让周围8个地雷都做一次GetAroundNum(获得周围地雷个数)的动作。

3.得到周围地雷个数后,再判断是否为0,不是0,那就直接更新show[x][y],是0,那就这个坐标周围8个再进行一次GetAroundNum(获取周围地雷个数)……以此类推,根据1,2步骤循环即可

步骤代码

void Expand(char(*mine)[COLS], char(*show)[COLS], int x, int y)
{
	if (x < 1 || x>9 || y < 1 || y>9)//判断坐标合理性
	{
		return;
	}
	if (show[x][y] != '*')   //如果已经被排查了
	{
		return;
	}
	int num = GetAroundNum(mine, x, y); //周围雷的数量
	if (num > 0)  //如果不是零,直接更新数量就行
	{
		show[x][y] = num + '0';
		win++;  //安全区域++
		return;
	}
	else   //如果是0
	{
		show[x][y]='0';   //此处还要更新
		win++;  //安全区域++
		//周围8个都要递归
		Expand(mine, show, x + 1, y - 1);
		Expand(mine, show, x - 1, y);
		Expand(mine, show, x - 1, y+1);
		Expand(mine, show, x, y - 1);
		Expand(mine, show, x, y + 1);
		Expand(mine, show, x + 1, y - 1);
		Expand(mine, show, x + 1, y);
		Expand(mine, show, x + 1, y+1);
	}
}

三.完整代码

game.h

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

#define ROWS 11
#define COLS 11
#define ROW 9
#define COL 9
#define MINE_NUM  10 //雷的个数

//棋盘
void menu();

//初始化棋盘
void InitBoard(char (*arr)[COLS],int row,int col,char set);

//展示棋盘
void ShowBoard(char(*arr)[COLS], int row, int col);

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

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

//得出周围雷的数量
int GetAroundNum(char(*mine)[COLS], int x, int y);

//拓展周围是不是雷的区域
void Expand(char(*mine)[COLS], char(*show)[COLS], int x, int y);

game.c

#include"game.h"

int win = 0;//表示不是雷的数量

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

void InitBoard(char(*arr)[COLS],int row,int col,char set)
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			arr[i][j] = set;
		}
	}
}

void ShowBoard(char (*arr)[COLS],int row,int col)
{
	int i = 0, 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 ", arr[i][j]);
		}
		printf("\n");
	}
}

void SetMine(char(*arr)[COLS], int row, int col)
{
	srand((unsigned int)time(NULL));//随机布置雷
	int count = MINE_NUM;   //雷的数量
	while (count)
	{
		int x = rand() % ROW + 1;
		int y = rand() % COL + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
			count--;
		}
	}
}

void ClearMine(char(*mine)[COLS], char(*show)[COLS], int row, int col)
{
	int x = 0, y = 0;
	int sum = ROW * COL;//总格子数
	while (win + MINE_NUM < sum)   //如果雷的数量和没踩到雷的数量=81,那就胜利
	{
		printf("请输入扫雷坐标(x,y)\n");
		scanf("%d%d", &x, &y);
		if ((x >= 1 && x <= row) && (y >= 1 && y <= col))//坐标合法
		{
			if (mine[x][y] == '1')
			{
				printf("踩雷!\n游戏结束!\n");
				ShowBoard(mine, row, col);
				system("pause");
				system("cls");
				return;
			}
			else
			{
				//递归
				int num = GetAroundNum(mine, x, y);
				win++;
				if (num==0)//如果周围的地雷数量为0
				{
					Expand(mine, show, x, y);//那就开始拓展
				}
				else
				{
					show[x][y] = num + '0';
				}
				ShowBoard(show, row, col);
			}
		}
		else
		{
			printf("坐标输入值非法\n");
		}
	}
	printf("排雷成功!\n");
	system("pause");
	system("cls");
}

int GetAroundNum(char(*mine)[COLS], int x, int y)
{
	int cnt = 0;
	int i = 0, j = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			cnt += mine[i][j] - '0';
		}
	}
	return cnt;
}

void Expand(char(*mine)[COLS], char(*show)[COLS], int x, int y)
{
	if (x < 1 || x>9 || y < 1 || y>9)//判断坐标合理性
	{
		return;
	}
	if (show[x][y] != '*')   //如果已经被排查了
	{
		return;
	}
	int num = GetAroundNum(mine, x, y); //周围雷的数量
	if (num > 0)  //如果不是零,直接写出周围的数字就行
	{
		show[x][y] = num + '0';
		win++;
		return;
	}
	else
	{
		show[x][y]='0';
		win++;
		Expand(mine, show, x + 1, y - 1);
		Expand(mine, show, x - 1, y);
		Expand(mine, show, x - 1, y+1);
		Expand(mine, show, x, y - 1);
		Expand(mine, show, x, y + 1);
		Expand(mine, show, x + 1, y - 1);
		Expand(mine, show, x + 1, y);
		Expand(mine, show, x + 1, y+1);
	}
}

test.c

#include"game.h"

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);  //布置雷
	ShowBoard(mine, ROW, COL);  //展示棋盘
	ShowBoard(show, ROW, COL);
	//ShowBoard(mine, ROW, COL);  //展示棋盘
	ClearMine(mine, show, ROW, COL);   //排雷
}

int main()
{
	int input = 0;
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
			case 1: game();
				break;
			case 0:
				printf("退出游戏\n");
				break;
		}
	} while (input);
}
做一个后台管理系统的页面设计需要考虑多个方面,包括用户体验、信息架构、可用性、可维护性等等。下面详细介绍一下如何设计一个高质量的后台管理系统页面。 1. 用户体验 在设计后台管理系统页面时,用户体验是非常重要的。这意味着页面需要易于使用,用户能够轻松找到他们需要的信息。以下是一些设计后台管理系统页面的建议: - 简洁明了的布局:页面应该有清晰的结构和组织,避免过于繁杂的页面布局。在设计页面时,应该尽量简化和组织页面元素,使用户更容易找到所需的信息。 - 直观的导航:设计一个清晰、直观的导航系统,可以帮助用户快速找到他们需要的功能和信息。导航菜单应该被放置在页面的顶部或侧边栏,并使用易于理解的标签和图标。 - 交互式元素:添加交互式元素,如下拉菜单、标签、按钮等,可以提高用户体验。这些元素可以让用户快速完成操作,并让页面更具吸引力。 - 明确的反馈:设计页面时,应该考虑用户在执行操作时需要明确的反馈。例如,在表单提交后显示成功或失败的消息。这可以帮助用户了解他们的操作是否成功。 2. 信息架构 一个良好的信息架构可以帮助用户快速找到他们需要的信息,并且可以让页面看起来更加整洁和有条理。以下是一些设计后台管理系统页面的建议: - 分类信息:对信息进行分类和分组,以帮助用户快速找到他们需要的信息。例如,可以将用户管理、内容管理等相关的功能组合在一起。 - 标签化:在页面上使用标签来帮助用户快速了解信息。例如,在列表中,可以使用标签来标识不同的项目状态。 - 搜索功能:添加搜索功能,可以帮助用户快速找到他们需要的信息。搜索框应该被放置在页面的顶部或侧边栏,并使用易于理解的搜索关键字和提示信息。 3. 可用性 可用性是指页面应该易于使用和理解。以下是一些设计后台管理系统页面的建议: - 明确的标签和标题:页面上的标签和标题应该易于理解和记忆。这可以帮助用户更快地找到他们需要的信息。 - 明确的指示:对用户进行明确的指示可以让他们更容易完成操作。例如,在表单中使用明确的标签和提示信息可以让用户更
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

答辣喇叭

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

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

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

打赏作者

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

抵扣说明:

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

余额充值