c语言扫雷进阶(手把手超详细)

Posted DinosaurKing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c语言扫雷进阶(手把手超详细)相关的知识,希望对你有一定的参考价值。

声明:跟随鹏哥视频学习,内容有相关性,侵删。

何为扫雷游戏?这里给一个简单的概述。比如一个9x9的方格表(如图),

初始化之后, 每个‘*’后面或者有雷,或者没雷,雷的个数可以根据要求确定。然后你在键盘上输入一个坐标,如果这个坐标上有雷,你就被炸死,游戏结束。如果没有雷,就会显示这个坐标周围8个坐标上有多少个雷,如图,表示(2,2)坐标上没有雷,而且它周围有8个坐标上一共有1个雷。

但当输入的坐标上不是雷而且周围也没有雷的时候,它就会依次判断周围8个坐标的周围有多少个雷。如果8个坐标中的一个周围还是没有雷,继续依次判断该坐标周围坐标的周围有多少个雷,直到坐标周围有雷为止(如图,输入坐标分别为(6,3),(2,9))。

 

 游戏的目标就是找出所有不是雷的坐标。

如何实现这个小游戏?

为了简明高效,代码大致可以分为三部分:

1.头文件及函数声明区(game1.h)。

2.主函数实现区(main1.c)。

3.调用函数实现区(game1.c)。

我的习惯是先做好可能用到的头文件声明(声明了没用到也没事)和引用头文件。如图:

在项目总头文件处引用常用头文件,然后在项目的其它文件处引用项目头文件即可,可以避免大量重复的引用。并且提高简明度。

思考一个问题,接下来该做什么?

想像一下,你打开一个游戏,第一眼会看到什么?

界面。 游戏界面,所以第一步我们应该初始化一个简易的游戏界面。但是,问题又来了,界面初始化一次够了没有?显然不是,因为我们可能不仅仅玩一局。

      进入界面,用户可以选择开始,也可以选择退出,当然也会出现选择错误。所以这是一个多分支的程序,而且还应该可以循环。

      所以我们应该选用switch-case与do-while嵌套。如图。

 该设计巧妙在选择0是退出,也是终止循环的条件。

接下来是game函数的实现。

怎么做?做什么?游戏界面像什么?一个棋盘。所以我们应该先初始化一个二维数组来表达一个棋盘。

然后呢?

怎么表示雷和非雷?假设我们用0表示非雷,用1表示有雷。在这个二维数组每个位置上存储0和1,那么问题来了,如果一个没有雷的坐标周围恰好有一个雷,我们也应该在这个坐标上放一个1,那么怎么区分这个1到底是雷还是之后放上去的?

有一个办法,创建两个二维数组,一个放雷专用,一个找雷专用。

如果我们需要一个9x9的雷盘,创建一个多少行多少列的数组?

考虑极端处的效果。如果是第一行或者第一列的坐标,周围没有满8个坐标,难道后面遍历周围8坐标的时候要对他们特殊处理?或许我们可以让我们能选择的每一个坐标周围都有8个坐标。

       怎么做?9x9的基础上往外伸展一行一列,就是11X11。

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
char mine[ROWS][COLS];
char show[ROWS][COLS];
chushihua(mine,ROW,COL,'0');
chushihua(show,ROW,COL,'*');
void chushihua(char board[ROWS][COLS],int row,int col,char set)
{
	int i,j;
	for(i=0;i<ROWS;i++)
	{
		for(j=0;j<COLS;j++)
		{
			board[i][j]=set;
		}
	}
}

这里采用宏定义行列数,方便以后一键修改。mine为埋雷字符数组,初始化为字符‘0’;show为展示数组,初始化为‘*’。为什么是字符数组,因为放的是字符。

初始化之后应该将show展示给玩家。为了使game函数简洁,所有功能都通过调用函数实现。

void game()
{
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
	chushihua(mine,ROW,COL,'0');
	chushihua(show,ROW,COL,'*');
	Dispaly(show,ROW,COL);//打印show棋盘
}
void Dispaly(char board[ROWS][COLS],int row,int col)
{
	int i,j;
	for(i=0;i<ROWS-1;i++)//先打一行0到9
	{
		printf("%d ",i);
	}
	printf("\\n");//换行
	for(i=1;i<ROWS-1;i++)
	{
		printf("%d ",i);//每行开头应该先打行号
		for(j=1;j<COLS-1;j++)
		{
		    printf("%c ",board[i][j]);
		}
	    printf("\\n");//打完一行后换行
	}
	
}

效果应如图:

 下一步应该埋雷。通过函数setmine(mine,ROW,COL)实现。

#include <stdlib.h>
#include <time.h>
void setmine(char mine[ROWS][COLS],int row,int col)
{
	int count=LEI;//LEI为宏定义的埋雷个数

	do
	{
		int x=rand()%row+1;//产生随机数1~9,因为能埋雷的坐标在二维数组中刚好位于1~9行列之中
	    int y=rand()%col+1;//若col为9,任何数取余9都在(0,8)之间,+1变成(0,9)
		if(mine[x][y]=='0')//没有埋过雷的位置才能埋,不然会重复,达不到预期效果
		{
			mine[x][y]='1';
			count--;
		}
	}while(count);//埋完雷后count为0,退出循环
}

埋好雷之后就开始zhaolei函数。首先用户应该可以不断地输入坐标,所以应该用循环。

然后思考一个问题,先判断一个坐标的周围有没有雷,如果没有,继续判断周围8个坐标的周围有没有雷,直到那个坐标周围有雷为止。一个函数被反复调用,而且函数里面还要调用一样的函数,这是什么?递归。

void zhaolei(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col)
{
	int x,y;
    do
    {
    	printf("请输入坐标:\\n");
	    scanf("%d%d",&x,&y);
    	if(x>=1&&x<=row&&y>=1&&y<=col)//判断坐标是否在范围内
    	{
    		if(mine[x][y]=='1')//说明有雷
    		{
    		    printf("很遗憾,你被炸死了\\n");
    		    Dispaly(mine,ROW,COL);//炸死后应将雷棋盘展示给玩家
				break; 
			}
			else
			{
				A(mine,show,x,y);//A函数实现一连串的遍历点工作
				Dispaly(show,ROW,COL);
			}
		}
		else
		{
			printf("坐标非法,请重新输入:");
		}
	}while();
	

怎么实现递归?怎么判断输赢?

   如果一个show上的坐标已经被赋予数字(字符),即已经遍历过了其周围8个坐标,那么在后面的递归中不应再次被遍历,否则将进入无穷循环即死循环。通俗的说,假设(4,4)(4,5)两个坐标周围8个坐标都没有雷,那么将会遍历它们周围的坐标,由(4,4)进入(4,5),再由(4,5)进入(4,4),循环往复,无法停止。所以我们应该先判断。

   然后while的条件是什么,即怎么判断赢了?

   赢的条件是找出所有没有雷的点,所以我们应该每次遍历一个坐标周围给它赋值后记录下来,然而遍历是在函数内做的,条件是while的,我们应该让函数可以修改主函数里面的数据。

   怎么做?指针。

void zhaolei(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col)
{
	int x,y,win=0;
	int *p=&win;//定义一个指针传给A函数,使函数A可以修改win的值
    do
    {
    	printf("请输入坐标:\\n");
	    scanf("%d%d",&x,&y);
    	if(x>=1&&x<=row&&y>=1&&y<=col)
    	{
    		if(mine[x][y]=='1')
    		{
    		    printf("很遗憾,你被炸死了\\n");
    		    Dispaly(mine,ROW,COL);
				break; 
			}
			else
			{
				A(mine,show,x,y,p);
				Dispaly(show,ROW,COL);
			}
		}
		else
		{
			printf("坐标非法,请重新输入:");
		}
	}while(win<ROW*COL-LEI);//找完所有点后,win不再满足条件,跳出循环
	if(win==ROW*COL-LEI)//因为炸死也会跳出循环,所以应该判断win的大小
	{
		printf("恭喜你,游戏胜利!\\n");
	}

}

接下来是递归函数A的实现。

int getmine(char mine[ROWS][COLS],int x,int y)//遍历一个坐标周围8个坐标
{
	return mine[x+1][y]+mine[x+1][y-1]+mine[x+1][y+1]+mine[x][y+1]+mine[x][y-1]+mine[x-1][y]+mine[x-1][y-1]+mine[x-1][y+1]-8*'0';//因为mine上面放的是字符,所以应减去变成整型
}
void A(char mine[ROWS][COLS],char show[ROWS][COLS],int x,int y,int *p)
{
	int count=getmine(mine,x,y);
	show[x][y]=count+'0';//count为整型,加上‘0’,变为对应的字符
	(*p)++;//指针一定要先括起来,然后在加加,应为+运算级高于*
	if(count==0)
	{
		int i,j;
			for(i=-1;i<=1;i++)//两个for完成遍历
			{
				for(j=-1;j<=1;j++)
				{
					if(i!=0||j!=0)//本身不用再次遍历
					{
						if(show[x+i][y+j]=='*')//遍历过的也不用再次遍历
						{
							if((x+i)!=0&&(y+j)!=0&&(x+i)!=(ROWS-1)&&(y+j)!=(COLS-1))
							{//该处条件是极限处,即最外围的一圈,就是9x9之外的一圈,不用遍历,否则会造成判断区域断裂
								A(mine,show,x+i,y+j,p);//递归!
							}
						}
					}
				}
			}
   }
}

有一个比较难懂的地方,就是-8*‘0’那里,其实很简单,字符数字与整型数字在ASCII中成顺序排列。数字0就是0,而‘0’是48,所以字符减去48即变为相应的数字。

比如:

printf("%d",'3'-48);
结果是数字三

扫雷就完成啦!赶紧玩一把吧!

代码链接:https://pan.baidu.com/s/1_lasz7Fy_K0UQ_XUNIVynQ?pwd=3cn6
提取码:3cn6

以上是关于c语言扫雷进阶(手把手超详细)的主要内容,如果未能解决你的问题,请参考以下文章

如何用C语言快速实现初级版扫雷(步骤详细)

C语言游戏超详解扫雷游戏完整版,细节满满!!

C语言游戏超详解扫雷游戏完整版,细节满满!!

C语言实现扫雷游戏(超详细)

[C语言]超详细讲解扫雷游戏(递归+标记),附思维导图

手把手教你c语言队列实现代码,通俗易懂超详细!