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语言扫雷进阶(手把手超详细)的主要内容,如果未能解决你的问题,请参考以下文章