简介: C++文本模式下模拟经典的扫雷游戏,有简单的解答计算。 概要: 其中一处用到区域生长算法,其余的没有技术含量,纯属娱乐。 核心算法和数据结构: 见代码. AI部分待续..
#include <iostream>
#include <fstream>
#include <cmath>
#include <ctime>
#include <vector>
#include <windows.h>

class CMyMine
  {
public:
CMyMine();
~CMyMine();
public:
void Run();
static int rand(void);
protected:
void InitGame(void);
void Show(void);
void GetNeighbor(int pos,int* nb);
bool CheckState();
public:
void DeMark(int x ,int y,bool isC = false);
void Mark(int x, int y,bool isC = false); //做标记
void Mark(int pos,bool isC = false);
void DeMark(int pos,bool isC = false);
void WaitForInput();
void Open(int x , int y); //打开
void Open(int pos);
int* m_UserMark;
int* m_AIMark;
protected:
void AI();
 enum GAMESTATE {START,OVER,WIN,PAUSE};
char m_nHard;
enum GAMESTATE m_eGameState;
int m_nWidth;
int m_nHeight;
int m_nMineNum;
bool m_UsingAI;

int* m_CoreMap;
int* m_ViewMap;
int m_nGamep[12]; //游戏参数
char* m_graphs[4]; //图像
};



CMyMine::CMyMine()
  {
//难度常数设置
m_nGamep[0] = 10;
m_nGamep[1] = 10;
m_nGamep[2] = 10;
m_nGamep[3] = 20;
m_nGamep[4] = 15;
m_nGamep[5] = 38;
m_nGamep[6] = 30;
m_nGamep[7] = 20;
m_nGamep[8] = 100;
m_nGamep[9] = 35;
m_nGamep[10] = 25;
m_nGamep[11] = 175;
//图形符号
m_graphs[0] = "#"; //空
m_graphs[1] = "@"; //炸弹
m_graphs[2] = "#"; //未打开
m_graphs[3] = "*"; //标记


//??
m_CoreMap = 0;
m_ViewMap = 0;
m_nHard = '\0';
m_nWidth = 0;
m_nHeight = 0;
m_nMineNum= 0;
m_UsingAI = false;
m_eGameState = OVER;
}

CMyMine::~CMyMine()
  {
if(m_CoreMap)
 {
delete[] m_CoreMap;
m_CoreMap = 0;
}
if(m_ViewMap)
 {
delete[] m_ViewMap;
m_ViewMap = 0;
}
}

void CMyMine::GetNeighbor(int pos, int* nb)
  {
//周围地雷的位置,如果不存在这个点,则赋值为-1
nb[0] = pos - m_nWidth - 1;
nb[1] = pos - m_nWidth;
nb[2] = pos - m_nWidth + 1;
nb[3] = pos - 1;
nb[4] = pos + 1;
nb[5] = pos + m_nWidth - 1;
nb[6] = pos + m_nWidth;
nb[7] = pos + m_nWidth + 1;

if(pos == 0) //左上角
 {
nb[0] = nb[1] = nb[2] =nb[3] = nb[5] = -1;
}
else if(pos == m_nWidth - 1) //右上角
 {
nb[0] = nb[1] = nb[2] =nb[4] = nb[7] = -1;
}
else if(pos == m_nWidth*(m_nHeight-1)) //左下角
 {
nb[0] = nb[3] = nb[5] =nb[6] = nb[7] = -1;
}
else if(pos==m_nWidth*m_nHeight-1) //右下角
 {
nb[2] = nb[4] = nb[5] =nb[6] = nb[7] = -1;
}
else if( pos < m_nWidth - 1 && pos > 0) //第一排
 {
nb[0] = nb[1] = nb[2] = -1;
}
else if(pos > m_nWidth*(m_nHeight-1) && pos < m_nWidth*m_nHeight-1)//最后一排
 {
nb[5] =nb[6] = nb[7] = -1;
}
else if(pos % m_nWidth == 0)
 {
nb[0] = nb[3] = nb[5] = -1;
}
else if((pos+1)%m_nWidth == 0)
 {
nb[2] = nb[4] = nb[7] = -1;
}
}

int CMyMine::rand(void)
  {
static bool initlize = false;
static int randint[1000];
static int randIndex = 0;
if(!initlize || randIndex > 999 )
 {
initlize = true;
srand((unsigned)time(NULL));
for(int i = 0; i < 1000; i++)
randint[i] = ::rand();
}
return randint[randIndex++];
}


void CMyMine::Run(void)
  {
using namespace std;
while(true)
 {
char inputkey = 'a';
bool bExit = false;
cout<<"welcome to text mine game!"<<endl;
cout<<"please input a,b,c. to select:"<<endl;
cout<<"a,[start game]"<<endl;
cout<<"b,[game info]"<<endl;
cout<<"c,[exit]"<<endl;
cin>>inputkey;
switch(inputkey)
 {
case 'a':
 {
//游戏状态控制:
InitGame();
while(!CheckState())
 {
Show();
WaitForInput();
}
Show();
//bExit = true;
break;
}
case 'b':
 {
cout<<m_graphs[0]<<m_graphs[1]<<m_graphs[2]<<m_graphs[3]<<endl;
break;
}
case 'c':
 {
bExit = true;
break;
}
default:
cout<<"input a,b,c to select!"<<endl<<endl<<endl<<endl;
break;
}
if(bExit)
break;
}
}



void CMyMine::InitGame(void)
  {
using namespace std;
do
 {
cout<<"select diffculty:"<<endl;
cout<<"a easy,b normal,c hard,d crazy(a-d Only)"<<endl;
cin>>this->m_nHard;
}while(this->m_nHard<'a' && this->m_nHard >'d');
m_nHard = (m_nHard-'a')*3;
m_nWidth = m_nGamep[m_nHard];
m_nHeight = m_nGamep[m_nHard+1];
m_nMineNum = m_nGamep[m_nHard+2];
//分配空间
m_CoreMap = new int[m_nWidth*m_nHeight];
m_ViewMap = new int[m_nWidth*m_nHeight];
m_UserMark = new int[m_nWidth*m_nHeight];
m_AIMark = new int[m_nWidth*m_nHeight];
int i=0,j=0;
//生成地雷 9代表地雷!
int k = 0;
while(k<m_nMineNum)
 {
int pindex = this->rand()%(m_nWidth*m_nHeight);
m_CoreMap[pindex] = 9;
for(i=0,k=0;i<m_nWidth*m_nHeight;i++)
 {
if(m_CoreMap[i]==9)
 {
k++;
}
}
}
for(i=0;i<m_nWidth*m_nHeight;i++)
 {//地图生成
if(m_CoreMap[i] != 9)
 {
m_CoreMap[i] = 0;
}
m_ViewMap[i] = -1;//未开采
m_UserMark[i] = 0;//未标记
m_AIMark[i] = 0;//未标记
int nb[8];
int minenum = 0;
GetNeighbor(i,nb);
for(j=0;j<8;j++)
 {
if(nb[j]<0 || nb[j]>= m_nWidth*m_nHeight)
continue;
if(m_CoreMap[nb[j]] == 9)
minenum ++;
}
if(m_CoreMap[i]!=9)
m_CoreMap[i]= minenum;
}
m_eGameState = START;
}


void CMyMine::Show(void)
  {//如果已经显示
using namespace std;
int i=0,j=0;
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
if(m_eGameState == START)
 {
cout<<endl;
cout<<endl;
for(i=0;i<=m_nHeight;i++)
 {
if(i==0)
 {
for(j=0;j<=m_nWidth;j++)
 {
if(j==0)
cout<<" ";
else
 {
if(j-1<10)
 {
cout<<' ';
}
cout<<j-1;
}
}
cout<<endl;
continue;
}
for(j=0;j<=m_nWidth;j++)
 {
if(j==0)
 {
cout<<i-1;
if(i-1<10)
 {
cout<<' ';
}
continue;
}
if(m_ViewMap[(i-1)*m_nWidth+(j-1)] == -1)
 {
if(m_UserMark[(i-1)*m_nWidth+(j-1)]==1 || m_AIMark[(i-1)*m_nWidth+(j-1)]==1)
 {
cout<<' '<<m_graphs[3];
}
else
 {
cout<<' '<<m_graphs[2];
}
}
else
 {
if(m_ViewMap[(i-1)*m_nWidth+(j-1)] == 0)
 {
cout<<' '<<' ';
}
else
 {
if(m_ViewMap[(i-1)*m_nWidth+(j-1)]>=0)
cout<<' ';
cout<<m_ViewMap[(i-1)*m_nWidth+(j-1)];
}
}
}// for j
cout<<endl;
}//for i
cout<<endl;
}
else if(m_eGameState == OVER)
 {
cout<<"Game Over!\n"<<endl;
}//ifelse
else if(m_eGameState == WIN)
 {
cout<<"Congratulation!You Win!!\n"<<endl;
}
}



bool CMyMine::CheckState()
  {
if(m_eGameState == START)
 {
bool bsucceed = true;
//如果所有不是雷的区域都被开发出来,则胜利
for(int i=0;i<m_nWidth*m_nHeight;i++)
 {
if(m_CoreMap[i]!= 9 && m_ViewMap[i] == -1)
 {
bsucceed = false;
break;
}
}
if(bsucceed)
 {
m_eGameState = WIN;
}
return bsucceed;
}
return true;
}




void CMyMine::WaitForInput()
  {
using namespace std;
while(true)
 {
if(m_UsingAI)
 {
AI();
Sleep(1000);
break;
}
char op = '0';
int x = 0;
int y = 0;
cout<<"wait for your input,your choice: \n\"o x y\" to open the unware place,\n\"m x y\" to mark a mine \n\"d x y\" delete a mark \n \"h\" to ask for Help \n \"a\" to using AI\n \"e\" to end game.."<<endl;
cin>>op;
if(op == 'o' || op == 'm')
cin>>x>>y;
if(op == 'o' || op == 'm' || op == 'd' ||op == 'h' ||op == 'e'||op == 'a')
 {
if(!(x<0 ||x>m_nWidth || y<0 || y> m_nHeight))
 {
switch(op)
 {
case 'o':
Open(x,y);
break;
case 'm':
Mark(x,y);
break;
case 'd':
DeMark(x,y);
break;
case 'h':
AI();
break;
case 'a':
m_UsingAI = true;
break;
case 'e':
m_eGameState = OVER;
break;
default:
break;
}
return;
}
}
}
}


int main(void)
  {
CMyMine game;
game.Run();
return 0;
}


void CMyMine::Open(int pos)
  {
int y = pos/m_nWidth;
int x = pos%m_nWidth;
Open(x,y);
}

void CMyMine::Open(int x, int y)
  {
using namespace std;
int j=0,i =0;
int pid =0;
if(m_ViewMap[y*m_nWidth+x] != -1)
return;
if(m_CoreMap[y*m_nWidth+x] == 9)
 {
m_eGameState = OVER;
return;
}
else if(m_CoreMap[y*m_nWidth+x] >0 && m_CoreMap[y*m_nWidth+x] < 9)
 {
m_ViewMap[y*m_nWidth+x] = m_CoreMap[y*m_nWidth+x];
return;
}
else if(m_CoreMap[y*m_nWidth+x] == 0)
 {//生长
vector<int> barray;
vector<int> tarray;
barray.push_back(y*m_nWidth+x);
pid = 0;
while(pid <= barray.size())
 {
int nb[8];
int temp = barray[pid];
GetNeighbor(temp,nb);
pid = pid + 1;
for(j=0;j<8;j++)
 {
if(nb[j]<0 || nb[j] >= m_nWidth*m_nHeight)
continue;
if(m_CoreMap[nb[j]] == 0)
 { //考虑nb[j] 是不是在barray内,如果在需要考察nb[j]的邻居
bool isNotInbary = true;
for(i = 0;i< barray.size();i++)
 {
if(barray[i] == nb[j])
 {
isNotInbary = false;
break;
}
}
if(isNotInbary)
 {
barray.push_back(nb[j]);
}
}
else if(m_CoreMap[nb[j]]>0 && m_CoreMap[nb[j]] < 9)
 {
bool isNotIntary = true;
for(i = 0;i<tarray.size();i++)
 {
if(tarray[i] == nb[j])
 {
isNotIntary = false;
break;
}
}
if(isNotIntary)
 {
tarray.push_back(nb[j]);
}
}
}//for
}//while
for(i = 0;i<barray.size();i++)
 {
m_ViewMap[barray[i]] = m_CoreMap[barray[i]];
}
for(i = 0;i<tarray.size();i++)
 {
m_ViewMap[tarray[i]] = m_CoreMap[tarray[i]];
}
}//if
}

void CMyMine::Mark(int x, int y,bool isC)
  {
if(isC)
 {
if(m_AIMark[y*m_nWidth+x] != 1)
m_AIMark[y*m_nWidth+x] = 1;
}
else
 {
if(m_UserMark[y*m_nWidth+x] != 1)
m_UserMark[y*m_nWidth+x] = 1;
}

}

void CMyMine::DeMark(int x, int y,bool isC)
  {
if(isC)
 {
if(m_AIMark[y*m_nWidth+x] == 1)
m_AIMark[y*m_nWidth+x] = 0;
}
else
 {
if(m_UserMark[y*m_nWidth+x] == 1)
m_UserMark[y*m_nWidth+x] = 0;
}
}

void CMyMine::Mark(int pos,bool isC)
  {
if(isC)
 {
if(m_AIMark[pos] != 1)
m_AIMark[pos] = 1;
}
else
 {
if(m_UserMark[pos] != 1)
m_UserMark[pos] = 1;
}
}

void CMyMine::DeMark(int pos,bool isC)
  {
if(isC)
 {
if(m_AIMark[pos] == 1)
m_AIMark[pos] = 0;
}
else
 {
if(m_UserMark[pos] == 1)
m_UserMark[pos] = 0;
}
}

void CMyMine::AI()
  {
using namespace std;
int i,j,k;
//只提供ViewMap函数
//状态参数
int* lastView = NULL;
int* lastMark = NULL;
lastView = new int[m_nWidth*m_nHeight];
lastMark = new int[m_nWidth*m_nHeight];
memcpy(lastView,m_ViewMap,sizeof(int)*m_nWidth*m_nHeight);
memcpy(lastMark,m_AIMark, sizeof(int)*m_nWidth*m_nHeight);

vector<int> UnknowPointPos; //周围有未知点的安全已知点
vector<int> CurUMineNum; //当前未知炸弹的个数
vector<int> UnknowPointSet; //未知点点集 格式为[size1 p11 p12 .. size2 p21 p22..]
double* mP = new double[m_nWidth*m_nHeight];//每一点更像mine的概率
memset(mP,0,m_nWidth*m_nHeight*sizeof(double)); //初始化为0

//计算整体概率[未知mine的数目/未知点的个数]
double avgP = (double)m_nMineNum/(double)(m_nWidth*m_nHeight);//初始平均概率
int UnknownPoint = 0;
int UnknownMine = 0;
for(i = 0;i<m_nWidth*m_nHeight;i++)
 {
if(m_AIMark[i] == 1)
UnknownMine--;
else if(m_ViewMap[i] == -1)
UnknownPoint ++;
}
avgP = (double)(m_nMineNum - UnknownMine)/(double)UnknownPoint;
for(i = 0;i<m_nWidth*m_nHeight;i++)
 {
mP[i] =avgP; //给每个未知点赋值
}
//将>=1 <9的数据并且其四周有未探索空间 计入向量 UnknowPointPos
//UnknowPointSet CurUMineNum
for(i=0;i<m_nHeight;i++)
 {
for(j=0;j<m_nWidth;j++)
 {
if(m_ViewMap[i*m_nWidth+j]>0 && m_ViewMap[i*m_nWidth+j]<9)
 {
int nb[8];
GetNeighbor(i*m_nWidth+j,nb);
k = 0;
//邻域中是否有未探索的区域和已经发现的mine
bool IsUse = false;
int fakeUMine = 0;
while(k<8)
 {
if(nb[k]>-1)//此邻域存在
 {
if(m_ViewMap[nb[k]] == -1 && m_AIMark[nb[k]] == 1)
 {
fakeUMine++;
}
else if(m_ViewMap[nb[k]] == -1)
 {
if(IsUse == false)
 {
IsUse = true;
UnknowPointPos.push_back(i*m_nWidth+j);
CurUMineNum.push_back(m_ViewMap[i*m_nWidth+j]);
UnknowPointSet.push_back(0);
}
//先找到此邻域大小的位置
int tm = 1,offset = 0;
while( tm<UnknowPointPos.size() )
 {//肯定是最尾巴上的点
offset += UnknowPointSet[offset]+1;
tm++;
}
UnknowPointSet[offset]++;
UnknowPointSet.push_back(nb[k]);
}
}
k++;
}//while
if(IsUse)
CurUMineNum[CurUMineNum.size()-1] -= fakeUMine;
}//if
}//for j
}//for i



//如果没有打开过 选取中间的点打开
if(UnknowPointPos.size() == 0)
 {
int x = (m_nWidth-1)/2;
int y = (m_nHeight-1)/2;
Open(x,y);
}

//计算单个集合的概率,把已经确认为空 或 已经确认为mine的挑选出来
//遍历UnknowPointPos中的所有点
for(i=0;i<UnknowPointPos.size();i++)
 {
//先找到此邻域大小的位置
int tm = 0,offset = 0;
while( tm<i )
 {//找到第i个点
offset += UnknowPointSet[offset]+1;
tm++;
}
double curp = (double)CurUMineNum[i]/(double)UnknowPointSet[offset];
for(j=0;j<UnknowPointSet[offset];j++)
 {
mP[UnknowPointSet[j+1+offset]] = curp;
if(curp >= 0.999999)
 {
Mark(UnknowPointSet[j+1+offset],true);
}
else if(curp <= 0.000001)
 {
Open(UnknowPointSet[j+1+offset]);
}
}
}
//状态是否改变
bool noChange = true;
for(i = 0;i<m_nWidth*m_nHeight;i++)
 {
if(!(m_AIMark[i] == lastMark[i] && m_ViewMap[i] == lastView[i]))
 {
noChange = false;
break;
}
}
//算单一概率已经解决不了!
if(noChange)
 {
m_UsingAI = false;
//计算联合概率待加入
}

//清理垃圾
delete[] mP;
delete[] lastMark;
delete[] lastView;

}
|
|
CALENDER
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|
30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|
常用链接
留言簿(4)
随笔分类
随笔档案
文章档案
A.I.
Libs
Net Resources
VR & 3DGame
搜索
最新评论

Powered By: 博客园 模板提供:沪江博客
|