【扫雷游戏】C语言版——老妈子版讲解

大家好,即三子棋后一款游戏——扫雷,这个游戏就比三子棋可玩性高很多了,相对应的有个算法会比较难想到,但是跟三子棋的游戏框架很像。废话少说,接下来我们就开始分析扫雷如何实现吧。

目录

一、棋盘的初始化

二、棋盘的打印

三、存放地雷。

四、查找雷

递归展开

五、鉴定排雷状况,结束排雷


事先准备和用户交互画面和三子棋是相同的,我就不复制粘贴了,可以去看我的上一篇博客,这里直接说这两步之后的程序。

一、棋盘的初始化

我们这里用使用两个棋盘,一个是我们安放地雷的棋盘,一个是用户用来排雷的棋盘,这样分开设置有利于我们设计游戏

我们要将两个棋盘设置为11*11,但只显示1-10行、列的数据,方便我们排查雷。初始化代码如下:

char mine[ROW][COL];
	char show[ROW][COL];
	//初始化棋盘mine数组全初始化0,shou数组全初始化为*
	inti_board(mine, '0');
	inti_board(show, '*');

我们将初始化函数设置为,传什么就赋值什么,这个还是比较好实现的,11*11的数组全部附上就行了

//数组初始化
void inti_board(char board[ROW][COL], char c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < ROW; i++)
	{
        for (j = 0; j < COL; j++)
		{
			board[i][j] = c;
		}
	}
}

二、棋盘的打印

我这里打印的棋盘是我设计过的,容易对应坐标,打印其实没什么,主要是个人意愿 

但是这个打印要做到传入show数组则打印用户界面,传入mine数组则打印开发页面

这里我放上我的打印函数,因为这个不是扫雷的重点。

void print_board(char board[ROW][COL])
{
	printf("***********************************************\n\n");
	int i = 0;
	int j = 0;
	for (i = 1; i <= row; i++)
	{
		if (i == 1)
			printf("   ");
      printf("  %d ", i);
	}
	printf("\n");
	printf("    ");
	for (i = 1; i <= row; i++)
	{
		if (i<row)
		printf("----");
		if (i == row)
			printf("---");
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf(" %d ", i);
		for (j = 1; j <=col; j++)
		{
			printf("|");
			printf(" %c ", board[i][j]);
			if (j == col)printf("|");
		}
		printf("\n");
		printf("    ");
		for (j = 1; j <= col; j++)
		{
			if (j<col)
			printf("----");
			if (j == col)
				printf("---");
		}
		printf("\n");
	}
	printf("\n");
	printf("***********************************************\n");
	printf("\n");
}

三、存放地雷。

这个跟三子棋电脑走子是差不多的,也是用rand函数生成10对坐标来插入雷放到mine数组中,也没什么太大的难度

//安置雷
void set_bomb(char board[ROW][COL])
{
	int x;
	int y;
	int count = 10;//count是安放雷的数量
	while (count)
	{
		x = rand() % col;
		y = rand() % row;
		if (x >= 1 && x <= 9 && y >= 1 && y <= 9 && board[x][y] == '0')
		{//位子没有被占用,而且是显示的棋盘内
			count --;
			board[x][y] = '1'; //放1代表是雷
		}
	}
}

四、查找雷

查找雷是我们今天的重点,我们要确定好好方向和流程:

1、用户输入坐标(设置循环判断坐标的合法性)

2、对坐标进行判断

     2.1检查这个坐标的mine数组是不是雷,是雷就打印一下棋盘然后直接return;游戏结束

     2.2不是雷,我们进行判断周围是不是雷,并将检查到的雷的数量进行返回,显示在show数组用户输入的位子上(并进行递归展开),游戏继续。

(这里给大家一个小思路,如果想对雷进行标记,可以输入0 0,输入0 0就进入标记页面,再输入要标记坐标,将show数组上对应的坐标进行更改就行了)

1.1和2.1还是很简单的,我们重点来实现2.2

排查不是雷坐标周围的雷数

我们将用户输入的坐标传入到排查统计雷的函数中,思路是:

因为我们将雷设置为1,非雷设置为0,所以我们将9个坐标的值全部相加,再减去8个‘0’的值,就可以算出要返回的值了,因为0的ascii码值为48,1的ascii码值为49,我们只要返回48,就是返回’0‘,表示周围一圈也是无雷,返回49,就是返回'1',表示1个雷,以此类推。并可以把返回的值放到show数组对应的坐标上,这样下次显示的时候也就显示出来了。

代码实现如下:

int count_mine(char mine[ROW][COL], int x, int y)
{
	int sum = 0;
	for (int i = -1; i <= 1; i++)
		for (int j = -1; j <= 1; j++)
			sum += mine[x - i][y - j];
	return sum - 8 * 48;
}

递归展开

一圈都不是雷

这时我们就要思考,如果周围一圈全不是雷,那是不是应该要展开一片呢,我们可以怎么设定这个一圈都展开的递归公式呢?

先来看看这个递归应该如何设计:

 return的地方都是需要用if判断的,而这些子坐标是什么意思呢,接下来看一下的这个图,这8个子坐标就代表着母坐标向8个方向展开。

其实就是他周围的8个小方块啦,坐标也是很好写的,代码如下:

//递归展开
void spread_board(char mine[ROW][COL], char show[ROW][COL], int x, int y)
{		//排查一圈,发现有雷,将show上的位子设为雷的个数
	int i = count_mine(mine, x, y);
	if (i != '0')
	{
		show[x][y] = i;
		return;
	}
	//排查一圈,找到起点空格,停止.
	if (show[x][y] == ' ')
	{
		return;
	}
	//超出边界
	if (x <= 0 || x > row || y <= 0 || y > col)
		return;
	//排查一圈,发现没有雷,将show对应的位子设为空格
	show[x][y] = ' ';
	/*print_board(show);*/
	spread_board(mine, show, x + 1, y);
	spread_board(mine, show, x + 1, y - 1);
	spread_board(mine, show, x + 1, y + 1);
	spread_board(mine, show, x, y - 1);
	spread_board(mine, show, x, y + 1);
	spread_board(mine, show, x - 1, y - 1);
	spread_board(mine, show, x - 1, y + 1);
	spread_board(mine, show, x - 1, y);
}

接下来是输入排雷函数的代码:

//查找雷
void lookup_bomb(char show[ROW][COL], char mine[ROW][COL])
{
	int x;
	int y;
	char count=0;
	while (1)
	{
		printf("请输入排查的坐标或标记:\n");
        scanf("%d %d", &x, &y);
		while (1)
		{
        if (x == y && x == 0)
		{
			printf("请输入坐标来标记:\n");
			scanf("%d %d", &x, &y);
			show[x][y] = '@';
			print_board(show);
			printf("请输入排查的坐标或标记:\n");
			scanf("%d %d", &x, &y);
		}
		if (0 != y && x != 0)
			break; 
		}
		
		 if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
		 {
			 if (mine[x][y] == '1')
			 {
				 system("cls");
				 printf("sorry,您被炸死了\n雷的分布如下:\n");
				 print_board(mine);
				 sign = 2;
				 return;
			 }
			 else
			 {
				 //查找该坐标周围的有多少雷
				 count = count_mine(mine, x, y);
				 if (count == '0')
				 {//统计周围没有雷,开始递归展开					 
					 spread_board(mine,show, x, y);
				 }
				 if (count != '0')
				 show[x][y] = count;
				 break ;
			 }

		 }
		 else
		 {
           printf("输入有误,请重新输入:\n");
		 }
			 
	}
}

五、鉴定排雷状况,结束排雷

最后就剩下一个收尾工作了,我的想法和大家的不同,我是检查show数组上星号(*)和标记号(@)的总和是不是等于10个来停止的。因为,玩家要是将shou数组上81个符号检查到10个,而且一直没踩雷跳出,就是玩家排到了最后的10个,就可以安全退出恭喜玩家了。而我又设置了一个全局变量sign,在玩家踩雷后就将sign改为0,这样game函数中如果判断sign为0,直接就中断了游戏,用户踩雷了。这就是我的想法排雷结束思路,当然我的想法不是最好的,甚至可能有BUG,大家可以对我的代码采取适当的借鉴。,代码如下:

//检查游戏结束
int inspect_game(char show[ROW][COL])
{
	int i = 0;
	int j = 0;
	int count=0;
	for (i=1;i<=row;i++)
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '*'||show[i][j]=='@')
			{
				count++;
			}
		}
	if (count <= 10)
		return 1;
	return 0;
}

这样,扫雷游戏游戏就设计完了,会发现游戏间总体思路是大同小异的,扫雷也就递归展开那块有点难想到,但是多练习一下游戏设计,也有利于理清编程思路,增强编程能力。

下一篇博客我会放下3个文件的全部代码。

最后,看到这里,留个赞呗~~

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Brant_zero2022

素材免费分享不求打赏,只求关注

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

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

打赏作者

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

抵扣说明:

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

余额充值