📄 tanksview.cpp
字号:
CTanksDoc* CTanksView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTanksDoc)));
return (CTanksDoc*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CTanksView message handlers
void CTanksView::OnTimer(UINT nIDEvent)
{
CTanksDoc* pDoc = GetDocument();
//If an event (e.g. a collision) occured in the game world, clear the window contents and
//redraw. The TimeSptep function returns TRUE if an event occurred.
if(pWorld->TimeStep())
{
//Clear the screen
CClientDC *pDC=new CClientDC(this);
pDC->Rectangle(0,0,1000,1000);
delete pDC;
}
//Tell Windows that the window contents will need to be redraw but not to clear the window contents first
Invalidate(FALSE);
//If there's no projectile in the world...
if(!pWorld->pProjectile)
{
//...and its the AI's turn to fire,
if(pWorld->nActivePlayer==1)
{
//Get the size of the window
CRect rect;
GetClientRect(&rect);
int x = (rect.Width());
int y = (rect.Height());
//If we're not generating training data (i.e. this is a normal game)
if(!boGeneratingErrorTrainingData)
{
//Inside this if statement is where the AI uses the barrel angle calculating neural network
//to compute the optimal barrel angle, and the unconditional and conditional distribution models
//to add random variation.
pWorld->ulShotNumber++;
//Allocate space for the three barrel angle neural network inputs (x-displacement between player
//and AI tanks, y-displacement and wind speed)
double dInputs[3];
//Introduce temporary variables to store the relative displacements of the tanks
//in the game world
double dxDisplacement=-pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dxPosition;
double dyDisplacement=-pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dyPosition;
//Set the network inputs to scaled versions of x-displacement, y-displacement and
//wind speed. Scaling factors are calculated from the example data and ensure that
//all network inputs lie roughly in the range -1 to +1. Scaling is not strictly
//necessary but can often reduce the time needed to train the network. You MUST be
//consistent with scaling: the same scaling factors that were used during training
//MUST be used to here
dInputs[0]=2.0*((dxDisplacement-pdMin[0])/(pdMax[0]-pdMin[0])-0.5);
dInputs[1]=2.0*((dyDisplacement-pdMin[1])/(pdMax[1]-pdMin[1])-0.5);
dInputs[2]=2.0*((pWorld->dWindSpeed-pdMin[2])/(pdMax[2]-pdMin[2])-0.5);
//Pass the inputs to the network and compute its output.
double *pdOutputs=pMLP->pdGetOutputs(dInputs);
//Prepare to get the random aiming error
double dAngularError=0.0;
//This will point to the vector of bin probabilities and is only used to plot the error distribution on-screen
double *pdErrorOutputs;
//If this is the first shot in a new game,
if(pWorld->ulShotNumber==1)
{
//Choose an aiming error by randomly sampling from the unconditional distribution
dAngularError=pUnconditionalErrorDistribution->dGetOutputs()*(dErrorMax-dErrorMin)+dErrorMin;
//Get the bin probabilities so we can plot them on the screen
pdErrorOutputs=pUnconditionalErrorDistribution->pdBinProbabilities;
}
else
{
//Otherwise, choose it by sampling from the conditional distribution, which takes into account the error
//made on the preceding shot.
//The input to the conditional aiming error distribution is a scaled version of the aiming error made on the
//preceding shot. Again, the scaling must be consistent with that used when the distribution was trained.
double dErrorDistributionInput=2.0*((pWorld->dLastAngularError-dErrorMin)/(dErrorMax-dErrorMin)-0.5);
//Get a sample from the conditional error distribution and descale it
dAngularError=(pErrorDistribution->dGetOutputs(&dErrorDistributionInput))*(dErrorMax-dErrorMin)+dErrorMin;
//Get the bin probabilities so we can plot them on the screen
pdErrorOutputs=pErrorDistribution->pdGetBinProbabilities(&dErrorDistributionInput);
}
//This error will be the "last error" on the next turn
pWorld->dLastAngularError=dAngularError;
//Add the aiming error to the optimal barrel angle recommnded by the barrel angle neural network
pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination=pdOutputs[0]+dAngularError;
////Draw the bar-graph of the error distribution (just for curiosity)
CClientDC *pDC=new CClientDC(this);
pDC->Rectangle(0,0,1000,1000);
for(unsigned long i=0;i<pErrorDistribution->ulNumberOfOutputs;i++)
{
pDC->Rectangle(100+i*4,250,104+i*4,250-400*pdErrorOutputs[i]);
}
if(pWorld->ulShotNumber!=1)
{
//Don't delete this in the case of the unconditional distribution because it points to
//a member variable itself rather than a copy of it
delete []pdErrorOutputs;
}
delete pDC;
//Clean up the barrel angle neural network's outputs
delete []pdOutputs;
//Set the AI tank's barrel to the chosen angle
pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx=sin(pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination);
pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely=cos(pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination);
//Create a new projectile with location (roughly) at the end of the AI
//tank's barrel, and direction given by its inclination.
pWorld->pProjectile=new CProjectile(pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition+18*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx+4,
pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition+18*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely+7,
9*sin(pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination),
9*cos(pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination));
}
else
{
pWorld->nActivePlayer=0;
}
}
}
CView::OnTimer(nIDEvent);
}
void CTanksView::OnLButtonUp(UINT nFlags, CPoint point)
{
CTanksDoc* pDoc = GetDocument();
//If the active player is not the AI and a projectile does not already exist
//in the world,
if(pWorld->nActivePlayer==0 && !pWorld->pProjectile)
{
//Get the size of the window
CRect rect;
GetClientRect(&rect);
int x = (rect.Width());
int y = (rect.Height());
//Use the location of the mouse click relative to the player's tank to
//set the inclination of the player's tank's barrel. If we're not generating error training data,
//the player is controlling the usual (lefthand) tank. Otherwise, the player is controlling
//the AI tank (the rightmost one)
if(!boGeneratingErrorTrainingData)
{
//Work out the direction of the player tank's barrel
pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx=(point.x-pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition)
/sqrt(pow(point.x-pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition,2.0)+pow(point.y-y+pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition,2.0));
pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely=-(point.y-y+pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition)
/sqrt(pow(point.x-pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition,2.0)+pow(point.y-y+pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition,2.0));
//Create a new projectile
pWorld->pProjectile=new CProjectile(
pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition+18*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx,
pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition+18*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely+6,
9*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx,
9*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely);
}
else
{
//Work out the angle of the AI tank's barrel (if we're generating aiming error data, the player is controlling
//the AI tank)
pWorld->ppPlayer[1-pWorld->nActivePlayer]->dBarrelx=(point.x-pWorld->ppPlayer[1-pWorld->nActivePlayer]->dxPosition)
/sqrt(pow(point.x-pWorld->ppPlayer[1-pWorld->nActivePlayer]->dxPosition,2.0)+pow(point.y-y+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dyPosition,2.0));
pWorld->ppPlayer[1-pWorld->nActivePlayer]->dBarrely=-(point.y-y+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dyPosition)
/sqrt(pow(point.x-pWorld->ppPlayer[1-pWorld->nActivePlayer]->dxPosition,2.0)+pow(point.y-y+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dyPosition,2.0));
//Increment the shot count
pWorld->ulShotNumber++;
//Make sure the AI doesn't get a turn
pWorld->nActivePlayer=1-pWorld->nActivePlayer;
//Create a new projectile
pWorld->pProjectile=new CProjectile(
pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition+18*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx+2,
pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition+18*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely+7,
9*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx,
9*pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely);
//We need to know how the tank should have aimed in order to calculate the aiming error. To this end,
//we now use the barrel angle neural network to calculate the optimal barrel angle:
double Inputs[3];
double dxDisplacement=-pWorld->ppPlayer[pWorld->nActivePlayer]->dxPosition+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dxPosition;
double dyDisplacement=-pWorld->ppPlayer[pWorld->nActivePlayer]->dyPosition+pWorld->ppPlayer[1-pWorld->nActivePlayer]->dyPosition;
//Set up the scaled inputs to the neural network (x-displacement, y-displacement, wind speed)
Inputs[0]=2.0*((dxDisplacement-pdMin[0])/(pdMax[0]-pdMin[0])-0.5);
Inputs[1]=2.0*((dyDisplacement-pdMin[1])/(pdMax[1]-pdMin[1])-0.5);
Inputs[2]=2.0*((pWorld->dWindSpeed-pdMin[2])/(pdMax[2]-pdMin[2])-0.5);
//Pass the inputs to the network and compute its output - the optimal barrel angle
double *pdOutputs=pMLP->pdGetOutputs(Inputs);
//Work out the angle that the player chose
pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination=asin(pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx/sqrt(pow(pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrelx,2.0)+pow(pWorld->ppPlayer[pWorld->nActivePlayer]->dBarrely,2.0)));
//Calculate the aiming error as the difference between the two. This error information will be written
//to a data file in the world class when the projectile is destroyed (i.e. lands, goes out of range, etc.)
pWorld->dAngularError=pWorld->ppPlayer[pWorld->nActivePlayer]->dInclination-pdOutputs[0];
//Clean up the barrel angle neural network's outputs
delete []pdOutputs;
}
//Redraw the game world with the tank's barrel in its new position
Invalidate(TRUE);
}
CView::OnLButtonUp(nFlags, point);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -