C语言中简易扫雷游戏的实现过程(结尾附完整代码)

扫雷游戏制作思路的讲解

扫雷的主界面设置
1.首先扫雷作为一款游戏,一点开它一定会有一个主界面,而主界面必然会有开始游戏或者退出游戏的选项,而这该如何实现呢?我们可以创建一个menu()函数再用printf函数来打印主界面上我们所要呈现出来的内容。如图所示:
在这里插入图片描述

2.在这里我们需要注意的是,由于我们是代码所制作的游戏,不能用鼠标点击,因此我们要用scanf函数来让用户输入,选择是play,还是exit。而在这其中最终要的一点是,我们游戏如果想玩的话是能够一直玩的,那么我们就应该选择循环语句,而循环语句有三种又该如何选择呢?因为一个游戏,进入界面时无论如何都会跳出选项的,针对于这一点,在循环中最适合的便是使用do while循环,因为该循环在使用时一开始无需判断条件就能开始执行,非常符合游戏的需求,那么我们就可以理所当然的打出以下的代码:
在这里插入图片描述
而在这里,由于用户输出的值的不同,会导致不同的结果,所以我们运用的分支语句是switch,相比与if语句,在这里switch语句使用起来会更加的简洁,而值得注意的是对于switch语句的使用,我们应该主要在每一种情况之后都加上break,这个是我个人在制作时所犯下的错误。
在这其中input的使用也是十分的恰到好处,由于在menu的设置中,我们设置exit为0,即我们如果要退出应该输入0,而0在C语言中又是非常特殊的存在,可以作为假,正好可以作为do while循环中后面条件的判断。
以上的仅仅只是扫雷游戏最初始的阶段。

扫雷游戏的开始
游戏的主界面建立好了之后,我们便要开始游戏的制作阶段,这时候为了代码更易于让人理解,我们便可以创建出一个叫做game的函数专门来防止扫雷游戏的代码如上图那样。
1.创建以及初始化的过程:一开始我们对游戏如何制作肯定一头雾水,那么我们边可以到网上去查找扫雷游戏如下:
在这里插入图片描述
很显然,扫雷游戏一开始呈现给我们的是一排排一列列整齐的未知的方块,我们不知道里面哪里有雷哪里没雷,但是呢我们应该根据它这一排排一列列的分布从而想到我们的二维数组,因为二维数组在我们的认知也是一排排一列列的,好了我们有了思路便可以开始创建数组了,根据以上的扫雷游戏我们可以创建出一个9* 9的数组代表有九行九列,和上面的扫雷游戏相对应。
那么这时候我们建立完了,便该想,扫雷游戏有的地方有雷有的地方没雷,我们也应该这样设置用两个数来对于有雷或者没雷,而这里我使用的是0和1,0代表没雷,1代表有雷(具体的后面会加以解释),那么雷的设置也就这样了。
一个问题解决了我们顺着扫雷游戏玩下去我们会发现,我们如果扫雷扫到没雷的地方会显示出周围有多少雷,而这里便与前面起冲突了,起了啥冲突呢?就是当我们扫一个地方没雷时,假设周围只有一个雷,那么该地方就会显示1,而1又与我前面设置的雷是1冲突了,这样的话就会导致不好区分这地区是雷还是显示周围有一个雷,那么有人就会想那我们不用1用其他不好不就好了(这其实不好,在后面降到检测周围雷的个数时会讲到)。那么为了解决这个冲突,我们便想到创建出两个数组一个专门放雷(在初始化时先全部都显示0,之后在将雷1放进去),一个专门来检测周围雷的个数(初始化为* 表示未知,随着我们扫雷的进行,该部分会被周围有多少个雷代替(如下图)),但是对于检测雷的个数问题我们应该注意临界问题,因为数组是里的元素是有限的。
在这里插入图片描述
上图的1就是* 被取代后的结果。
在这里插入图片描述
对于边缘地区的检测(如上图),会存在越界的情况,那么为了解决这个问题呢,我们可以多给出一行一列来并且由于是边界本来超出的部分就是没有的,那么我们把超出的部分换成0,也相当于没有的意思。如下图:
在这里插入图片描述
好了有了,以上的这些基础我们就可以为数组初始化,我们把放雷的数组叫做mine,把表示周围雷数的叫做show,由于这两个数组初始化时,都是将数组内的元素放成同一个字符,那么我们可以用一个函数(叫做Setboard)来简便代码如下:
在这里插入图片描述
上图中的ROWS COLS ROW COL等等都是自定义的东西,将行数和列数分别用ROW和COL表示,而ROWS和COLS表示行列数分别对应加二,来解决临界问题。
因为扫雷游戏不只有9* 9规格的还有其他规格,用这样定义,可以随时改变自己扫雷程序的规格大小,只需要在定义的地方进行修改,不用全部代码都进行修改。当然啦这些肯定定义在自建的头文件中,之后再进行声明就好了。
在这里插入图片描述

在这里插入图片描述

当然图一中数组传参要如何实现可以看我写的博客有关函数部分的内容。
https://blog.csdn.net/2302_78979254/article/details/132016563?spm=1001.2014.3001.5501
然后图二就是最经典的数组初始化的循环代码。

2.展示和设置雷的过程
展示的话我们可以设置一个函数,为什么呢?因为在后面我们查找一个位置时,对于show数组内容就会改变,而改变一次我们就应该展现一次(显示周围的雷数将所查找地方的* 代替),该函数我们称为Displayboard函数,如下代码:
在这里插入图片描述
然后我们进行运行可以看到界面:
在这里插入图片描述
正如我们想象的界面就出来的但是呢?有个缺点就是用户在输入行和列时还要自己数,这就不利于游戏的游玩,那么我们便可以进行改良,将每一行每一列的序号都标出来。代码如下:
在这里插入图片描述
便有以下的效果(当然我们还有注意每个字符的间隔)
在这里插入图片描述
而对于雷的设置应该随机,而对于随机数的设置,也可以去看我的博客(https://blog.csdn.net/2302_78979254/article/details/131988833?spm=1001.2014.3001.5501)有一篇专门讲的,这里就不解释啦,代码如下:
在这里插入图片描述
在上面的代码中我将EASY_COUNT设置为雷的个数,也是自定义。
在设置雷的过程中最重要的是,雷的个数一定要是我们所要求的,就是说我们不能重复,那么在上图的代码中就出现了if语句的判断,来避免同一个位置被重复设置雷,导致雷的个数的减少,在设置完之后我们可以打印出来看看,如下:
在这里插入图片描述
如上图我们可以清楚的看到有10个1随机的排列在数组中。

3.查找雷及其个数的计数
对于雷的查找,我们也可以单独设置一个函数()叫做Findmine。
首先对于雷的查找我们是要在mine数组中进行的,而查找出来后周围的雷数,我们则是要输出到show函数当中的,那么我们在建立函数时,就要传入两个两个数组,并且传入行数与列数
如下:

Findmine(mine, show, ROW, COL);//简单形式,具体情况见下文

由于这时候是我们要查找,所以就应该有scanf函数让我们输入两个数字分别表示行和列,当然啦由于数组是有限的,所以我们对于输入的数也要进行限制,输入错误应该提醒用户重新输入。大致的代码如下:

int x = 0;
int y = 0;
printf("请输入你要查找的地方:>");
scanf("%d%d", &x, &y);
if (x > 0 && x < row && y>0 && y < row)
{
	int count = Getminecount(mine, x, y);
}
else
{
	printf("输入地方非法,请重新输入");
}

然后将输入的数放到mine数组中去,来进行进一步的分析其周围雷的数量。而雷的数量的计算则还需创建另一个函数叫做(Getminecount),用该函数的返回值来代表周围雷的个数所以这里的数据类型为int。如上图所示,由于输入的位置为x行y列,那么其周围的数用x,y表示就是这样:
在这里插入图片描述
那么我们就可以根据这个来求了,因为我们设置的是字符类型的数组,所以’0’代表的ASCII值是48,在计算时,‘0’便会变成48,同理’1’会变成49,那么用’1’-'0’的结果便是1。那么这时候我们就可以计算了如下代码:

int Getminecount(char mine[ROWS][COLS], int x, int y)
{
	return mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] +
		mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] - 8 * '0';
}

因为mine[][]数组中只有’1’或者’0’,如果周围八个都为’0’的话,那么减去8个’0’得到的结果就是0,即周围没雷,如果周围有5个’1’的话,那么剩下还有3个’0’,与所要减去的8个’0’相消后,就变成5个’1’减去5个’0’,得出的结果就是5,计算的结果符合周围有5个雷。这就是为什么我前面要用字符’1’来表示雷而字符’0’来表示无雷,就是为了后面方便雷个数的计算。
那么这样周围雷个数的计算就好了。计算完之后我们得到的值要放到show数组中,而show数组也是字符类型肯定不能将Getminecount函数的int类型的返回值直接放入还需要进一步的加工,即加上字符’0’,因为字符’0’与字符’1’的ASCII值的差距仅仅为1,同理’0’与’2’的ASCII值的差距也只为而,在count值算出来后加上字符’0’(计算时转化为ASCII值来计算),之后再将计算出的结果转化为字符类型就合理了,举个例子,假设count算出来为3,那么加上’0’后其实时是变成了51,而51在ASCII表中又对应着字符’3’。那么完整的代码如下:

printf("请输入你要查找的地方:>");
		scanf("%d%d", &x, &y);
		if (x > 0 && x < row && y>0 && y < row)
		{
			int count = Getminecount(mine, x, y);
			show[x][y] = count + '0';
			Displayboard(show, ROW, COL);

而在后面加上Displayboard是为了让我们看到我们查找的地方由变成显示周围雷的个数,由于我们查找不只只进行一次,所以就要用循环,而循环的次数与我们雷数挂钩,为什么呢?假设我们雷有十个,那么在99中就有71个地方没雷,那么我们只有把这71个地方都找出来才算我们获胜。那么代码就可以进一步修改成这样:

while (win < ROW * COL - EASY_COUNT)
	{
		printf("请输入你要查找的地方:>");
		scanf("%d%d", &x, &y);
		if (x > 0 && x <= row && y>0 && y <= row)
		{
			
			 if(mine[x][y]=='0')
			{
				int count = Getminecount(mine, x, y);
			show[x][y] = count + '0';
			Displayboard(show, ROW, COL);
			win++;
			}
			else if(mine[x][y]=='1')
			{
				printf("你扫到雷了,挑战失败\n");
				Displayboard(mine, ROW, COL);
			}
		}
		
	}
	

在上面代码中,我们应该对于输入地方范围超过了数组范围的值进行提醒,并且在查找过程中我们也不能重复的查找相同的地方。根据以上两点我们又可以进行修改:

while (win < ROW * COL - EASY_COUNT)
	{
		printf("请输入你要查找的地方:>");
		scanf("%d%d", &x, &y);
		if (x > 0 && x <= row && y>0 && y <= row)
		{
			if (show[x][y] != '*')
			{
				printf("此处已经查找过,请重新选择地方:>");
				
			}
			else if(mine[x][y]=='0')
			{
				int count = Getminecount(mine, x, y);
			show[x][y] = count + '0';
			Displayboard(show, ROW, COL);
			win++;
			}
			else if(mine[x][y]=='1')
			{
				printf("你扫到雷了,挑战失败\n");
				Displayboard(mine, ROW, COL);
			}
		}
		else
		{
			printf("输入地方非法,请重新输入");
		}
	}

而最终如果我们扫雷成功了(即循环也结束了),也应该有提醒语句那么再进行最后一步修改:

while (win < ROW * COL - EASY_COUNT)
	{
		printf("请输入你要查找的地方:>");
		scanf("%d%d", &x, &y);
		if (x > 0 && x <= row && y>0 && y <= row)
		{
			if (show[x][y] != '*')
			{
				printf("此处已经查找过,请重新选择地方:>");
				
			}
			else if(mine[x][y]=='0')
			{
				int count = Getminecount(mine, x, y);
			show[x][y] = count + '0';
			Displayboard(show, ROW, COL);
			win++;
			}
			else if(mine[x][y]=='1')
			{
				printf("你扫到雷了,挑战失败\n");
				Displayboard(mine, ROW, COL);
			}
		}
		else
		{
			printf("输入地方非法,请重新输入");
		}
	}
	if (win == ROW * COL - EASY_COUNT)
	{
		printf("恭喜你,扫雷成功!!!\n");
	}

那么到这我们扫雷的所有流程都结束了,我们可以启动我们的代码试试了。

在这里插入图片描述

在这里插入图片描述
那么以上就代码的正常执行过程啦!!!

//game.h中的代码
#pragma once
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define EASY_COUNT 78

void Setboard(board,rows,cols,r);
void Displayboard(board, row, col);
void Setmine(board, row, col);
void Findmine(mine,show, row, col);

//test.c中的代码
#define  _CRT_SECURE_NO_WARNINGS 1
#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];
	Setboard(mine, ROWS, COLS,'0');
	Setboard(show, ROWS, COLS,'*');
	Displayboard(show, ROW, COL);
	Setmine(mine, ROW, COL);
	
	

	Findmine(mine, show, ROW, COL);


}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("-------扫雷游戏-------\n");
			game();
			break;
		case 0 :
			printf("退出游戏成功\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;//注意switch语句的语法,每种情况都应该有break
		}

	} while (input);
	return 0;
}
//game.c中的代码
#define  _CRT_SECURE_NO_WARNINGS 1

#include"game.h"
void Setboard(char board[ROWS][COLS], int rows, int cols,char r)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = r;
		}
	}
} 
void Displayboard(char board[ROWS][COLS], int row, int col)//注意数组初始化时行和列别忘记敲了
{
	int i = 0;
	for (i = 0; i <= ROW; i++)
	{
		printf("%d ", i);
	}
	//以上循环表示列序号的打印
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);//该地方表示行序号打印,在每一行打印的开头加上
		int j = 0;
		for (j = 1; j <= row; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}
void Setmine(char board[ROWS][COLS], int row, int col)
{
	
	int n = EASY_COUNT;
	while (n>0)
	{
		int i = rand() % ROW + 1;
		int j = rand() % ROW + 1;
		if (board[i][j] == '0')
		{
			board[i][j] = '1';
			n--;
		}
	}
}
int Getminecount(char mine[ROWS][COLS], int x, int y)
{
	return mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] +
		mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] - 8 * '0';
}
void Findmine(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 > 0 && x <= row && y>0 && y <= row)
		{
			if (show[x][y] != '*')
			{
				printf("此处已经查找过,请重新选择地方:>");
				
			}
			else if(mine[x][y]=='0')
			{
				int count = Getminecount(mine, x, y);
			show[x][y] = count + '0';
			Displayboard(show, ROW, COL);
			win++;
			}
			else if(mine[x][y]=='1')
			{
				printf("你扫到雷了,挑战失败\n");
				Displayboard(mine, ROW, COL);
			}
		}
		else
		{
			printf("输入地方非法,请重新输入");
		}
	}
	if (win == ROW * COL - EASY_COUNT)
	{
		printf("恭喜你,扫雷成功!!!\n");
	}	
}

那么扫雷游戏就这样讲解结束了!!!希望对大家有所帮助,如有内容不妥之处恳请各位大佬指正!!!
萌新一位制作不易谢谢!!!

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值