Browse Source

Priorization-improvements

Manarecoveryreward now uses float instead of unsigned int in order to avoid extremely high instead of negative scores when the hero has more mana than his mana-limit for example due to mana-vortex.

Moved upgrading armies to a lower priority tier as otherwise the AI would go back to their cities all the time even though there were plenty of other things to do.

Improved exploration logic by putting different kinds of exploration to different priority-tiers.
Looking at the other side of a portal has high priority, visiting an observatory has medium priority and scouting by visiting nearby tiles has low priority.
Xilmi 1 year ago
parent
commit
22222f0fba

+ 1 - 1
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -387,7 +387,7 @@ void Nullkiller::makeTurn()
 			if(bestTask->priority > 0)
 			{
 #if NKAI_TRACE_LEVEL >= 1
-				logAi->info("Pass %d: Performing task %s with prio: %d", i, bestTask->toString(), bestTask->priority);
+				logAi->info("Pass %d: Performing prio 0 task %s with prio: %d", i, bestTask->toString(), bestTask->priority);
 #endif
 				if(!executeTask(bestTask))
 					return;

+ 42 - 8
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -63,7 +63,8 @@ EvaluationContext::EvaluationContext(const Nullkiller* ai)
 	involvesSailing(false),
 	isTradeBuilding(false),
 	isExchange(false),
-	isExplore(false)
+	isArmyUpgrade(false),
+	explorePriority(0)
 {
 }
 
@@ -493,7 +494,7 @@ uint64_t RewardEvaluator::townArmyGrowth(const CGTownInstance * town) const
 	return result;
 }
 
-uint64_t RewardEvaluator::getManaRecoveryArmyReward(const CGHeroInstance * hero) const
+float RewardEvaluator::getManaRecoveryArmyReward(const CGHeroInstance * hero) const
 {
 	return ai->heroManager->getMagicStrength(hero) * 10000 * (1.0f - std::sqrt(static_cast<float>(hero->mana) / hero->manaLimit()));
 }
@@ -868,6 +869,7 @@ public:
 
 		evaluationContext.armyReward += upgradeValue;
 		evaluationContext.addNonCriticalStrategicalValue(upgradeValue / (float)armyUpgrade.hero->getArmyStrength());
+		evaluationContext.isArmyUpgrade = true;
 	}
 };
 
@@ -882,7 +884,24 @@ public:
 		int tilesDiscovered = task->value;
 
 		evaluationContext.addNonCriticalStrategicalValue(0.03f * tilesDiscovered);
-		evaluationContext.isExplore = true;
+		for (auto obj : evaluationContext.evaluator.ai->cb->getVisitableObjs(task->tile))
+		{
+			switch (obj->ID.num)
+			{
+			case Obj::MONOLITH_ONE_WAY_ENTRANCE:
+			case Obj::MONOLITH_TWO_WAY:
+			case Obj::SUBTERRANEAN_GATE:
+			case Obj::WHIRLPOOL:
+				evaluationContext.explorePriority = 1;
+				break;
+			case Obj::REDWOOD_OBSERVATORY:
+			case Obj::PILLAR_OF_FIRE:
+				evaluationContext.explorePriority = 2;
+				break;
+			}
+		}
+		if (evaluationContext.explorePriority == 0)
+			evaluationContext.explorePriority = 3;
 	}
 };
 
@@ -1320,7 +1339,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
 		float score = 0;
 		float maxWillingToLose = ai->cb->getTownsInfo().empty() ? 1 : 0.25;
 #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, 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",
 			priorityTier,
 			task->toString(),
 			evaluationContext.armyLossPersentage,
@@ -1340,6 +1359,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
 			evaluationContext.conquestValue,
 			evaluationContext.closestWayRatio,
 			evaluationContext.enemyHeroDangerRatio,
+			evaluationContext.explorePriority,
 			evaluationContext.isDefend);
 #endif
 
@@ -1369,7 +1389,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
 			}
 			case PriorityTier::KILL: //Take towns / kill heroes that are further away
 			{
-				if (evaluationContext.conquestValue > 0)
+				if (evaluationContext.conquestValue > 0 || evaluationContext.explorePriority == 1)
 					score = 1000;
 				if (vstd::isAlmostZero(score) || (evaluationContext.enemyHeroDangerRatio > 1 && (evaluationContext.turn > 0 || evaluationContext.isExchange) && !ai->cb->getTownsInfo().empty()))
 					return 0;
@@ -1388,6 +1408,10 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
 					return 0;
 				if (evaluationContext.isDefend && (evaluationContext.enemyHeroDangerRatio < 1 || evaluationContext.threatTurns > 0 || evaluationContext.turn > 0))
 					return 0;
+				if (evaluationContext.explorePriority == 3)
+					return 0;
+				if (evaluationContext.isArmyUpgrade)
+					return 0;
 				score += evaluationContext.strategicalValue * 1000;
 				score += evaluationContext.goldReward;
 				score += evaluationContext.skillReward * evaluationContext.armyInvolvement * (1 - evaluationContext.armyLossPersentage) * 0.05;
@@ -1397,8 +1421,6 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
 				score -= evaluationContext.armyInvolvement * evaluationContext.armyLossPersentage;
 				if (score > 0)
 				{
-					if(!evaluationContext.isExplore)
-						score = 1000;
 					score *= evaluationContext.closestWayRatio;
 					if (evaluationContext.enemyHeroDangerRatio > 1)
 						score /= evaluationContext.enemyHeroDangerRatio;
@@ -1408,11 +1430,23 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
 				}
 				break;
 			}
+			case PriorityTier::LOW_PRIO_EXPLORE:
+			{
+				if (evaluationContext.enemyHeroDangerRatio > 1)
+					return 0;
+				if (evaluationContext.explorePriority != 3)
+					return 0;
+				score = 1000;
+				score *= evaluationContext.closestWayRatio;
+				if (evaluationContext.movementCost > 0)
+					score /= evaluationContext.movementCost;
+				break;
+			}
 			case PriorityTier::DEFEND: //Defend whatever if nothing else is to do
 			{
 				if (evaluationContext.enemyHeroDangerRatio > 1 && evaluationContext.isExchange)
 					return 0;
-				if (evaluationContext.isDefend)
+				if (evaluationContext.isDefend || evaluationContext.isArmyUpgrade)
 					score = 1000;
 				score *= evaluationContext.closestWayRatio;
 				score /= (evaluationContext.turn + 1);

+ 4 - 2
AI/Nullkiller/Engine/PriorityEvaluator.h

@@ -49,7 +49,7 @@ public:
 	uint64_t getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const;
 	const HitMapInfo & getEnemyHeroDanger(const int3 & tile, uint8_t turn) const;
 	uint64_t townArmyGrowth(const CGTownInstance * town) const;
-	uint64_t getManaRecoveryArmyReward(const CGHeroInstance * hero) const;
+	float getManaRecoveryArmyReward(const CGHeroInstance * hero) const;
 };
 
 struct DLL_EXPORT EvaluationContext
@@ -80,7 +80,8 @@ struct DLL_EXPORT EvaluationContext
 	bool involvesSailing;
 	bool isTradeBuilding;
 	bool isExchange;
-	bool isExplore;
+	bool isArmyUpgrade;
+	int explorePriority;
 
 	EvaluationContext(const Nullkiller * ai);
 
@@ -112,6 +113,7 @@ public:
 		INSTADEFEND,
 		KILL,
 		HUNTER_GATHER,
+		LOW_PRIO_EXPLORE,
 		DEFEND
 	};