⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gpexampledoc.cpp

📁 C人工智能游戏开发的一些实例源代码 C Game development in artificial intelligence source code of some examples
💻 CPP
字号:
//GPExample
//Copyright John Manslow
//29/09/2001

// GPExampleDoc.cpp : implementation of the CGPExampleDoc class
//

#include "stdafx.h"
#include "GPExample.h"

#include "GPExampleDoc.h"
#include "fstream.h"
#include "math.h"

#include "CWorld.h"
#include "CGP.h"
#include "CGPNode.h"
#include "CGPANDNode.h"
#include "CGPIfGreaterThanZeroNode.h"
#include "CGPIsFoodNode.h"
#include "CGPIsPoisonNode.h"
#include "CGPIsWallNode.h"
#include "CGPMoveForwardNode.h"
#include "CGPNOTNode.h"
#include "CGPORNode.h"
#include "CGPRandomNumberNode.h"
#include "CGPTurnLeftNode.h"
#include "CGPTurnRightNode.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//List of prototype GP functions/node tpyes
extern unsigned long ulNumberOfPrototypes;
extern CGPNode **pPrototypeList;

//Pointer toalog file - useful for debugging
ofstream *pLogFile;

/////////////////////////////////////////////////////////////////////////////
// CGPExampleDoc

IMPLEMENT_DYNCREATE(CGPExampleDoc, CDocument)

BEGIN_MESSAGE_MAP(CGPExampleDoc, CDocument)
	//{{AFX_MSG_MAP(CGPExampleDoc)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

//Pointers to the view so we can draw to its window
class CGPExampleView;
extern CGPExampleView *pView;

//The environment in which the agent will be evaluated
CWorld *pWorld;

//Pointer to the genetic programming class
CGP *pGP;

//Indicates which way the agent is facing 0-4 = E N W S
int nFacing;
long dx,dy;

//The "health" of the agent, i.e. its performance
double dHealth=0.0;

//The number of fitness evaluations
unsigned long ulIteration=0;

//The number of evaluations of the current program
unsigned long ulEvaluationIteration=0;

//The number of food and poison items eaten by the current program
unsigned long ulFoodEaten=0;
unsigned long ulPoisonEaten=0;

//Whether we're running in slow motion (and hance watching the behaviour of the best program discovered
//thus far
int nSlowMotion=0;

//Pointer to the top node of the program
CGPNode *pdChromosome;

/////////////////////////////////////////////////////////////////////////////
// CGPExampleDoc construction/destruction

CGPExampleDoc::CGPExampleDoc()
{
	unsigned long i;

	//Seed the random number generator
	srand(unsigned(time(NULL)));

	//Creates a log file that records progress - useful for debugging and comparing debug and release versions
	char pString[10000];
#ifdef _DEBUG
	pLogFile=new ofstream("c:\\DebugGPLog.txt");
#else
	pLogFile=new ofstream("c:\\ReleaseGPLog.txt");
#endif
	_strtime(pString);
	*pLogFile<<"Starting at: ";
	*pLogFile<<pString;
	*pLogFile<<"\n";

	//This section of code bubble sorts the prorotypes according to their type IDs. This is only necessary in order
	//to compare the progress of debug and release versions. Without the sort, direct comparisons can't be made 
	//because the prortypes register themselves in a different order
	int nSwaps;
	do
	{
		nSwaps=0;
		for(i=0;i<ulNumberOfPrototypes-1;i++)
		{
			if(pPrototypeList[i]->nType>pPrototypeList[i+1]->nType)
			{
				CGPNode *pBackup=pPrototypeList[i];
				pPrototypeList[i]=pPrototypeList[i+1];
				pPrototypeList[i+1]=pBackup;
				nSwaps=1;
			}
		}
	}
	while(nSwaps);

	//Create the genetic programming class
	pGP=new CGP(100);

	//Create the world
	pWorld=new CWorld(50,50);
}

CGPExampleDoc::~CGPExampleDoc()
{
	//Close the log file and deallocate all the memory
	pLogFile->close();
	delete pLogFile;
	delete pGP;
	delete pWorld;
	delete []pPrototypeList;
}

void TurnLeft(void)
{
	//Rotates to the left
	nFacing--;
	if(nFacing<0)
	{
		nFacing=3;
	}	
}

void TurnRight(void)
{
	//Rotates to the right
	nFacing++;
	if(nFacing>3)
	{
		nFacing=0;
	}	
}

int nLookForward(void)
{
	long lx,ly;

	//Set displacements into the world array according the direction we're facing
	if(nFacing==0)
	{
		dx=1;
		dy=0;
	}
	if(nFacing==1)
	{
		dx=0;
		dy=-1;
	}
	if(nFacing==2)
	{
		dx=-1;
		dy=0;
	}
	if(nFacing==3)
	{
		dx=0;
		dy=1;
	}

	//Convert the displacements into indices into the world array
	lx=pWorld->ulCharacterLocationX+dx;
	ly=pWorld->ulCharacterLocationY+dy;

	//If we're looking beyond the edge of the world, return "wall"
	if(lx<1 || ly<1 || lx>=long(pWorld->ulWorldSizeX) || ly>=long(pWorld->ulWorldSizeY))
	{
		return 2;
	}

	//Otherwise return the contents of the world array
	return pWorld->ppnWorld[lx][ly];
}

int nLookLeft(void)
{
	int ReturnValue;
		
	//We look left by tunring left, looking and turning back
	TurnLeft();
	ReturnValue=nLookForward();
	TurnRight();
	return ReturnValue;
}

int nLookRight(void)
{
	int ReturnValue;

	//We look right by tunring right, looking and turning back;
	TurnRight();
	ReturnValue=nLookForward();
	TurnLeft();
	return ReturnValue;
}

void MoveForward(void)
{
	double dScale=10.0*4.0/5.0;
	//Create a drawing surface
	CClientDC *pDC=new CClientDC((CView*)pView);

	//If we're running in slow motion, its so that we can see the motion of the agent - so we bettwer display it.
	//Since the agent is about to move, we better remove it from the display
	if(nSlowMotion)
	{
		pDC->SelectStockObject(WHITE_PEN);
		pDC->SelectStockObject(WHITE_BRUSH);
		pDC->Rectangle(
									int(pWorld->ulCharacterLocationX*dScale),
									int(pWorld->ulCharacterLocationY*dScale),
									int((pWorld->ulCharacterLocationX+1)*dScale+1),
									int((pWorld->ulCharacterLocationY+1)*dScale+1));
	}

	//Work out which way we're going to move from which way we're facing
	if(nFacing==0)
	{
		dx=1;
		dy=0;
	}
	if(nFacing==1)
	{
		dx=0;
		dy=-1;
	};
	if(nFacing==2)
	{
		dx=-1;
		dy=0;
	}
	if(nFacing==3)
	{
		dx=0;
		dy=1;
	}

	//See whether we've eaten any food, poison, etc. and update the health measure accordingly
	if(pWorld->ppnWorld[pWorld->ulCharacterLocationX+dx][pWorld->ulCharacterLocationY+dy]==1)
	{
		ulFoodEaten++;
		dHealth++;
	}
	if(pWorld->ppnWorld[pWorld->ulCharacterLocationX+dx][pWorld->ulCharacterLocationY+dy]==3)
	{
		ulPoisonEaten++;
		dHealth--;
	}

	//If we can actually move forward (i.e. there's not a wall in front of us) do so
	if(pWorld->ppnWorld[pWorld->ulCharacterLocationX+dx][pWorld->ulCharacterLocationY+dy]!=2)
	{
		pWorld->ppnWorld[pWorld->ulCharacterLocationX][pWorld->ulCharacterLocationY]=0;
		pWorld->ulCharacterLocationX+=dx;
		pWorld->ulCharacterLocationY+=dy;
	}

	//If we're running in slow motion, draw the agent in its new position 
	if(nSlowMotion)
	{
		pDC->SelectStockObject(BLACK_PEN);
		pDC->SelectStockObject(BLACK_BRUSH);
		pDC->Rectangle(
									int(pWorld->ulCharacterLocationX*dScale),
									int(pWorld->ulCharacterLocationY*dScale),
									int((pWorld->ulCharacterLocationX+1)*dScale+1),
									int((pWorld->ulCharacterLocationY+1)*dScale+1));

		//And wait a while before continuing
		unsigned long ulDelay=0;
		do
		{
			ulDelay++;
		}
		while(ulDelay<1e+6);
	}
	delete pDC;

	//Update a counter of the number of steps used so far to evaluate the fitness of this individual
	ulEvaluationIteration++;
}

void Evaluate(void)
{
	unsigned long ulDelay=0;

	SYSTEMTIME StartTime,CurrentTime;
	GetSystemTime(&StartTime);
	unsigned long ulCurrentTime;

	//Create a drawing surface
	CClientDC *pDC=new CClientDC((CView*)pView);

	//If we've finished evaluating the last chromosome and need a new one
	if(ulEvaluationIteration==0)
	{
		//Reset the record of the amount of food and poison eaten
		ulFoodEaten=0;
		ulPoisonEaten=0;

		//Clear the window
		pDC->SelectStockObject(WHITE_PEN);
		pDC->Rectangle(500,0,2000,2000);

		//Keep track of the number of programs evalutaed so far
		ulIteration++;

		//Reste the health (fitness/performance) estimate
		dHealth=0.0;

		//Reset the world
		pWorld->Initialise();

		//If not we're runnning in slow motion (and hence not watching the best performing program)
		if(!nSlowMotion)
		{
			//Get a new program from the GP class so that we can evaluate its performance
			pdChromosome=pGP->pGetChromosomeForEvaluation();
		}
		else
		{
			//Otherwise get a copy of the best program found so far so that we can watch it perform
			pdChromosome=pGP->pGetBestChromosome();

			//Draw the world
			pWorld->Draw(pDC);
		}
	}
	char pString[1000];
	unsigned long ulStartTime=	
													StartTime.wMilliseconds
													+1000*StartTime.wSecond
													+60*StartTime.wMinute
													+3600*StartTime.wHour
													+24*3600*StartTime.wDay;
	do
	{
		//Run the program
		pdChromosome->dEvaluate();

		//Keep track of the number of times its been executed
		ulEvaluationIteration++;

		//Put some info on the screen so that we can monitor the progress
		sprintf(pString,"Food eaten:           %u",ulFoodEaten);
		pDC->TextOut(550,20,pString);
		sprintf(pString,"Poison eaten:         %u",ulPoisonEaten);
		pDC->TextOut(550,40,pString);
		sprintf(pString,"Highest fitness:    %+6.2lf",pGP->dGetBestPerformance());
		pDC->TextOut(550,60,pString);

		GetSystemTime(&CurrentTime);
		ulCurrentTime=	
													CurrentTime.wMilliseconds
													+1000*CurrentTime.wSecond
													+60*CurrentTime.wMinute
													+3600*CurrentTime.wHour
													+24*3600*CurrentTime.wDay;
	}
	//Keep going for either 50ms or until we've finished. See additional comments for this line in 
	//CGAPBILExampleDoc.cpp
	while(ulCurrentTime-ulStartTime<1000 && ulEvaluationIteration<=5000);

	//If we have actually finished
	if(ulEvaluationIteration>5000)
	{
		//Reset the counter of the number of step made in evaluating the current program
		ulEvaluationIteration=0;

		//Reduce the estmiate of the program's health (i.e. performance) by subtracting some function of the number
		//of nodes (instricutions) in the program. Typcially, the lengths of the programs produced by GP tend to 
		//increase in length with time and usually contain large redundant sections. This makes the programs inefficient
		//and impossible to understand. The penalty provided by the line below helps to encourage the emergence of
		//shorter programs
		dHealth-=0.1*pow(pdChromosome->ulGetNumberOfNodesInSubtree(0),1.0);

		//If we're not just watching the best program perform
		if(!nSlowMotion)
		{
			//Set the health (performance) of the program that was just evaluated
			pGP->SetFitness(dHealth);

			//If we've reached out performance target, stop evolving and just watch the best performer
			if(pGP->dGetBestPerformance()>250)
			{
				nSlowMotion=1;
			}
		}
	}
	//Delete the drawing surface
	delete pDC;
}

BOOL CGPExampleDoc::OnNewDocument()
{
	if (!CDocument::OnNewDocument())
		return FALSE;

	// TODO: add reinitialization code here
	// (SDI documents will reuse this document)

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CGPExampleDoc serialization

void CGPExampleDoc::Serialize(CArchive& ar)
{
	if (ar.IsStoring())
	{
		// TODO: add storing code here
	}
	else
	{
		// TODO: add loading code here
	}
}

/////////////////////////////////////////////////////////////////////////////
// CGPExampleDoc diagnostics

#ifdef _DEBUG
void CGPExampleDoc::AssertValid() const
{
	CDocument::AssertValid();
}

void CGPExampleDoc::Dump(CDumpContext& dc) const
{
	CDocument::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CGPExampleDoc commands

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -