Browse Source

Chase & FFA-changes

The AI should no longer chase enemy heroes that are not reachable in the same turn, when there's other options as this behavior was quite exploitable.

The AI should now take their overall strength into account when deciding whether to attack or not.
Previously it would attack as long as their assumed army-loss was at most 25%.
Now that is 50% times the ratio of their power compared to the total power of everyone.
Xilmi 1 year ago
parent
commit
90d72a4458

+ 22 - 3
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -64,6 +64,7 @@ EvaluationContext::EvaluationContext(const Nullkiller* ai)
 	isTradeBuilding(false),
 	isTradeBuilding(false),
 	isExchange(false),
 	isExchange(false),
 	isArmyUpgrade(false),
 	isArmyUpgrade(false),
+	isHero(false),
 	explorePriority(0)
 	explorePriority(0)
 {
 {
 }
 }
@@ -1037,6 +1038,8 @@ public:
 			evaluationContext.skillReward += evaluationContext.evaluator.getSkillReward(target, hero, heroRole);
 			evaluationContext.skillReward += evaluationContext.evaluator.getSkillReward(target, hero, heroRole);
 			evaluationContext.addNonCriticalStrategicalValue(evaluationContext.evaluator.getStrategicalValue(target));
 			evaluationContext.addNonCriticalStrategicalValue(evaluationContext.evaluator.getStrategicalValue(target));
 			evaluationContext.conquestValue += evaluationContext.evaluator.getConquestValue(target);
 			evaluationContext.conquestValue += evaluationContext.evaluator.getConquestValue(target);
+			if (target->ID == Obj::HERO)
+				evaluationContext.isHero = true;
 			evaluationContext.goldCost += evaluationContext.evaluator.getGoldCost(target, hero, army);
 			evaluationContext.goldCost += evaluationContext.evaluator.getGoldCost(target, hero, army);
 			evaluationContext.armyInvolvement += army->getArmyCost();
 			evaluationContext.armyInvolvement += army->getArmyCost();
 		}
 		}
@@ -1336,9 +1339,22 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
 	else
 	else
 	{
 	{
 		float score = 0;
 		float score = 0;
-		float maxWillingToLose = ai->cb->getTownsInfo().empty() ? 1 : 0.25;
+		float myPower = 0;
+		float totalPower = 0;
+		for (auto heroInfo : ai->cb->getHeroesInfo(false))
+		{
+			if (heroInfo->getOwner() == ai->cb->getPlayerID())
+				myPower += heroInfo->getTotalStrength();
+			totalPower += heroInfo->getTotalStrength();
+		}
+		float powerRatio = 1;
+		if (totalPower > 0)
+			powerRatio = myPower / totalPower;
+
+		float maxWillingToLose = ai->cb->getTownsInfo().empty() ? 1 : 0.5 * powerRatio;
+
 #if NKAI_TRACE_LEVEL >= 2
 #if NKAI_TRACE_LEVEL >= 2
-		logAi->trace("BEFORE: priorityTier %d, Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, gold: %f, cost: %d, army gain: %f, army growth: %f skill: %f danger: %d, threatTurns: %d, threat: %d, role: %s, strategical value: %f, conquest value: %f cwr: %f, fear: %f, explorePriority: %d isDefend: %d",
+		logAi->trace("BEFORE: priorityTier %d, Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, gold: %f, cost: %d, army gain: %f, army growth: %f skill: %f danger: %d, threatTurns: %d, threat: %d, role: %s, strategical value: %f, conquest value: %f cwr: %f, fear: %f, explorePriority: %d isDefend: %d powerRatio: %d",
 			priorityTier,
 			priorityTier,
 			task->toString(),
 			task->toString(),
 			evaluationContext.armyLossPersentage,
 			evaluationContext.armyLossPersentage,
@@ -1359,7 +1375,8 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
 			evaluationContext.closestWayRatio,
 			evaluationContext.closestWayRatio,
 			evaluationContext.enemyHeroDangerRatio,
 			evaluationContext.enemyHeroDangerRatio,
 			evaluationContext.explorePriority,
 			evaluationContext.explorePriority,
-			evaluationContext.isDefend);
+			evaluationContext.isDefend,
+			powerRatio);
 #endif
 #endif
 
 
 		switch (priorityTier)
 		switch (priorityTier)
@@ -1388,6 +1405,8 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
 			}
 			}
 			case PriorityTier::KILL: //Take towns / kill heroes that are further away
 			case PriorityTier::KILL: //Take towns / kill heroes that are further away
 			{
 			{
+				if (evaluationContext.turn > 0 && evaluationContext.isHero)
+					return 0;
 				if (evaluationContext.conquestValue > 0 || evaluationContext.explorePriority == 1)
 				if (evaluationContext.conquestValue > 0 || evaluationContext.explorePriority == 1)
 					score = 1000;
 					score = 1000;
 				if (vstd::isAlmostZero(score) || (evaluationContext.enemyHeroDangerRatio > 1 && (evaluationContext.turn > 0 || evaluationContext.isExchange) && !ai->cb->getTownsInfo().empty()))
 				if (vstd::isAlmostZero(score) || (evaluationContext.enemyHeroDangerRatio > 1 && (evaluationContext.turn > 0 || evaluationContext.isExchange) && !ai->cb->getTownsInfo().empty()))

+ 1 - 0
AI/Nullkiller/Engine/PriorityEvaluator.h

@@ -81,6 +81,7 @@ struct DLL_EXPORT EvaluationContext
 	bool isTradeBuilding;
 	bool isTradeBuilding;
 	bool isExchange;
 	bool isExchange;
 	bool isArmyUpgrade;
 	bool isArmyUpgrade;
+	bool isHero;
 	int explorePriority;
 	int explorePriority;
 
 
 	EvaluationContext(const Nullkiller * ai);
 	EvaluationContext(const Nullkiller * ai);