一个简单的扫雷程序
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; }
相关文章推荐
- 一个简单的thread池源程序
- Ubuntu下编写一个简单的c程序
- 一个SAX解析xml的简单程序。
- Test__GUI__用java编写一个简单的记事本程序,打包成jar双击运行
- 一个最简单聊天程序
- 一个简单的程序教你检测你的机器是大端还是小端?
- 利用java实现一个简单的远程监控程序
- 一个简单的Web爬虫程序
- 一个简单的录音软件程序代码【C++】
- 创建一个简单的WCF程序(二)
- C语言实现的一个简单的HTTP程序
- Go学习笔记:写一个简单的web程序
- 一个简单的jms聊天室示例程序,pub/sub模型
- 一个简单的Linux内核代码整合到一个文件的Java程序
- 一个简单的C语言小程序,打印九九乘法表
- 【C++ in Qt5】一个简单的通讯录程序,支持文件存取
- 用C#写的一个简单屏幕保护程序
- 郭克华手机编程教学视频----我的练习源码(6)实战:实现一个简单的监听程序
- 微信小程序入门之构建一个简单TODOS应用
- (作业1)将一个简单的C程序编译成汇编代码,讨论计算机是如何工作的进行