ソースを参照

AI pathfinding calculated in parallel

Andrii Danylchenko 6 年 前
コミット
675406589c

+ 5 - 0
AI/VCAI/AIUtility.cpp

@@ -216,6 +216,11 @@ ui64 evaluateDanger(crint3 tile)
 }
 }
 
 
 ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
 ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
+{
+	return evaluateDanger(tile, visitor, cb.get());
+}
+
+ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const CPlayerSpecificInfoCallback * cb)
 {
 {
 	const TerrainTile * t = cb->getTile(tile, false);
 	const TerrainTile * t = cb->getTile(tile, false);
 	if(!t) //we can know about guard but can't check its tile (the edge of fow)
 	if(!t) //we can know about guard but can't check its tile (the edge of fow)

+ 1 - 0
AI/VCAI/AIUtility.h

@@ -167,6 +167,7 @@ bool isWeeklyRevisitable(const CGObjectInstance * obj);
 bool shouldVisit(HeroPtr h, const CGObjectInstance * obj);
 bool shouldVisit(HeroPtr h, const CGObjectInstance * obj);
 
 
 ui64 evaluateDanger(const CGObjectInstance * obj);
 ui64 evaluateDanger(const CGObjectInstance * obj);
+ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const CPlayerSpecificInfoCallback * cb);
 ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);
 ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);
 bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property!
 bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property!
 bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength);
 bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength);

+ 2 - 0
AI/VCAI/FuzzyEngines.cpp

@@ -202,6 +202,8 @@ TacticalAdvantageEngine::TacticalAdvantageEngine()
 
 
 float TacticalAdvantageEngine::getTacticalAdvantage(const CArmedInstance * we, const CArmedInstance * enemy)
 float TacticalAdvantageEngine::getTacticalAdvantage(const CArmedInstance * we, const CArmedInstance * enemy)
 {
 {
+	boost::unique_lock<boost::mutex> lock(mx);
+
 	float output = 1;
 	float output = 1;
 	try
 	try
 	{
 	{

+ 1 - 0
AI/VCAI/FuzzyEngines.h

@@ -35,6 +35,7 @@ private:
 	fl::InputVariable * bankPresent;
 	fl::InputVariable * bankPresent;
 	fl::InputVariable * castleWalls;
 	fl::InputVariable * castleWalls;
 	fl::OutputVariable * threat;
 	fl::OutputVariable * threat;
+	boost::mutex mx;
 };
 };
 
 
 class HeroMovementGoalEngineBase : public engineBase //in future - maybe derive from some (GoalEngineBase : public engineBase) class for handling non-movement goals with common utility for goal engines
 class HeroMovementGoalEngineBase : public engineBase //in future - maybe derive from some (GoalEngineBase : public engineBase) class for handling non-movement goals with common utility for goal engines

+ 2 - 4
AI/VCAI/Pathfinding/AINodeStorage.cpp

@@ -17,9 +17,6 @@
 #include "../../../lib/PathfinderUtil.h"
 #include "../../../lib/PathfinderUtil.h"
 #include "../../../lib/CPlayerState.h"
 #include "../../../lib/CPlayerState.h"
 
 
-extern boost::thread_specific_ptr<CCallback> cb;
-
-
 AINodeStorage::AINodeStorage(const int3 & Sizes)
 AINodeStorage::AINodeStorage(const int3 & Sizes)
 	: sizes(Sizes)
 	: sizes(Sizes)
 {
 {
@@ -187,9 +184,10 @@ std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
 	return neighbours;
 	return neighbours;
 }
 }
 
 
-void AINodeStorage::setHero(HeroPtr heroPtr)
+void AINodeStorage::setHero(HeroPtr heroPtr, const CPlayerSpecificInfoCallback * _cb)
 {
 {
 	hero = heroPtr.get();
 	hero = heroPtr.get();
+	cb = _cb;
 }
 }
 
 
 std::vector<CGPathNode *> AINodeStorage::calculateTeleportations(
 std::vector<CGPathNode *> AINodeStorage::calculateTeleportations(

+ 2 - 1
AI/VCAI/Pathfinding/AINodeStorage.h

@@ -59,6 +59,7 @@ private:
 
 
 	/// 1-3 - position on map, 4 - layer (air, water, land), 5 - chain (normal, battle, spellcast and combinations)
 	/// 1-3 - position on map, 4 - layer (air, water, land), 5 - chain (normal, battle, spellcast and combinations)
 	boost::multi_array<AIPathNode, 5> nodes;
 	boost::multi_array<AIPathNode, 5> nodes;
+	const CPlayerSpecificInfoCallback * cb;
 	const CGHeroInstance * hero;
 	const CGHeroInstance * hero;
 
 
 	STRONG_INLINE
 	STRONG_INLINE
@@ -102,7 +103,7 @@ public:
 	std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const;
 	std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const;
 	bool isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const;
 	bool isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const;
 
 
-	void setHero(HeroPtr heroPtr);
+	void setHero(HeroPtr heroPtr, const CPlayerSpecificInfoCallback * cb);
 
 
 	const CGHeroInstance * getHero() const
 	const CGHeroInstance * getHero() const
 	{
 	{

+ 30 - 4
AI/VCAI/Pathfinding/AIPathfinder.cpp

@@ -53,6 +53,15 @@ void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes)
 {
 {
 	storageMap.clear();
 	storageMap.clear();
 
 
+	auto calculatePaths = [&](const CGHeroInstance * hero, std::shared_ptr<AIPathfinding::AIPathfinderConfig> config)
+	{
+		logAi->debug("Recalculate paths for %s", hero->name);
+
+		cb->calculatePaths(config, hero);
+	};
+
+	std::vector<Task> calculationTasks;
+
 	// TODO: go parallel?
 	// TODO: go parallel?
 	for(HeroPtr hero : heroes)
 	for(HeroPtr hero : heroes)
 	{
 	{
@@ -69,12 +78,29 @@ void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes)
 		}
 		}
 
 
 		storageMap[hero] = nodeStorage;
 		storageMap[hero] = nodeStorage;
-		nodeStorage->setHero(hero.get());
+		nodeStorage->setHero(hero, cb);
 
 
 		auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, nodeStorage);
 		auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, nodeStorage);
 
 
-		logAi->debug("Recalculate paths for %s", hero->name);
-		cb->calculatePaths(config, hero.get());
+		calculationTasks.push_back(std::bind(calculatePaths, hero.get(), config));
+	}
+
+	int threadsCount = std::min(
+		boost::thread::hardware_concurrency(),
+		(uint32_t)calculationTasks.size());
+
+	if(threadsCount == 1)
+	{
+		for(auto task : calculationTasks)
+		{
+			task();
+		}
+	}
+	else
+	{
+		CThreadHelper helper(&calculationTasks, threadsCount);
+
+		helper.run();
 	}
 	}
 }
 }
 
 
@@ -95,7 +121,7 @@ void AIPathfinder::updatePaths(const HeroPtr & hero)
 		}
 		}
 
 
 		storageMap[hero] = nodeStorage;
 		storageMap[hero] = nodeStorage;
-		nodeStorage->setHero(hero.get());
+		nodeStorage->setHero(hero, cb);
 	}
 	}
 	else
 	else
 	{
 	{

+ 3 - 3
AI/VCAI/Pathfinding/Actions/BoatActions.cpp

@@ -28,7 +28,7 @@ namespace AIPathfinding
 	}
 	}
 
 
 	void SummonBoatAction::applyOnDestination(
 	void SummonBoatAction::applyOnDestination(
-		const HeroPtr & hero,
+		const CGHeroInstance * hero,
 		CDestinationNodeInfo & destination,
 		CDestinationNodeInfo & destination,
 		const PathNodeInfo & source,
 		const PathNodeInfo & source,
 		AIPathNode * dstMode,
 		AIPathNode * dstMode,
@@ -38,7 +38,7 @@ namespace AIPathfinding
 		dstMode->theNodeBefore = source.node;
 		dstMode->theNodeBefore = source.node;
 	}
 	}
 
 
-	bool SummonBoatAction::isAffordableBy(const HeroPtr & hero, const AIPathNode * source) const
+	bool SummonBoatAction::isAffordableBy(const CGHeroInstance * hero, const AIPathNode * source) const
 	{
 	{
 #ifdef VCMI_TRACE_PATHFINDER
 #ifdef VCMI_TRACE_PATHFINDER
 		logAi->trace(
 		logAi->trace(
@@ -52,7 +52,7 @@ namespace AIPathfinding
 		return hero->mana >= source->manaCost + getManaCost(hero);
 		return hero->mana >= source->manaCost + getManaCost(hero);
 	}
 	}
 
 
-	uint32_t SummonBoatAction::getManaCost(const HeroPtr & hero) const
+	uint32_t SummonBoatAction::getManaCost(const CGHeroInstance * hero) const
 	{
 	{
 		SpellID summonBoat = SpellID::SUMMON_BOAT;
 		SpellID summonBoat = SpellID::SUMMON_BOAT;
 
 

+ 3 - 3
AI/VCAI/Pathfinding/Actions/BoatActions.h

@@ -44,16 +44,16 @@ namespace AIPathfinding
 		virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
 		virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
 
 
 		virtual void applyOnDestination(
 		virtual void applyOnDestination(
-			const HeroPtr & hero,
+			const CGHeroInstance * hero,
 			CDestinationNodeInfo & destination,
 			CDestinationNodeInfo & destination,
 			const PathNodeInfo & source,
 			const PathNodeInfo & source,
 			AIPathNode * dstMode,
 			AIPathNode * dstMode,
 			const AIPathNode * srcNode) const override;
 			const AIPathNode * srcNode) const override;
 
 
-		bool isAffordableBy(const HeroPtr & hero, const AIPathNode * source) const;
+		bool isAffordableBy(const CGHeroInstance * hero, const AIPathNode * source) const;
 
 
 	private:
 	private:
-		uint32_t getManaCost(const HeroPtr & hero) const;
+		uint32_t getManaCost(const CGHeroInstance * hero) const;
 	};
 	};
 
 
 	class BuildBoatAction : public VirtualBoatAction
 	class BuildBoatAction : public VirtualBoatAction

+ 1 - 1
AI/VCAI/Pathfinding/Actions/ISpecialAction.h

@@ -21,7 +21,7 @@ public:
 	virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const = 0;
 	virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const = 0;
 
 
 	virtual void applyOnDestination(
 	virtual void applyOnDestination(
-		const HeroPtr & hero,
+		const CGHeroInstance * hero,
 		CDestinationNodeInfo & destination,
 		CDestinationNodeInfo & destination,
 		const PathNodeInfo & source,
 		const PathNodeInfo & source,
 		AIPathNode * dstMode,
 		AIPathNode * dstMode,

+ 1 - 1
AI/VCAI/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp

@@ -122,7 +122,7 @@ namespace AIPathfinding
 			}
 			}
 
 
 			auto hero = nodeStorage->getHero();
 			auto hero = nodeStorage->getHero();
-			auto danger = evaluateDanger(destination.coord, hero);
+			auto danger = evaluateDanger(destination.coord, hero, cb);
 
 
 			destination.node = battleNode;
 			destination.node = battleNode;
 			nodeStorage->commit(destination, source);
 			nodeStorage->commit(destination, source);