2015-11-29 08:32

# c语言扫雷算法有问题求助

#include
#include
#include
#include

#define len 20 //边数
int mine[len+2][len+2] = {0}; //+2是为了在计算周围一圈雷数时不必分四周顶点边界区域，不越界

int dispMine[len+2][len+2] = {0}; //记录当前格子状态：0为未点开、1为已点开、2为双击、3为插旗、4为周围一圈无雷自动翻开
int endindex = 0; //点击到地雷的结束标志
int mines = 0; //记录已排除的地雷数

void SetMine(int n) //布雷
{
srand(time(0));
int k, row, col;
if(n == 1) //初级难度
k = 25;
else if(n == 2) //中级难度
k = 50;
else //高级难度
k = 75;
mines = k;
while(k)
{
row = rand() % len + 1; //+1是为了将地雷分布在除四边以外的区域 显示地图时实际只显示中间部分 不显示四周
col = rand() % len + 1;
if(!mine[row][col])

{
mine[row][col] = 9;
k--;
}
}
}

void round(int mine[][len+2], int x, int y)
{
int k = 0; //地雷数
if(mine[x][y] == 9 && dispMine[x][y] != 3) //点击到地雷
endindex++;
else
{
if(mine[x][y+1] == 9) //计算周围一圈是否有雷
k++;
if(mine[x+1][y+1] == 9)
k++;
if(mine[x+1][y] == 9)
k++;
if(mine[x+1][y-1] == 9)
k++;
if(mine[x][y-1] == 9)
k++;
if(mine[x-1][y-1] == 9)
k++;
if(mine[x-1][y] == 9)
k++;
if(mine[x-1][y+1] == 9)
k++;
mine[x][y] = k; //记录当前位置地雷数
}

}

void open(int mine[][len+2], int x, int y) //翻开
{
if(mine[x][y] == 9) //点击到雷直接返回
return;
if(dispMine[x][y] == 3) //输入坐标位置为插旗的位置
return;
if(mine[x][y] == 0) //无雷区自动翻开
{
dispMine[x][y] = 4; //无雷区标志为4
if(x != 1 && y != 1) //考虑各个特殊位置的坐标
{
round(mine, x-1, y-1);
if(!mine[x-1][y-1] && dispMine[x-1][y-1] != 4) //若无雷自动翻开区域里又有无雷区 继续自动翻开 为了防止死循环
open(mine, x-1, y-1); //要标志已翻开的
else if(mine[x-1][y-1] && !dispMine[x-1][y-1]) //若是周围一周有雷的，则直接显示，标志为1
dispMine[x-1][y-1] = 1;
}
if(x != 1 && y != len)
{
round(mine, x-1, y+1);
if(!mine[x-1][y+1] && dispMine[x-1][y+1] != 4)
open(mine, x-1, y+1);
else if(mine[x-1][y+1] && !dispMine[x-1][y+1])
dispMine[x-1][y+1] = 1;
}
if(x != len && y != 1)
{
round(mine, x+1, y-1);
if(!mine[x+1][y-1] && dispMine[x+1][y-1] != 4)
open(mine, x+1, y-1);
else if(mine[x+1][y-1] && !dispMine[x+1][y-1])
dispMine[x+1][y-1] = 1;
}
if(x != len && y != len)
{
round(mine, x+1, y+1);
if(!mine[x+1][y+1] && dispMine[x+1][y+1] != 4)
open(mine, x+1, y+1);
else if(mine[x+1][y+1] && !dispMine[x+1][y+1])
dispMine[x+1][y+1] = 1;
}
if(x != 1)
{
round(mine, x-1, y);
if(!mine[x-1][y] && dispMine[x-1][y] != 4)
open(mine, x-1, y);
else if(mine[x-1][y] && !dispMine[x-1][y])
dispMine[x-1][y] = 1;
}
if(x != len)
{
round(mine, x+1, y);
if(!mine[x+1][y] && dispMine[x+1][y] != 4)
open(mine, x+1, y);
else if(mine[x+1][y] && !dispMine[x+1][y])
dispMine[x+1][y] = 1;
}
if(y != 1)
{
round(mine, x, y-1);
if(!mine[x][y-1] && dispMine[x][y-1] != 4)
open(mine, x, y-1);
else if(mine[x][y-1] && !dispMine[x][y-1])
dispMine[x][y-1] = 1;
}
if(y != len)
{
round(mine, x, y+1);
if(!mine[x][y+1] && dispMine[x][y+1] != 4)
open(mine, x, y+1);
else if(mine[x][y+1] && !dispMine[x][y+1])
dispMine[x][y+1] = 1;
}
}
if(dispMine[x][y] = 2) //如果双击
{
dispMine[x][y] = 1; //置为已点击防止死循环
int flag = 0; //记录周围一周插旗数
if(dispMine[x-1][y] == 3) //若周围有旗子，flag++
flag++;
if(dispMine[x-1][y+1] == 3)
flag++;
if(dispMine[x][y+1] == 3)
flag++;
if(dispMine[x+1][y+1] == 3)
flag++;
if(dispMine[x+1][y] == 3)
flag++;
if(dispMine[x+1][y-1] == 3)
flag++;
if(dispMine[x][y-1] == 3)
flag++;
if(dispMine[x-1][y-1] == 3)
flag++;
if(mine[x][y] == flag) //双击的当前位置插旗数与该位置地雷数相同时自动翻开周围一圈未点击的位置
{
if(y != len && dispMine[x][y+1] == 0) //考虑各个特殊位置防止越界以及是否为未翻开位置
{
round(mine, x, y+1);
dispMine[x][y+1] = 1;
if(!mine[x][y+1]) //若翻开为无雷区则递归调用
open(mine, x, y+1);
}
if(x != len && y != len && dispMine[x+1][y+1] == 0)
{
round(mine, x+1, y+1);
dispMine[x+1][y+1] = 1;
if(!mine[x+1][y+1])
open(mine, x+1, y+1);
}
if(x != len && dispMine[x+1][y] == 0)
{
round(mine, x+1, y);
dispMine[x+1][y] = 1;
if(!mine[x+1][y])
open(mine, x+1, y);
}
if(x != len && y != 1 && dispMine[x+1][y-1] == 0)
{
round(mine, x+1, y-1);
dispMine[x+1][y-1] = 1;
if(!mine[x+1][y-1])
open(mine, x+1, y-1);
}
if(y != 1 && dispMine[x][y-1] == 0)
{
round(mine, x, y-1);
dispMine[x][y-1] = 1;
if(!mine[x][y-1])
open(mine, x, y-1);
}
if(x != 1 && y != 1 && dispMine[x-1][y-1] == 0)
{
round(mine, x-1, y-1);
dispMine[x-1][y-1] = 1;
if(!mine[x-1][y-1])
open(mine, x-1, y-1);
}
if(x != 1 && dispMine[x-1][y] == 0)
{
round(mine, x-1, y);
dispMine[x-1][y] = 1;
if(!mine[x-1][y])
open(mine, x-1, y);
}
if(x != 1 && y != len && dispMine[x-1][y+1] == 0)
{
round(mine, x-1, y+1);
dispMine[x-1][y+1] = 1;
if(!mine[x-1][y+1])
open(mine, x-1, y+1);
}
}
}

}

void chaqi(int x, int y) //插旗
{
if(dispMine[x][y] == 3) //已经插旗的位置再次插旗视为取消插旗
{
dispMine[x][y] = 0; //置为0表示未点击区域
return;
}
else if(dispMine[x][y]) //若该位置已点开则不改变
return;
dispMine[x][y] = 3; //插旗标志为3

}

void disp(int x, int y) //显示地图
{
cout<<" ";
for(int k = 1; k < len+1; k++)
cout<<setw(2)<<k;
cout<<endl;
if(mine[x][y] == 9 && dispMine[x][y] != 3) //如果点到地雷
{
for(int i = 1; i < len+1; i++)
{

cout<<setw(2)<<i;
for(int j = 1; j < len+1; j++)
{
if(dispMine[i][j] == 3) //插旗的地方显示为■
cout<<"■";
else if(mine[i][j] == 9) //有雷的地方显示为*
cout<<" *";

else if(dispMine[i][j]) //无雷区显示数字
cout<<" "<<mine[i][j];
else //未翻开的地方显示为□
cout<<"□";
}
cout<<endl;
}
return;
}

``````for(int i = 1; i < len+1; i++)           //没有点击到地雷
{
cout<<setw(2)<<i;
for(int j = 1; j < len+1; j++)
{
if(dispMine[i][j] == 3)
cout<<"■";
else if(dispMine[i][j])
cout<<" "<<mine[i][j];
else
cout<<"□";
}
cout<<endl;
}
return;
``````

}

bool win(int mines) //判断胜利条件
{
bool win = false; //默认为没有取得胜利
int noFlag = 0, yesFlag = 0, halfFlag = 0; //noFlag记录未翻开的格子数 yesFlag、halfFlag记录插旗数且该位置有雷 noClip
for(int i = 1; i < len+1; i++)
for(int j = 1; j < len+1; j++)
{

``````        if(dispMine[i][j] == 0 )         //未翻开位置
noFlag++;
if(dispMine[i][j] == 3 && mine[i][j] == 9)        //插旗位置正确
{
yesFlag++;
halfFlag++;
}
}
if(noFlag == mines || (yesFlag == mines && !noFlag) || halfFlag == len*len - noFlag) //未翻开的格子数等于地雷数 插旗数等于地雷数
win = true;                                                                //插旗与未翻开格子数等于地雷数
return win;
``````

}
void main()
{
int x = 0, y = 0;
cout<<"请选择游戏难度,1.初级 2.中级 3.高级:";
int rank = 0;
while(1)
{
cin>>rank;
if(rank < 1 || rank > 3)
cout<<"输入有误，请重新输入：";
else
{
system("CLS");
break;
}
}
SetMine(rank);
disp(x, y);
while(1)
{
cout<<"1.点击 2.插旗 3.双击"< cout int n;
while(1)
{
cin>>n;
cin>>x>>y;
if(n < 1 || n > 3)
{
cout<<"输入有误，请重新输入：";
continue;
}
else if(x < 1 || x > 20 || y < 1 || y > 20)
{
cout<<"输入有误，请重新输入：";
continue;
}
break;
}

``````    if(n == 1 || n == 3)
{
if(n == 3 && dispMine[x][y] == 3)
continue;
round(mine, x, y);
if(dispMine[x][y] < 2)
dispMine[x][y]++;
open(mine, x, y);

}
else if(n == 2)
chaqi(x, y);

system("CLS");
disp(x, y);
if(endindex)
{
cout<<"你输了！"<<endl;
break;
}
if(win(mines))
{
cout<<"你赢了！"<<endl;
break;
}
}
``````

}

• 写回答
• 好问题 提建议
• 关注问题
• 收藏
• 邀请回答

#### 4条回答默认 最新

• ysuwood 2015-11-29 09:02
已采纳

程序很长，基本没人会给你调试找错误的。会花费很长时间。
建议你自己学会调试程序，掌握设置断点，查看变量，单步执行等手段。
怀疑是哪里出了问题，就单步执行去试试。

已采纳该答案
评论
解决 无用
打赏 举报
• 纹枰老妖 2015-11-29 09:20

首先，题主您的代码没有排版，这样看着真的很累。
其次，既然您说是输入“某些”坐标程序会没有反应，那么我的理解就是：输入某些数值时，程序会正常运行，但输入另外某些数值时，程序却会卡住。如果是这样的话，那么基本可以断定是因为算法没有照顾到所有的数值了，可以的话，请题主提供几个能让程序卡住的数值，大家分析一下这些数值在数学上有何共同点，或许会对您有所帮助。

评论
解决 无用
打赏 举报
• wn_yqzhao_sx2015 2015-11-29 16:49

其实好的方法是：做一些资源表示雷区域的信息，然后用MSDN去实现，这个算法中最难得地方是翻开周围不是雷点的区域，我用堆栈的算法实现过，用了两天才搞好。

评论
解决 无用
打赏 举报
• 纹枰老妖 2015-12-27 14:04

题主是不是忘了什么了？？？

评论
解决 无用
打赏 举报