2
0
Эх сурвалжийг харах

Nullkiller AI: new prioritization engine stabilization

Andrii Danylchenko 4 жил өмнө
parent
commit
6bebb766a6

+ 5 - 0
AI/Nullkiller/AIhelper.cpp

@@ -192,6 +192,11 @@ std::vector<SlotInfo> AIhelper::getSortedSlots(const CCreatureSet * target, cons
 	return armyManager->getSortedSlots(target, source);
 }
 
+std::vector<creInfo> AIhelper::getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const
+{
+	return armyManager->getArmyAvailableToBuy(hero, dwelling);
+}
+
 int AIhelper::selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const
 {
 	return heroManager->selectBestSkill(hero, skills);

+ 1 - 0
AI/Nullkiller/AIhelper.h

@@ -79,6 +79,7 @@ public:
 	std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const override;
 	std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const override;
 	std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override;
+	std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const override;
 
 	const std::map<HeroPtr, HeroRole> & getHeroRoles() const override;
 	HeroRole getHeroRole(const HeroPtr & hero) const override;

+ 33 - 21
AI/Nullkiller/ArmyManager.cpp

@@ -110,37 +110,49 @@ bool ArmyManager::canGetArmy(const CArmedInstance * target, const CArmedInstance
 ui64 ArmyManager::howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDwelling * t) const
 {
 	ui64 aivalue = 0;
-	TResources availableRes = cb->getResourceAmount();
-	int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();
+	auto army = getArmyAvailableToBuy(h, t);
 
-	for(auto const dc : t->creatures)
+	for(const creInfo & ci : army)
 	{
-		creInfo ci = infoFromDC(dc);
+		aivalue += ci.count * ci.cre->AIValue;
+	}
+
+	return aivalue;
+}
+
+std::vector<creInfo> ArmyManager::getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const
+{
+	auto availableRes = cb->getResourceAmount();
+	std::vector<creInfo> creaturesInDwellings;
+	int freeHeroSlots = GameConstants::ARMY_SIZE - hero->stacksCount();
+
+	for(int i = dwelling->creatures.size() - 1; i >= 0; i--)
+	{
+		auto ci = infoFromDC(dwelling->creatures[i]);
 
 		if(!ci.count || ci.creID == -1)
 			continue;
 
-		vstd::amin(ci.count, availableRes / ci.cre->cost); //max count we can afford
-
-		if(ci.count && ci.creID != -1) //valid creature at this level
+		SlotID dst = hero->getSlotFor(ci.creID);
+		if(!hero->hasStackAtSlot(dst)) //need another new slot for this stack
 		{
-			//can be merged with another stack?
-			SlotID dst = h->getSlotFor(ci.creID);
-			if(!h->hasStackAtSlot(dst)) //need another new slot for this stack
-			{
-				if(!freeHeroSlots) //no more place for stacks
-					continue;
-				else
-					freeHeroSlots--; //new slot will be occupied
-			}
-
-			//we found matching occupied or free slot
-			aivalue += ci.count * ci.cre->AIValue;
-			availableRes -= ci.cre->cost * ci.count;
+			if(!freeHeroSlots) //no more place for stacks
+				continue;
+			else
+				freeHeroSlots--; //new slot will be occupied
 		}
+
+		vstd::amin(ci.count, availableRes / ci.cre->cost); //max count we can afford
+
+		if(!ci.count)
+			continue;
+
+		ci.level = i; //this is important for Dungeon Summoning Portal
+		creaturesInDwellings.push_back(ci);
+		availableRes -= ci.cre->cost * ci.count;
 	}
 
-	return aivalue;
+	return creaturesInDwellings;
 }
 
 ui64 ArmyManager::howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const

+ 2 - 0
AI/Nullkiller/ArmyManager.h

@@ -36,6 +36,7 @@ public:
 	virtual std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const = 0;
 	virtual std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const = 0;
 	virtual std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const = 0;
+	virtual std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const = 0;
 };
 
 class DLL_EXPORT ArmyManager : public IArmyManager
@@ -54,4 +55,5 @@ public:
 	std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const override;
 	std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const override;
 	std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override;
+	std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const override;
 };

+ 20 - 18
AI/Nullkiller/Goals/ExecuteHeroChain.cpp

@@ -103,36 +103,38 @@ void ExecuteHeroChain::accept(VCAI * ai)
 					if(!targetNode->accessible || targetNode->turns != 0)
 					{
 						logAi->error(
-							"Enable to complete chain. Expected hero %s to arive to %s but he in 0 turns but he can not do this",
+							"Enable to complete chain. Expected hero %s to arive to %s in 0 turns but he can not do this",
 							hero.name,
-							node.coord.toString(),
-							hero->visitablePos().toString());
+							node.coord.toString());
 
 						return;
 					}
 				}
 
-				try
+				if(hero->movement)
 				{
-					Goals::VisitTile(node.coord).sethero(hero).accept(ai);
-				}
-				catch(cannotFulfillGoalException)
-				{
-					if(hero->movement > 0)
+					try
 					{
-						CGPath path;
-						bool isOk = cb->getPathsInfo(hero.get())->getPath(path, node.coord);
-
-						if(isOk && path.nodes.back().turns > 0)
+						Goals::VisitTile(node.coord).sethero(hero).accept(ai);
+					}
+					catch(cannotFulfillGoalException)
+					{
+						if(hero->movement > 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());
+							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;
+								ai->nullkiller->lockHero(hero.get());
+								return;
+							}
 						}
-					}
 
-					throw;
+						throw;
+					}
 				}
 			}
 

+ 0 - 5
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -201,11 +201,6 @@ void AINodeStorage::commit(
 	int movementLeft, 
 	float cost) const
 {
-	if(destination->actor->chainMask == 195 && turn == 0)
-	{
-		throw std::exception();
-	}
-
 	destination->action = action;
 	destination->cost = cost;
 	destination->moveRemains = movementLeft;

+ 21 - 34
AI/Nullkiller/VCAI.cpp

@@ -1068,7 +1068,7 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
 		{
 			makePossibleUpgrades(h.get());
 
-			if(!h->visitedTown->garrisonHero)
+			if(!nullkiller || !h->visitedTown->garrisonHero || !nullkiller->isHeroLocked(h->visitedTown->garrisonHero))
 				moveCreaturesToHero(h->visitedTown);
 
 			townVisitsThisWeek[h].insert(h->visitedTown);
@@ -2192,46 +2192,33 @@ void VCAI::tryRealize(Goals::BuyArmy & g)
 
 	makePossibleUpgrades(t);
 
-	while (valueBought < g.value)
-	{
-		auto res = ah->allResources();
-		std::vector<creInfo> creaturesInDwellings;
-
-		for (int i = t->creatures.size() - 1; i >= 0; i--)
-		{
-			auto ci = infoFromDC(t->creatures[i]);
-
-			if(!ci.count
-				|| ci.creID == -1
-				|| (g.objid != -1 && ci.creID != g.objid)
-				|| t->getUpperArmy()->getSlotFor(ci.creID) == SlotID())
-				continue;
+	auto armyToBuy = ah->getArmyAvailableToBuy(t->getUpperArmy(), t);
 
-			vstd::amin(ci.count, res / ci.cre->cost); //max count we can afford
+	if(armyToBuy.empty())
+	{
+		throw cannotFulfillGoalException("No creatures to buy.");
+	}
 
-			if(!ci.count)
-				continue;
+	for (int i = 0; valueBought < g.value && i < armyToBuy.size(); i++)
+	{
+		auto res = ah->allResources();
+		auto & ci = armyToBuy[i];
 
-			ci.level = i; //this is important for Dungeon Summoning Portal
-			creaturesInDwellings.push_back(ci);
-		}
+		if(g.objid != -1 && ci.creID != g.objid)
+			continue;
 
-		if (creaturesInDwellings.empty())
-			throw cannotFulfillGoalException("Can't buy any more creatures!");
+		vstd::amin(ci.count, res / ci.cre->cost);
 
-		creInfo ci =
-			*boost::max_element(creaturesInDwellings, [](const creInfo & lhs, const creInfo & rhs)
+		if(ci.count)
 		{
-			//max value of creatures we can buy with our res
-			int value1 = lhs.cre->AIValue * lhs.count,
-				value2 = rhs.cre->AIValue * rhs.count;
-
-			return value1 < value2;
-		});
-
+			cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level);
+			valueBought += ci.count * ci.cre->AIValue;
+		}
+	}
 
-		cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level);
-		valueBought += ci.count * ci.cre->AIValue;
+	if(!valueBought)
+	{
+		throw cannotFulfillGoalException("No creatures to buy.");
 	}
 
 	if(t->visitingHero)