Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the penalty calculation for enemy units that can evade the engagement during battle #9229

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 28 additions & 15 deletions src/fheroes2/ai/ai_battle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,13 +483,6 @@ namespace
&& !commander->Modes( Heroes::SPELLCASTED ) && !arena.isSpellcastDisabled();
}

bool isUnitFaster( const Battle::Unit & currentUnit, const Battle::Unit & target )
{
if ( currentUnit.isFlying() == target.isFlying() )
return currentUnit.GetSpeed() > target.GetSpeed();
return currentUnit.isFlying();
}

double commanderMaximumSpellDamageValue( const HeroBase & commander )
{
double bestValue = 0;
Expand Down Expand Up @@ -1536,10 +1529,6 @@ AI::BattleTargetPair AI::BattlePlanner::meleeUnitOffense( Battle::Arena & arena,
const Castle * castle = Battle::Arena::GetCastle();
const bool isMoatBuilt = castle && castle->isBuild( BUILD_MOAT );

// The usual distance between units of different armies at the beginning of a battle is 10-14 tiles. For each tile passed, 20% of the total army value will be
// lost to make sure that the difference of 4 tiles matters.
const double attackDistanceModifier = _enemyArmyStrength / 5.0;

double maxPriority = std::numeric_limits<double>::lowest();

for ( const Battle::Unit * enemy : enemies ) {
Expand All @@ -1551,10 +1540,34 @@ AI::BattleTargetPair AI::BattlePlanner::meleeUnitOffense( Battle::Arena & arena,
}

// Do not pursue faster units that can move away and avoid an engagement
const uint32_t dist
= ( !enemy->isArchers() && isUnitFaster( *enemy, currentUnit ) ? nearestCellInfo.dist + Battle::Board::widthInCells + Battle::Board::heightInCells
: nearestCellInfo.dist );
const double unitPriority = enemy->evaluateThreatForUnit( currentUnit ) - dist * attackDistanceModifier;
const uint32_t dist = [&currentUnit, enemy, baseDist = nearestCellInfo.dist]() {
// If the current unit was flying, this enemy unit would have already been attacked by it
assert( !currentUnit.isFlying() );

// Penalty does not apply to archers because they always pose a threat
if ( enemy->isArchers() ) {
return baseDist;
}

const uint32_t enemySpeed = enemy->GetSpeed( false, true );
// If the enemy unit is immovable, it will not be able to evade an engagement
if ( enemySpeed == Speed::STANDING ) {
return baseDist;
}

const uint32_t penaltyDist = baseDist + Battle::Board::widthInCells + Battle::Board::heightInCells;

// Flying unit, even inferior in speed, can move around the entire battlefield and easily evade an engagement
if ( enemy->isFlying() ) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a flying unit is slowed, it can't fly over the whole battlefield. Does it make sense to take this into account?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @nogov you definitely should take a closer look at the implementation of this method.

return penaltyDist;
}

return currentUnit.GetSpeed( false, true ) > enemySpeed ? baseDist : penaltyDist;
}();

// The usual distance between units of different armies at the beginning of a battle is 10-14 tiles. For each
// tile passed, 20% of the total threat value will be lost to make sure that the difference of 5 tiles matters.
const double unitPriority = enemy->evaluateThreatForUnit( currentUnit ) * ( 1.0 - dist * 0.20 );

if ( unitPriority < maxPriority ) {
continue;
Expand Down
2 changes: 1 addition & 1 deletion src/fheroes2/battle/battle_troop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ uint32_t Battle::Unit::GetSpeed( const bool skipStandingCheck, const bool skipMo
if ( Modes( SP_HASTE ) ) {
return Speed::GetHasteSpeedFromSpell( speed );
}
else if ( Modes( SP_SLOW ) ) {
if ( Modes( SP_SLOW ) ) {
return Speed::GetSlowSpeedFromSpell( speed );
}

Expand Down
Loading