布雷程序的实现.docx
- 文档编号:15650162
- 上传时间:2023-07-06
- 格式:DOCX
- 页数:22
- 大小:32.28KB
布雷程序的实现.docx
《布雷程序的实现.docx》由会员分享,可在线阅读,更多相关《布雷程序的实现.docx(22页珍藏版)》请在冰点文库上搜索。
布雷程序的实现
布雷程序的实现
一:
游戏说明。
(1)允许用户输入地图的大小,现在用条件所给的9*9。
(2)地图中的地雷个数允许用户输入,在地图上的分布是随机的。
(3)初始时显示地图,确保每个位置都没有标记。
(4)开始玩游戏时,用户每次操作时从键盘上输入3个数据:
x,y和op。
(x,y)表示操作的位置,字符op表示要执行的操作。
点开用“.”表示;标记地雷用“!
”表示;取消标记地雷用“c”表示;退出游戏用“q”表示。
(5)游戏开始时,踩到地雷则结束游戏,当标记出来的每个地雷位置与实际的位置一样,并且标记出来的地雷个数跟实际地雷个数一样则游戏成功完成。
(6)没执行一次操作后,显示操作后的地图。
二:
地图的表示和输出。
地图我们可以用一组二维数组来表示,如下。
……..
…………
(1)如果点开的位置的8个相邻位置上都没有地雷,则自动点开一片连续的没有地雷的区域。
当有个位置有地雷时,则其周边一定有相应的数字表示周围有多少地雷。
即构不成零位置,不能再点开。
但该数字被点开。
(2)在程序中,我们用二维数组map来表示这个地图,为了与生活中的习惯一致,我们不用二维数组map中的第0行和第0列元素。
而且在计算每个位置周围8个相邻位置上的地雷个数时,不需要再判断该位置是否为第0行或第0列。
所以从第1行,第1列开始计算的话,地图中的每个位置的8个相邻位置都是在二维数组范围内,可以统一处理。
而不用专门去处理左边界和上边界的位置。
同样的道理,在处理右边界和下边界也是一样,我们只需再增加第N+1行和第N+1列就可以了。
因此如果最大的地图为9*9,则二维数组map需定义成11*11。
(3)Map数组中各元素map[x][y]的取值及其含义为:
-1:
(x,y)位置为地雷;
0~8:
(x,y)位置上没有地雷,该数字为8个相邻位置上地雷的个数。
(4)那么我们如何表示一个位置的8个相邻位置呢?
如图:
在(x,y)位置上的左上角位置,其行坐标为x-1,列坐标为y-1,行,列坐标的增量均为-1.右边位置相对于(x,y)位置,行,列坐标的增量分别为0和1。
因此在程序中可以定义一个8*2的二维数组dir,表示8个相邻位置的行,列坐标相对于(x,y)位置行,列坐标的增量。
注意dir数组所表示的8个位置的顺序是按顺时针从左上角位置开始的。
有了这个二维数组,统计(x,y)位置的8个相邻位置上地雷的个数就变得方便多了,我们可以用下面的代码:
intk,mines=0;
for(k=0;k<8;k++)
{
If(map[x+dir[k][0]][y+dir[k][1]]==-1)
mines++;
}
(5)那么怎样才能把地图输出来呢?
在字符界面我们可以用“|”和“_”来构造框架,形象的把地图表示出来。
对于N*N的地图,共输出N+1行。
如下图:
具体代码如下:
voidout_map()
{
inti,j;
line[0]='\40';
for(i=1;i<=4*N-1;i++)
line[i]='_';
line[i]='\0';
puts(line);
for(i=1;i<=N;i++)
{
line[0]='|';
for(j=1;j<=N;j++)
{
line[4*j-3]='_';
if(map[i][j]==-1)
line[4*j-2]='*';
elseline[4*j-2]=48+map[i][j];
line[4*j-1]='_';
line[4*j]='|';
}
line[4*N+1]='\0';
puts(line);
}
}
注意:
第1行除了第一个字符是空格字符外,还有4×N-1个下划线“_”,最后结束标志是\0;
因为输出的是对应的数字字符,所以需要在数字字符“0”的ASCII编码值48的基础上加上map[i][j]的值,最后显示出来的才是希望的结果。
三:
地雷的随机生成
如果地雷是手动设置,那这个游戏就没有可重复性,就没有玩的可能了,所以地雷的位置必须是随机的,这儿引人一个随机函数rand(),它包含在
该函数用于长生0到RAND_MAX之间的伪随机数。
为了保证每次运行程序时产生的随机数不同,所以在rand()函数前,需要调用srand()函数设置随机数种子。
该函数的原型为:
Voidsrand(unsignedintseed);
所以在调用该函数形式为:
Srand((unsigned)time(0));
即用当前系统时间作为随机种子,其中time函数是
解决了随机数的生成的问题,现在来考虑随机地图的生成。
要随机地生成地图,需要随机地生成minenum个地雷的位置(num1,num2),这个位置的行坐标num1和列坐标num2都是1~N的随机数。
而且minenum个位置不能重复,而为了随机产生一对1~N之间的随机数,需做些改动:
num1=1+int(1.0*N*rand()/(RAND_MAX+1));
num2=1+int(1.0*N*rand()/(RAND_MAX+1));
当生成一对随机数(num1,num2)后,还得判断map[num1][num2]是否为-1,如果为-1表示之前已经在该位置上设置了地雷,需要重新生成一对随机数。
voidrandom_map()
{
inti=1,j,k;
srand((unsigned)time(0));
intnum1,num2;
while(i<=minenum)
{
num1=1+(int)(1.0*N*rand()/(RAND_MAX+1));//产生1~N之间的随机数;
num2=1+(int)(1.0*N*rand()/(RAND_MAX+1));//产生1~N之间的随机数;
if(num1<1||num1>N||num2<1||num2>N||map[num1][num2]==-1)//排除第0行和第0列,及重复的地雷位置
continue;
map[num1][num2]=-1;
i++;
}
intmines;//对于其余位置统计其周围8个位置上地雷的个数
for(i=1;i<=N;i++)
{
for(j=1;j<=N;j++)
{
if(map[i][j]==-1)
continue;
mines=0;
for(k=0;k<8;k++)
{
if(map[i+dir[k][0]][j+dir[k][1]]==-1)
mines++;
}
map[i][j]=mines;
}
}
}
四:
游戏的实现
(1):
在玩游戏时不仅要有生成的游戏地图,还要有玩家的操作地图,通过这个地图我们就可以控制和确定游戏是否结束等等了。
这个地图也是用一个二维数组user_map来记录。
该数组中的各元素user_map[x][y]的取值及含义分别为:
0:
表示用户还没有点开(x,y)位置;
1:
表示用户已经点开(x,y)位置;
2:
表示用户已经将(x,y)位置标记为地雷。
下面来看看map数组和user_map数组的关系和区别:
(a)实际的地图
(b)对应的map二维数组
(c)显示给用户看的地图
(c)对应的user_map二维数组
注:
有颜色部分表示数组中没用到的
该游戏的地图如同(a)所示,地图中有三个地雷,map数组各元素的值如图(b),map数组各元素的值在游戏过程中是始终不变的。
当点开一片连续的空白区域且正确的标记地雷后,user_map数组各元素的值如图(d),显示给用户看到的是地图(c)。
(2)了解用户地图后,现在我们可以考虑怎么输出了。
和前面map输出一样,user_map也是同样的道理,只不过输出时要做些变化。
而且可以判断游戏是否成功结束,即能判断用户标记的每个地雷位置是否跟实际地雷的位置吻合,并且正确标记出的地雷个数跟地图中地雷的总个数是否相等,如果相等,则游戏成功结束。
游戏成功时,把没有点开的位置全部点开。
(3)没操作一次,都要重新显示地图,所以每次都要调用out_user_map函数。
函数代码如下:
voidout_user_map()
{
rightmarknum=0;
inti,j;
for(i=1;i<=N;i++)
{
for(j=1;j<=N;j++)
if(user_map[i][j]==2&&map[i][j]==-1)
rightmarknum++;
}
if(rightmarknum==minenum)//用于显示所有还未打开的地图
{
k=true;
for(i=1;i<=N;i++)
{
for(j=1;j<=N;j++)
if(user_map[i][j]==0)
user_map[i][j]=1;
}
}
//显示地图:
line[0]='\40';
for(i=1;i<=4*N-1;i++)
line[i]='_';
line[i]='\0';
puts(line);
for(i=1;i<=N;i++)
{
line[0]='|';
for(j=1;j<=N;j++)
{
line[4*j-3]='_';
if(user_map[i][j]==0)
line[4*j-2]='_';
elseif(user_map[i][j]==1)
line[4*j-2]=48+map[i][j];
elseif(user_map[i][j]==2)
line[4*j-2]='!
';
line[4*j-1]='_';
line[4*j]='|';
}
line[4*N+1]='\0';
puts(line);
}
}
(4)解决了用户地图的输出问题,现在我们就只有考虑最麻烦的一步了。
那就是如何点开一片连续的没有地雷的区域。
说它麻烦是因为这儿用到了循环算法,用到的函数search().函数原型为:
Voidsearch(intx,inty);
其中参数x,y表示要点开的位置。
如果点开的位置是一个零位置,则调用search()函数,search()函数自动点开周围连续的零位置及他们相邻的位置。
具体代码如下:
voidsearch(intx,inty)
{
user_map[x][y]=1;//标记点开(x,y)位置
intdx,dy;
for(inti=0;i<8;i++)//是否点开周围的8个位置
{
dx=x+dir[i][0];
dy=y+dir[i][1];
if(dx<1||dx>N||dy<1||dy>N)
continue;
elseif(map[dx][dy]!
=0)
user_map[dx][dy]=1;
elseif(map[dx][dy]==0&&user_map[dx][dy]==0)//如果(x,y)周围相邻位置没有地雷,且也没有被点开过,则沿着这些位置递归地点开
search(dx,dy);
}
}
(5)有了上面的准备,我们就可以玩游戏了,我们可以统一在一个函数里完成,这儿我们生成一个playgame(),在该函数中,所有的操作都是在永真循环内执行的,只有当用户请求退出游戏,踩到地雷,或游戏成功结束时才能退出永真循环,下面是具体的代码:
voidplaygame()
{
out_user_map();
intx,y;
charop;
printf("StartGame\n");
while
(1)
{
printf("请输入执行操作的位置及要执行的操作:
\n");
scanf("%d%d%c",&x,&y,&op);
if(x==0&&y==0&&op=='p')
{
printf("用户请求退出程序\n");
break;
}
elseif((user_map[x][y]==1||user_map[x][y]==2)&&op!
='c')
{
printf("该坐标也被点开或已被标记为地雷,请重新输入坐标\n");
}
elseif(x<1||y<1||x>N||y>N)
{
printf("操作错误,找不到相应坐标\n");
continue;
}
elseif(op=='.')
{
if(map[x][y]==-1)
{
printf("你踩到地雷了嘿嘿正确的地雷位置是这样的\n");
out_map();
break;
}
elseif(map[x][y]==0&&user_map[x][y]==0)
search(x,y);
elseuser_map[x][y]=1;
}
elseif(op=='!
')
{
if(user_map[x][y]==1)
{
printf("该位置已被标记,不能再标记\n");
continue;
}
user_map[x][y]=2;
marknum++;
}
elseif(op=='c'&&user_map[x][y]==2)//取消(x,y)的标记
{
user_map[x][y]=0;
marknum--;
}
else{printf("操作错误该操作无意义\n");continue;}
out_user_map();
if(k)
{
printf("googood!
yougetit游戏成功结束\n");
break;
}
if(marknum printf("还有%d颗地雷没有被标记\n",minenum-marknum); elseif(marknum==minenum&&! k) printf("%d颗地雷已经全部被标记,但部分标记错误,请重试下! \n",minenum); } } 五: 程序设计 所有的准备工作都做好了,现在就来构造程序的框架吧,这是我们的大体思路: voidrandom_map() { …….. ……… } Voidplaygame() { ….. ….. } out_user_map(); While (1) { …… ……. {…. Search(); …… } { ….. } } out_user_map(); 主程序 intmain() { ….. } 根据这个框架,我们很快就能把程序写出来。 下面是完整的程序代码: #include #include #include #include #defineN10 #defineminenum10 /*在map中各字符或数字分别表示: -1: (x,y)位置上为地雷,显示时用*表示。 0~8: (x,y)位置上没有地雷,该数字为8个相邻位置上地雷的个数。 在umap中各字符或数字分别表示: 0: 表示用户没有点开(x,y)位置; 1: 表示用户已经点开(x,y)位置; 2: 表示用户已经把(x,y)位置标记为地雷。 操作说明: ".": 表示点开; "! ": 表示地雷; "c": 表示取消标记的地雷; "q": 表示退出游戏。 */ intmap[100][100]={0};//初始化的地图 intuser_map[100][100]={0};//显示给用户看的地图 charline[100];//用于地图图像化的输出 intrightmarknum=0;//用户正确地标记出地雷的个数 intmarknum=0;//用户标记出地雷的个数 intdir[8][2]={{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1}};//方便计算每个位置相邻的8个位置用的。 boolk=false;//在out_umap()函数中判断是否游戏结束; //输出用户地图: voidout_user_map() { rightmarknum=0; inti,j; for(i=1;i<=N;i++) { for(j=1;j<=N;j++) if(user_map[i][j]==2&&map[i][j]==-1) rightmarknum++; } if(rightmarknum==minenum)//用于显示所有还未打开的地图 { k=true; for(i=1;i<=N;i++) { for(j=1;j<=N;j++) if(user_map[i][j]==0) user_map[i][j]=1; } } //显示地图: line[0]='\40'; for(i=1;i<=4*N-1;i++) line[i]='_'; line[i]='\0'; puts(line); for(i=1;i<=N;i++) { line[0]='|'; for(j=1;j<=N;j++) { line[4*j-3]='_'; if(user_map[i][j]==0) line[4*j-2]='_'; elseif(user_map[i][j]==1) line[4*j-2]=48+map[i][j]; elseif(user_map[i][j]==2) line[4*j-2]='! '; line[4*j-1]='_'; line[4*j]='|'; } line[4*N+1]='\0'; puts(line); } } //输出实际地图: voidout_map() { inti,j; line[0]='\40'; for(i=1;i<=4*N-1;i++) line[i]='_'; line[i]='\0'; puts(line); for(i=1;i<=N;i++) { line[0]='|'; for(j=1;j<=N;j++) { line[4*j-3]='_'; if(map[i][j]==-1) line[4*j-2]='*'; elseline[4*j-2]=48+map[i][j]; line[4*j-1]='_'; line[4*j]='|'; } line[4*N+1]='\0'; puts(line); } } //随机生成地图 voidrandom_map() { inti=1,j,k; srand((unsigned)time(0)); intnum1,num2; while(i<=minenum) { num1=1+(int)(1.0*N*rand()/(RAND_MAX+1));//产生1~N之间的随机数; num2=1+(int)(1.0*N*rand()/(RAND_MAX+1));//产生1~N之间的随机数; if(num1<1||num1>N||num2<1||num2>N||map[num1][num2]==-1)//排除第0行和第0列,及重复的地雷位置 continue; map[num1][num2]=-1; i++; } intmines;//对于其余位置统计其周围8个位置上地雷的个数 for(i=1;i<=N;i++) { for(j=1;j<=N;j++) { if(map[i][j]==-1) continue; mines=0; for(k=0;k<8;k++) { if(map[i+dir[k][0]][j+dir[k][1]]==-1) mines++; } map[i][j]=mines; } } } //如果用户点开的位置上雷的个数为0,则程序自然点开一片连续的没有地雷的区域 voidsearch(intx,inty) { user_map[x][y]=1;//标记点开(x,y)位置 intdx,dy; for(inti=0;i<8;i++)//是否点开周围的8个位置 { dx=x+dir[i][0]; dy=y+dir[i][1]; if(dx<1||dx>N||dy<1||dy>N) continue; elseif(map[dx][dy]! =0) user_map[dx][dy]=1; elseif(map[dx][dy]==0&&user_map[dx][dy]==0)//如果(x,y)周围相邻位置没有地雷,且也没有被点开过,则沿着这些位置递归地点开 search(dx,dy); } } //主程序 voidplaygame() { out_user_map(); intx,y; charop; printf("StartGame\n"); while (1) { printf("请输入执行操作的位置及要执行的操作: \n"); scanf("%d%d%c",&x,&y,&op); if(x==0&&y==0&&op=='p') { printf("用户请求退出程序\n"); break; } elseif((user_map[x][y]==1||user_map[x][y]==2)&&op! ='c') { printf("该坐标也被点开或已被标记为地雷,请重新输入坐标\n"); } elseif(x<1||y<1||x>N||y>N) { printf("操作错误,找不到相应坐标\n"); continue; } elseif(op=='.') { if(map[x][y]==-1) { printf("你踩到地雷了嘿嘿正确的地雷位置是这样的\n"); out_map(); break; } elseif(map[x][y]==0&&user_map[x][y]==0) search(x,y); elseuser_map[x][y]=1; } elseif(op=='! ') { if(user_map[x][y]==1) { printf("该位置已被标记,不能再标记\n"); continue; } user_map[x][y]=2; marknum++; } elseif(op=='c'&&user_map
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 布雷 程序 实现