📄 cworld.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 + -