Browse Source

Merge pull request #529 from nullkiller/AI-fix-freeze-on-buyarmy

AI: fix freeze on BuyArmy
Alexander Shishkin 6 years ago
parent
commit
0a461829fb
4 changed files with 42 additions and 21 deletions
  1. 3 2
      AI/VCAI/AIUtility.cpp
  2. 2 2
      AI/VCAI/AIUtility.h
  3. 16 3
      AI/VCAI/Goals/GatherArmy.cpp
  4. 21 14
      AI/VCAI/VCAI.cpp

+ 3 - 2
AI/VCAI/AIUtility.cpp

@@ -524,7 +524,8 @@ creInfo infoFromDC(const dwellingContent & dc)
 	return ci;
 }
 
-ui64 howManyReinforcementsCanBuy(HeroPtr h, const CGDwelling * t)
+
+ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t)
 {
 	ui64 aivalue = 0;
 
@@ -552,7 +553,7 @@ ui64 howManyReinforcementsCanBuy(HeroPtr h, const CGDwelling * t)
 	return aivalue;
 }
 
-ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance * t)
+ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t)
 {
 	ui64 ret = 0;
 	int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();

+ 2 - 2
AI/VCAI/AIUtility.h

@@ -179,8 +179,8 @@ bool compareMovement(HeroPtr lhs, HeroPtr rhs);
 bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
 bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
 bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
-ui64 howManyReinforcementsCanBuy(HeroPtr h, const CGDwelling * t);
-ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance * t);
+ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t);
+ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t);
 int3 whereToExplore(HeroPtr h);
 uint32_t distanceToTile(const CGHeroInstance * hero, int3 pos);
 

+ 16 - 3
AI/VCAI/Goals/GatherArmy.cpp

@@ -64,7 +64,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 		if(ai->isAccessibleForHero(pos, hero))
 		{
 			//grab army from town
-			if(!t->visitingHero && howManyReinforcementsCanGet(hero, t))
+			if(!t->visitingHero && howManyReinforcementsCanGet(hero.get(), t))
 			{
 				if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
 					ret.push_back(sptr(VisitTile(pos).sethero(hero)));
@@ -72,10 +72,23 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 			//buy army in town
 			if (!t->visitingHero || t->visitingHero == hero.get(true))
 			{
-				ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero, t));
+				std::vector<int> values = {
+					value,
+					(int)howManyReinforcementsCanBuy(t->getUpperArmy(), t),
+					(int)howManyReinforcementsCanBuy(hero.get(), t) };
+
+				int val = *std::min_element(values.begin(), values.end());
+
+				logAi->trace(
+					"Army value need %i, to hero %i, to town %i",
+					value,
+					(int)howManyReinforcementsCanBuy(hero.get(), t),
+					(int)howManyReinforcementsCanBuy(t->getUpperArmy(), t));
+
 				if (val)
 				{
 					auto goal = sptr(BuyArmy(t, val).sethero(hero));
+
 					if(!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
 						ret.push_back(goal);
 					else
@@ -137,7 +150,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 			{
 				auto dwelling = dynamic_cast<const CGDwelling *>(obj);
 
-				ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero, dwelling));
+				ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero.get(), dwelling));
 
 				if(val)
 				{

+ 21 - 14
AI/VCAI/VCAI.cpp

@@ -1456,12 +1456,13 @@ void VCAI::wander(HeroPtr h)
 
 			auto compareReinforcements = [h](const CGTownInstance * lhs, const CGTownInstance * rhs) -> bool
 			{
-				auto r1 = howManyReinforcementsCanGet(h, lhs),
-					r2 = howManyReinforcementsCanGet(h, rhs);
+				const CGHeroInstance * hptr = h.get();
+				auto r1 = howManyReinforcementsCanGet(hptr, lhs),
+					r2 = howManyReinforcementsCanGet(hptr, rhs);
 				if (r1 != r2)
 					return r1 < r2;
 				else
-					return howManyReinforcementsCanBuy(h, lhs) < howManyReinforcementsCanBuy(h, rhs);
+					return howManyReinforcementsCanBuy(hptr, lhs) < howManyReinforcementsCanBuy(hptr, rhs);
 			};
 
 			std::vector<const CGTownInstance *> townsReachable;
@@ -2200,6 +2201,8 @@ void VCAI::tryRealize(Goals::BuyArmy & g)
 	ui64 valueBought = 0;
 	//buy the stacks with largest AI value
 
+	makePossibleUpgrades(t);
+
 	while (valueBought < g.value)
 	{
 		auto res = ah->allResources();
@@ -2209,7 +2212,15 @@ void VCAI::tryRealize(Goals::BuyArmy & g)
 		{
 			auto ci = infoFromDC(t->creatures[i]);
 
-			if(!ci.count || ci.creID == -1 || (g.objid != -1 && ci.creID != g.objid))
+			if(!ci.count 
+				|| ci.creID == -1 
+				|| (g.objid != -1 && ci.creID != g.objid)
+				|| t->getUpperArmy()->getSlotFor(ci.creID) == SlotID())
+				continue;
+
+			vstd::amin(ci.count, res / ci.cre->cost); //max count we can afford
+
+			if(!ci.count)
 				continue;
 
 			ci.level = i; //this is important for Dungeon Summoning Portal
@@ -2223,21 +2234,17 @@ void VCAI::tryRealize(Goals::BuyArmy & g)
 			*boost::max_element(creaturesInDwellings, [&res](const creInfo & lhs, const creInfo & rhs)
 		{
 			//max value of creatures we can buy with our res
-			int value1 = lhs.cre->AIValue * (std::min(lhs.count, res / lhs.cre->cost)),
-				value2 = rhs.cre->AIValue * (std::min(rhs.count, res / rhs.cre->cost));
+			int value1 = lhs.cre->AIValue * lhs.count,
+				value2 = rhs.cre->AIValue * rhs.count;
 
 			return value1 < value2;
 		});
 
-		vstd::amin(ci.count, res / ci.cre->cost); //max count we can afford
-		if (ci.count > 0 && t->getUpperArmy()->getSlotFor(ci.creID) != SlotID())
-		{
-			cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level);
-			valueBought += ci.count * ci.cre->AIValue;
-		}
-		else
-			throw cannotFulfillGoalException("Can't buy any more creatures!");
+
+		cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level);
+		valueBought += ci.count * ci.cre->AIValue;
 	}
+
 	throw goalFulfilledException(sptr(g)); //we bought as many creatures as we wanted
 }