1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

BattleAI: fix health bounty calculation

This commit is contained in:
Andrii Danylchenko 2023-10-22 21:37:43 +03:00
parent 870fbd50e3
commit f74daa2e1f
2 changed files with 39 additions and 10 deletions

View File

@ -114,6 +114,23 @@ float AttackPossibility::attackValue() const
return damageDiff();
}
float hpFunction(uint64_t unitHealthStart, uint64_t unitHealthEnd, uint64_t maxHealth)
{
float ratioStart = static_cast<float>(unitHealthStart) / maxHealth;
float ratioEnd = static_cast<float>(unitHealthEnd) / maxHealth;
float base = 0.666666f;
// reduce from max to 0 must be 1.
// 10 hp from end costs bit more than 10 hp from start because our goal is to kill unit, not just hurt it
// ********** 2 * base - ratioStart *********
// * *
// * height = ratioStart - ratioEnd *
// * *
// ******************** 2 * base - ratioEnd ******
// S = (a + b) * h / 2
return (base * (4 - ratioStart - ratioEnd)) * (ratioStart - ratioEnd) / 2 ;
}
/// <summary>
/// How enemy damage will be reduced by this attack
/// Half bounty for kill, half for making damage equal to enemy health
@ -127,6 +144,7 @@ float AttackPossibility::calculateDamageReduce(
std::shared_ptr<CBattleInfoCallback> state)
{
const float HEALTH_BOUNTY = 0.5;
const float KILL_BOUNTY = 0.5;
// FIXME: provide distance info for Jousting bonus
auto attackerUnitForMeasurement = attacker;
@ -157,9 +175,20 @@ float AttackPossibility::calculateDamageReduce(
auto enemyDamageBeforeAttack = damageCache.getOriginalDamage(defender, attackerUnitForMeasurement, state);
auto enemiesKilled = damageDealt / maxHealth + (damageDealt % maxHealth >= defender->getFirstHPleft() ? 1 : 0);
auto damagePerEnemy = enemyDamageBeforeAttack / (double)defender->getCount();
auto lastUnitKillValue = (damageDealt % maxHealth) / (double)maxHealth;;
auto exceedingDamage = (damageDealt % maxHealth);
float hpValue = (damageDealt / maxHealth);
if(defender->getFirstHPleft() >= exceedingDamage)
{
hpValue += hpFunction(defender->getFirstHPleft(), defender->getFirstHPleft() - exceedingDamage, maxHealth);
}
else
{
hpValue += hpFunction(defender->getFirstHPleft(), 0, maxHealth);
hpValue += hpFunction(maxHealth, maxHealth + defender->getFirstHPleft() - exceedingDamage, maxHealth);
}
return damagePerEnemy * (enemiesKilled + lastUnitKillValue * HEALTH_BOUNTY);
return damagePerEnemy * (enemiesKilled * KILL_BOUNTY + hpValue * HEALTH_BOUNTY);
}
int64_t AttackPossibility::evaluateBlockedShootersDmg(

View File

@ -268,7 +268,7 @@ EvaluationResult BattleExchangeEvaluator::findBestTarget(
{
float score = evaluateExchange(ap, 0, targets, damageCache, hb);
if(score > result.score || score == result.score && result.wait)
if(score > result.score || (score == result.score && result.wait))
{
result.score = score;
result.bestAttack = ap;
@ -558,7 +558,7 @@ BattleScore BattleExchangeEvaluator::calculateExchange(
vstd::removeDuplicates(melleeAttackers);
vstd::erase_if(melleeAttackers, [&](const battle::Unit * u) -> bool
{
return !cb->battleCanShoot(u);
return cb->battleCanShoot(u);
});
bool canUseAp = true;
@ -687,7 +687,10 @@ BattleScore BattleExchangeEvaluator::calculateExchange(
for(auto hex : hexes)
reachabilityMap[hex] = getOneTurnReachableUnits(turn, hex);
v.adjustPositions(melleeAttackers, ap, reachabilityMap);
if(!ap.attack.shooting)
{
v.adjustPositions(melleeAttackers, ap, reachabilityMap);
}
#if BATTLE_TRACE_LEVEL>=1
logAi->trace("Exchange score: enemy: %2f, our -%2f", v.getScore().enemyDamageReduce, v.getScore().ourDamageReduce);
@ -714,11 +717,8 @@ void BattleExchangeVariant::adjustPositions(
return attackerValue[u1->unitId()].value > attackerValue[u2->unitId()].value;
});
if(!ap.attack.shooting)
{
vstd::erase_if_present(hexes, ap.from);
vstd::erase_if_present(hexes, ap.attack.attacker->occupiedHex(ap.attack.attackerPos));
}
vstd::erase_if_present(hexes, ap.from);
vstd::erase_if_present(hexes, ap.attack.attacker->occupiedHex(ap.attack.attackerPos));
float notRealizedDamage = 0;