浏览代码

NKAI: stability fixes and regression

Andrii Danylchenko 1 年之前
父节点
当前提交
3939c4b9d3

+ 18 - 1
AI/Nullkiller/AIGateway.cpp

@@ -1111,7 +1111,24 @@ void AIGateway::recruitCreatures(const CGDwelling * d, const CArmedInstance * re
 
 		if(!recruiter->getSlotFor(creID).validSlot())
 		{
-			continue;
+			for(auto stack : recruiter->Slots())
+			{
+				if(!stack.second->type)
+					continue;
+				
+				auto duplicatingSlot = recruiter->getSlotFor(stack.second->type);
+
+				if(duplicatingSlot != stack.first)
+				{
+					cb->mergeStacks(recruiter, recruiter, stack.first, duplicatingSlot);
+					break;
+				}
+			}
+
+			if(!recruiter->getSlotFor(creID).validSlot())
+			{
+				continue;
+			}
 		}
 
 		vstd::amin(count, cb->getResourceAmount() / creID.toCreature()->getFullRecruitCost());

+ 16 - 2
AI/Nullkiller/AIUtility.cpp

@@ -91,7 +91,7 @@ bool HeroPtr::operator<(const HeroPtr & rhs) const
 
 const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const
 {
-	return get(cb);
+	return get(cb, doWeExpectNull);
 }
 
 const CGHeroInstance * HeroPtr::get(const CPlayerSpecificInfoCallback * cb, bool doWeExpectNull) const
@@ -300,6 +300,19 @@ uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock>
 	return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
 }
 
+int getDuplicatingSlots(const CArmedInstance * army)
+{
+	int duplicatingSlots = 0;
+
+	for(auto stack : army->Slots())
+	{
+		if(stack.second->type && army->getSlotFor(stack.second->type) != stack.first)
+			duplicatingSlots++;
+	}
+
+	return duplicatingSlots;
+}
+
 // todo: move to obj manager
 bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObjectInstance * obj)
 {
@@ -347,13 +360,14 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject
 			return false;
 
 		const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj);
+		auto duplicatingSlotsCount = getDuplicatingSlots(h);
 
 		for(auto level : d->creatures)
 		{
 			for(auto c : level.second)
 			{
 				if(level.first
-					&& h->getSlotFor(CreatureID(c)) != SlotID()
+					&& (h->getSlotFor(CreatureID(c)) != SlotID() || duplicatingSlotsCount > 0)
 					&& ai->cb->getResourceAmount().canAfford(c.toCreature()->getFullRecruitCost()))
 				{
 					return true;

+ 1 - 0
AI/Nullkiller/AIUtility.h

@@ -242,6 +242,7 @@ uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock>
 
 // todo: move to obj manager
 bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObjectInstance * obj);
+int getDuplicatingSlots(const CArmedInstance * army);
 
 template <class T>
 class SharedPool

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

@@ -318,7 +318,7 @@ void Nullkiller::makeTurn()
 
 	Goals::TGoalVec bestTasks;
 
-	for(int i = 1; i <= settings->getMaxPass(); i++)
+	for(int i = 1; i <= settings->getMaxPass() && cb->getPlayerStatus(playerID) == EPlayerStatus::INGAME; i++)
 	{
 		auto start = std::chrono::high_resolution_clock::now();
 		updateAiState(i);
@@ -372,6 +372,9 @@ void Nullkiller::makeTurn()
 
 		for(auto bestTask : selectedTasks)
 		{
+			if(cb->getPlayerStatus(playerID) != EPlayerStatus::INGAME)
+				return;
+
 			std::string taskDescription = bestTask->toString();
 			HeroPtr hero = bestTask->getHero();
 			HeroRole heroRole = HeroRole::MAIN;

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

@@ -158,6 +158,8 @@ uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHero
 
 	const auto& slots = hero->Slots();
 	ui64 weakestStackPower = 0;
+	int duplicatingSlots = getDuplicatingSlots(hero);
+
 	if (slots.size() >= GameConstants::ARMY_SIZE)
 	{
 		//No free slot, we might discard our weakest stack
@@ -172,7 +174,7 @@ uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHero
 	{
 		//Only if hero has slot for this creature in the army
 		auto ccre = dynamic_cast<const CCreature*>(c.data.type);
-		if (hero->getSlotFor(ccre).validSlot())
+		if (hero->getSlotFor(ccre).validSlot() || duplicatingSlots > 0)
 		{
 			result += (c.data.type->getAIValue() * c.data.count) * c.chance;
 		}
@@ -1125,7 +1127,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
 	}
 
 #if NKAI_TRACE_LEVEL >= 2
-	logAi->trace("Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, gold: %f, cost: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, cwr: %f, fear: %f, result %f",
+	logAi->trace("Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, gold: %f, cost: %d, army gain: %f, danger: %d, role: %s, strategical value: %f, cwr: %f, fear: %f, result %f",
 		task->toString(),
 		evaluationContext.armyLossPersentage,
 		(int)evaluationContext.turn,

+ 7 - 0
AI/Nullkiller/Goals/ExecuteHeroChain.cpp

@@ -104,6 +104,13 @@ void ExecuteHeroChain::accept(AIGateway * ai)
 		const CGHeroInstance * hero = node->targetHero;
 		HeroPtr heroPtr = hero;
 
+		if(!heroPtr.validAndSet())
+		{
+			logAi->error("Hero %s was lost. Exit hero chain.", heroPtr.name);
+
+			return;
+		}
+
 		if(node->parentIndex >= i)
 		{
 			logAi->error("Invalid parentIndex while executing node " + node->coord.toString());

+ 2 - 0
client/CMT.cpp

@@ -375,6 +375,8 @@ int main(int argc, char * argv[])
 		while(!headlessQuit)
 			boost::this_thread::sleep_for(boost::chrono::milliseconds(200));
 
+		boost::this_thread::sleep_for(boost::chrono::milliseconds(500));
+
 		quitApplication();
 	}