您的位置:首页 > 其它

一个简单的扫雷程序

2010-12-14 16:49 176 查看
不多说,请看注释,直接上代码

/************************************************************************
@brief 简单的自动扫雷算法实现,算法说明:该程序没有考虑算法优化,
使用了每操作一次判断一次矩阵的方式进行模拟,效率很低,仅供参考
@note  该自动算法与实际情况相似,不能保证每次扫雷成功,目前已知的导致失败
的情况有下面几种:
1.第一次选择位置或雷阵展开不足无法继续下去的情况下会随机选择位置继续展开,
如果随机到一个地雷位置,则扫雷失败。
2.出现死局的情况下,导致随机选取位置时选择了地雷,死局的示例情况如下,
其中数字表示周围地雷个数,@表示为地雷,*表示待确定位置,下图10*10雷阵,
10个地雷,在第十行1、2号无法继续确实那个位置为雷。
0      0      0      0      0      1      @      1      0
0      0      0      0      0      1      1      1      0
1      1      0      0      0      0      0      0      0
@      1      0      0      0      0      0      0      0
1      1      0      0      0      0      0      1      1
0      0      0      0      0      0      0      1      @
0      1      1      2      2      3      2      3      2
1      3      @      3      @      @      @      2      @
*      *      @      3      2      3      2      2      1
@author mack
@email xxq2050@gmail.com
************************************************************************/
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <windows.h>
#include <string.h>
#include <math.h>
#include <time.h>
using namespace std;
/*!
@brief 记录位置信息的结构体
*/
typedef struct position
{
bool isMine;///<是否为地雷
int  around;///<周围的地雷数
bool display;///<是否已经显示(对用户),
}position;
/*!
@brief 初始化n*m阶的position数组
@param p n*m的数组首地址
@param n 二维数组行长度
@param m 二维数组列长度
*/
void initArray(position** p, int n,int m)
{
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
p[i][j].around=0;
p[i][j].isMine=false;
p[i][j].display=false;
}

return ;
}
/*!
@brief 随机生成一个n*m,地雷个数为nMine的雷阵
@param p n*m的数组首地址
@param n 二维数组行长度
@param m 二维数组列长度
@param nMine 布雷个数
*/
void createMine(position** p,int n,int m,int nMine)
{
int i=0;
int j=0;
///设置rand()函数的随机数种子,防止每次程序启动生成的随机数一样
srand(int(time(0)));
for(int iMine=0;iMine<nMine;)
{
i = rand()%n;
j = rand()%m;
if(p[i][j].isMine) continue;
p[i][j].isMine=true;
for (int k=i-1;k<=i+1;k++)
for(int l=j-1;l<=j+1;l++)
{
if(k<0 || k>(n-1) || l<0 || l>(m-1))
continue;

p[k][l].around++;
}
iMine++;

}
}
/*!
@brief 输出原始的雷阵信息
@param p n*m的数组首地址
@param n 二维数组行长度
@param m 二维数组列长度
*/
void printArray(position** p,int n, int m)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
///如果位置是地雷,打印“@”,否则打印周围地雷数目
if(p[i][j].isMine==1)
cout<<"   "<<"@"<<"   ";
else
cout<<"   "<<p[i][j].around<<"   ";
}
cout<<endl;
}
cout<<endl;
return ;
}
/*!
@brief 随机选择一个位置
@param display n*m的数组首地址
@param n 二维数组行长度
@param m 二维数组列长度
@param line OUT 二维数组行索引号(从0开始)
@param colunm OUT 二维数组列索引号(从0开始)
*/
void randChoose(position** display,int n,int m,int& line,int& column)
{
int i=0;
int j=0;
for (;true;)
{
i = rand()%n;
j = rand()%m;
if(display[i][j].display)
continue;
line = i;
column = j;
break;
}
return ;

}
/*!
@brief 搜索一个地雷位置
@param display n*m的数组首地址
@param n 二维数组行长度
@param m 二维数组列长度
@param line OUT 二维数组行索引号(从0开始)
@param colunm OUT 二维数组列索引号(从0开始)
@return bool 是否搜索到地雷位置
*/
bool searchMine(position** display,int n,int m,int& line,int& column)
{
bool bFindMine = false;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(!display[i][j].display || (display[i][j].around==0 && display[i][j].display))
continue;
//周围未知状态个数
int unknown = 0;
//周围已知的地雷个数
int nMine = 0;
for (int k=i-1;k<=i+1;k++)
{
for(int l=j-1;l<=j+1;l++)
{
if(k<0 || k>(n-1) || l<0 || l>(m-1))
continue;
if(!display[k][l].display)
unknown++;
if(display[k][l].isMine)
nMine++;
}
}
for (int k=i-1;k<=i+1;k++)
{
for(int l=j-1;l<=j+1;l++)
{
if(k<0 || k>(n-1) || l<0 || l>(m-1))
continue;
///根据(中心位置周围地雷个数-周围已知的地雷个数=位置状态呆确定的各数)来判断该位置是否为地雷
if(!display[k][l].display && (display[i][j].around-nMine) >=unknown)
{
line = k;
column = l;
bFindMine = true;
//display[i][j].display = true;
//display[i][j].isMine = true;
return true; //一次找一个地雷
}
}
}
}
}
return false;
}
/*!
@brief 搜索一个非地雷位置
@param display n*m的数组首地址
@param n 二维数组行长度
@param m 二维数组列长度
@param line OUT 二维数组行索引号(从0开始)
@param colunm OUT 二维数组列索引号(从0开始)
@return bool 是否搜索到非地雷位置
*/
bool searchClear(position** display,int n,int m, int& line, int& column)
{
bool bFind = false;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(!display[i][j].display || display[i][j].isMine)
continue;
//周围地雷个数
int nMine = 0;
for (int k=i-1;k<=i+1;k++)
{
for(int l=j-1;l<=j+1;l++)
{
if(k<0 || k>(n-1) || l<0 || l>(m-1))
continue;
if(display[k][l].isMine)
nMine++;
}
}
///如果该位置周围的地雷都已经显示,那么剩下的待确定位置肯定不是地雷
if(nMine >= display[i][j].around)
for (int k=i-1;k<=i+1;k++)
{
for(int l=j-1;l<=j+1;l++)
{
if(k<0 || k>(n-1) || l<0 || l>(m-1))
continue;
if(!display[k][l].display)
{
line = k;
column = l;
//display[i][j].display = true;
bFind = true;
return bFind;
}
}
}
}
}
return bFind;
}
///
bool pushMinePosition(position** display, int n,int m,int& line,int& column)
{
return searchMine(display,n,m,line,column);
}
///
bool pushClearPosition(position** display,int n,int m, int& line, int& column)
{
return searchClear(display, n, m, line, column);
}
///
/*!
@brief 如果搜索到一个非雷区位置且周围地雷数为0,则展开周围位置的地雷数
@param display n*m的数组首地址,输出的雷阵信息
@param n 二维数组行长度
@param m 二维数组列长度
@param i  二维数组行索引号(从0开始)
@param j  二维数组列索引号(从0开始)
@param src 原始雷阵信息
*/
void displayAround(position** display,int n,int m,int i,int j,position** src)
{
for (int k=i-1;k<=i+1;k++)
{
for(int l=j-1;l<=j+1;l++)
{
if(k<0 || k>(n-1) || l<0 || l>(m-1))
continue;
if(display[k][l].display) continue;
display[k][l].around = src[k][l].around;
display[k][l].display = true;
if(display[k][l].around==0 /*&& !display[k][l].display*/)
displayAround(display,n,m,k,l,src);

}
}

}
/*!
@brief 检查外部判断的位置是否正确
@param display n*m的数组首地址
@param n 二维数组行长度
@param m 二维数组列长度
@param inLine  二维数组行索引号(从0开始)
@param inColumn  二维数组列索引号(从0开始)
@param bFindMine 位置是否被判断为地雷
@return bool 返回检查结果
*/
bool checkResult(position** display, int n, int m, int inLine,int inColumn,bool bFindMine,position** src)
{
if (bFindMine)
{
display[inLine][inColumn].isMine=true;
display[inLine][inColumn].display=true;
return true;
}
if(src[inLine][inColumn].isMine)
return false;
display[inLine][inColumn].around=src[inLine][inColumn].around;
display[inLine][inColumn].display=true;
if (display[inLine][inColumn].around==0)
displayAround(display,n,m,inLine,inColumn,src);
return true;
}
///打印正在破解的雷阵信息
void printDisplay(position** p,int n, int m)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if (p[i][j].display)
{
if(p[i][j].isMine)
cout<<"   "<<"@"<<"   ";
else
cout<<"   "<<p[i][j].around<<"   ";
}
else
cout<<"   "<<"*"<<"   ";
}
cout<<endl;
}
cout<<endl;
return ;
}
///检查扫雷是否成功
bool CheckSuccess(position** display,int n,int m,position** src)
{
bool bSuccess = false;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(src[i][j].isMine && display[i][j].isMine)
continue;
if(src[i][j].around != display[i][j].around)
return bSuccess;
}
}

return true;

}
void freeArray(position**p,int n)
{
for (int i=0;i<n;i++)
free(p[i]);
free(p);
}
/*!
@brief 扫雷过程模拟函数
@param n 设置模拟雷阵的二维数组行长度
@param m 设置模拟雷阵的二维数组列长度
@param nMine  设置雷阵的地雷数目
@return bool 返回扫雷结果
*/
bool Run(int n, int m,int nMine)
{
///生成一个原始雷阵并布雷
position** p = (position**)malloc(sizeof(position)*n);
for (int i=0;i<n;i++)
p[i]=(position*)malloc(sizeof(position)*m);
initArray(p,n,m);
createMine(p,n,m,nMine);
///打印原始雷阵信息
cout<<"Source Mines information:"<<endl;
printArray(p,n,m);
///生成用来显示扫雷过程中雷阵状态数组
position** display = (position**)malloc(sizeof(position)*n);
for (int i=0;i<n;i++)
display[i]=(position*)malloc(sizeof(position)*m);
initArray(display,n,m);
try{
bool bFirst=true;
int nFindMine = 0; ///<已经找出的地雷数
bool bFindMine=true;
bool bFindClear=true;
///打印未扫雷前的雷阵
printDisplay(display,n,m);
for (;true;)
{
int line=0;
int column=0;
///第一次或者扫雷无法继续时随机定位位置
if (bFirst|| (!bFindMine && !bFindClear))
{
randChoose(display, n, m, line, column);
cout<<"rand input line["<<line<<"]column["<<column<<"]"<<endl;
bFirst = false;
bFindClear=true;
bFindMine=true;
if(!checkResult(display,n,m,line,column,false,p))
throw 0;
printDisplay(display,n,m);
continue;
}
bFindMine=false;
bFindClear=false;
///搜索所有可以确定的地雷位置
while(pushMinePosition(display,n,m,line,column))
{
cout<<"input line["<<line<<"]column["<<column<<"]"<<endl;
bFindMine = true;
nFindMine++;
if(!checkResult(display,n,m,line,column,bFindMine,p))
throw 0;
printDisplay(display,n,m);
}
///搜索所有可以确定的非地雷位置
while(pushClearPosition(display,n,m,line,column))
{
cout<<"input line["<<line<<"]column["<<column<<"]"<<endl;
bFindClear = true;
if(!checkResult(display,n,m,line,column,false,p))
throw 0;
printDisplay(display,n,m);
}
///检查是否扫雷完毕
if(nFindMine==nMine && CheckSuccess(display,m,n,p))
{
cout<<"Successed!!! the source Mines is:"<<endl;
printArray(p,n,m);
freeArray(p,n);
freeArray(display,n);
return true;
}
}
}catch(int e){
cout<<"the position you choose is a mine, you failed!!! source Mines is:"<<endl;
printArray(p,n,m);
freeArray(p,n);
freeArray(display,n);
return false;
}
}
int main(char argc,char** argv)
{
///雷阵的行数,列数,地雷数
int n=9,m=9,nMine=10;
if (argc==4)
{
n = atoi(argv[1]);
m = atoi(argv[2]);
nMine = atoi(argv[3]);
}
else
{
cout<<"Useage: landmine.exe
[m] [mine]"<<endl;
cout<<"n: Mines width/r/nm: Mines height/r/nmine: Mine count"<<endl;
return 0;
}
Run(n,m,nMine);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: