扫雷游戏的实现

扫雷游戏相信大部分人都玩过,那怎么用代码来实现它呢?

要实现这个游戏我们首先要清楚这个游戏的玩法,以及它的输赢是怎么判断的。

让我们先来看一眼这个游戏,首先得有一个棋盘,然后玩家选中位置进行扫雷,如果这个位置不是雷的话,就会显示出一个数字,这个数字告诉你这个位置周围八个位置中有多少个位置是雷。然后我们进行进一步的扫雷。如果某次扫雷的位置是雷的话就游戏失败,直到把所有不是雷的位置找出来,才获得游戏胜利。

                          

 

根据游戏的规则,我们大致知道了我们要实现哪些功能:

第一:有一个可以扫雷的方格,并且自动生成一些雷。

第二:扫雷,被扫非雷位置打印出周围有几个雷。

第三:判断胜负的机制。

有了这三个功能扫雷游戏就基本实现了,但是我们应该考虑到游戏的体验感,如果位置周围没有雷,我们就要一个一个点,多费劲啊!我们玩网页版扫雷的时候会发现,有时点一个位置好多位置都出来了。有了这个扫一片的功能体验感不就上来了吗!! 

有了要实现的功能这下我们就只要根据功能写出代码了

首先我们先将游戏的大致框架代码写出来(这个框架用过多次,不知道的可以看之前的文章),这里采用do while 循环,实现玩了一局可以接着玩。

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
	printf("欢迎来到扫雷游戏!\n");
	printf("*****************************\n");
	printf("********    play(1)   *******\n");
	printf("********    exit(0)   *******\n");
	printf("*****************************\n");
}
int main()
{
	int a;
	do
	{	
		menu();
		printf("请输入>:\n");
		scanf("%d",&a);
		switch(a)
		{ 
		case 1:
			Sleep(1000);
			system("cls");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入>:\n");
		}
		Sleep(2500);
		system("cls");
	} while (a);
	return 0;
}

然后我们来进行游戏代码内容的编写。

第一   我们要实现一个可以扫雷的方格,方格的形式多样(这里沿用三子棋的棋盘),但是本质确实一样的。我们来想一想这个方格怎么编,我们要实现

1.开始的时候是不知道雷的位置的(意味着每个格子内的东西要相同,雷的位置是不可知的)。

2.在扫雷后就可以显示周围雷的数目。

3.雷的位置必须要不变(也就是雷要存起来)。

实现可以变的格子二维数组是必须要用到的,但是这时候就会出现一个问题,就是如果我只用一个二维数组的话,那么要开始时每个位置看起来一样(可以有很多种,我这里采用#来表示),又要存放雷和显示该位置周围雷的数目,显然这是不可能的。所以我们用两个二维数组一个用于存放雷,一个用于显示数字和初始的位置,而这个要显示的二维数组是要显示出周围有几个雷的,所以这两个数组必须要一一对应,但是存放雷的数组要比显示的数组要大一圈,这个我们等会再讲。

所以打印方格的代码就可以得到了,其中方格修饰可以看本人三子棋那一篇文章,这里便不再赘述。其中值得注意的是扫雷游戏不同于三子棋的是它的方格多容易眼花,所以我们可以加上每一格对应的格数。

所得到的代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void board(char dis[ROW][COL]) //打印出棋盘,用|和-来区分。
{
	int i = 0;
	int a, b;
	printf("  ");
	for (i = 1; i <= ROW; i++)
		printf("  %d ",i);
	printf("\n");
	for (a = 0; a < ROW; a++)
	{
		printf("—");
		for (i = 0; i < COL; i++)//每一行的分隔符打印总体为|---|---|---|.....
		{
			if (i < COL - 1)//加上if对最后那个进行特殊处理,即最后面加上|,使其更加美观。
				printf("|---");
			if(i == COL - 1)
				printf("|---|");
		}
		printf("\n");
		printf("%d ",a + 1);
		for (b = 0; b < COL; b++)//打印出存放于数组的字符。
		{
			
			if (b < COL - 1)
				printf("| %c ", dis[a][b]);//
			if (b == COL - 1)
				printf("| %c |",dis[a][b]);
		}
		printf("\n");
		if (a == COL - 1)//最后面一行单独打印
		{
			printf("—");
			for (i = 0; i < COL; i++)
			{
				if (i < COL - 1)
					printf("|---");
				if (i == COL - 1)
					printf("|---|");
			}
		}
	}
	printf("\n");
}

方格得到了,我们就要设置雷了,雷的设置同猜数字游戏中的被猜数字一样,必须要自动生成,否则就失去了意义。我们可以利用随机数来实现这个功能,即随机获得一个 位置让其为雷(雷可以用很多符号表示,但要注意不要与前面设置的初始位置的符号一样),这里就要用到随机数的生成了,不熟悉的同学可以看本人”随机数的生成“这篇文章。

得到的代码如下(其中srand放在了game函数里面):

void my_board(char arr[ROW + 2][COL + 2])//另一个记录雷位置的棋盘,通过两个棋盘来统计出每个位置雷的个数并显示出来到dis数组上。
{
	int i;
	int a;
	int b;
	for (i = 0; i < ROW; )
	{
		a = rand() % ROW + 1;
		b = rand() % COL + 1;
		if (arr[a][b] != '*')//防止出现两个重复的雷使得雷的数目变少而出现bug
		{
			arr[a][b] = '*';
			i++;
		}
	}
}

第二  扫雷实现

这时候我们来扫雷了,扫雷位置简单,输入两组数字即可我们可以放到game主体中。实现game游戏的主体,首先定义两个数组,放入上面的board函数中,打印出来,然后再由玩游戏的人来输入,知道游戏胜利或者失败,这里我们可以do while来实现,其中判断条件就可以用判断函数返回值来充当。所以得到的主体函数代码为:

void game()//游戏的整体采用do while循环。
{
	srand((int)time(NULL));
	char arr[ROW + 2][COL + 2];
	char dis[ROW][COL];
	int a,b;
	for (a = 0; a < ROW + 2; a++)//防止其为随机值导致bug所以将其初始化
		for (b = 0; b < COL + 2; b++)
			arr[a][b] = '#';
	for (a = 0; a < ROW; a++)//先存放进去一个字符,防止其为*导致出现Bug。
	{
		for (b = 0; b < COL; b++)
		{
			dis[a][b] = '#';
		}
	}
	my_board(arr);//布置雷
	board(dis);//打印棋盘
	do //扫雷部分,
	{
		scanf("%d %d", &a, &b);
		if (dis[a - 1][b - 1] != '#' || a > ROW || b > COL || a < 1 || b < 1)//防止输入错误
		{
			printf("输入错误,请重新输入>:\n");
			continue;
		}
		judge(a, b, dis, arr);
		system("cls");
		board(dis);
	} while (judge(a,b,dis,arr));//利用函数返回值进行判断是否继续循环。
	if (dis[a - 1][b - 1] == '*')
		printf("游戏失败!\n");
	else
		printf("游戏胜利!\n");
}

第三 判断部分的实现

被扫位置的判断是否为雷,和是否胜利,如果不是雷就自动判断周围有几个雷。

顺序为:首先要判断所选位置是否为雷,如果是雷则直接退出并返回0退出game中的循环,如果不是雷就判断其是否胜利,(这里我们可以通过看剩下的位置与雷的位置是否相同),如果胜利一样返回0退出循环。(这个时候就要与之前失败退出区分开来,这里我们可以这样,再失败退出前将要显示的数组所选位置变成‘  *  ’,然后在出game中的循环后再用if来区分,这样既可以达到游戏视觉效果,又可以实现区分失败和胜利。)如果没有胜利,则计算周围的雷的数目。

雷的数目怎么计算呢?我们可以这样,用两个for循环从所选位置的左上格来逐一计数,然后再统计到所选位置打印出来。这里值得注意的是两个数组都是char类型的要打印出数字就要用到ascii码值了,0对应的ascii码值为48,所以只要再统计的数上面加上48就可以了。(这里值得注意的有两个,第一,由于如果创建的两个数组大小相同的话,那么就要靠考虑存放雷的数组的界限问题了,这样就略显复杂了,但是如果将存放雷的数组扩大一圈的话,就不要考虑这个问题了,第二,由于两个数组的大小不同,就要考虑数组[]内的数字变化,当然为了防止出现这个情况,你也可以将显示数组也扩大一圈。

判断部分总的代码实现为:

int  judge(int a, int b, char dis[ROW][COL], char arr[ROW + 2][COL + 2])//判断输赢还是既没有输也没有赢,通过整形返回从而达到如果没有赢就继续下的效果。
{
	int count1 = 0,count2 = 0;
	int col, row;
	int i, j;
	for (i = 0; i < ROW; i++)//判断是否胜利,即#是否等于雷的个数,如果等于则返回0结束循环。
		for (j = 0; j < COL; j++)
		{
			if (dis[i][j] == '#')
				count2++;
		}
	if (count2 == ROW)
	{
		return 0;
	}
	if (arr[a][b] != '*')
	{
		for (row = a - 1; row <= a + 1; row++)//对周围的雷进行计数。
		{
			for (col = b - 1; col <= b + 1; col++)
			{
				if (arr[row][col] == '*')
					count1++;
			}
		}
		dis[a - 1][b - 1] = 48 + count1;//将所选的非雷位置边上八个位置的雷的数目统计出来,由于为字符所以就加上48(ascii码值)
		arr[a][b] = '&';//标记出已经判断过的位置。
		if (dis[a - 1][b - 1] == 48 )//判断是否该位置旁边位置都没有雷,如果是则将这些位置清除。
			clear(arr,dis,a,b);
		return 1;
	}
	else //判断是否为雷如果是雷就返回0结束循环。
	{
		dis[a - 1][b - 1] = '*';//标记dis用于判断输赢和打印出雷
		return 0;
	}
}

 到这里游戏就基本完成了,但是为了加强游戏体验感,我们可以加上一个功能:清除0周围的位置

这个功能的实现首先要判断所选位置是否为0,如果是的话就进入这个功能,如果不是就跳过(上面代码已经报括了这一步)。至于这个功能怎么实现呢?这里我们就要先了解一下扫一片雷是按照什么标准扫的。其实它扫一片的标准为:如果所选位置对应的数字为0,则将周围的位置都进行judge,如果周围位置还有0的话继续进行同样的操作,等等,听到了一个敏感词吗?重复同样的操作,这不是标准的递归吗?所以我们用递归来实现,但是这里有一个隐藏的易出bug的点如果你碰到0位置周围还有0,这个时候,你所选中的位置也在那个0的周围,如果两两都对周围进行judge那不是会一直重复下去吗,这不是死递归了吗?这样游戏可运行不下去,所以我们应该标记一下让已经judge过的位置不会再次进入judge,至于怎么对周围的位置进行操作,我们同样可以用两个for循环然后把对应位置的坐标放进judge中。(这里值得注意的是如果所选位置为周围的一圈的话,那么放到for循环中会超出数组的范围所以要对周围位置加上限制。)

总代码如下:

void clear(char arr[ROW + 2][COL + 2], char dis[ROW][COL], int a, int b)
{
	int i, j;
	for (i = a - 1; i <= a + 1; i++)
		for (j = b - 1; j <= b + 1; j++)
			if (arr[i][j] != '&' && i >= 1 && j >= 1 && i <= 9 && j <= 9)
				judge(i, j, dis, arr);
}

虽然这个函数代码量只有这么点但是易错点却不少,这就要靠多动脑子和多自己去调试了。

好了这个游戏到这就结束了。

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值