Browse Source

Nullkiller AI: new prioritization engine

Andrii Danylchenko 4 years ago
parent
commit
b261734905

+ 1 - 1
AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp

@@ -97,7 +97,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
 					continue;
 
 				auto hero = path.targetHero;
-				auto danger = path.getTotalDanger(hero);
+				auto danger = path.getTotalDanger();
 
 				if(danger == 0 && path.exchangeCount > 1)
 					continue;

+ 8 - 0
AI/Nullkiller/Behaviors/DefenceBehavior.cpp

@@ -206,6 +206,14 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 				path.movementCost(),
 				path.toString());
 #endif
+			if(path.turn() <= treat.turn - 2)
+			{
+				logAi->trace("Deffer defence of %s by %s because he has enough time to rich the town next trun",
+					town->name,
+					path.targetHero->name);
+
+				continue;
+			}
 
 			float priority = basicPriority
 				+ std::min(SAFE_ATTACK_CONSTANT, (float)path.getHeroStrength() / treat.danger) / (treat.turn + 1);

+ 24 - 3
AI/Nullkiller/Goals/ExecuteHeroChain.cpp

@@ -26,9 +26,9 @@ using namespace Goals;
 ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj)
 	:CGoal(Goals::EXECUTE_HERO_CHAIN), chainPath(path)
 {
-	evaluationContext.danger = path.getTotalDanger(hero);
+	evaluationContext.danger = path.getTotalDanger();
 	evaluationContext.movementCost = path.movementCost();
-	evaluationContext.armyLoss = path.armyLoss;
+	evaluationContext.armyLoss = path.getTotalArmyLoss();
 	evaluationContext.heroStrength = path.getHeroStrength();
 	hero = path.targetHero;
 	tile = path.targetTile();
@@ -112,7 +112,28 @@ void ExecuteHeroChain::accept(VCAI * ai)
 					}
 				}
 
-				Goals::VisitTile(node.coord).sethero(hero).accept(ai);
+				try
+				{
+					Goals::VisitTile(node.coord).sethero(hero).accept(ai);
+				}
+				catch(cannotFulfillGoalException)
+				{
+					if(hero->movement > 0)
+					{
+						CGPath path;
+						bool isOk = cb->getPathsInfo(hero.get())->getPath(path, node.coord);
+
+						if(isOk && path.nodes.back().turns > 0)
+						{
+							logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero.name, hero->movement, node.coord.toString());
+
+							ai->nullkiller->lockHero(hero.get());
+							return;
+						}
+					}
+
+					throw;
+				}
 			}
 
 			if(node.turns == 0)

+ 7 - 1
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -875,6 +875,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
 		path.heroArmy = node.actor->creatureSet;
 		path.armyLoss = node.armyLoss;
 		path.targetObjectDanger = evaluateDanger(pos, path.targetHero);
+		path.targetObjectArmyLoss = evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), path.targetObjectDanger);
 		path.chainMask = node.actor->chainMask;
 		path.exchangeCount = node.actor->actorExchangeCount;
 		
@@ -985,7 +986,7 @@ uint64_t AIPath::getHeroStrength() const
 	return targetHero->getFightingStrength() * heroArmy->getArmyStrength();
 }
 
-uint64_t AIPath::getTotalDanger(HeroPtr hero) const
+uint64_t AIPath::getTotalDanger() const
 {
 	uint64_t pathDanger = getPathDanger();
 	uint64_t danger = pathDanger > targetObjectDanger ? pathDanger : targetObjectDanger;
@@ -993,6 +994,11 @@ uint64_t AIPath::getTotalDanger(HeroPtr hero) const
 	return danger;
 }
 
+uint64_t AIPath::getTotalArmyLoss() const
+{
+	return armyLoss + targetObjectArmyLoss;
+}
+
 std::string AIPath::toString()
 {
 	std::stringstream str;

+ 12 - 1
AI/Nullkiller/Pathfinding/AINodeStorage.h

@@ -49,6 +49,7 @@ struct AIPath
 	std::shared_ptr<const ISpecialAction> specialAction;
 	uint64_t targetObjectDanger;
 	uint64_t armyLoss;
+	uint64_t targetObjectArmyLoss;
 	const CGHeroInstance * targetHero;
 	const CCreatureSet * heroArmy;
 	uint64_t chainMask;
@@ -60,7 +61,10 @@ struct AIPath
 	uint64_t getPathDanger() const;
 
 	/// Gets danger of path including danger of visiting the target object like creature bank
-	uint64_t getTotalDanger(HeroPtr hero) const;
+	uint64_t getTotalDanger() const;
+
+	/// Gets danger of path including danger of visiting the target object like creature bank
+	uint64_t getTotalArmyLoss() const;
 
 	int3 firstTileToGet() const;
 	int3 targetTile() const;
@@ -168,6 +172,13 @@ public:
 		return dangerEvaluator->evaluateDanger(tile, hero, ai);
 	}
 
+	uint64_t evaluateArmyLoss(const CGHeroInstance * hero, uint64_t armyValue, uint64_t danger) const
+	{
+		double ratio = (double)danger / (armyValue * hero->getFightingStrength());
+
+		return (uint64_t)(armyValue * ratio * ratio * ratio);
+	}
+
 private:
 	STRONG_INLINE
 	void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility);

+ 2 - 2
AI/Nullkiller/Pathfinding/PathfindingManager.cpp

@@ -154,7 +154,7 @@ Goals::TGoalVec PathfindingManager::findPaths(
 #endif
 		if(ai->isTileNotReserved(hero.get(), firstTileToGet))
 		{
-			danger = path.getTotalDanger(hero);
+			danger = path.getTotalDanger();
 
 			if(isSafeToVisit(hero, path.heroArmy, danger))
 			{
@@ -178,7 +178,7 @@ Goals::TGoalVec PathfindingManager::findPaths(
 					solution->evaluationContext.danger = danger;
 
 				solution->evaluationContext.movementCost += path.movementCost();
-				solution->evaluationContext.armyLoss += path.armyLoss;
+				solution->evaluationContext.armyLoss += path.getTotalArmyLoss();
 				solution->evaluationContext.heroStrength = path.getHeroStrength();
 #ifdef VCMI_TRACE_PATHFINDER
 				logAi->trace("It's safe for %s to visit tile %s with danger %s, loss %s, army strength %s, goal %s", 

+ 1 - 3
AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp

@@ -163,9 +163,7 @@ namespace AIPathfinding
 			auto hero = nodeStorage->getHero(source.node);
 			auto danger = nodeStorage->evaluateDanger(destination.coord, hero);
 			double actualArmyValue = srcNode->actor->armyValue - srcNode->armyLoss;
-			double ratio = (double)danger / (actualArmyValue * hero->getFightingStrength());
-
-			uint64_t loss = (uint64_t)(actualArmyValue * ratio * ratio * ratio);
+			double loss = nodeStorage->evaluateArmyLoss(hero, actualArmyValue, danger);
 
 			if(loss < actualArmyValue)
 			{