home *** CD-ROM | disk | FTP | other *** search
/ AI Game Programming Wisdom / AIGameProgrammingWisdom.iso / SourceCode / 11 Learning / 09 Laramée / Troll.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-08-22  |  10.0 KB  |  402 lines

  1. /*****************************************************************
  2.  * TROLL IMPLEMENTATION
  3.  ****************************************************************/
  4.  
  5. #include "Troll.h"
  6. #include "Chromosome.h"
  7. #include <iostream.h>
  8. #include <stdlib.h>
  9.  
  10.  
  11. /*****************************************************************
  12.  * THE MESSAGES
  13.  ****************************************************************/
  14.  
  15. // Take some damage, either from a fight or from incoming fire
  16. // from a Tower.  HitPoints stores the current health of the 
  17. // troll; StatsDamageTaken stores the cumulative damage taken
  18. // since the beginning of the simulation
  19. void Troll::SendDamageMessage( int ouch )
  20. {
  21.     HitPoints -= ouch;
  22.     StatsDamageTaken += ouch;
  23.  
  24.     if ( HitPoints <= 0 )
  25.         IsDead = true;
  26. }
  27.  
  28. // Capture message sent by traps
  29. void Troll::SendCaptureMessage()
  30. {
  31.     IsCaptured = true;
  32. }
  33.  
  34. // Fight message, sent by knights.  Returns true if the
  35. // troll wins the duel
  36. bool Troll::SendFightMessage()
  37. {
  38.     // Not a very elaborate fight system: we just roll the damage
  39.   // caused by the knight in the whole fight at once, randomly
  40.  
  41.     // Fighting while captive ain't a smart idea
  42.     if( IsCaptured )
  43.     {
  44.         SendDamageMessage( rand() % 15 + 5 );
  45.     }
  46.     else
  47.     {
  48.         SendDamageMessage( rand() % 10 );
  49.     }
  50.  
  51.     if( IsDead )
  52.     {
  53.       // Troll is killed
  54.         return false;
  55.     }
  56.     else
  57.     {
  58.         // Knight is killed
  59.         StatsKnightsKilled++;
  60.         return true;
  61.     }
  62. }
  63.  
  64.  
  65. /*****************************************************************
  66.  * TURN UPDATE
  67.  ****************************************************************/
  68.  
  69. bool Troll::Update()
  70. {
  71.     // We should never get here with a dead troll, but just in case...
  72.     if( IsDead )
  73.         return true;
  74.  
  75.   // If the troll hasn't eaten in a while, it sustains damage
  76.     if( StatsTimeSinceLastMeal++ > 25 )
  77.         SendDamageMessage( 1 );
  78.     
  79.     StatsTimeAlive++;
  80.  
  81.     // A captured troll will attempt to escape.  If he fails, he 
  82.     // can't do a thing.
  83.     if( IsCaptured && ( rand() % 10 < 7 ) )
  84.     {
  85.         StatsTimeCaptive++;
  86.         return true;
  87.     }
  88.  
  89.     IsCaptured = false;
  90.  
  91.     // Which goal do we follow?
  92.     switch( PickStrategy() )
  93.     {
  94.         case EATING_PRIORITY: ImplementEatingStrategy(); break;
  95.         case KILLING_PRIORITY: ImplementKillingStrategy(); break;
  96.         case EXPLORING_PRIORITY: ImplementExploringStrategy(); break;
  97.         case FLEEING_PRIORITY: ImplementFleeingStrategy(); break;
  98.         case HEALING_PRIORITY: ImplementHealingStrategy(); break;
  99.         default: break;
  100.     }
  101.  
  102.     return true;
  103. }
  104.  
  105.  
  106. /****************************************************************
  107.  * STRATEGY SELECTION
  108.  ***************************************************************/
  109.  
  110. int Troll::PickStrategy()
  111. {
  112.     // Look at the state of the world around the troll and pick a
  113.   // strategy that makes sense.  The biases encoded in the troll's
  114.     // DNA play a crucial role here
  115.  
  116.     double bestSoFar = DNA->Priorities[ EATING_PRIORITY ] * EatingStrategy();
  117.     int choice = EATING_PRIORITY;
  118.  
  119.     double newCandidate = DNA->Priorities[ KILLING_PRIORITY ] * KillingStrategy();
  120.  
  121.     if ( newCandidate > bestSoFar )
  122.     {
  123.         bestSoFar = newCandidate;
  124.         choice = KILLING_PRIORITY;
  125.     }
  126.  
  127.     newCandidate = DNA->Priorities[ HEALING_PRIORITY ] * HealingStrategy();
  128.     if ( newCandidate > bestSoFar )
  129.     {
  130.         bestSoFar = newCandidate;
  131.         choice = HEALING_PRIORITY;
  132.     }
  133.  
  134.     newCandidate = DNA->Priorities[ FLEEING_PRIORITY ] * FleeingStrategy();
  135.     if ( newCandidate > bestSoFar )
  136.     {
  137.         bestSoFar = newCandidate;
  138.         choice = FLEEING_PRIORITY;
  139.     }
  140.  
  141.     newCandidate = DNA->Priorities[ EXPLORING_PRIORITY ] * ExploringStrategy();
  142.     if ( newCandidate > bestSoFar )
  143.     {
  144.         bestSoFar = newCandidate;        // Unnecessary, but kept there in case I add other goals later
  145.         choice = EXPLORING_PRIORITY;
  146.     }
  147.  
  148.     return choice;
  149. }
  150.  
  151.  
  152. // Trolls like to kill isolated adventurers, but stay away from
  153. // packs of them.  Plus, a fight sounds a lot better when healthy!
  154. double Troll::KillingStrategy()
  155. {
  156.     double score;
  157.     int knights = ptrGrid->HowManyCloseToTroll( ENTITY_KNIGHT, 6 );
  158.     if ( knights == 0 )
  159.         score = 0.0;
  160.     else if ( knights <= 2 )
  161.         score = 0.8;
  162.     else if ( knights <= 4 )
  163.         score = 0.4;
  164.     else
  165.         score = 0.25;
  166.  
  167.     score -= 0.02 * ( FULL_HEALTH - HitPoints );
  168.     return score;
  169. }
  170.  
  171.  
  172. // Eating is more interesting if there are sheep nearby and if
  173. // the troll is hungry
  174. double Troll::EatingStrategy()
  175. {
  176.     double score = 0.02 * StatsTimeSinceLastMeal;
  177.     score += 0.1 * ptrGrid->HowManyCloseToTroll( ENTITY_SHEEP, 5 );
  178.     return score;
  179. }
  180.  
  181.  
  182. // Exploring is always a decent option, except when heavily wounded
  183. double Troll::ExploringStrategy()
  184. {
  185.     return 0.5 - 0.01 * ( FULL_HEALTH - HitPoints );
  186. }
  187.  
  188.  
  189. // If there are too many enemies around, and/or the troll is
  190. // wounded, he should get away if he can.  Especially if there is
  191. // a safe haven nearby!
  192. double Troll::FleeingStrategy()
  193. {
  194.     double score;
  195.  
  196.     if ( HitPoints > REASONABLY_HEALTHY )
  197.         score = 0.0;
  198.     else if ( HitPoints > HEAVILY_DAMAGED )
  199.         score = 0.3;
  200.     else if ( HitPoints > CRITICALLY_DAMAGED )
  201.         score = 0.6;
  202.  
  203.     score += 0.1 * ptrGrid->HowManyCloseToTroll( ENTITY_KNIGHT, 4 );
  204.     score += 0.05 * ptrGrid->HowManyCloseToTroll( ENTITY_TOWER, 3 );
  205.     if( ptrGrid->HowManyCloseToTroll( ENTITY_HAVEN, 3 ) > 0 )
  206.         score += 0.2;
  207.  
  208.     return 0.2;
  209. }
  210.  
  211.  
  212. // Is it time to just do nothing and heal?  Well, if we are within
  213. // range of a firing tower, we're wasting our time because the
  214. // damage will just keep piling on.  And if we're very close to a
  215. // haven without actually being in it, we probably should go there
  216. // first because we'll heal faster.
  217. double Troll::HealingStrategy()
  218. {
  219.     double score = 0.02 * ( FULL_HEALTH - HitPoints );
  220.     if( ptrGrid->HowManyCloseToTroll( ENTITY_TOWER, 3 ) > 0 )
  221.         score -= 0.2;
  222.     if( ptrGrid->HowManyCloseToTroll( ENTITY_KNIGHT, 3 ) > 0 )
  223.         score -= 0.1;
  224.     if( ptrGrid->HowManyCloseToTroll( ENTITY_HAVEN, 2 ) > 0 )
  225.         score -= 0.1;
  226.     if( ptrGrid->HowManyCloseToTroll( ENTITY_HAVEN, 0 ) > 0 )
  227.         score += 0.5;
  228.  
  229.     return score;
  230. }
  231.  
  232.  
  233. /*****************************************************************
  234.  * STRATEGY IMPLEMENTATIONS
  235.  * These methods are pretty dumb, but we're talking about a troll 
  236.  * here, not the Dalai Lama.  Plus, this sample code is cluttered
  237.  * enough as it is; no need to make it even worse! ;-)
  238.  ****************************************************************/
  239.  
  240. void Troll::ImplementEatingStrategy()
  241. {
  242.     // Look for a sheep nearby
  243.     int target = ptrGrid->FindClosestFromTroll( ENTITY_SHEEP );
  244.     if ( target == -1 )
  245.     {
  246.         // No more sheep on the grid; wander instead
  247.         ImplementExploringStrategy();    
  248.     }
  249.  
  250.     // Can we reach it at once?  If so, let's do it
  251.     if ( ptrGrid->DistanceFromTroll( target ) <= 4 )
  252.     {
  253.       MoveEntity( ptrGrid->GetEntityX( target ) - GetX(), ptrGrid->GetEntityY( target ) - GetY() );
  254.         StatsSheepEaten += ptrGrid->HowManyCloseToTroll( ENTITY_SHEEP, 0 );
  255.     }
  256.  
  257.     // Otherwise, march towards the sheep in question
  258.     else
  259.     {
  260.         int dx, dy;
  261.         if( ptrGrid->GetEntityX( target ) > GetX() )
  262.             dx = 2;
  263.         else if ( ptrGrid->GetEntityX( target ) == GetX() )
  264.             dx = 0;
  265.         else
  266.             dx = -2;
  267.         if( ptrGrid->GetEntityY( target ) > GetY() )
  268.             dy = 2;
  269.         else if ( ptrGrid->GetEntityY( target ) == GetY() )
  270.             dy = 0;
  271.         else
  272.             dy = -2;
  273.         MoveEntity( dx, dy );
  274.     }
  275. }
  276.  
  277.  
  278. void Troll::ImplementKillingStrategy()
  279. {
  280.     // Look for a suitable target
  281.     int target = ptrGrid->FindClosestFromTroll( ENTITY_KNIGHT );
  282.     if ( target == -1 )
  283.     {
  284.         ImplementExploringStrategy();    
  285.     }
  286.  
  287.     // Can we attack it immediately?  If so, let's move into its square
  288.     if ( ptrGrid->DistanceFromTroll( target ) <= 4 )
  289.     {
  290.         // the knight will initiate combat next turn, so we don't have to.
  291.       MoveEntity( ptrGrid->GetEntityX( target ) - GetX(), ptrGrid->GetEntityY( target ) - GetY() );
  292.     }
  293.  
  294.     // Otherwise, march towards the target
  295.     else
  296.     {
  297.         int dx, dy;
  298.         if( ptrGrid->GetEntityX( target ) > GetX() )
  299.             dx = 2;
  300.         else if ( ptrGrid->GetEntityX( target ) == GetX() )
  301.             dx = 0;
  302.         else
  303.             dx = -2;
  304.         if( ptrGrid->GetEntityY( target ) > GetY() )
  305.             dy = 2;
  306.         else if ( ptrGrid->GetEntityY( target ) == GetY() )
  307.             dy = 0;
  308.         else
  309.             dy = -2;
  310.         MoveEntity( dx, dy );
  311.     }
  312. }
  313.  
  314.  
  315. void Troll::ImplementHealingStrategy()
  316. {
  317.     // Doing nothing restores some health
  318.     HitPoints += ( rand() % 3 );
  319.  
  320.     // Safe havens are filled with decaying flesh, carnivorous
  321.     // plants, venomous mushrooms and other delicacies that speed
  322.     // up troll healing.
  323.     if( ptrGrid->HowManyCloseToTroll( ENTITY_HAVEN, 0 ) > 0 )
  324.     {
  325.         HitPoints += 2;
  326.     }
  327.     
  328.     // Can't get healthier than perfect health
  329.     if( HitPoints > FULL_HEALTH )
  330.         HitPoints = FULL_HEALTH;
  331. }
  332.  
  333.  
  334. void Troll::ImplementFleeingStrategy()
  335. {
  336.     // Can we retreat to a safe haven?  If so, do it
  337.     int haven = ptrGrid->FindClosestFromTroll( ENTITY_HAVEN );
  338.     if( ptrGrid->DistanceFromTroll( haven ) <= 4 )
  339.     {
  340.       MoveEntity( ptrGrid->GetEntityX( haven ) - GetX(), ptrGrid->GetEntityY( haven ) - GetY() );        
  341.     }
  342.  
  343.     // Otherwise, run away from the closest knight
  344.     else
  345.     {
  346.         int target = ptrGrid->FindClosestFromTroll( ENTITY_KNIGHT );
  347.         int dx, dy;
  348.  
  349.         if( ptrGrid->GetEntityX( target ) > GetX() )
  350.             dx = -2;
  351.         else
  352.             dx = 2;
  353.         if( ptrGrid->GetEntityY( target ) > GetY() )
  354.             dy = -2;
  355.         else
  356.             dy = 2;
  357.  
  358.         MoveEntity( dx, dy );
  359.     }
  360. }
  361.  
  362.  
  363. // The troll's exploring "strategy" consists of 
  364. // wandering about aimlessly.
  365. void Troll::ImplementExploringStrategy()
  366. {
  367.     MoveEntity( rand() % 5 - 2, rand() % 5 - 2 );
  368. }
  369.  
  370.  
  371. /*****************************************************************
  372.  * SIMULATION
  373.  *****************************************************************/
  374.  
  375. void Troll::Reset()
  376. {
  377.     HitPoints = FULL_HEALTH;
  378.     IsCaptured = false;
  379.     IsDead = false;
  380.     StatsTimeSinceLastMeal = 0;
  381.     StatsKnightsKilled = 0;
  382.     StatsSheepEaten = 0;
  383.     StatsDamageTaken = 0;
  384.     StatsTimeAlive = 0;
  385.     StatsTimeCaptive = 0;
  386. }
  387.  
  388.  
  389. double Troll::GetEvaluation()
  390. {
  391.     double score = 8.0 * StatsKnightsKilled +     10.0 * StatsSheepEaten +    1.5 * StatsTimeAlive -
  392.                     1.0 * StatsTimeCaptive - 2.50 * StatsDamageTaken;
  393.     return score;
  394. }
  395.  
  396.  
  397.  
  398.  
  399. void Troll::Dump()
  400. {
  401.     cout << "TROLL DUMPED at x,y = " << GetX() << "," << GetY() << endl;
  402. }