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

📄 tanksview.cpp

📁 游戏编程AI源码
💻 CPP
📖 第 1 页 / 共 2 页
字号:

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 + -