扫雷

来源:互联网 发布:大连民族大学网络教学 编辑:程序博客网 时间:2024/05/22 08:04

从发表问题到今天10月28日,这几天在超级课程表上认真回复我的童鞋只有 小短腿 一个人,所以我尽最大的努力试着写了一个C语言版本的扫雷,本来没准备写的,因为以前第一次写类似的程序是编写贪吃蛇,用了三个月时间(大部分时间都在调试BUG),那时候没想过先写过程,再码代码,而是自以为是的认为可以凭借自己的小脑核一下子全部写出代码的,自此留下心理阴影。。。

现在学过java后,理解了面向对象编程,又知道了大型工程都是分块的团队合作完的之后,总之,现在不会上来就码代码,而是分析代码运行过程,看看能不能分成几块,就像蚂蚁搬家一样,既然我没办法一下子写完,就只能一块块写完,只要每一块都没问题,最后调试就相对简单了。

在10月26日看到回复后,用了两天的课间时间思考整个程序的运行过程,并用了三页的纸画流程图(估摸着只有自己能看懂,第一张最简单,第二张有大致分类,第三张比较详细),最后今天用了一个下午的时间用代码实现功能。里面的随机函数是网上找的,不常用就没记住函数名,知道到哪里去找就可以。

大致分为四部分

1图形部分

2扫雷部分

3随机位置生成部分

4接收输入部分

使用方法及简介在最下面


#include<stdio.h> 

#include<stdlib.h>
#include <time.h>
#define X 10+1
#define Y 10+1
int storage[X][Y]={0}; //位置显示存储 
/*    显示      数字表达 
*          9
初始 +      -2
无雷   0-8      0-8
被标记  @         -1
*/ 
int mine[X][Y]={0};  //雷位置存储  
/*
有雷   无雷 
1
*/ 
int mark=0,markNum=0;
/*
mark记录可标记数量 
markNum记录已标记数量 
*/ 
int suiJiShu(int max)//产生1到max的随机数
{
if(max<=0)
return 1;
return rand()%max+1;
}
void suiji()// 随机生成雷的位置并保存到mine数组
{
int i,j;
int sum;
int N=X*Y*0.20;
//使雷的总数达到总位置数的 20% 左右
//(测试时这个数量的图像看上去比较养眼) 
int k=0;
int b=1;

int small;//找 X 和 Y 中最小的
if(X>Y)
{
small=X-1;
}
else
{
small=Y-1;
}

while(1)
{
int a;
a=suiJiShu(small);
mine[a][b]=1;
b++;
if(b==Y)
{
b=1;
}
k++;
if(N==k)
{
break;
}
}
}


void show()// test 成功
{
int i,j;

for(i=0;i<20;i++)
printf("\n");

for(i=0;i<X;i++)
{
for(j=0;j<Y;j++)
{
if(0==i||0==j)
{
if(0==i)
{
if(j==0)
{
printf("   ");
}
else
{
printf("%-3d",j);
}
}
if(0==j)
{
if(i==0)
{
}
else
{
printf("%-3d",i);
}
}
}
else
{
//判断
int n;
n=storage[i][j];
switch(n)
{
case -2:printf("+  ");break;
case -1:printf("@  ");break;
case  9:printf("*  ");break;
default:printf("%-3d",n);
}
}
}
printf("\n");
}
}
/*
   1  2  3
1  (1,2)
2  (2,2)
3


*/
int flag[X][Y]={0};//可以理解为标志位,避免重复搜索同一个地方


int sweep(int x,int y)//深度搜索旁边有雷就停并显示雷的数量
{
if(flag[x][y]==1)
{
return 0;
}
if(1==mine[x][y])
{
int i;
for(i=0;i<20;i++)
printf("\n");
printf("Game over! \n 你踩到雷了!\n");
return 4;
}

int M=0;//假设周围为 0 个雷,每遇到一个就加一
int i;
int situationX[9]={0};
int situationY[9]={0};
int si=0;

for(i=1;i<=8;i++)
{

int a=0,b=0;
switch(i)
{
case 1:

if(0==(x-1))//表示上边是边界 ,什么都不做 
{
continue ; 
}
else
{
a=x-1;
b=y;
}
break;
case 2:
//右上
if((x-1)==0||(y+1)==Y)//表示是边界 ,什么都不做 
{
continue ;
}
else
{
a=x-1;
b=y+1;
}
break;
case 3:
//右
if((y+1)==Y)//表示是边界 ,什么都不做 
{
continue ;
}
else
{
a=x;
b=y+1;
}
break;
case 4:
//右下
if(X==(x+1)||(y+1)==Y)//表示是边界 ,什么都不做 
{
continue ;
}
else
{
a=x+1;
b=y+1;
}
break;
case 5:    
if(X==(x+1))  
{
continue;
}
else
       {
a=x+1;
b=y;
}
break;
case 6:
//左下
if(X==(x+1)||(y-1)==0)//表示是边界 ,什么都不做 
{
continue ;
}
else
{
a=x+1;
b=y-1;
}
break;
case 7:
//左
if((y-1)==0)//表示是边界 ,什么都不做 
{
continue ;
}
else
{
a=x;
b=y-1;
}
break;
case 8:
//左上
if(0==(x-1)||(y-1)==0)//表示是边界 ,什么都不做 
{
continue ;
}
else
{
a=x-1;
b=y-1;
}
break;
}
if(1==mine[a][b])//是雷 
{
M++;
}
else
{
si++;
situationX[si]=a;
situationY[si]=b;
}
}
storage[x][y]=M;

if(M!=0)
return 0;

flag[x][y]=1;
while(si)
{
sweep(situationX[si],situationY[si]); //递归循环本函数 
si--;
}
flag[x][y]=0;
return 0;



int receive() //test 成功 接收位置和命令
{
int x,y,operate;
while(1)
{
scanf("%d%d%d",&x,&y,&operate);

int x1=0,y1=0,ope=0;
if(x<1||x>X-1)
{
x1=1;
printf("\n x 坐标数值<1~%d> ",X-1);
}
if(y<1||y>Y-1)
{
y1=1;
printf(" y 坐标数值<1~%d> ",Y-1);
}
if(operate<1||operate>3)
{
ope=1;
printf("操作命令数值<1~3> ");
}
if(0!=(x1+y1+ope))
{
printf("出错,请重新输入:\n");
}
else
{
break;
}
}
//printf("input yes:%d %d %d\n\n",x,y,operate);
//int x,y,operate;
switch(operate)
{
case 1:
//printf("在x=%d,y=%d位置扫雷\n",x,y);
//条件  位置应是初始状态 
if(-2==storage[x][y])
{
int numOfSweep=0;
//printf("jin ru sweep\n");//------------------------调试时的剩余代码
numOfSweep=sweep(x,y);
//printf("sweep han shu jie shu\n");//--------------调试时的剩余代码
if(4==numOfSweep)
{
return 4;
//点到炸弹 
}
else
{
//正常 
}
}
else
{
printf("需要扫雷的位置不是初始状态\n");
}
break;
case 2:
//printf("判断x=%d,y=%d位置为空,就标记\n",x,y); //调试时的剩余代码
//加一条 如果标记数等于0就不允许再添加 
if(-2==storage[x][y])
{
if(markNum==mark)
{
printf("禁止将标记数量超过雷的总数\n");
break;
}
else
{
markNum++;
storage[x][y]=-1;
}
}
break;
case 3:
//printf("判断x=%d,y=%d位置被标记,就取消\n",x,y);
if(-1==storage[x][y])
{
markNum--;
storage[x][y]=-2;
}
break;
default:printf("no possible! 不可能到这里滴... ^_^\n"); 
}
return 0; 
}


int main()
{
srand((unsigned)time( NULL ));// 以时间为种子,运行一次即可  yes 
suiji();//生成随机雷位置,并存储到 mine[X][Y] 中   yes 

int i,j;
//for 循环找到所有雷数量  yes 
//初始化显示存储 
for(i=1;i<X;i++)
{
for(j=1;j<Y;j++)
{
if(1==mine[i][j])
{
mark+=1;
}
storage[i][j]=-2; 
}
}
/*
mark记录可标记数量 (雷的总数 )
markNum记录已标记数量 (标记数量) 
*/ 
int end=0;
while(1)
{
//检查是否所有的雷是否都被正确标记 yes
int n=0;
for(i=1;i<X;i++)
{
for(j=1;j<Y;j++)
{
if(1==mine[i][j]&&-1==storage[i][j])//(与雷位置一一对应 ) 
{
n++;
}
}
}
if(n==mark)
{
break;

else 
{

show();//显示图像  yes
end=receive();//接收数据输入并执行 
if(4==end)//正常返回 0  返回 4 表示 触到雷 
{
return 0;
}


//显示所有雷的位置,并将其他全部清空为加号。 yes
for(i=1;i<X;i++)
{
for(j=1;j<Y;j++)
{
if(1==mine[i][j])
{
storage[i][j]=9;
}
else
{
storage[i][j]=-2;
}
}
}
show(); 
//输出祝贺语句  yes
printf("祝贺你成功了!\n");
return 0;       
}
/*
storage[X][Y]={0}; //位置显示存储 
      显示      数字表达 
*          9
初始 +      -2
无雷   0-8      0-8
被标记  @         -1

*/ 


/*

使用方法,和linux中的shell命令行操作一致,或者说和Windows中的cmd差不太多,

界面初始状态下显示为

输入的只有三个数,用空格隔开

比如:2 4 1

代表的含义是

在第2行第4列执行操作1

一共10行10列,可自定义

1操作是扫雷

2操作是标记

3操作是取消标记

输入错误会有简单提示

踩到也会说出来的

没版权,没限制,自由复制,自由学习>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

*/


原创粉丝点击