Browse Source

BattleAI: fix health bounty calculation

Andrii Danylchenko 2 years ago
parent
commit
f74daa2e1f
2 changed files with 39 additions and 10 deletions
  1. 31 2
      AI/BattleAI/AttackPossibility.cpp
  2. 8 8
      AI/BattleAI/BattleExchangeVariant.cpp

+ 31 - 2
AI/BattleAI/AttackPossibility.cpp

@@ -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(

+ 8 - 8
AI/BattleAI/BattleExchangeVariant.cpp

@@ -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;