Browse Source

AIGateway.cpp extracting methods that can be isolated/static

Mircea TheHonestCTO 3 months ago
parent
commit
76e91f7689

+ 204 - 181
AI/Nullkiller2/AIGateway.cpp

@@ -134,7 +134,7 @@ void AIGateway::heroMoved(const TryMoveHero & details, bool verbose)
 	{
 		auto boat = dynamic_cast<const CGBoat *>(o1);
 		if(boat)
-			addVisitableObj(boat);
+			memorizeVisitableObj(boat, nullkiller->memory, nullkiller->dangerHitMap, playerID);
 	}
 }
 
@@ -284,7 +284,7 @@ void AIGateway::tileRevealed(const FowTilesType & pos)
 	for(int3 tile : pos)
 	{
 		for(const CGObjectInstance * obj : myCb->getVisitableObjs(tile))
-			addVisitableObj(obj);
+			memorizeVisitableObj(obj, nullkiller->memory, nullkiller->dangerHitMap, playerID);
 	}
 
 	if (nullkiller->settings->isUpdateHitmapOnTileReveal() && !pos.empty())
@@ -306,7 +306,7 @@ void AIGateway::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID her
 		auto transferFrom2to1 = [this](const CGHeroInstance * h1, const CGHeroInstance * h2) -> void
 		{
 			this->pickBestCreatures(h1, h2);
-			this->pickBestArtifacts(h1, h2);
+			pickBestArtifacts(h1, h2);
 		};
 
 		//Do not attempt army or artifacts exchange if we visited ally player
@@ -370,7 +370,7 @@ void AIGateway::newObject(const CGObjectInstance * obj)
 	NET_EVENT_HANDLER;
 	nullkiller->invalidatePathfinderData();
 	if(obj->isVisitable())
-		addVisitableObj(obj);
+		memorizeVisitableObj(obj, nullkiller->memory, nullkiller->dangerHitMap, playerID);
 }
 
 //to prevent AI from accessing objects that got deleted while they became invisible (Cover of Darkness, enemy hero moved etc.) below code allows AI to know deletion of objects out of sight
@@ -587,7 +587,7 @@ void AIGateway::initGameInterface(std::shared_ptr<Environment> env, std::shared_
 
 	nullkiller->init(CB, this);
 
-	retrieveVisitableObjs();
+	memorizeVisitableObjs(nullkiller->memory, nullkiller->dangerHitMap, playerID, myCb);
 }
 
 void AIGateway::yourTurn(QueryID queryID)
@@ -705,7 +705,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
 	{
 		int sel = 0;
 
-		if(selection) //select from multiple components -> take the last one (they're indexed [1-size])
+		if(selection) //select from multiple components -> take the last one (they'bool executeTask(Goals::TTask task);re indexed [1-size])
 			sel = components.size();
 
 		{
@@ -852,23 +852,9 @@ void AIGateway::makeTurn()
 
 	std::shared_lock gsLock(CGameState::mutex);
 
-	if(nullkiller->isOpenMap())
-	{
-		cb->sendMessage("vcmieagles");
-	}
-
-	retrieveVisitableObjs();
-
-	if(cb->getDate(Date::DAY_OF_WEEK) == 1)
-	{
-		for(const CGObjectInstance * obj : nullkiller->memory->visitableObjs)
-		{
-			if(isWeeklyRevisitable(nullkiller.get(), obj))
-			{
-				nullkiller->memory->markObjectUnvisited(obj);
-			}
-		}
-	}
+	cheatMapReveal(nullkiller);
+	memorizeVisitableObjs(nullkiller->memory, nullkiller->dangerHitMap, playerID, myCb);
+	memorizeRevisitableObjs(nullkiller->memory, playerID);
 
 #if NKAI_TRACE_LEVEL == 0
 	try
@@ -902,7 +888,6 @@ void AIGateway::makeTurn()
 	catch (const TerminationRequestedException &)
 	{
 		logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn.");
-		return;
 	}
 }
 
@@ -1042,139 +1027,6 @@ void AIGateway::pickBestCreatures(const CArmedInstance * destinationArmy, const
 	//TODO - having now strongest possible army, we may want to think about arranging stacks
 }
 
-void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other)
-{
-	auto equipBest = [](const CGHeroInstance * h, const CGHeroInstance * otherh, bool giveStuffToFirstHero) -> void
-	{
-		bool changeMade = false;
-		std::set<std::pair<ArtifactInstanceID, ArtifactInstanceID> > swappedSet;
-		do
-		{
-			changeMade = false;
-
-			//we collect gear always in same order
-			std::vector<ArtifactLocation> allArtifacts;
-			if(giveStuffToFirstHero)
-			{
-				for(auto p : h->artifactsWorn)
-				{
-					if(p.second.getArt())
-						allArtifacts.push_back(ArtifactLocation(h->id, p.first));
-				}
-			}
-			for(auto slot : h->artifactsInBackpack)
-				allArtifacts.push_back(ArtifactLocation(h->id, h->getArtPos(slot.getArt())));
-
-			if(otherh)
-			{
-				for(auto p : otherh->artifactsWorn)
-				{
-					if(p.second.getArt())
-						allArtifacts.push_back(ArtifactLocation(otherh->id, p.first));
-				}
-				for(auto slot : otherh->artifactsInBackpack)
-					allArtifacts.push_back(ArtifactLocation(otherh->id, otherh->getArtPos(slot.getArt())));
-			}
-			//we give stuff to one hero or another, depending on giveStuffToFirstHero
-
-			const CGHeroInstance * target = nullptr;
-			if(giveStuffToFirstHero || !otherh)
-				target = h;
-			else
-				target = otherh;
-
-			for(auto location : allArtifacts)
-			{
-				if(location.artHolder == target->id && ArtifactUtils::isSlotEquipment(location.slot))
-					continue; //don't reequip artifact we already wear
-
-				if(location.slot == ArtifactPosition::MACH4) // don't attempt to move catapult
-					continue;
-
-				auto artHolder = cb->getHero(location.artHolder);
-				auto s = artHolder->getSlot(location.slot);
-				if(!s || s->locked) //we can't move locks
-					continue;
-				auto artifact = s->getArt();
-				if(!artifact)
-					continue;
-				//FIXME: why are the above possible to be null?
-
-				bool emptySlotFound = false;
-				for(auto slot : artifact->getType()->getPossibleSlots().at(target->bearerType()))
-				{
-					if(target->isPositionFree(slot) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move
-					{
-						ArtifactLocation destLocation(target->id, slot);
-						cb->swapArtifacts(location, destLocation); //just put into empty slot
-						emptySlotFound = true;
-						changeMade = true;
-						break;
-					}
-				}
-				if(!emptySlotFound) //try to put that atifact in already occupied slot
-				{
-					int64_t artifactScore = getArtifactScoreForHero(target, artifact);
-
-					for(auto slot : artifact->getType()->getPossibleSlots().at(target->bearerType()))
-					{
-						auto otherSlot = target->getSlot(slot);
-						if(otherSlot && otherSlot->getArt()) //we need to exchange artifact for better one
-						{
-							int64_t otherArtifactScore = getArtifactScoreForHero(target, otherSlot->getArt());
-							logAi->trace( "Comparing artifacts of %s: %s vs %s. Score: %d vs %d", target->getHeroTypeName(), artifact->getType()->getJsonKey(), otherSlot->getArt()->getType()->getJsonKey(), artifactScore, otherArtifactScore);
-
-							//if that artifact is better than what we have, pick it
-							//combined artifacts are not always allowed to move
-							if(artifactScore > otherArtifactScore && artifact->canBePutAt(target, slot, true))
-							{
-								std::pair swapPair = std::minmax<ArtifactInstanceID>({artifact->getId(), otherSlot->artifactID});
-								if (swappedSet.find(swapPair) != swappedSet.end())
-								{
-									logAi->warn(
-										"Artifacts % s < -> % s have already swapped before, ignored.",
-										artifact->getType()->getJsonKey(),
-										otherSlot->getArt()->getType()->getJsonKey());
-									continue;
-								}
-								logAi->trace(
-									"Exchange artifacts %s <-> %s",
-									artifact->getType()->getJsonKey(),
-									otherSlot->getArt()->getType()->getJsonKey());
-
-								if(!otherSlot->getArt()->canBePutAt(artHolder, location.slot, true))
-								{
-									ArtifactLocation destLocation(target->id, slot);
-									ArtifactLocation backpack(artHolder->id, ArtifactPosition::BACKPACK_START);
-
-									cb->swapArtifacts(destLocation, backpack);
-									cb->swapArtifacts(location, destLocation);
-								}
-								else
-								{
-									cb->swapArtifacts(location, ArtifactLocation(target->id, target->getArtPos(otherSlot->getArt())));
-								}
-
-								changeMade = true;
-								swappedSet.insert(swapPair);
-								break;
-							}
-						}
-					}
-				}
-				if(changeMade)
-					break; //start evaluating artifacts from scratch
-			}
-		}
-		while(changeMade);
-	};
-
-	equipBest(h, other, true);
-
-	if(other)
-		equipBest(h, other, false);
-}
-
 void AIGateway::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter)
 {
 	//now used only for visited dwellings / towns, not BuyArmy goal
@@ -1253,17 +1105,6 @@ void AIGateway::waitTillFree()
 	status.waitTillFree();
 }
 
-void AIGateway::retrieveVisitableObjs()
-{
-	foreach_tile_pos([&](const int3 & pos)
-	{
-		for(const CGObjectInstance * obj : myCb->getVisitableObjs(pos, false))
-		{
-			addVisitableObj(obj);
-		}
-	});
-}
-
 std::vector<const CGObjectInstance *> AIGateway::getFlaggedObjects() const
 {
 	std::vector<const CGObjectInstance *> ret;
@@ -1275,19 +1116,6 @@ std::vector<const CGObjectInstance *> AIGateway::getFlaggedObjects() const
 	return ret;
 }
 
-void AIGateway::addVisitableObj(const CGObjectInstance * obj)
-{
-	if(obj->ID == Obj::EVENT)
-		return;
-
-	nullkiller->memory->addVisitableObject(obj);
-
-	if(obj->ID == Obj::HERO && cb->getPlayerRelations(obj->tempOwner, playerID) == PlayerRelations::ENEMIES)
-	{
-		nullkiller->dangerHitMap->resetHitmap();
-	}
-}
-
 bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
 {
 	if(h->isGarrisoned() && h->getVisitedTown())
@@ -1848,4 +1676,199 @@ void AIGateway::invalidatePaths()
 	nullkiller->invalidatePaths();
 }
 
+/*
+ * ////////////////////////////////////
+ * ////////// STATIC Methods //////////
+ * ////////////////////////////////////
+ */
+
+void AIGateway::cheatMapReveal(const std::unique_ptr<Nullkiller> & nullkiller)
+{
+	if(cb->getDate(Date::DAY) == 1) // No need to execute every day, only the first time
+	{
+		if(nullkiller->isOpenMap())
+		{
+			cb->sendMessage("vcmieagles");
+		}
+	}
+}
+
+void AIGateway::memorizeVisitableObjs(const std::unique_ptr<AIMemory> & memory,
+                                      const std::unique_ptr<DangerHitMapAnalyzer> & dangerHitMap,
+                                      const PlayerColor & playerID,
+                                      const std::shared_ptr<CCallback> & myCb)
+{
+	foreach_tile_pos([&](const int3 & pos)
+	{
+		// TODO: Inspect what not visible means when using verbose true
+		for(const CGObjectInstance * obj : myCb->getVisitableObjs(pos, false))
+		{
+			memorizeVisitableObj(obj, memory, dangerHitMap, playerID);
+		}
+	});
+}
+
+void AIGateway::memorizeVisitableObj(const CGObjectInstance * obj,
+                                     const std::unique_ptr<AIMemory> & memory,
+                                     const std::unique_ptr<DangerHitMapAnalyzer> & dangerHitMap,
+                                     const PlayerColor & playerID)
+{
+	if(obj->ID == Obj::EVENT)
+		return;
+
+	memory->addVisitableObject(obj);
+
+	if(obj->ID == Obj::HERO && cb->getPlayerRelations(obj->tempOwner, playerID) == PlayerRelations::ENEMIES)
+	{
+		dangerHitMap->resetHitmap();
+	}
+}
+
+void AIGateway::memorizeRevisitableObjs(const std::unique_ptr<AIMemory> & memory, const PlayerColor & playerID)
+{
+	if(cb->getDate(Date::DAY_OF_WEEK) == 1)
+	{
+		for(const CGObjectInstance * obj : memory->visitableObjs)
+		{
+			if(isWeeklyRevisitable(playerID, obj))
+			{
+				memory->markObjectUnvisited(obj);
+			}
+		}
+	}
+}
+
+void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other)
+{
+	auto equipBest = [](const CGHeroInstance * h, const CGHeroInstance * otherh, bool giveStuffToFirstHero) -> void
+	{
+		bool changeMade = false;
+		std::set<std::pair<ArtifactInstanceID, ArtifactInstanceID> > swappedSet;
+		do
+		{
+			changeMade = false;
+
+			//we collect gear always in same order
+			std::vector<ArtifactLocation> allArtifacts;
+			if(giveStuffToFirstHero)
+			{
+				for(auto p : h->artifactsWorn)
+				{
+					if(p.second.getArt())
+						allArtifacts.push_back(ArtifactLocation(h->id, p.first));
+				}
+			}
+			for(auto slot : h->artifactsInBackpack)
+				allArtifacts.push_back(ArtifactLocation(h->id, h->getArtPos(slot.getArt())));
+
+			if(otherh)
+			{
+				for(auto p : otherh->artifactsWorn)
+				{
+					if(p.second.getArt())
+						allArtifacts.push_back(ArtifactLocation(otherh->id, p.first));
+				}
+				for(auto slot : otherh->artifactsInBackpack)
+					allArtifacts.push_back(ArtifactLocation(otherh->id, otherh->getArtPos(slot.getArt())));
+			}
+			//we give stuff to one hero or another, depending on giveStuffToFirstHero
+
+			const CGHeroInstance * target = nullptr;
+			if(giveStuffToFirstHero || !otherh)
+				target = h;
+			else
+				target = otherh;
+
+			for(auto location : allArtifacts)
+			{
+				if(location.artHolder == target->id && ArtifactUtils::isSlotEquipment(location.slot))
+					continue; //don't reequip artifact we already wear
+
+				if(location.slot == ArtifactPosition::MACH4) // don't attempt to move catapult
+					continue;
+
+				auto artHolder = cb->getHero(location.artHolder);
+				auto s = artHolder->getSlot(location.slot);
+				if(!s || s->locked) //we can't move locks
+					continue;
+				auto artifact = s->getArt();
+				if(!artifact)
+					continue;
+				//FIXME: why are the above possible to be null?
+
+				bool emptySlotFound = false;
+				for(auto slot : artifact->getType()->getPossibleSlots().at(target->bearerType()))
+				{
+					if(target->isPositionFree(slot) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move
+					{
+						ArtifactLocation destLocation(target->id, slot);
+						cb->swapArtifacts(location, destLocation); //just put into empty slot
+						emptySlotFound = true;
+						changeMade = true;
+						break;
+					}
+				}
+				if(!emptySlotFound) //try to put that atifact in already occupied slot
+				{
+					int64_t artifactScore = getArtifactScoreForHero(target, artifact);
+
+					for(auto slot : artifact->getType()->getPossibleSlots().at(target->bearerType()))
+					{
+						auto otherSlot = target->getSlot(slot);
+						if(otherSlot && otherSlot->getArt()) //we need to exchange artifact for better one
+						{
+							int64_t otherArtifactScore = getArtifactScoreForHero(target, otherSlot->getArt());
+							logAi->trace( "Comparing artifacts of %s: %s vs %s. Score: %d vs %d", target->getHeroTypeName(), artifact->getType()->getJsonKey(), otherSlot->getArt()->getType()->getJsonKey(), artifactScore, otherArtifactScore);
+
+							//if that artifact is better than what we have, pick it
+							//combined artifacts are not always allowed to move
+							if(artifactScore > otherArtifactScore && artifact->canBePutAt(target, slot, true))
+							{
+								std::pair swapPair = std::minmax<ArtifactInstanceID>({artifact->getId(), otherSlot->artifactID});
+								if (swappedSet.find(swapPair) != swappedSet.end())
+								{
+									logAi->warn(
+										"Artifacts % s < -> % s have already swapped before, ignored.",
+										artifact->getType()->getJsonKey(),
+										otherSlot->getArt()->getType()->getJsonKey());
+									continue;
+								}
+								logAi->trace(
+									"Exchange artifacts %s <-> %s",
+									artifact->getType()->getJsonKey(),
+									otherSlot->getArt()->getType()->getJsonKey());
+
+								if(!otherSlot->getArt()->canBePutAt(artHolder, location.slot, true))
+								{
+									ArtifactLocation destLocation(target->id, slot);
+									ArtifactLocation backpack(artHolder->id, ArtifactPosition::BACKPACK_START);
+
+									cb->swapArtifacts(destLocation, backpack);
+									cb->swapArtifacts(location, destLocation);
+								}
+								else
+								{
+									cb->swapArtifacts(location, ArtifactLocation(target->id, target->getArtPos(otherSlot->getArt())));
+								}
+
+								changeMade = true;
+								swappedSet.insert(swapPair);
+								break;
+							}
+						}
+					}
+				}
+				if(changeMade)
+					break; //start evaluating artifacts from scratch
+			}
+		}
+		while(changeMade);
+	};
+
+	equipBest(h, other, true);
+
+	if(other)
+		equipBest(h, other, false);
+}
+
 }

+ 10 - 5
AI/Nullkiller2/AIGateway.h

@@ -77,7 +77,6 @@ public:
 	std::shared_ptr<CCallback> myCb;
 	std::unique_ptr<AsyncRunner> asyncTasks;
 
-public:
 	ObjectInstanceID selectedObject;
 
 	std::unique_ptr<Nullkiller> nullkiller;
@@ -162,7 +161,7 @@ public:
 	// TODO: all the routines like recruiting hero or building army should be removed from here and extracted to elementar goals or whatever
 	void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);
 	void pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source); //called when we can't find a slot for new stack
-	void pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other = nullptr);
+
 	void moveCreaturesToHero(const CGTownInstance * t);
 	void performObjectInteraction(const CGObjectInstance * obj, HeroPtr h);
 	bool makePossibleUpgrades(const CArmedInstance * obj);
@@ -173,17 +172,23 @@ public:
 	void lostHero(HeroPtr h); //should remove all references to hero (assigned tasks and so on)
 	void waitTillFree();
 
-	void addVisitableObj(const CGObjectInstance * obj);
-
 	void validateObject(const CGObjectInstance * obj); //checks if object is still visible and if not, removes references to it
 	void validateObject(ObjectIdRef obj); //checks if object is still visible and if not, removes references to it
-	void retrieveVisitableObjs();
 	virtual std::vector<const CGObjectInstance *> getFlaggedObjects() const;
 
 	void requestSent(const CPackForServer * pack, int requestID) override;
 	void answerQuery(QueryID queryID, int selection);
 	//special function that can be called ONLY from game events handling thread and will send request ASAP
 	void executeActionAsync(const std::string & description, const std::function<void()> & whatToDo);
+
+	static void cheatMapReveal(const std::unique_ptr<Nullkiller>& nullkiller);
+	static void memorizeVisitableObj(const CGObjectInstance* obj, const std::unique_ptr<AIMemory>& memory, const std::unique_ptr<DangerHitMapAnalyzer>& dangerHitMap, const
+	                                 PlayerColor& playerID);
+	static void memorizeVisitableObjs(const std::unique_ptr<AIMemory>& memory, const std::unique_ptr<DangerHitMapAnalyzer>& dangerHitMap, const PlayerColor&
+	                                  playerID, const std::shared_ptr<CCallback>& myCb);
+	static void memorizeRevisitableObjs(const std::unique_ptr<AIMemory>& memory, const PlayerColor& playerID);
+
+	static void pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other = nullptr);
 };
 
 }

+ 2 - 2
AI/Nullkiller2/AIUtility.cpp

@@ -600,7 +600,7 @@ int64_t getArtifactScoreForHero(const CGHeroInstance * hero, const CArtifactInst
 	return totalScore;
 }
 
-bool isWeeklyRevisitable(const Nullkiller * ai, const CGObjectInstance * obj)
+bool isWeeklyRevisitable(const PlayerColor & playerID, const CGObjectInstance * obj)
 {
 	if(!obj)
 		return false;
@@ -618,7 +618,7 @@ bool isWeeklyRevisitable(const Nullkiller * ai, const CGObjectInstance * obj)
 		return true;
 	case Obj::BORDER_GATE:
 	case Obj::BORDERGUARD:
-		return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID); //FIXME: they could be revisited sooner than in a week
+		return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(playerID); //FIXME: they could be revisited sooner than in a week
 	}
 	return false;
 }

+ 1 - 1
AI/Nullkiller2/AIUtility.h

@@ -205,7 +205,7 @@ bool isObjectPassable(const Nullkiller * ai, const CGObjectInstance * obj);
 bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, PlayerRelations objectRelations);
 bool isBlockVisitObj(const int3 & pos);
 
-bool isWeeklyRevisitable(const Nullkiller * ai, const CGObjectInstance * obj);
+bool isWeeklyRevisitable(const PlayerColor & playerID, const CGObjectInstance * obj);
 
 bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property!
 bool isSafeToVisit(const CGHeroInstance * h, uint64_t dangerStrength, float safeAttackRatio);

+ 1 - 1
AI/Nullkiller2/Analyzers/ObjectClusterizer.cpp

@@ -250,7 +250,7 @@ bool ObjectClusterizer::shouldVisitObject(const CGObjectInstance * obj) const
 
 	auto playerRelations = ai->cb->getPlayerRelations(ai->playerID, obj->tempOwner);
 
-	if(playerRelations != PlayerRelations::ENEMIES && !isWeeklyRevisitable(ai, obj))
+	if(playerRelations != PlayerRelations::ENEMIES && !isWeeklyRevisitable(ai->playerID, obj))
 	{
 		return false;
 	}

+ 1 - 1
AI/Nullkiller2/Behaviors/RecruitHeroBehavior.cpp

@@ -85,7 +85,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose(const Nullkiller * ai) const
 				if ((obj->ID == Obj::RESOURCE)
 					|| obj->ID == Obj::TREASURE_CHEST
 					|| obj->ID == Obj::CAMPFIRE
-					|| isWeeklyRevisitable(ai, obj)
+					|| isWeeklyRevisitable(ai->playerID, obj)
 					|| obj->ID == Obj::ARTIFACT)
 				{
 					auto tile = obj->visitablePos();

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

@@ -563,7 +563,7 @@ void Nullkiller::makeTurn()
 		}
 
 		for (auto heroInfo : cb->getHeroesInfo())
-			gateway->pickBestArtifacts(heroInfo);
+			AIGateway::pickBestArtifacts(heroInfo);
 
 		if(i == settings->getMaxPass())
 		{

+ 1 - 0
AI/Nullkiller2/Engine/Nullkiller.h

@@ -105,6 +105,7 @@ public:
 	std::unique_ptr<DeepDecomposer> decomposer;
 	std::unique_ptr<ArmyFormation> armyFormation;
 	std::unique_ptr<Settings> settings;
+	/// Same value as AIGateway->playerID
 	PlayerColor playerID;
 	std::shared_ptr<CCallback> cb;
 	std::mutex aiStateMutex;