[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[crimson-list] Improve AI behavior



Hello,

Here is a little patch in order to improve the AI behavior. There is also a little level (minimal case) which explain the difference.

Before explaining the patch, let me explain how AI chose the target to attack (Jens correct me if I'm saying something wrong) :

The AI evaluate the target by applying some modifiers for each unit, and choose the unit with higher value. The modifier is calculated witch stategical indicators (the target can conquer buildings, the target is a transport…)
If two units have the same value, the first is kept.

Once the target has been chosen, the AI look for the best way to attack it. Here, the way to evaluate the best position require operationals indicators (support, wedging, backstab…).

After this small introduction, you can load the testmap (this is a small map this 3 units in each camp facing each other) and see how the AI play (be fair, let the AI start :)) :

As each unit have the same value, the AI select the first one and attack it. But the only way to attack this unit is to go just in front of it, between the two other. No problem, let's go to suicide, orders are orders ! The attack does not do much damages, and the counter-attack is deadly.

In my patch, the attack position is evaluated in the same time as the target estimation and the indicators are evaluated together. (I also added some other modifiers, but you can change them if you want). In this game exemple, the AI first attack a unit on the side, and concentrate its attack on the same unit. The central position, between the skipped in preference of safer position. At the end of turn 1, the AI has a better situation (see the screenshot after first turn, human is blue : the first move is in white, second red, and third cyan).

I would like you make some tests with this new behavior and tell me what do you think of that.

Jens : do you think that it would be easy to add some tests in the compilation process ? I think that that the AI is the more important piece in the code, and I could try to write some test in order to check regression in future evolution, but I'll need help to write the frame.

--
Sébastien

Index: src/cf/ai.h
===================================================================
RCS file: /home/cvspsrv/cvsroot/games/crimson/src/cf/ai.h,v
retrieving revision 1.7
diff -r1.7 ai.h
93a94
>   int EvaluateSupport( const Unit *attacker, const Point position, const Unit *target) const;
Index: src/cf/ai.cpp
===================================================================
RCS file: /home/cvspsrv/cvsroot/games/crimson/src/cf/ai.cpp,v
retrieving revision 1.13
diff -r1.13 ai.cpp
853a854
> 
865c866
<           val = MAX( 0, 10000 - UnitStrength( tg ) - cost * 50 );
---
>           val = MAX( 0, 10000 - cost * 50);
872a874,881
> 
>           if (cost <= 1) {
>             Point position = FindBestHex(u, tg);
>             int support = EvaluateSupport( u, position, tg );
>             val += support;
>           }
> 
> 
884a894
>  
912d921
<     Unit *sup;
914c923
<     short vals[6], bestval = _CF_BEST_HEX_INVALID;
---
>     short val, bestval = _CF_BEST_HEX_INVALID;
922,923c931,942
<         vals[i] = -Distance( u->Position(), nb[i] );
<       } else vals[i] = _CF_BEST_HEX_INVALID;
---
>         val = -Distance( u->Position(), nb[i] );
>       } else {
>         continue;
>       }
> 
>       int support = EvaluateSupport(u, nb[i], enemy);
>       val += support;
> 
>       if ( val > bestval ) {
>         bestval = val;
>         hex = nb[i];
>       }
924a944
>   }
926,928c946,948
<     for ( i = NORTH; i <= NORTHWEST; ++i ) {
<       if ( vals[i] != _CF_BEST_HEX_INVALID ) {
<         vals[i] += map->AttackMod( nb[i] );
---
>   return hex;
> }
> #undef _CF_BEST_HEX_INVALID
930,938d949
<         // check for support in the back of the enemy
<         int j = ReverseDir( (Direction)i );
<         if ( (nb[j].x != -1) && (nb[j].y != -1) ) {
<           sup = map->GetUnit( nb[j] );
<           if ( sup && (sup->Owner() == player) ) {
<             if ( sup->CouldHit( enemy ) ) vals[i] += 7;
<             else vals[i] += 2;
<           }
<         }
940,945c951,982
<         // check for enemy units supporting defence
<         Direction attdir = Hex2Dir( nb[i], enemy->Position() );
<         if ( !map->Dir2Hex( nb[i], TurnLeft(attdir), hlp ) &&
<            (sup = map->GetUnit( hlp )) && (sup->Owner() == player) ) vals[i] -= 8;
<         if ( !map->Dir2Hex( nb[i], TurnRight(attdir), hlp ) &&
<            (sup = map->GetUnit( hlp )) && (sup->Owner() == player) ) vals[i] -= 8;
---
> ////////////////////////////////////////////////////////////////////////
> // NAME       : AI::EvaluateSupport
> // DESCRIPTION: Determine the hex from which an attack can be expected
> //              to bear the best result.
> // PARAMETERS : attacker - attacking unit
> //              position - the estimated attacker positon
> //              enemy    - target unit
> // RETURNS    : A value represent a modifier wich can be used as 
> //              modifier for evaluating if the position give bonus or 
> //              malus to the attacker.
> ////////////////////////////////////////////////////////////////////////
> 
> int AI::EvaluateSupport(const Unit *attacker, const Point position, const Unit *enemy) const {
> 
>     Unit *sup;
>     Point neighbors[6], enemyNeighbors[6], hlp;
>     Point enemyPosition = enemy->Position();
> 
>     int val = map->AttackMod( enemyPosition );
> 
>     map->GetNeighbors( enemyPosition, enemyNeighbors );
>     map->GetNeighbors( position, neighbors );
> 
>     Direction attdir = Hex2Dir( enemyPosition, position );
> 
>     // check for support in the back of the enemy
>     int j = ReverseDir( static_cast<Direction> (enemy->Facing()) );
>     if ( (enemyNeighbors[j].x != -1) && (enemyNeighbors[j].y != -1) ) {
>       sup = map->GetUnit( enemyNeighbors[j] );
>       if ( sup && (sup->Owner() == enemy->Owner()) ) {
>         if ( sup->CouldHit( enemy ) ) val += 7;
>         else val += 2;
949,952c986,1001
<     for ( i = NORTH; i <= NORTHWEST; ++i ) {
<       if ( vals[i] > bestval ) {
<         bestval = vals[i];
<         hex = nb[i];
---
>     // check for enemy units supporting defence
>     if ( !map->Dir2Hex( enemyPosition, TurnLeft(attdir), hlp ) &&
>        (sup = map->GetUnit( hlp )) && (sup->Owner() == enemy->Owner()) ) {
>           val -= 8;
>     }
>     if ( !map->Dir2Hex( enemyPosition, TurnRight(attdir), hlp ) &&
>        (sup = map->GetUnit( hlp )) && (sup->Owner() == enemy->Owner()) ) {
>           val -= 8;
>     }
> 
>     // look for a counter-attack
>     for ( int i = NORTH; i <= NORTHWEST; ++i ) {
>       if ( (neighbors[i].x != -1) && (neighbors[i].y != -1) && 
>         (sup = map->GetUnit( neighbors[i] )) ) {
>         if (sup->Owner() != attacker->Owner()) val -= sup->OffensiveStrength( attacker );
> 
955d1003
<   }
957c1005,1018
<   return hex;
---
>     // now look for friend arount the target
>     for ( int i = NORTH; i <= NORTHWEST; ++i ) {
>       if ( (enemyNeighbors[i].x != -1) && (enemyNeighbors[i].y != -1) && 
>         (sup = map->GetUnit( enemyNeighbors[i] )) ) {
>         if (sup->Owner() == attacker->Owner()) {
>             val += 10;
>             val += sup->OffensiveStrength( enemy );
>         }
> 
>       }
>     }
> 
>     return val;
> 
959d1019
< #undef _CF_BEST_HEX_INVALID

Attachment: testmap.lev
Description: Binary data

Attachment: attack.png
Description: PNG image