资源描述
。
实验四 有限状态机实验
实验报告
一、实验目的
通过蚂蚁世界实验掌握游戏中追有限状态机算法
二、实验仪器
Windows7系统
Microsoft Visual Studio2015
三、实验原理及过程
1) 制作菜单
设置参数:点击会弹出对话框,设置一些参数,红、黑蚂蚁的家会在地图上标记出来
运行:设置好参数后点击运行,毒药、食物、水会在地图上随机显示
下一步:2只红蚂蚁和2只黑蚂蚁会随机出现在地图上,窗口右方还会出现红、黑蚂蚁当前数量的统计
不断按下一步,有限状态机就会不断运行,使蚁群产生变化
2)添加加速键
资源视图中
下方
选择ID和键值
3) 新建头文件def.h
在AntView.cpp中加入#include "def.h"
与本实验有关的数据大都是在这里定义的
int flag=0;
#define kForage 1
#define kGoHome 2
#define kThirsty 3
#define kDead 4
#define kMaxEntities 200
class ai_Entity{
public:
int type;
int state;
int row;
int col;
ai_Entity();
~ai_Entity() {}
void New (int theType,int theState,int theRow,int theCol);
void Forage();
void GoHome();
void Thirsty();
void Dead();
};
ai_Entity entityList[kMaxEntities];
#define kRedAnt 1
#define kBlackAnt 2
int RedHomeRow;
int RedHomeCol;
int BlackHomeRow;
int BlackHomeCol;
int RedNum=2;
int BlackNum=2;
//地图大小,可改变
#define kMaxRows 30
#define kMaxCols 40
#define LENGTH 20
int terrain[kMaxRows][kMaxCols];
#define kGround 1
#define kWater 2
#define kBlackHome 3
#define kRedHome 4
#define kPoison 5
#define kFood 6
//ai_Entity类中函数的定义
ai_Entity::ai_Entity()
{
type=0;
state=0;
row=0;
col=0;
}
int Rnd(int min, int max)//不能产生负数
{
int result;
do{
result=rand()%max;
}while(result<=min);
return result;
}
void ai_Entity::New (int theType,int theState,int theRow,int theCol)
{
type=theType;
row=theRow;
col=theCol;
state=theState;
}
void ai_Entity::Forage()
{
int rowMove;
int colMove;
int newRow;
int newCol;
int foodRow;
int foodCol;
int poisonRow;
int poisonCol;
rowMove=Rnd(-1,3)-1;
colMove=Rnd(-1,3)-1;
newRow=row+rowMove;
newCol=col+colMove;
if(newRow<0)
return;
if(newCol<0)
return;
if(newRow>=kMaxRows)
return;
if(newCol>=kMaxCols)
return;
if((terrain[newRow][newCol]==kGround)||(terrain[newRow][newCol]==kWater))
{
row=newRow;
col=newCol;
}
if(terrain[newRow][newCol]==kFood)
{
row=newRow;
col=newCol;
terrain[row][col]=kGround;
state=kGoHome;
do{
foodRow=Rnd(-1,kMaxRows);
foodCol=Rnd(-1,kMaxCols);
}while(terrain[foodRow][foodCol]!=kGround);
terrain[foodRow][foodCol]=kFood;
}
if(terrain[newRow][newCol]==kPoison)
{
row=newRow;
col=newCol;
terrain[row][col]=kGround;
state=kDead;
do{
poisonRow=Rnd(-1,kMaxRows);
poisonCol=Rnd(-1,kMaxCols);
}while(terrain[poisonRow][poisonCol]!=kGround);
terrain[poisonRow][poisonCol]=kPoison;
}
}
void ai_Entity::GoHome()
{
int rowMove;
int colMove;
int newRow;
int newCol;
int homeRow;
int homeCol;
int poisonRow;
int poisonCol;
int i;
if(type==kRedAnt)
{
homeRow=RedHomeRow;
homeCol=RedHomeCol;
}
else
{
homeRow=BlackHomeRow;
homeCol=BlackHomeCol;
}
if(row<homeRow)
rowMove=1;
else if(row>homeRow)
rowMove=-1;
else
rowMove=0;
if(col<homeCol)
colMove=1;
else if(col>homeCol)
colMove=-1;
else
colMove=0;
newRow=row+rowMove;
newCol=col+colMove;
if(newRow<0)
return;
if(newCol<0)
return;
if(newRow>=kMaxRows)
return;
if(newCol>=kMaxCols)
return;
if(terrain[newRow][newCol]!=kPoison)
{
row=newRow;
col=newCol;
}
else
{
row=newRow;
col=newCol;
terrain[row][col]=kGround;
state=kDead;
do{
poisonRow=Rnd(-1,kMaxRows);
poisonCol=Rnd(-1,kMaxCols);
}while(terrain[poisonRow][poisonCol]!=kGround);
terrain[poisonRow][poisonCol]=kPoison;
}
if((newRow==homeRow)&&(newCol==homeCol))
{
row=newRow;
col=newCol;
state=kThirsty;
for(i=0;i<kMaxEntities;i++)
if(entityList[i].type==0)
{
entityList[i].New(type,kForage,homeRow,homeCol);
if(type==kRedAnt)
RedNum++;
if(type==kBlackAnt)
BlackNum++;
break;
}
}
}
void ai_Entity::Thirsty()
{
int rowMove;
int colMove;
int newRow;
int newCol;
int foodRow;
int foodCol;
int poisonRow;
int poisonCol;
rowMove=Rnd(-1,3)-1;
colMove=Rnd(-1,3)-1;
newRow=row+rowMove;
newCol=col+colMove;
if(newRow<0)
return;
if(newCol<0)
return;
if(newRow>=kMaxRows)
return;
if(newCol>=kMaxCols)
return;
if((terrain[newRow][newCol]==kGround)||(terrain[newRow][newCol]==kFood))
{
row=newRow;
col=newCol;
}
if(terrain[newRow][newCol]==kWater)
{
row=newRow;
col=newCol;
terrain[row][col]=kGround;
state=kForage;
do{
foodRow=Rnd(-1,kMaxRows);
foodCol=Rnd(-1,kMaxCols);
}while(terrain[foodRow][foodCol]!=kGround);
terrain[foodRow][foodCol]=kWater;
}
if(terrain[newRow][newCol]==kPoison)
{
row=newRow;
col=newCol;
terrain[row][col]=kGround;
state=kDead;
do{
poisonRow=Rnd(-1,kMaxRows);
poisonCol=Rnd(-1,kMaxCols);
}while(terrain[poisonRow][poisonCol]!=kGround);
terrain[poisonRow][poisonCol]=kPoison;
}
}
void ai_Entity::Dead()
{
if(type==kRedAnt)
RedNum--;
if(type==kBlackAnt)
BlackNum--;
type=0;
}
4) 制作对话框
添加资源,拖动控件
9个静态文本框,7个编辑框
右键对话框添加类InputDlg,添加成员变量
int m_RedHomeRow;
int m_RedHomeCol;
int m_BlackHomeRow;
int m_BlackHomeCol;
int m_food;
int m_water;
int m_poison;
在AntView.cpp中加入#include "InputDlg.h"
5) 对菜单项添加事件
给CAntView类添加一些成员变量
没做说明都是添加到CAntView类,没做说明都是COMMAND事件。
设置参数:点击出现对话框,输入值,确定后赋给CAntView的成员变量,刷新窗口
void CAntView::OnData()
{
// TODO: 在此添加命令处理程序代码
InputDlg dlg;
if(dlg.DoModal()==IDOK)
{
RedHomeRow=kRedHomeRow=dlg.m_RedHomeRow;
RedHomeCol=kRedHomeCol=dlg.m_RedHomeCol;
BlackHomeRow=kBlackHomeRow=dlg.m_BlackHomeRow;
BlackHomeCol=kBlackHomeCol=dlg.m_BlackHomeCol;
kMaxWater=dlg.m_water;
kMaxFood=dlg.m_food;
kMaxPoison=dlg.m_poison;
}
if(kRedHomeRow>=kMaxRows||kBlackHomeRow>=kMaxRows||kRedHomeCol>=kMaxCols||kBlackHomeCol>=kMaxCols
||kRedHomeRow<0||kBlackHomeRow<0||kRedHomeCol<0||kBlackHomeCol<0
||kRedHomeRow==kBlackHomeRow&&kRedHomeCol==kBlackHomeCol)
{
AfxMessageBox(L"输入坐标不合法,请重新输入!");
return;
}
flag=1;
Invalidate();
}
运行:初始化数组,随机生成毒药、事物、水,刷新窗口
void CAntView::OnRun()
{
// TODO: 在此添加命令处理程序代码
if(flag==0)
{
AfxMessageBox(L"还未设置蚂蚁家的位置!");
return ;
}
if(flag!=1)
return ;
int i,j;
for(i=0;i<kMaxRows;i++)
for(j=0;j<kMaxCols;j++)
{
terrain[i][j]=kGround;
}
terrain[kRedHomeRow][kRedHomeCol]=kRedHome;
terrain[kBlackHomeRow][kBlackHomeCol]=kBlackHome;
srand(time(NULL)); //取系统时间为随机种子
int r,c;
for(i=0;i<kMaxWater;i++)
{
r=Rnd(-1,kMaxRows);
c=Rnd(-1,kMaxCols);
if(terrain[r][c]==kGround)
terrain[r][c]=kWater;
else
i--;
}
for(i=0;i<kMaxPoison;i++)
{
r=Rnd(-1,kMaxRows);
c=Rnd(-1,kMaxCols);
if(terrain[r][c]==kGround)
terrain[r][c]=kPoison;
else
i--;
}
for(i=0;i<kMaxFood;i++)
{
r=Rnd(-1,kMaxRows);
c=Rnd(-1,kMaxCols);
if(terrain[r][c]==kGround)
terrain[r][c]=kFood;
else
i--;
}
flag=2;
Invalidate();
}
下一步:有限状态机的核心代码,刷新窗口
void CAntView::OnNext()
{
// TODO: 在此添加命令处理程序代码
if(flag==2)
{
//kGround才能新建
int x,y;
for(int i=0;i<2;i++)
{
x=Rnd(-1,kMaxCols);
y=Rnd(-1,kMaxRows);
if(terrain[y][x]==kGround)
{
entityList[i].New(kRedAnt,kForage,y,x);
}
else
i--;
}
for(int i=2;i<4;i++)
{
x=Rnd(-1,kMaxCols);
y=Rnd(-1,kMaxRows);
if(terrain[y][x]==kGround)
{
entityList[i].New(kBlackAnt,kForage,y,x);
}
else
i--;
}
flag=3;
}
else
{
for(int i=0;i<kMaxEntities;i++)
{
switch(entityList[i].state)
{
case kForage:
entityList[i].Forage();
break;
case kGoHome:
entityList[i].GoHome();
break;
case kThirsty:
entityList[i].Thirsty();
break;
case kDead:
entityList[i].Dead();
break;
default:
break;
}
}
}
Invalidate();
}
6) OnDraw函数
flag是标志变量,便于控制绘图
void CAntView::OnDraw(CDC* pDC)
{
CAntDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
int i,j;
//绘制网格
for(i=0;i<=LENGTH*kMaxRows;i+=LENGTH)
{
//画横线
pDC->MoveTo(0,i);
pDC->LineTo(LENGTH*kMaxCols,i);
}
for(i=0;i<=LENGTH*kMaxCols;i+=LENGTH)
{
//画竖线
pDC->MoveTo(i,0);
pDC->LineTo(i,LENGTH*kMaxRows);
}
//画出红黑房子
if(flag>=1)
{
pDC->TextOutW(kBlackHomeCol*LENGTH+1,kBlackHomeRow*LENGTH+1,L"H");
pDC->SetTextColor(RGB(255,0,0));
pDC->TextOutW(kRedHomeCol*LENGTH+1,kRedHomeRow*LENGTH+1,L"H");
pDC->SetTextColor(RGB(0,0,0));
}
if(flag>=2)//遍历数组在相应位置显示图片(水滴、骷髅、苹果)
{
CDC dcMemory;
dcMemory.CreateCompatibleDC(pDC);
CBitmap bmp1,bmp2,bmp3;
BITMAP bmpInfo1,bmpInfo2,bmpInfo3;
bmp1.LoadBitmapW(IDB_BITMAP1);
bmp1.GetBitmap(&bmpInfo1);
bmp2.LoadBitmapW(IDB_BITMAP2);
bmp2.GetBitmap(&bmpInfo2);
bmp3.LoadBitmapW(IDB_BITMAP3);
bmp3.GetBitmap(&bmpInfo3);
CBitmap *pOldBitmap=dcMemory.SelectObject(&bmp1);
for(i=0;i<kMaxRows;i++)
{
for(j=0;j<kMaxCols;j++)
{
if(terrain[i][j]==kWater)
{
dcMemory.SelectObject(&bmp1);
pDC->StretchBlt(j*LENGTH+1,i*LENGTH+1,19,19,&dcMemory,0,0,bmpInfo1.bmWidth,bmpInfo1.bmHeight,SRCCOPY);
}
else if(terrain[i][j]==kPoison)
{
dcMemory.SelectObject(&bmp2);
pDC->StretchBlt(j*LENGTH+1,i*LENGTH+1,19,19,&dcMemory,0,0,bmpInfo2.bmWidth,bmpInfo2.bmHeight,SRCCOPY);
}
else if(terrain[i][j]==kFood)
{
dcMemory.SelectObject(&bmp3);
pDC->StretchBlt(j*LENGTH+1,i*LENGTH+1,18,18,&dcMemory,0,0,bmpInfo3.bmWidth,bmpInfo3.bmHeight,SRCCOPY);
}
}
}
dcMemory.SelectObject(pOldBitmap);
}
if(flag>=3) //绘制蚂蚁
{
CDC dcMemory;
dcMemory.CreateCompatibleDC(pDC);
CBitmap bmp1,bmp2;
BITMAP bmpInfo1,bmpInfo2;
bmp1.LoadBitmapW(IDB_BITMAP4);
bmp1.GetBitmap(&bmpInfo1);
bmp2.LoadBitmapW(IDB_BITMAP5);
bmp2.GetBitmap(&bmpInfo2);
CBitmap *pOldBitmap=dcMemory.SelectObject(&bmp1);
for(i=0;i<kMaxEntities;i++)
{
if(entityList[i].type==kRedAnt)
{
dcMemory.SelectObject(&bmp1);
pDC->StretchBlt(entityList[i].col*LENGTH+1,entityList[i].row*LENGTH+1,19,19,&dcMemory,0,0,bmpInfo1.bmWidth,bmpInfo1.bmHeight,SRCCOPY);
}
if(entityList[i].type==kBlackAnt)
{
dcMemory.SelectObject(&bmp2);
pDC->StretchBlt(entityList[i].col*LENGTH+1,entityList[i].row*LENGTH+1,19,19,&dcMemory,0,0,bmpInfo2.bmWidth,bmpInfo2.bmHeight,SRCCOPY);
}
}
dcMemory.SelectObject(pOldBitmap);
CString s;
s.Format(_T("红蚂蚁数:%d 黑蚂蚁数:%d"),RedNum,BlackNum);//输出当前蚂蚁数
pDC->TextOutW(43*LENGTH,0,s);
}
}
四、实验结果
运行:
点击菜单项,设置参数,弹出对话框
如果设置参数不对:(房子坐标越界、红黑房子坐标相等、食物毒物水数为负数)
参数设置好后,房子出现:
点击运行或F5,食物毒物水出现:
点击下一步或F10,蚂蚁和文字出现:
多次点击下一步,蚂蚁数改变:
可以多次点击下一步
设置的参数不同蚂蚁的繁殖情况不同。比如,食物多,繁殖快,毒物多,蚂蚁可能很快就灭绝了
五、实验心得
通过有限状态机实现蚂蚁的繁殖,很有趣的一个实验,通过此次实验能增加AI和MFC的知识,程序还有很多需要改进的地方。比如,有时候不按指定的顺序点击菜单将会出现小错误;def.h里的东西太杂,可以新建cpp文件,整理进去。
六、主要代码
实验过程中已经列出主要代码,这里再列出与有限状态机算法有关的代码
ai_Entity::ai_Entity()
{
type=0;
state=0;
row=0;
col=0;
}
int Rnd(int min, int max)//不能产生负数
{
int result;
do{
result=rand()%max;
}while(result<=min);
return result;
}
void ai_Entity::New (int theType,int theState,int theRow,int theCol)
{
type=theType;
row=theRow;
col=theCol;
state=theState;
}
void ai_Entity::Forage()
{
int rowMove;
int colMove;
int newRow;
int newCol;
int foodRow;
int foodCol;
int poisonRow;
int poisonCol;
rowMove=Rnd(-1,3)-1;
colMove=Rnd(-1,3)-1;
newRow=row+rowMove;
newCol=col+colMove;
if(newRow<0)
return;
if(newCol<0)
return;
if(newRow>=kMaxRows)
return;
if(newCol>=kMaxCols)
return;
if((terrain[newRow][newCol]==kGround)||(terrain[newRow][newCol]==kWater))
{
row=newRow;
col=newCol;
}
if(terrain[newRow][newCol]==kFood)
{
row=newRow;
col=newCol;
terrain[row][col]=kGround;
state=kGoHome;
do{
foodRow=Rnd(-1,kMaxRows);
foodCol=Rnd(-1,kMaxCols);
}while(terrain[foodRow][foodCol]!=kGround);
terrain[foodRow][foodCol]=kFood;
}
if(terrain[newRow][newCol]==kPoison)
{
row=newRow;
col=newCol;
terrain[row][col]=kGround;
state=kDead;
do{
poisonRow=Rnd(-1,kMaxRows);
poisonCol=Rnd(-1,kMaxCols);
}while(terrain[poisonRow][poisonCol]!=kGround);
terrain[poisonRow][poisonCol]=kPoison;
}
}
void ai_Entity::GoHome()
{
int rowMove;
int colMove;
int newRow;
int newCol;
int homeRow;
int homeCol;
int poisonRow;
int poisonCol;
int i;
if(type==kRedAnt)
{
homeRow=RedHomeRow;
homeCol=RedHomeCol;
}
else
{
homeRow=BlackHomeRow;
homeCol=BlackHomeCol;
}
if(row<homeRow)
rowMove=1;
else if(row>homeRow)
rowMove=-1;
else
rowMove=0;
if(col<homeCol)
colMove=1;
else if(col>homeCol)
colMove=-1;
else
colMove=0;
newRow=row+rowMove;
newCol=col+colMove;
if(newRow<0)
return;
if(newCol<0)
return;
if(newRow>=kMaxRows)
return;
if(newCol>=kMaxCols)
return;
if(terrain[newRow][newCol]!=kPoison)
{
row=newRow;
col=newCol;
}
else
{
row=newRow;
col=newCol;
terrain[row][col]=kGround;
state=kDead;
do{
poisonRow=Rnd(-1,kMaxRows);
poisonCol=Rnd(-1,kMaxCols);
}while(terrain[poisonRow][poisonCol]!=kGround);
terrain[poisonRow][poisonCol]=kPoison;
}
if((newRow==homeRow)&&(newCol==homeCol))
{
row=newRow;
col=newCol;
state=kThirsty;
for(i=0;i<kMaxEntities;i++)
if(entityList[i].type==0)
{
entityList[i].New(type,kForage,homeRow,homeCol);
if(type==kRedAnt)
RedNum++;
if(type==kBlackAnt)
BlackNum++;
break;
}
}
}
void ai_Entity::Thirsty()
{
int rowMove;
int colMove;
int newRow;
int newCol;
int foodRow;
int foodCol;
int poisonRow;
int poisonCol;
rowMove=Rnd(-1,3)-1;
colMove=Rnd(-1,3)-1;
newRow=row+rowMove;
newCol=col+colMove;
if(newRow<0)
return;
if(newCol<0)
return;
if(newRow>=kMaxRows)
return;
if(newCol>=kMaxCols)
return;
if((terrain[newRow][newCol]==kGround)||(terrain[newRow][newCol]==kFood))
{
row=newRow;
col=newCol;
}
if(terrain[newRow][newCol]==kWater)
{
row=newRow;
col=newCol;
terrain[row][col]=kGround;
state=kForage;
do{
foodRow=Rnd(-1,kMaxRows);
foodCol=Rnd(-1,kMaxCols);
}while(terrain[foodRow][foodCol]!=kGround);
terrain[foodRow][foodCol]=kWater;
}
if(terrain[newRow][newCol]==kPoison)
{
row=newRow;
col=newCol;
terrain[row][col]=kGround;
state=kDead;
do{
poisonRow=Rnd(-1,kMaxRows);
poisonCol=Rnd(-1,kMaxCols);
}while(terrain[poisonRow][poisonCol]!=kGround);
terrain[poisonRow][poisonCol]=kPoison;
}
}
void ai_Entity::Dead()
{
if(type==kRedAnt)
RedNum--;
if(type==kBlackAnt)
BlackNum--;
type=0;
}
void CAntView::OnNext()
{
// TODO: 在此
展开阅读全文