home *** CD-ROM | disk | FTP | other *** search
/ AI Game Programming Wisdom / AIGameProgrammingWisdom.iso / SourceCode / 11 Learning / 08 Manslow / CWorld.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-01  |  11.5 KB  |  431 lines

  1. //Tanks
  2. //Copyright John Manslow
  3. //29/09/2001
  4.  
  5. #include "stdafx.h"
  6. #include "CWorld.h"
  7. #include "CTank.h"
  8. #include "CProjectile.h"
  9. #include "assert.h"
  10. #include "math.h"
  11. #include "fstream.h"
  12.  
  13. //if not compiling under Windows, use #define BOOL bool, #define TRUE true, #define FALSE false, etc.
  14. extern BOOL boGeneratingErrorTrainingData;
  15.  
  16. //Where example aiming error data will be saved to create the exemplar set with which the aiming error
  17. //distribution models will be trained 
  18. #define FileForExemplarData "NewExemplarData.txt"
  19.  
  20. CWorld::CWorld(const unsigned long culNewTerrainResolution)
  21. {
  22.     TRACE("\tConstructing world...\n");
  23.  
  24.     //If we're going to generate training data, open a file stream. If not, make sure the
  25.     //pointer to the non-existent file stream is NULL so we don't try to delete it in the 
  26.     //destructor
  27.     pHitData=NULL;
  28.     if(boGeneratingErrorTrainingData)
  29.     {
  30.         pHitData=new ofstream(FileForExemplarData);
  31.         pHitData->precision(15);
  32.     }
  33.  
  34.     //Record the resolution of the terrain and allocate memory for the terrain height array.
  35.     ulTerrainResolution=culNewTerrainResolution;
  36.     plHeight=new long[ulTerrainResolution];
  37.     if(!plHeight)
  38.     {
  39.         TRACE("failed.\n");
  40.         assert(FALSE);
  41.     }
  42.  
  43.     //Declare the array of tanks objects. Only two players and one tank per player.
  44.     ppPlayer=new CTank*[2];
  45.     if(!ppPlayer)
  46.     {
  47.         TRACE("failed.\n");
  48.         assert(FALSE);
  49.     }
  50.     ppPlayer[0]=new CTank();
  51.     if(!ppPlayer[0])
  52.     {
  53.         TRACE("failed.\n");
  54.         assert(FALSE);
  55.     }
  56.     ppPlayer[1]=new CTank();
  57.     if(!ppPlayer[1])
  58.     {
  59.         TRACE("failed.\n");
  60.         assert(FALSE);
  61.     }
  62.     //Initialise the tanks' barrels to point horizontally in the direction of thge enemy tank.
  63.     ppPlayer[0]->dBarrelx=1.0;
  64.     ppPlayer[1]->dBarrelx=-1.0;
  65.  
  66.     //Set the projectile pointer to NULL to indicate that there is currently no projectile in
  67.     //the world. Note that only one projectile is allowed at any one time.
  68.     pProjectile=NULL;
  69.  
  70.     //Set each player's score to zero
  71.     dScore[0]=0.0;
  72.     dScore[1]=0.0;
  73.  
  74.     //Create the terrain, place the tanks on it, decide who goes first, pick a random wind 
  75.     //speed, etc. etc.
  76.     Initialise();
  77.  
  78.     //If we get this far, we're doing well!
  79.     TRACE("\tsuccessful.\n");
  80. }
  81.  
  82. void CWorld::Initialise(void)
  83. {
  84.     //Indicate that no shots have yet been made in the current game
  85.     ulShotNumber=0;
  86.  
  87.     //Pick a random wind speed. For simplicity, wind speed is fixed for each shot.
  88.     dWindSpeed=2.0*(double(rand())/double(RAND_MAX)-0.5);
  89.  
  90.     //If we're generating training data, the active player (the one that has the
  91.     //controls is always player 1 - the AI.
  92.     if(boGeneratingErrorTrainingData)
  93.     {
  94.         nActivePlayer=0;
  95.     }
  96.     else
  97.     {
  98.         //Otherwise, choose whether the AI or human player goes first
  99.         nActivePlayer=rand()%2;
  100.     }
  101.  
  102.     //Generate some terrain for the world.
  103.     GenerateTerrain();
  104.  
  105.     //The follwoing code searches the terrain for relatively flat areas to place
  106.     //each tank. This improves the appearance of the game since the tank graphics 
  107.     //aren't adjusted if a tank appears on a steep slope.
  108.     long i,j;
  109.     double dDeviation;
  110.     double dMeanHeightInInterval;
  111.     double dMinimumDeviation=1e+99;
  112.     double dMeanHeightAtMinimumDeviation;
  113.     unsigned long ulPositionOfMinimumDeviation=0;
  114.  
  115.     ppPlayer[0]->dyPosition=300.0;
  116.  
  117.     //Find the flattest area towards the left part of the landscape where the
  118.     //player's tank will reside
  119.     for(i=20;i<250;i++)
  120.     {
  121.         //The flat regions are identified by measuring the variance of the landscape
  122.         //height over stretches of 20 elements in the landscape height array. Low
  123.         //variance implies flat lanscape.
  124.         dMeanHeightInInterval=0.0;
  125.  
  126.         //First, calculate the mean height of the landscape in the interval 
  127.         //currently under consideration.
  128.         for(j=-10;j<10;j++)
  129.         {
  130.             dMeanHeightInInterval+=double(plHeight[i+j]);
  131.         }
  132.         dMeanHeightInInterval/=20.0;
  133.  
  134.         //Then measure the variance of the landscape's height.
  135.         dDeviation=0.0;
  136.         for(j=-10;j<10;j++)
  137.         {
  138.             dDeviation+=(dMeanHeightInInterval-plHeight[i+j])*(dMeanHeightInInterval-plHeight[i+j]);
  139.         }
  140.  
  141.         //Test to see if this interval is the flattest so far. If so, record its
  142.         //location for future reference
  143.         if(dDeviation<dMinimumDeviation)
  144.         {
  145.             dMinimumDeviation=dDeviation;
  146.             ulPositionOfMinimumDeviation=i;
  147.             dMeanHeightAtMinimumDeviation=dMeanHeightInInterval;
  148.         }
  149.     }
  150.  
  151.     //Put the tank in the flattest area
  152.     ppPlayer[0]->dxPosition=ulPositionOfMinimumDeviation;
  153.     ppPlayer[0]->dyPosition=long(dMeanHeightAtMinimumDeviation);
  154.  
  155.     //Repeat the procedure for the AI tank.
  156.     dMinimumDeviation=1e+99;
  157.  
  158.     //Find the flattest area towards the rightmost part of the landscape where 
  159.     //the AI's tank will reside.
  160.     for(i=350;i<600;i++)
  161.     {
  162.         dMeanHeightInInterval=0.0;
  163.         for(j=-10;j<10;j++)
  164.         {
  165.             dMeanHeightInInterval+=double(plHeight[i+j]);
  166.         }
  167.         dMeanHeightInInterval/=20.0;
  168.  
  169.         dDeviation=0.0;
  170.         for(j=-10;j<10;j++)
  171.         {
  172.             dDeviation+=(dMeanHeightInInterval-plHeight[i+j])*(dMeanHeightInInterval-plHeight[i+j]);
  173.         }
  174.  
  175.         if(dDeviation<dMinimumDeviation)
  176.         {
  177.             dMinimumDeviation=dDeviation;
  178.             ulPositionOfMinimumDeviation=i;
  179.             dMeanHeightAtMinimumDeviation=dMeanHeightInInterval;
  180.         }
  181.     }
  182.  
  183.     //Put the AI tank on the landscape
  184.     ppPlayer[1]->dxPosition=ulPositionOfMinimumDeviation;
  185.     ppPlayer[1]->dyPosition=long(dMeanHeightAtMinimumDeviation);
  186. }
  187.  
  188. CWorld::~CWorld()
  189. {
  190.     //Destructor - deallocate memory.
  191.     TRACE("\tDestroying world...\n");
  192.  
  193.     //The height data should always exist at this time but check anyway
  194.     if(plHeight!=NULL)
  195.     {
  196.         delete []plHeight;
  197.     }
  198.  
  199.     //Must check this because the data file can be closed and deleted at any time
  200.     //by the user.
  201.     if(pHitData!=NULL)
  202.     {
  203.         pHitData->close();
  204.         delete pHitData;
  205.     }
  206.  
  207.     //If there's a projectile in the world, delete it.
  208.     if(pProjectile!=NULL)
  209.     {
  210.         delete pProjectile;
  211.     }
  212.     
  213.     //Delete the players' tanks
  214.     delete ppPlayer[0];
  215.     delete ppPlayer[1];
  216.     delete []ppPlayer;
  217.  
  218.     TRACE("\tsuccessful.\n");
  219. }
  220.  
  221. void CWorld::GenerateTerrain(void)
  222. {
  223.     unsigned long i,p;
  224.     //Variables to record the heights of the lowest and highest points on the
  225.     //terrain
  226.     long lLowest,lHighest;
  227.  
  228.     //Choose the height of the leftmost point on the terrain. The exact value 
  229.     //doesn't really matter because we'll normalise the terrain later.
  230.     plHeight[0]=150+rand()%100;
  231.  
  232.     //Since we only have one point on the terrain, its height must be the
  233.     //lowest and highest so far
  234.     lLowest=plHeight[0];
  235.     lHighest=plHeight[0];
  236.  
  237.     //p is a variable used to control the probability that the next element in 
  238.     //the terrain array will be higher or lower than the current one. This approach
  239.     //produces landscapes that systematically wander up and down on a large scale
  240.     //but also vary randomly at a smal scale.
  241.     p=rand()%5;
  242.  
  243.     //Fill the rest of the terrain array with height values.
  244.     for(i=1;i<ulTerrainResolution;i++)
  245.     {
  246.         //Occasionally, choose a new value for p to change the up/down trend
  247.         //of the terrain
  248.         if(rand()%50==0)
  249.         {
  250.             p=rand()%5;
  251.         }
  252.  
  253.         //Use p to decide randomly whether this element is higher or lower than
  254.         //the one to its left
  255.         if(unsigned long(rand()%7)>p+1)
  256.         {
  257.             plHeight[i]=plHeight[i-1]-1;
  258.         }
  259.         else
  260.         {
  261.             plHeight[i]=plHeight[i-1]+1;
  262.         }
  263.  
  264.         //Maintain a record of the highest and lowest points
  265.         if(plHeight[i]<lLowest)
  266.         {
  267.             lLowest=plHeight[i];
  268.         }
  269.         if(plHeight[i]>lHighest)
  270.         {
  271.             lHighest=plHeight[i];
  272.         }
  273.     }
  274.  
  275.     //This is necessary to ensure that the scaling of the terrain doesn't produce
  276.     //really jaggedy landscapes
  277.     if(lHighest-lLowest<100)
  278.     {
  279.         lHighest=100+lLowest;
  280.     }
  281.  
  282.     //Scale the terrain to show a minimum amount of variation.
  283.     for(i=0;i<ulTerrainResolution;i++)
  284.     {
  285.         plHeight[i]=long(double((plHeight[i]-lLowest)/double(lHighest-lLowest))*100.0+100.0);
  286.     }
  287. }
  288.  
  289. BOOL CWorld::TimeStep(void)
  290. {
  291.     //First, check to see if a projectile exists in the world
  292.     if(pProjectile)
  293.     {
  294.         //Check to see if the projectile has passed beyond the edge of the window and hence needs to
  295.         //be removed
  296.         if(    long(pProjectile->dxPosition)>750 
  297.             || long(pProjectile->dxPosition)<0)
  298.         {
  299.             if(boGeneratingErrorTrainingData)
  300.             {
  301.                 //Save aiming error data
  302.                 *pHitData<<ulShotNumber;
  303.                 *pHitData<<"\t";
  304.                 *pHitData<<dAngularError;
  305.                 *pHitData<<"\n";
  306.             }
  307.             delete pProjectile;
  308.             pProjectile=NULL;
  309.  
  310.             dWindSpeed=2.0*(double(rand())/double(RAND_MAX)-0.5);
  311.             if(!boGeneratingErrorTrainingData)
  312.             {
  313.                 //If we're not generating training data, its the other player's
  314.                 //turn
  315.                 nActivePlayer=1-nActivePlayer;
  316.  
  317.             }
  318.             //Return true to indicate that an event occured in this time step.
  319.             return TRUE;
  320.         }
  321.  
  322.         //See if the projectile collided with the enemy tank in this time step
  323.         if(ppPlayer[1-nActivePlayer]->nTestForProjectileTankCollision(pProjectile))
  324.         {
  325.             //If so and we're generating training data,
  326.             if(boGeneratingErrorTrainingData)
  327.             {
  328.                 //Save aiming error information
  329.                 *pHitData<<ulShotNumber;
  330.                 *pHitData<<"\t";
  331.                 *pHitData<<dAngularError;
  332.                 *pHitData<<"\n";
  333.             }
  334.             //Delete the projectile
  335.             delete pProjectile;
  336.             pProjectile=NULL;
  337.  
  338.             //Choose a new wind speed
  339.             dWindSpeed=2.0*(double(rand())/double(RAND_MAX)-0.5);
  340.  
  341.             //Update the players' scores
  342.             dScore[nActivePlayer]++;
  343.  
  344.             //Reinitialise the world (generate new terrain, etc.) for another
  345.             //game.
  346.             Initialise();
  347.  
  348.             //Return true to indicate that an event occured and the world should
  349.             //be redrawn.
  350.             return TRUE;
  351.         }
  352.  
  353.         //Finally, test to see if the projectile has hit the ground.
  354.         //Calculate the x and y movement of the projectile in this time step
  355.         double dx=pProjectile->dxPosition-pProjectile->dPreviousxPosition;
  356.         double dy=pProjectile->dyPosition-pProjectile->dPreviousyPosition;
  357.  
  358.         //Get the projectile's old position
  359.         double y=pProjectile->dPreviousyPosition;
  360.         double x=pProjectile->dPreviousxPosition;
  361.  
  362.         //Make sure we don't divide by zero later on!
  363.         if(long(dx)==0)
  364.         {
  365.             dx=1;
  366.         }
  367.  
  368.         unsigned long i;
  369.         long j;
  370.  
  371.         //For every height array element the projectile has moved in the last 
  372.         //time step
  373.         for(i=0;i<unsigned long(labs(long((dx))));i++)
  374.         {
  375.             //If the height of the projectile is less than the height of the 
  376.             //terrain element, the projectile has collided with the terrain
  377.             if(y<plHeight[long(x)])
  378.             {
  379.                 if(boGeneratingErrorTrainingData)
  380.                 {
  381.                     //Save aiming error data
  382.                     *pHitData<<ulShotNumber;
  383.                     *pHitData<<"\t";
  384.                     *pHitData<<dAngularError;
  385.                     *pHitData<<"\n";
  386.                 }
  387.                 //Delete the projectile...
  388.                 delete pProjectile;
  389.                 pProjectile=NULL;
  390.  
  391.                 //Pick a new wind speed
  392.                 dWindSpeed=2.0*(double(rand())/double(RAND_MAX)-0.5);
  393.  
  394.                 //...and deform the terrain to make a small crater.
  395.                 for(j=-10;j<11;j++)
  396.                 {
  397.                     double dCraterDepth=4+rand()%2;
  398.                     if(x+j>0 && x+j<700)
  399.                     {
  400.                         plHeight[long(x)+j]-=long(dCraterDepth*exp(-0.02*pow(j,2.0))+rand()%2);
  401.                     }
  402.                 }
  403.  
  404.                 //If we're not generating training data,
  405.                 dWindSpeed=2.0*(double(rand())/double(RAND_MAX)-0.5);
  406.                 if(!boGeneratingErrorTrainingData)
  407.                 {
  408.                     //Pick a new wind speed and switch to the other player
  409.                     nActivePlayer=1-nActivePlayer;
  410.                 }
  411.     
  412.                 //Return true to indicate that an event has occurred and the
  413.                 //display should be redrawn.
  414.                 return TRUE;
  415.             }
  416.  
  417.             //Move to the next terrain array element to test for a collision
  418.             x+=long(dx)/labs(long(dx));
  419.  
  420.             //Compute the projectile's height aboe that element.
  421.             y+=dy/fabs(dx);
  422.         }
  423.  
  424.         //Tell the projectile that a time step has occurred.
  425.         pProjectile->TimeStep(dWindSpeed);
  426.     }
  427.  
  428.     //If we get this far, nothing significant has happened in this time step.
  429.     //Return false to indicate this.
  430.     return FALSE;
  431. }