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

📄 cworld.cpp

📁 游戏编程AI源码
💻 CPP
字号:
//Tanks
//Copyright John Manslow
//29/09/2001

#include "stdafx.h"
#include "CWorld.h"
#include "CTank.h"
#include "CProjectile.h"
#include "assert.h"
#include "math.h"
#include "fstream.h"

//if not compiling under Windows, use #define BOOL bool, #define TRUE true, #define FALSE false, etc.
extern BOOL boGeneratingErrorTrainingData;

//Where example aiming error data will be saved to create the exemplar set with which the aiming error
//distribution models will be trained 
#define FileForExemplarData "NewExemplarData.txt"

CWorld::CWorld(const unsigned long culNewTerrainResolution)
{
	TRACE("\tConstructing world...\n");

	//If we're going to generate training data, open a file stream. If not, make sure the
	//pointer to the non-existent file stream is NULL so we don't try to delete it in the 
	//destructor
	pHitData=NULL;
	if(boGeneratingErrorTrainingData)
	{
		pHitData=new ofstream(FileForExemplarData);
		pHitData->precision(15);
	}

	//Record the resolution of the terrain and allocate memory for the terrain height array.
	ulTerrainResolution=culNewTerrainResolution;
	plHeight=new long[ulTerrainResolution];
	if(!plHeight)
	{
		TRACE("failed.\n");
		assert(FALSE);
	}

	//Declare the array of tanks objects. Only two players and one tank per player.
	ppPlayer=new CTank*[2];
	if(!ppPlayer)
	{
		TRACE("failed.\n");
		assert(FALSE);
	}
	ppPlayer[0]=new CTank();
	if(!ppPlayer[0])
	{
		TRACE("failed.\n");
		assert(FALSE);
	}
	ppPlayer[1]=new CTank();
	if(!ppPlayer[1])
	{
		TRACE("failed.\n");
		assert(FALSE);
	}
	//Initialise the tanks' barrels to point horizontally in the direction of thge enemy tank.
	ppPlayer[0]->dBarrelx=1.0;
	ppPlayer[1]->dBarrelx=-1.0;

	//Set the projectile pointer to NULL to indicate that there is currently no projectile in
	//the world. Note that only one projectile is allowed at any one time.
	pProjectile=NULL;

	//Set each player's score to zero
	dScore[0]=0.0;
	dScore[1]=0.0;

	//Create the terrain, place the tanks on it, decide who goes first, pick a random wind 
	//speed, etc. etc.
	Initialise();

	//If we get this far, we're doing well!
	TRACE("\tsuccessful.\n");
}

void CWorld::Initialise(void)
{
	//Indicate that no shots have yet been made in the current game
	ulShotNumber=0;

	//Pick a random wind speed. For simplicity, wind speed is fixed for each shot.
	dWindSpeed=2.0*(double(rand())/double(RAND_MAX)-0.5);

	//If we're generating training data, the active player (the one that has the
	//controls is always player 1 - the AI.
	if(boGeneratingErrorTrainingData)
	{
		nActivePlayer=0;
	}
	else
	{
		//Otherwise, choose whether the AI or human player goes first
		nActivePlayer=rand()%2;
	}

	//Generate some terrain for the world.
	GenerateTerrain();

	//The follwoing code searches the terrain for relatively flat areas to place
	//each tank. This improves the appearance of the game since the tank graphics 
	//aren't adjusted if a tank appears on a steep slope.
	long i,j;
	double dDeviation;
	double dMeanHeightInInterval;
	double dMinimumDeviation=1e+99;
	double dMeanHeightAtMinimumDeviation;
	unsigned long ulPositionOfMinimumDeviation=0;

	ppPlayer[0]->dyPosition=300.0;

	//Find the flattest area towards the left part of the landscape where the
	//player's tank will reside
	for(i=20;i<250;i++)
	{
		//The flat regions are identified by measuring the variance of the landscape
		//height over stretches of 20 elements in the landscape height array. Low
		//variance implies flat lanscape.
		dMeanHeightInInterval=0.0;

		//First, calculate the mean height of the landscape in the interval 
		//currently under consideration.
		for(j=-10;j<10;j++)
		{
			dMeanHeightInInterval+=double(plHeight[i+j]);
		}
		dMeanHeightInInterval/=20.0;

		//Then measure the variance of the landscape's height.
		dDeviation=0.0;
		for(j=-10;j<10;j++)
		{
			dDeviation+=(dMeanHeightInInterval-plHeight[i+j])*(dMeanHeightInInterval-plHeight[i+j]);
		}

		//Test to see if this interval is the flattest so far. If so, record its
		//location for future reference
		if(dDeviation<dMinimumDeviation)
		{
			dMinimumDeviation=dDeviation;
			ulPositionOfMinimumDeviation=i;
			dMeanHeightAtMinimumDeviation=dMeanHeightInInterval;
		}
	}

	//Put the tank in the flattest area
	ppPlayer[0]->dxPosition=ulPositionOfMinimumDeviation;
	ppPlayer[0]->dyPosition=long(dMeanHeightAtMinimumDeviation);

	//Repeat the procedure for the AI tank.
	dMinimumDeviation=1e+99;

	//Find the flattest area towards the rightmost part of the landscape where 
	//the AI's tank will reside.
	for(i=350;i<600;i++)
	{
		dMeanHeightInInterval=0.0;
		for(j=-10;j<10;j++)
		{
			dMeanHeightInInterval+=double(plHeight[i+j]);
		}
		dMeanHeightInInterval/=20.0;

		dDeviation=0.0;
		for(j=-10;j<10;j++)
		{
			dDeviation+=(dMeanHeightInInterval-plHeight[i+j])*(dMeanHeightInInterval-plHeight[i+j]);
		}

		if(dDeviation<dMinimumDeviation)
		{
			dMinimumDeviation=dDeviation;
			ulPositionOfMinimumDeviation=i;
			dMeanHeightAtMinimumDeviation=dMeanHeightInInterval;
		}
	}

	//Put the AI tank on the landscape
	ppPlayer[1]->dxPosition=ulPositionOfMinimumDeviation;
	ppPlayer[1]->dyPosition=long(dMeanHeightAtMinimumDeviation);
}

CWorld::~CWorld()
{
	//Destructor - deallocate memory.
	TRACE("\tDestroying world...\n");

	//The height data should always exist at this time but check anyway
	if(plHeight!=NULL)
	{
		delete []plHeight;
	}

	//Must check this because the data file can be closed and deleted at any time
	//by the user.
	if(pHitData!=NULL)
	{
		pHitData->close();
		delete pHitData;
	}

	//If there's a projectile in the world, delete it.
	if(pProjectile!=NULL)
	{
		delete pProjectile;
	}
	
	//Delete the players' tanks
	delete ppPlayer[0];
	delete ppPlayer[1];
	delete []ppPlayer;

	TRACE("\tsuccessful.\n");
}

void CWorld::GenerateTerrain(void)
{
	unsigned long i,p;
	//Variables to record the heights of the lowest and highest points on the
	//terrain
	long lLowest,lHighest;

	//Choose the height of the leftmost point on the terrain. The exact value 
	//doesn't really matter because we'll normalise the terrain later.
	plHeight[0]=150+rand()%100;

	//Since we only have one point on the terrain, its height must be the
	//lowest and highest so far
	lLowest=plHeight[0];
	lHighest=plHeight[0];

	//p is a variable used to control the probability that the next element in 
	//the terrain array will be higher or lower than the current one. This approach
	//produces landscapes that systematically wander up and down on a large scale
	//but also vary randomly at a smal scale.
	p=rand()%5;

	//Fill the rest of the terrain array with height values.
	for(i=1;i<ulTerrainResolution;i++)
	{
		//Occasionally, choose a new value for p to change the up/down trend
		//of the terrain
		if(rand()%50==0)
		{
			p=rand()%5;
		}

		//Use p to decide randomly whether this element is higher or lower than
		//the one to its left
		if(unsigned long(rand()%7)>p+1)
		{
			plHeight[i]=plHeight[i-1]-1;
		}
		else
		{
			plHeight[i]=plHeight[i-1]+1;
		}

		//Maintain a record of the highest and lowest points
		if(plHeight[i]<lLowest)
		{
			lLowest=plHeight[i];
		}
		if(plHeight[i]>lHighest)
		{
			lHighest=plHeight[i];
		}
	}

	//This is necessary to ensure that the scaling of the terrain doesn't produce
	//really jaggedy landscapes
	if(lHighest-lLowest<100)
	{
		lHighest=100+lLowest;
	}

	//Scale the terrain to show a minimum amount of variation.
	for(i=0;i<ulTerrainResolution;i++)
	{
		plHeight[i]=long(double((plHeight[i]-lLowest)/double(lHighest-lLowest))*100.0+100.0);
	}
}

BOOL CWorld::TimeStep(void)
{
	//First, check to see if a projectile exists in the world
	if(pProjectile)
	{
		//Check to see if the projectile has passed beyond the edge of the window and hence needs to
		//be removed
		if(	long(pProjectile->dxPosition)>750 
			|| long(pProjectile->dxPosition)<0)
		{
			if(boGeneratingErrorTrainingData)
			{
				//Save aiming error data
				*pHitData<<ulShotNumber;
				*pHitData<<"\t";
				*pHitData<<dAngularError;
				*pHitData<<"\n";
			}
			delete pProjectile;
			pProjectile=NULL;

			dWindSpeed=2.0*(double(rand())/double(RAND_MAX)-0.5);
			if(!boGeneratingErrorTrainingData)
			{
				//If we're not generating training data, its the other player's
				//turn
				nActivePlayer=1-nActivePlayer;

			}
			//Return true to indicate that an event occured in this time step.
			return TRUE;
		}

		//See if the projectile collided with the enemy tank in this time step
		if(ppPlayer[1-nActivePlayer]->nTestForProjectileTankCollision(pProjectile))
		{
			//If so and we're generating training data,
			if(boGeneratingErrorTrainingData)
			{
				//Save aiming error information
				*pHitData<<ulShotNumber;
				*pHitData<<"\t";
				*pHitData<<dAngularError;
				*pHitData<<"\n";
			}
			//Delete the projectile
			delete pProjectile;
			pProjectile=NULL;

			//Choose a new wind speed
			dWindSpeed=2.0*(double(rand())/double(RAND_MAX)-0.5);

			//Update the players' scores
			dScore[nActivePlayer]++;

			//Reinitialise the world (generate new terrain, etc.) for another
			//game.
			Initialise();

			//Return true to indicate that an event occured and the world should
			//be redrawn.
			return TRUE;
		}

		//Finally, test to see if the projectile has hit the ground.
		//Calculate the x and y movement of the projectile in this time step
		double dx=pProjectile->dxPosition-pProjectile->dPreviousxPosition;
		double dy=pProjectile->dyPosition-pProjectile->dPreviousyPosition;

		//Get the projectile's old position
		double y=pProjectile->dPreviousyPosition;
		double x=pProjectile->dPreviousxPosition;

		//Make sure we don't divide by zero later on!
		if(long(dx)==0)
		{
			dx=1;
		}

		unsigned long i;
		long j;

		//For every height array element the projectile has moved in the last 
		//time step
		for(i=0;i<unsigned long(labs(long((dx))));i++)
		{
			//If the height of the projectile is less than the height of the 
			//terrain element, the projectile has collided with the terrain
			if(y<plHeight[long(x)])
			{
				if(boGeneratingErrorTrainingData)
				{
					//Save aiming error data
					*pHitData<<ulShotNumber;
					*pHitData<<"\t";
					*pHitData<<dAngularError;
					*pHitData<<"\n";
				}
				//Delete the projectile...
				delete pProjectile;
				pProjectile=NULL;

				//Pick a new wind speed
				dWindSpeed=2.0*(double(rand())/double(RAND_MAX)-0.5);

				//...and deform the terrain to make a small crater.
				for(j=-10;j<11;j++)
				{
					double dCraterDepth=4+rand()%2;
					if(x+j>0 && x+j<700)
					{
						plHeight[long(x)+j]-=long(dCraterDepth*exp(-0.02*pow(j,2.0))+rand()%2);
					}
				}

				//If we're not generating training data,
				dWindSpeed=2.0*(double(rand())/double(RAND_MAX)-0.5);
				if(!boGeneratingErrorTrainingData)
				{
					//Pick a new wind speed and switch to the other player
					nActivePlayer=1-nActivePlayer;
				}
	
				//Return true to indicate that an event has occurred and the
				//display should be redrawn.
				return TRUE;
			}

			//Move to the next terrain array element to test for a collision
			x+=long(dx)/labs(long(dx));

			//Compute the projectile's height aboe that element.
			y+=dy/fabs(dx);
		}

		//Tell the projectile that a time step has occurred.
		pProjectile->TimeStep(dWindSpeed);
	}

	//If we get this far, nothing significant has happened in this time step.
	//Return false to indicate this.
	return FALSE;
}

⌨️ 快捷键说明

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