mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Nullkiller: tbb and hero chain calculation optimization and parallel cpathfinder initialization
This commit is contained in:
		
				
					committed by
					
						 Andrii Danylchenko
						Andrii Danylchenko
					
				
			
			
				
	
			
			
			
						parent
						
							3480f17a68
						
					
				
				
					commit
					fb3cda666f
				
			| @@ -189,7 +189,7 @@ bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectIns | ||||
| 	const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos()); | ||||
| 	const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos()); | ||||
|  | ||||
| 	return ln->cost < rn->cost; | ||||
| 	return ln->getCost() < rn->getCost(); | ||||
| } | ||||
|  | ||||
| bool isSafeToVisit(HeroPtr h, const CCreatureSet * heroArmy, uint64_t dangerStrength) | ||||
|   | ||||
| @@ -137,6 +137,8 @@ else() | ||||
| 	target_link_libraries(VCAI PRIVATE fl-static vcmi) | ||||
| endif() | ||||
|  | ||||
| target_link_libraries(VCAI PRIVATE TBB::tbb) | ||||
|  | ||||
| vcmi_set_output_dir(VCAI "AI") | ||||
|  | ||||
| set_target_properties(VCAI PROPERTIES ${PCH_PROPERTIES}) | ||||
|   | ||||
| @@ -44,19 +44,19 @@ struct armyStructure | ||||
|  | ||||
| armyStructure evaluateArmyStructure(const CArmedInstance * army) | ||||
| { | ||||
| 	ui64 totalStrenght = army->getArmyStrength(); | ||||
| 	double walkersStrenght = 0; | ||||
| 	double flyersStrenght = 0; | ||||
| 	double shootersStrenght = 0; | ||||
| 	ui64 totalStrength = army->getArmyStrength(); | ||||
| 	double walkersStrength = 0; | ||||
| 	double flyersStrength = 0; | ||||
| 	double shootersStrength = 0; | ||||
| 	ui32 maxSpeed = 0; | ||||
|  | ||||
| 	static const CSelector selectorSHOOTER = Selector::type(Bonus::SHOOTER); | ||||
| 	static const CSelector selectorSHOOTER = Selector::type()(Bonus::SHOOTER); | ||||
| 	static const std::string keySHOOTER = "type_"+std::to_string((int32_t)Bonus::SHOOTER); | ||||
|  | ||||
| 	static const CSelector selectorFLYING = Selector::type(Bonus::FLYING); | ||||
| 	static const CSelector selectorFLYING = Selector::type()(Bonus::FLYING); | ||||
| 	static const std::string keyFLYING = "type_"+std::to_string((int32_t)Bonus::FLYING); | ||||
|  | ||||
| 	static const CSelector selectorSTACKS_SPEED = Selector::type(Bonus::STACKS_SPEED); | ||||
| 	static const CSelector selectorSTACKS_SPEED = Selector::type()(Bonus::STACKS_SPEED); | ||||
| 	static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)Bonus::STACKS_SPEED); | ||||
|  | ||||
| 	for(auto s : army->Slots()) | ||||
| @@ -65,23 +65,23 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army) | ||||
| 		const CCreature * creature = s.second->type; | ||||
| 		if(creature->hasBonus(selectorSHOOTER, keySHOOTER)) | ||||
| 		{ | ||||
| 			shootersStrenght += s.second->getPower(); | ||||
| 			shootersStrength += s.second->getPower(); | ||||
| 			walker = false; | ||||
| 		} | ||||
| 		if(creature->hasBonus(selectorFLYING, keyFLYING)) | ||||
| 		{ | ||||
| 			flyersStrenght += s.second->getPower(); | ||||
| 			flyersStrength += s.second->getPower(); | ||||
| 			walker = false; | ||||
| 		} | ||||
| 		if(walker) | ||||
| 			walkersStrenght += s.second->getPower(); | ||||
| 			walkersStrength += s.second->getPower(); | ||||
|  | ||||
| 		vstd::amax(maxSpeed, creature->valOfBonuses(selectorSTACKS_SPEED, keySTACKS_SPEED)); | ||||
| 	} | ||||
| 	armyStructure as; | ||||
| 	as.walkers = walkersStrenght / totalStrenght; | ||||
| 	as.shooters = shootersStrenght / totalStrenght; | ||||
| 	as.flyers = flyersStrenght / totalStrenght; | ||||
| 	as.walkers = static_cast<float>(walkersStrength / totalStrength); | ||||
| 	as.shooters = static_cast<float>(shootersStrength / totalStrength); | ||||
| 	as.flyers = static_cast<float>(flyersStrength / totalStrength); | ||||
| 	as.maxSpeed = maxSpeed; | ||||
| 	assert(as.walkers || as.flyers || as.shooters); | ||||
| 	return as; | ||||
|   | ||||
| @@ -262,4 +262,4 @@ void Nullkiller::makeTurn() | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| } | ||||
| @@ -9,6 +9,8 @@ | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #include <boost/asio.hpp> | ||||
|  | ||||
| #include "PriorityEvaluator.h" | ||||
| #include "FuzzyHelper.h" | ||||
| #include "AIMemory.h" | ||||
|   | ||||
| @@ -148,7 +148,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals() | ||||
| 			{ | ||||
| 				auto dwelling = dynamic_cast<const CGDwelling *>(obj); | ||||
|  | ||||
| 				ui32 val = std::min<ui32>(value, ai->ah->howManyReinforcementsCanBuy(hero.get(), dwelling)); | ||||
| 				ui32 val = std::min((ui32)value, (ui32)ai->ah->howManyReinforcementsCanBuy(hero.get(), dwelling)); | ||||
|  | ||||
| 				if(val) | ||||
| 				{ | ||||
|   | ||||
| @@ -95,11 +95,11 @@ TSubgoal Win::whatToDoToAchieve() | ||||
| 					{ | ||||
| 						auto towns = cb->getTownsInfo(); | ||||
| 						towns.erase(boost::remove_if(towns, | ||||
| 									     [](const CGTownInstance * t) -> bool | ||||
| 										[](const CGTownInstance * t) -> bool | ||||
| 							{ | ||||
| 								return vstd::contains(t->forbiddenBuildings, BuildingID::GRAIL); | ||||
| 							}), | ||||
| 							    towns.end()); | ||||
| 								towns.end()); | ||||
| 						boost::sort(towns, CDistanceSorter(h.get())); | ||||
| 						if(towns.size()) | ||||
| 						{ | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| * | ||||
| */ | ||||
| #include "StdInc.h" | ||||
| #include <tbb/tbb.h> | ||||
| #include "AINodeStorage.h" | ||||
| #include "Actions/TownPortalAction.h" | ||||
| #include "../Goals/Goals.h" | ||||
| @@ -19,6 +20,8 @@ | ||||
| #include "../../../lib/PathfinderUtil.h" | ||||
| #include "../../../lib/CPlayerState.h" | ||||
|  | ||||
| using namespace tbb; | ||||
|  | ||||
| std::shared_ptr<boost::multi_array<AIPathNode, 5>> AISharedStorage::shared; | ||||
| std::set<int3> commitedTiles; | ||||
| std::set<int3> commitedTilesInitial; | ||||
| @@ -61,50 +64,56 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta | ||||
| 		return; | ||||
|  | ||||
| 	//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline | ||||
| 	int3 pos; | ||||
| 	const PlayerColor player = playerID; | ||||
| 	const PlayerColor fowPlayer = ai->playerID; | ||||
| 	const int3 sizes = gs->getMapSize(); | ||||
| 	const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(fowPlayer)->fogOfWarMap; | ||||
| 	const int3 sizes = gs->getMapSize(); | ||||
|  | ||||
| 	//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching) | ||||
| 	const bool useFlying = options.useFlying; | ||||
| 	const bool useWaterWalking = options.useWaterWalking; | ||||
|  | ||||
| 	for(pos.x=0; pos.x < sizes.x; ++pos.x) | ||||
| 	parallel_for(blocked_range<size_t>(0, sizes.x), [&](const blocked_range<size_t>& r) | ||||
| 	{ | ||||
| 		for(pos.y=0; pos.y < sizes.y; ++pos.y) | ||||
| 		//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching) | ||||
| 		const bool useFlying = options.useFlying; | ||||
| 		const bool useWaterWalking = options.useWaterWalking; | ||||
| 		const PlayerColor player = playerID; | ||||
|  | ||||
| 		int3 pos; | ||||
|  | ||||
| 		for(pos.x = r.begin(); pos.x != r.end(); ++pos.x) | ||||
| 		{ | ||||
| 			for(pos.z=0; pos.z < sizes.z; ++pos.z) | ||||
| 			for(pos.y = 0; pos.y < sizes.y; ++pos.y) | ||||
| 			{ | ||||
| 				const TerrainTile * tile = &gs->map->getTile(pos); | ||||
| 				switch(tile->terType) | ||||
| 				for(pos.z = 0; pos.z < sizes.z; ++pos.z) | ||||
| 				{ | ||||
| 				case ETerrainType::ROCK: | ||||
| 					break; | ||||
| 					const TerrainTile * tile = &gs->map->getTile(pos); | ||||
| 					switch(tile->terType) | ||||
| 					{ | ||||
| 					case ETerrainType::ROCK: | ||||
| 						break; | ||||
|  | ||||
| 				case ETerrainType::WATER: | ||||
| 					resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs)); | ||||
| 					if(useFlying) | ||||
| 						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs)); | ||||
| 					if(useWaterWalking) | ||||
| 						resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs)); | ||||
| 					break; | ||||
| 					case ETerrainType::WATER: | ||||
| 						resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs)); | ||||
| 						if(useFlying) | ||||
| 							resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs)); | ||||
| 						if(useWaterWalking) | ||||
| 							resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs)); | ||||
| 						break; | ||||
|  | ||||
| 				default: | ||||
| 					resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs)); | ||||
| 					if(useFlying) | ||||
| 						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs)); | ||||
| 					break; | ||||
| 					default: | ||||
| 						resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs)); | ||||
| 						if(useFlying) | ||||
| 							resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs)); | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| void AINodeStorage::clear() | ||||
| { | ||||
| 	CCreature::DisableChildLinkage = true; | ||||
| 	actors.clear(); | ||||
| 	CCreature::DisableChildLinkage = false; | ||||
| 	heroChainPass = EHeroChainPass::INITIAL; | ||||
| 	heroChainTurn = 0; | ||||
| 	heroChainMaxTurns = 1; | ||||
| @@ -161,7 +170,7 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode( | ||||
| std::vector<CGPathNode *> AINodeStorage::getInitialNodes() | ||||
| { | ||||
| 	if(heroChainPass) | ||||
| 	{ | ||||
| { | ||||
| 		calculateTownPortalTeleportations(heroChain); | ||||
|  | ||||
| 		return heroChain; | ||||
| @@ -184,7 +193,7 @@ std::vector<CGPathNode *> AINodeStorage::getInitialNodes() | ||||
| 		initialNode->turns = actor->initialTurn; | ||||
| 		initialNode->moveRemains = actor->initialMovement; | ||||
| 		initialNode->danger = 0; | ||||
| 		initialNode->cost = actor->initialTurn; | ||||
| 		initialNode->setCost(actor->initialTurn); | ||||
| 		initialNode->action = CGPathNode::ENodeAction::NORMAL; | ||||
|  | ||||
| 		if(actor->isMovable) | ||||
| @@ -205,7 +214,7 @@ std::vector<CGPathNode *> AINodeStorage::getInitialNodes() | ||||
| void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility) | ||||
| { | ||||
| 	for(AIPathNode & heroNode : nodes.get(coord, layer)) | ||||
| 	{ | ||||
| { | ||||
| 		heroNode.actor = nullptr; | ||||
| 		heroNode.danger = 0; | ||||
| 		heroNode.manaCost = 0; | ||||
| @@ -246,7 +255,7 @@ void AINodeStorage::commit( | ||||
| 	float cost) const | ||||
| { | ||||
| 	destination->action = action; | ||||
| 	destination->cost = cost; | ||||
| 	destination->setCost(cost); | ||||
| 	destination->moveRemains = movementLeft; | ||||
| 	destination->turns = turn; | ||||
| 	destination->armyLoss = source->armyLoss; | ||||
| @@ -360,61 +369,133 @@ bool AINodeStorage::calculateHeroChainFinal() | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	return heroChain.size(); | ||||
| } | ||||
|  | ||||
| struct DelayedWork | ||||
| { | ||||
| 	AIPathNode * carrier; | ||||
| 	AIPathNode * other; | ||||
|  | ||||
| 	DelayedWork() | ||||
| 	{ | ||||
| 	} | ||||
| 	 | ||||
| 	DelayedWork(AIPathNode * carrier, AIPathNode * other) : carrier(carrier), other(other) | ||||
| 	{ | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class HeroChainCalculationTask | ||||
| { | ||||
| private: | ||||
| 	AISharedStorage & nodes; | ||||
| 	AINodeStorage & storage; | ||||
| 	std::vector<AIPathNode *> existingChains; | ||||
| 	std::vector<ExchangeCandidate> newChains; | ||||
| 	uint64_t chainMask; | ||||
| 	int heroChainTurn; | ||||
| 	std::vector<CGPathNode *> heroChain; | ||||
| 	const std::vector<int3> & tiles; | ||||
|  | ||||
| public: | ||||
| 	HeroChainCalculationTask( | ||||
| 		AINodeStorage & storage, AISharedStorage & nodes, const std::vector<int3> & tiles, uint64_t chainMask, int heroChainTurn) | ||||
| 		:existingChains(), newChains(), nodes(nodes), storage(storage), chainMask(chainMask), heroChainTurn(heroChainTurn), heroChain(), tiles(tiles) | ||||
| 	{ | ||||
| 		existingChains.reserve(NUM_CHAINS); | ||||
| 		newChains.reserve(NUM_CHAINS); | ||||
| 	} | ||||
|  | ||||
| 	void execute(const blocked_range<size_t>& r) | ||||
| 	{ | ||||
| 		for(int i = r.begin(); i != r.end(); i++) | ||||
| 		{ | ||||
| 			auto & pos = tiles[i]; | ||||
|  | ||||
| 			for(auto layer : phisycalLayers) | ||||
| 			{ | ||||
| 				auto chains = nodes.get(pos, layer); | ||||
|  | ||||
| 				// fast cut inactive nodes | ||||
| 				if(chains[0].blocked()) | ||||
| 					continue; | ||||
|  | ||||
| 				existingChains.clear(); | ||||
| 				newChains.clear(); | ||||
|  | ||||
| 				for(AIPathNode & node : chains) | ||||
| 				{ | ||||
| 					if(node.turns <= heroChainTurn && node.action != CGPathNode::ENodeAction::UNKNOWN) | ||||
| 						existingChains.push_back(&node); | ||||
| 				} | ||||
|  | ||||
| 				std::random_shuffle(existingChains.begin(), existingChains.end()); | ||||
|  | ||||
| 				for(AIPathNode * node : existingChains) | ||||
| 				{ | ||||
| 					if(node->actor->isMovable) | ||||
| 					{ | ||||
| 						calculateHeroChain(node, existingChains, newChains); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				cleanupInefectiveChains(newChains); | ||||
| 				addHeroChain(newChains); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void calculateHeroChain( | ||||
| 		AIPathNode * srcNode, | ||||
| 		const std::vector<AIPathNode *> & variants, | ||||
| 		std::vector<ExchangeCandidate> & result); | ||||
|  | ||||
| 	void calculateHeroChain( | ||||
| 		AIPathNode * carrier, | ||||
| 		AIPathNode * other, | ||||
| 		std::vector<ExchangeCandidate> & result); | ||||
|  | ||||
| 	void cleanupInefectiveChains(std::vector<ExchangeCandidate> & result) const; | ||||
| 	void addHeroChain(const std::vector<ExchangeCandidate> & result); | ||||
|  | ||||
| 	ExchangeCandidate calculateExchange( | ||||
| 		ChainActor * exchangeActor, | ||||
| 		AIPathNode * carrierParentNode, | ||||
| 		AIPathNode * otherParentNode) const; | ||||
|  | ||||
| 	void flushResult(std::vector<CGPathNode *> & result) | ||||
| 	{ | ||||
| 		vstd::concatenate(result, heroChain); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| bool AINodeStorage::calculateHeroChain() | ||||
| { | ||||
| 	heroChainPass = EHeroChainPass::CHAIN; | ||||
| 	heroChain.resize(0); | ||||
| 	heroChain.clear(); | ||||
|  | ||||
| 	std::vector<AIPathNode *> existingChains; | ||||
| 	std::vector<ExchangeCandidate> newChains; | ||||
| 	std::vector<int3> data(commitedTiles.begin(), commitedTiles.end()); | ||||
|  | ||||
| 	existingChains.reserve(NUM_CHAINS); | ||||
| 	newChains.reserve(NUM_CHAINS); | ||||
| 	CCreature::DisableChildLinkage = true; | ||||
|  | ||||
| 	for(auto & pos : commitedTiles) | ||||
| 	{ | ||||
| 		for(auto layer : phisycalLayers) | ||||
| 		{ | ||||
| 			auto chains = nodes.get(pos, layer); | ||||
| 	auto r = blocked_range<size_t>(0, data.size()); | ||||
| 	HeroChainCalculationTask task(*this, nodes, data, chainMask, heroChainTurn); | ||||
|  | ||||
| 			// fast cut inactive nodes | ||||
| 			if(chains[0].blocked()) | ||||
| 				continue; | ||||
| 	task.execute(r); | ||||
| 	task.flushResult(heroChain); | ||||
|  | ||||
| 			existingChains.clear(); | ||||
| 			newChains.clear(); | ||||
|  | ||||
| 			for(AIPathNode & node : chains) | ||||
| 			{ | ||||
| 				if(node.turns <= heroChainTurn && node.action != CGPathNode::ENodeAction::UNKNOWN) | ||||
| 					existingChains.push_back(&node); | ||||
| 			} | ||||
|  | ||||
| 			for(AIPathNode * node : existingChains) | ||||
| 			{ | ||||
| 				if(node->actor->isMovable) | ||||
| 				{ | ||||
| 					calculateHeroChain(node, existingChains, newChains); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			cleanupInefectiveChains(newChains); | ||||
| 			addHeroChain(newChains); | ||||
| 		} | ||||
| 	} | ||||
| 	CCreature::DisableChildLinkage = false; | ||||
|  | ||||
| 	commitedTiles.clear(); | ||||
|  | ||||
| 	return heroChain.size(); | ||||
| 	return !heroChain.empty(); | ||||
| } | ||||
|  | ||||
| bool AINodeStorage::selectFirstActor() | ||||
| { | ||||
| 	if(!actors.size()) | ||||
| 	if(actors.empty()) | ||||
| 		return false; | ||||
|  | ||||
| 	auto strongest = *vstd::maxElementByFun(actors, [](std::shared_ptr<ChainActor> actor) -> uint64_t | ||||
| @@ -454,6 +535,9 @@ bool AINodeStorage::selectNextActor() | ||||
|  | ||||
| 	if(nextActor != actors.end()) | ||||
| 	{ | ||||
| 		if(nextActor->get()->armyValue < 1000) | ||||
| 			return false; | ||||
|  | ||||
| 		chainMask = nextActor->get()->chainMask; | ||||
| 		commitedTiles = commitedTilesInitial; | ||||
|  | ||||
| @@ -463,22 +547,36 @@ bool AINodeStorage::selectNextActor() | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| void AINodeStorage::cleanupInefectiveChains(std::vector<ExchangeCandidate> & result) const | ||||
| void HeroChainCalculationTask::cleanupInefectiveChains(std::vector<ExchangeCandidate> & result) const | ||||
| { | ||||
| 	vstd::erase_if(result, [&](const ExchangeCandidate & chainInfo) -> bool | ||||
| 	{ | ||||
| 		auto pos = chainInfo.coord; | ||||
| 		auto chains = nodes.get(pos, EPathfindingLayer::LAND); | ||||
| 		auto isNotEffective = storage.hasBetterChain(chainInfo.carrierParent, &chainInfo, chains) | ||||
| 			|| storage.hasBetterChain(chainInfo.carrierParent, &chainInfo, result); | ||||
|  | ||||
| 		return hasBetterChain(chainInfo.carrierParent, &chainInfo, chains) | ||||
| 			|| hasBetterChain(chainInfo.carrierParent, &chainInfo, result); | ||||
| #if PATHFINDER_TRACE_LEVEL >= 2 | ||||
| 		if(isNotEffective) | ||||
| 		{ | ||||
| 			logAi->trace( | ||||
| 				"Skip exchange %s[%x] -> %s[%x] at %s is ineficient", | ||||
| 				chainInfo.otherParent->actor->toString(),  | ||||
| 				chainInfo.otherParent->actor->chainMask, | ||||
| 				chainInfo.carrierParent->actor->toString(), | ||||
| 				chainInfo.carrierParent->actor->chainMask, | ||||
| 				chainInfo.carrierParent->coord.toString()); | ||||
| 		} | ||||
| #endif | ||||
|  | ||||
| 		return isNotEffective; | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| void AINodeStorage::calculateHeroChain( | ||||
| void HeroChainCalculationTask::calculateHeroChain( | ||||
| 	AIPathNode * srcNode,  | ||||
| 	const std::vector<AIPathNode *> & variants,  | ||||
| 	std::vector<ExchangeCandidate> & result) const | ||||
| 	std::vector<ExchangeCandidate> & result) | ||||
| { | ||||
| 	for(AIPathNode * node : variants) | ||||
| 	{ | ||||
| @@ -531,16 +629,15 @@ void AINodeStorage::calculateHeroChain( | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void AINodeStorage::calculateHeroChain( | ||||
| void HeroChainCalculationTask::calculateHeroChain( | ||||
| 	AIPathNode * carrier,  | ||||
| 	AIPathNode * other,  | ||||
| 	std::vector<ExchangeCandidate> & result) const | ||||
| 	std::vector<ExchangeCandidate> & result) | ||||
| {	 | ||||
| 	if(carrier->armyLoss < carrier->actor->armyValue | ||||
| 		&& (carrier->action != CGPathNode::BATTLE || (carrier->actor->allowBattle && carrier->specialAction)) | ||||
| 		&& carrier->action != CGPathNode::BLOCKING_VISIT | ||||
| 		&& (other->armyLoss == 0 || other->armyLoss < other->actor->armyValue) | ||||
| 		&& carrier->actor->canExchange(other->actor)) | ||||
| 		&& (other->armyLoss == 0 || other->armyLoss < other->actor->armyValue)) | ||||
| 	{ | ||||
| #if PATHFINDER_TRACE_LEVEL >= 2 | ||||
| 		logAi->trace( | ||||
| @@ -566,20 +663,20 @@ void AINodeStorage::calculateHeroChain( | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		auto newActor = carrier->actor->exchange(other->actor); | ||||
| 		auto newActor = carrier->actor->tryExchange(other->actor); | ||||
| 		 | ||||
| 		result.push_back(calculateExchange(newActor, carrier, other)); | ||||
| 		if(newActor) result.push_back(calculateExchange(newActor, carrier, other)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void AINodeStorage::addHeroChain(const std::vector<ExchangeCandidate> & result) | ||||
| void HeroChainCalculationTask::addHeroChain(const std::vector<ExchangeCandidate> & result) | ||||
| { | ||||
| 	for(const ExchangeCandidate & chainInfo : result) | ||||
| 	{ | ||||
| 		auto carrier = chainInfo.carrierParent; | ||||
| 		auto newActor = chainInfo.actor; | ||||
| 		auto other = chainInfo.otherParent; | ||||
| 		auto chainNodeOptional = getOrCreateNode(carrier->coord, carrier->layer, newActor); | ||||
| 		auto chainNodeOptional = storage.getOrCreateNode(carrier->coord, carrier->layer, newActor); | ||||
|  | ||||
| 		if(!chainNodeOptional) | ||||
| 		{ | ||||
| @@ -594,24 +691,34 @@ void AINodeStorage::addHeroChain(const std::vector<ExchangeCandidate> & result) | ||||
| 		if(exchangeNode->action != CGPathNode::ENodeAction::UNKNOWN) | ||||
| 		{ | ||||
| #if PATHFINDER_TRACE_LEVEL >= 2 | ||||
| 			logAi->trace("Exchange at %s node is already in use. Blocked.", carrier->coord.toString()); | ||||
| 			logAi->trace( | ||||
| 				"Skip exchange %s[%x] -> %s[%x] at %s because node is in use", | ||||
| 				other->actor->toString(), | ||||
| 				other->actor->chainMask, | ||||
| 				carrier->actor->toString(), | ||||
| 				carrier->actor->chainMask, | ||||
| 				carrier->coord.toString()); | ||||
| #endif | ||||
| 			continue; | ||||
| 		} | ||||
| 		 | ||||
| 		if(exchangeNode->turns != 0xFF && exchangeNode->cost < chainInfo.cost) | ||||
| 		if(exchangeNode->turns != 0xFF && exchangeNode->getCost() < chainInfo.getCost()) | ||||
| 		{ | ||||
| #if PATHFINDER_TRACE_LEVEL >= 2 | ||||
| 			logAi->trace( | ||||
| 				"Exchange at %s is is not effective enough. %f < %f",  | ||||
| 				exchangeNode->coord.toString(),  | ||||
| 				exchangeNode->getCost(),  | ||||
| 				"Skip exchange %s[%x] -> %s[%x] at %s because not effective enough. %f < %f", | ||||
| 				other->actor->toString(), | ||||
| 				other->actor->chainMask, | ||||
| 				carrier->actor->toString(), | ||||
| 				carrier->actor->chainMask, | ||||
| 				carrier->coord.toString(), | ||||
| 				exchangeNode->getCost(), | ||||
| 				chainInfo.getCost()); | ||||
| #endif | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		commit(exchangeNode, carrier, carrier->action, chainInfo.turns, chainInfo.moveRemains, chainInfo.cost); | ||||
| 		storage.commit(exchangeNode, carrier, carrier->action, chainInfo.turns, chainInfo.moveRemains, chainInfo.getCost()); | ||||
|  | ||||
| 		if(carrier->specialAction || carrier->chainOther) | ||||
| 		{ | ||||
| @@ -644,7 +751,7 @@ void AINodeStorage::addHeroChain(const std::vector<ExchangeCandidate> & result) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ExchangeCandidate AINodeStorage::calculateExchange( | ||||
| ExchangeCandidate HeroChainCalculationTask::calculateExchange( | ||||
| 	ChainActor * exchangeActor,  | ||||
| 	AIPathNode * carrierParentNode,  | ||||
| 	AIPathNode * otherParentNode) const | ||||
| @@ -658,7 +765,7 @@ ExchangeCandidate AINodeStorage::calculateExchange( | ||||
| 	candidate.actor = exchangeActor; | ||||
| 	candidate.armyLoss = carrierParentNode->armyLoss + otherParentNode->armyLoss; | ||||
| 	candidate.turns = carrierParentNode->turns; | ||||
| 	candidate.cost = carrierParentNode->cost + otherParentNode->cost / 1000.0; | ||||
| 	candidate.setCost(carrierParentNode->getCost() + otherParentNode->getCost() / 1000.0); | ||||
| 	candidate.moveRemains = carrierParentNode->moveRemains; | ||||
|  | ||||
| 	if(carrierParentNode->turns < otherParentNode->turns) | ||||
| @@ -668,7 +775,7 @@ ExchangeCandidate AINodeStorage::calculateExchange( | ||||
| 			+ carrierParentNode->moveRemains / (float)moveRemains; | ||||
|  | ||||
| 		candidate.turns = otherParentNode->turns; | ||||
| 		candidate.cost += waitingCost; | ||||
| 		candidate.setCost(candidate.getCost() + waitingCost); | ||||
| 		candidate.moveRemains = moveRemains; | ||||
| 	} | ||||
|  | ||||
| @@ -873,7 +980,7 @@ struct TowmPortalFinder | ||||
| 					continue; | ||||
| 			} | ||||
|  | ||||
| 			if(!bestNode || bestNode->cost > node->cost) | ||||
| 			if(!bestNode || bestNode->getCost() > node->getCost()) | ||||
| 				bestNode = node; | ||||
| 		} | ||||
|  | ||||
| @@ -895,9 +1002,9 @@ struct TowmPortalFinder | ||||
| 		AIPathNode * node = nodeOptional.get(); | ||||
| 		float movementCost = (float)movementNeeded / (float)hero->maxMovePoints(EPathfindingLayer::LAND); | ||||
|  | ||||
| 		movementCost += bestNode->cost; | ||||
| 		movementCost += bestNode->getCost(); | ||||
|  | ||||
| 		if(node->action == CGPathNode::UNKNOWN || node->cost > movementCost) | ||||
| 		if(node->action == CGPathNode::UNKNOWN || node->getCost() > movementCost) | ||||
| 		{ | ||||
| 			nodeStorage->commit( | ||||
| 				node, | ||||
| @@ -1009,7 +1116,7 @@ bool AINodeStorage::hasBetterChain( | ||||
|  | ||||
| 		if(node.danger <= candidateNode->danger && candidateNode->actor == node.actor->battleActor) | ||||
| 		{ | ||||
| 			if(node.cost < candidateNode->cost) | ||||
| 			if(node.getCost() < candidateNode->getCost()) | ||||
| 			{ | ||||
| #if PATHFINDER_TRACE_LEVEL >= 2 | ||||
| 				logAi->trace( | ||||
| @@ -1033,7 +1140,7 @@ bool AINodeStorage::hasBetterChain( | ||||
| 		auto candidateArmyValue = candidateActor->armyValue - candidateNode->armyLoss; | ||||
|  | ||||
| 		if(nodeArmyValue > candidateArmyValue | ||||
| 			&& node.cost <= candidateNode->cost) | ||||
| 			&& node.getCost() <= candidateNode->getCost()) | ||||
| 		{ | ||||
| #if PATHFINDER_TRACE_LEVEL >= 2 | ||||
| 			logAi->trace( | ||||
| @@ -1052,10 +1159,10 @@ bool AINodeStorage::hasBetterChain( | ||||
| 		{ | ||||
| 			if(nodeArmyValue == candidateArmyValue | ||||
| 				&& nodeActor->heroFightingStrength >= candidateActor->heroFightingStrength | ||||
| 				&& node.cost <= candidateNode->cost) | ||||
| 				&& node.getCost() <= candidateNode->getCost()) | ||||
| 			{ | ||||
| 				if(nodeActor->heroFightingStrength == candidateActor->heroFightingStrength | ||||
| 					&& node.cost == candidateNode->cost | ||||
| 					&& node.getCost() == candidateNode->getCost() | ||||
| 					&& &node < candidateNode) | ||||
| 				{ | ||||
| 					continue; | ||||
| @@ -1141,7 +1248,8 @@ void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path, int pa | ||||
| 		//if(node->actor->hero->visitablePos() != node->coord) | ||||
| 		{ | ||||
| 			AIPathNodeInfo pathNode; | ||||
| 			pathNode.cost = node->cost; | ||||
|  | ||||
| 			pathNode.cost = node->getCost(); | ||||
| 			pathNode.targetHero = node->actor->hero; | ||||
| 			pathNode.chainMask = node->actor->chainMask; | ||||
| 			pathNode.specialAction = node->specialAction; | ||||
|   | ||||
| @@ -23,6 +23,14 @@ | ||||
| #include "Actions/SpecialAction.h" | ||||
| #include "Actors.h" | ||||
|  | ||||
| namespace AIPathfinding | ||||
| { | ||||
| 	const int BUCKET_COUNT = 11; | ||||
| 	const int BUCKET_SIZE = GameConstants::MAX_HEROES_PER_PLAYER; | ||||
| 	const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE; | ||||
| 	const int THREAD_COUNT = 8; | ||||
| } | ||||
|  | ||||
| struct AIPathNode : public CGPathNode | ||||
| { | ||||
| 	uint64_t danger; | ||||
| @@ -228,28 +236,14 @@ public: | ||||
| 		return (uint64_t)(armyValue * ratio * ratio * ratio); | ||||
| 	} | ||||
|  | ||||
| private: | ||||
| 	STRONG_INLINE | ||||
| 	void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility); | ||||
| 	STRONG_INLINE int getBucket(const ChainActor * actor) const | ||||
| 	{ | ||||
| 		return ((uintptr_t)actor * 395) % AIPathfinding::BUCKET_COUNT; | ||||
| 	} | ||||
|  | ||||
| 	void calculateHeroChain( | ||||
| 		AIPathNode * srcNode,  | ||||
| 		const std::vector<AIPathNode *> & variants,  | ||||
| 		std::vector<ExchangeCandidate> & result) const; | ||||
|  | ||||
| 	void calculateHeroChain( | ||||
| 		AIPathNode * carrier,  | ||||
| 		AIPathNode * other,  | ||||
| 		std::vector<ExchangeCandidate> & result) const; | ||||
| 	 | ||||
| 	void cleanupInefectiveChains(std::vector<ExchangeCandidate> & result) const; | ||||
| 	void addHeroChain(const std::vector<ExchangeCandidate> & result); | ||||
|  | ||||
| 	void calculateTownPortalTeleportations(std::vector<CGPathNode *> & neighbours); | ||||
| 	void fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const; | ||||
|  | ||||
| 	ExchangeCandidate calculateExchange( | ||||
| 		ChainActor * exchangeActor,  | ||||
| 		AIPathNode * carrierParentNode,  | ||||
| 		AIPathNode * otherParentNode) const; | ||||
| }; | ||||
|   | ||||
| @@ -114,7 +114,7 @@ namespace AIPathfinding | ||||
| 			source->manaCost); | ||||
| #endif | ||||
|  | ||||
| 		return hero->mana >= source->manaCost + getManaCost(hero); | ||||
| 		return hero->mana >= (si32)(source->manaCost + getManaCost(hero)); | ||||
| 	} | ||||
|  | ||||
| 	std::string SummonBoatAction::toString() const | ||||
|   | ||||
| @@ -145,14 +145,11 @@ void HeroActor::setupSpecialActors() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ChainActor * ChainActor::exchange(const ChainActor * specialActor, const ChainActor * other) const | ||||
| ChainActor * ChainActor::tryExchange(const ChainActor * specialActor, const ChainActor * other) const | ||||
| { | ||||
| 	return baseActor->exchange(specialActor, other); | ||||
| } | ||||
| 	if(!isMovable) return nullptr; | ||||
|  | ||||
| bool ChainActor::canExchange(const ChainActor * other) const | ||||
| { | ||||
| 	return isMovable && baseActor->canExchange(other); | ||||
| 	return baseActor->tryExchange(specialActor, other); | ||||
| } | ||||
|  | ||||
| namespace vstd | ||||
| @@ -172,71 +169,12 @@ namespace vstd | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool HeroActor::canExchange(const ChainActor * other) const | ||||
| { | ||||
| 	return exchangeMap->canExchange(other); | ||||
| } | ||||
|  | ||||
| bool HeroExchangeMap::canExchange(const ChainActor * other) | ||||
| { | ||||
| 	return vstd::getOrCompute(canExchangeCache, other, [&](bool & result) { | ||||
| 		result = (actor->chainMask & other->chainMask) == 0; | ||||
|  | ||||
| 		if(result) | ||||
| 		{ | ||||
| 			TResources resources = ai->cb->getResourceAmount(); | ||||
|  | ||||
| 			if(!resources.canAfford(actor->armyCost + other->armyCost)) | ||||
| 			{ | ||||
| 				result = false; | ||||
| #if PATHFINDER_TRACE_LEVEL >= 2 | ||||
| 				logAi->trace( | ||||
| 					"Can not afford exchange because of total cost %s but we have %s", | ||||
| 					(actor->armyCost + other->armyCost).toString(), | ||||
| 					resources.toString()); | ||||
| #endif | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			TResources availableResources = resources - actor->armyCost - other->armyCost; | ||||
|  | ||||
| 			auto upgradeInfo = ai->armyManager->calculateCreateresUpgrade( | ||||
| 				actor->creatureSet,  | ||||
| 				other->getActorObject(), | ||||
| 				availableResources); | ||||
|  | ||||
| 			uint64_t reinforcment = upgradeInfo.upgradeValue; | ||||
| 			 | ||||
| 			if(other->creatureSet->Slots().size()) | ||||
| 				reinforcment += ai->armyManager->howManyReinforcementsCanGet(actor->hero, actor->creatureSet, other->creatureSet); | ||||
| 			 | ||||
| 			auto obj = other->getActorObject(); | ||||
| 			if(obj && obj->ID == Obj::TOWN) | ||||
| 			{ | ||||
| 				reinforcment += ai->armyManager->howManyReinforcementsCanBuy( | ||||
| 					actor->creatureSet, | ||||
| 					ai->cb->getTown(obj->id), | ||||
| 					availableResources - upgradeInfo.upgradeCost); | ||||
| 			} | ||||
|  | ||||
| #if PATHFINDER_TRACE_LEVEL >= 2 | ||||
| 			logAi->trace( | ||||
| 				"Exchange %s->%s reinforcement: %d, %f%%", | ||||
| 				actor->toString(), | ||||
| 				other->toString(), | ||||
| 				reinforcment, | ||||
| 				100.0f * reinforcment / actor->armyValue); | ||||
| #endif | ||||
|  | ||||
| 			result = reinforcment > actor->armyValue / 10 || reinforcment > 1000; | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| ChainActor * HeroActor::exchange(const ChainActor * specialActor, const ChainActor * other) const | ||||
| ChainActor * HeroActor::tryExchange(const ChainActor * specialActor, const ChainActor * other) const | ||||
| { | ||||
| 	const ChainActor * otherBase = other->baseActor; | ||||
| 	HeroActor * result = exchangeMap->exchange(otherBase); | ||||
| 	HeroActor * result = exchangeMap->tryExchange(otherBase); | ||||
|  | ||||
| 	if(!result) return nullptr; | ||||
|  | ||||
| 	if(specialActor == this) | ||||
| 		return result; | ||||
| @@ -250,7 +188,7 @@ ChainActor * HeroActor::exchange(const ChainActor * specialActor, const ChainAct | ||||
| } | ||||
|  | ||||
| HeroExchangeMap::HeroExchangeMap(const HeroActor * actor, const Nullkiller * ai) | ||||
| 	:actor(actor), ai(ai) | ||||
| 	:actor(actor), ai(ai), sync() | ||||
| { | ||||
| } | ||||
|  | ||||
| @@ -258,6 +196,8 @@ HeroExchangeMap::~HeroExchangeMap() | ||||
| { | ||||
| 	for(auto & exchange : exchangeMap) | ||||
| 	{ | ||||
| 		if(!exchange.second) continue; | ||||
|  | ||||
| 		delete exchange.second->creatureSet; | ||||
| 		delete exchange.second; | ||||
| 	} | ||||
| @@ -265,44 +205,91 @@ HeroExchangeMap::~HeroExchangeMap() | ||||
| 	exchangeMap.clear(); | ||||
| } | ||||
|  | ||||
| HeroActor * HeroExchangeMap::exchange(const ChainActor * other) | ||||
| HeroActor * HeroExchangeMap::tryExchange(const ChainActor * other) | ||||
| { | ||||
| 	HeroActor * result; | ||||
| 	auto position = exchangeMap.find(other); | ||||
|  | ||||
| 	if(vstd::contains(exchangeMap, other)) | ||||
| 		result = exchangeMap.at(other); | ||||
| 	else  | ||||
| 	if(position != exchangeMap.end()) | ||||
| 	{ | ||||
| 		TResources availableResources = ai->cb->getResourceAmount() - actor->armyCost - other->armyCost; | ||||
| 		HeroExchangeArmy * upgradedInitialArmy = tryUpgrade(actor->creatureSet, other->getActorObject(), availableResources); | ||||
| 		HeroExchangeArmy * newArmy; | ||||
| 		 | ||||
| 		if(other->creatureSet->Slots().size()) | ||||
| 		{ | ||||
| 			if(upgradedInitialArmy) | ||||
| 			{ | ||||
| 				newArmy = pickBestCreatures(upgradedInitialArmy, other->creatureSet); | ||||
| 				newArmy->armyCost = upgradedInitialArmy->armyCost; | ||||
| 				newArmy->requireBuyArmy = upgradedInitialArmy->requireBuyArmy; | ||||
| 		return position->second; | ||||
| 	} | ||||
|  | ||||
| 				delete upgradedInitialArmy; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				newArmy = pickBestCreatures(actor->creatureSet, other->creatureSet); | ||||
| 			} | ||||
| 	auto inserted = exchangeMap.insert(std::pair<const ChainActor *, HeroActor *>(other, nullptr)); | ||||
|  | ||||
| 	if(!inserted.second) | ||||
| 	{ | ||||
| 		return inserted.first->second; // already inserted | ||||
| 	} | ||||
|  | ||||
| 	position = inserted.first; | ||||
|  | ||||
| 	auto differentMasks = (actor->chainMask & other->chainMask) == 0; | ||||
|  | ||||
| 	if(!differentMasks) return nullptr; | ||||
|  | ||||
| 	TResources resources = ai->cb->getResourceAmount(); | ||||
|  | ||||
| 	if(!resources.canAfford(actor->armyCost + other->armyCost)) | ||||
| 	{ | ||||
| #if PATHFINDER_TRACE_LEVEL >= 2 | ||||
| 		logAi->trace( | ||||
| 			"Can not afford exchange because of total cost %s but we have %s", | ||||
| 			(actor->armyCost + other->armyCost).toString(), | ||||
| 			resources.toString()); | ||||
| #endif | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	TResources availableResources = resources - actor->armyCost - other->armyCost; | ||||
| 	HeroExchangeArmy * upgradedInitialArmy = tryUpgrade(actor->creatureSet, other->getActorObject(), availableResources); | ||||
| 	HeroExchangeArmy * newArmy; | ||||
|  | ||||
| 	if(other->creatureSet->Slots().size()) | ||||
| 	{ | ||||
| 		if(upgradedInitialArmy) | ||||
| 		{ | ||||
| 			newArmy = pickBestCreatures(upgradedInitialArmy, other->creatureSet); | ||||
| 			newArmy->armyCost = upgradedInitialArmy->armyCost; | ||||
| 			newArmy->requireBuyArmy = upgradedInitialArmy->requireBuyArmy; | ||||
|  | ||||
| 			delete upgradedInitialArmy; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			newArmy = upgradedInitialArmy; | ||||
| 			newArmy = pickBestCreatures(actor->creatureSet, other->creatureSet); | ||||
| 		} | ||||
|  | ||||
| 		result = new HeroActor(actor, other, newArmy, ai); | ||||
| 		result->armyCost += newArmy->armyCost; | ||||
| 		exchangeMap[other] = result; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		newArmy = upgradedInitialArmy; | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| 	if(!newArmy) return nullptr; | ||||
|  | ||||
| 	auto reinforcement = newArmy->getArmyStrength() - actor->creatureSet->getArmyStrength(); | ||||
|  | ||||
| #if PATHFINDER_TRACE_LEVEL >= 2 | ||||
| 	logAi->trace( | ||||
| 		"Exchange %s->%s reinforcement: %d, %f%%", | ||||
| 		actor->toString(), | ||||
| 		other->toString(), | ||||
| 		reinforcement, | ||||
| 		100.0f * reinforcement / actor->armyValue); | ||||
| #endif | ||||
|  | ||||
| 	if(reinforcement <= actor->armyValue / 10 && reinforcement < 1000) | ||||
| 	{ | ||||
| 		delete newArmy; | ||||
|  | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	HeroActor * exchanged = new HeroActor(actor, other, newArmy, ai); | ||||
|  | ||||
| 	exchanged->armyCost += newArmy->armyCost; | ||||
| 	position->second = exchanged; | ||||
|  | ||||
| 	return exchanged; | ||||
| } | ||||
|  | ||||
| HeroExchangeArmy * HeroExchangeMap::tryUpgrade( | ||||
|   | ||||
| @@ -65,14 +65,13 @@ public: | ||||
|  | ||||
| 	ChainActor(){} | ||||
|  | ||||
| 	virtual bool canExchange(const ChainActor * other) const; | ||||
| 	virtual std::string toString() const; | ||||
| 	ChainActor * exchange(const ChainActor * other) const { return exchange(this, other); } | ||||
| 	ChainActor * tryExchange(const ChainActor * other) const { return tryExchange(this, other); } | ||||
| 	void setBaseActor(HeroActor * base); | ||||
| 	virtual const CGObjectInstance * getActorObject() const	{ return hero; } | ||||
|  | ||||
| protected: | ||||
| 	virtual ChainActor * exchange(const ChainActor * specialActor, const ChainActor * other) const; | ||||
| 	virtual ChainActor * tryExchange(const ChainActor * specialActor, const ChainActor * other) const; | ||||
| }; | ||||
|  | ||||
| class HeroExchangeMap | ||||
| @@ -80,15 +79,14 @@ class HeroExchangeMap | ||||
| private: | ||||
| 	const HeroActor * actor; | ||||
| 	std::map<const ChainActor *, HeroActor *> exchangeMap; | ||||
| 	std::map<const ChainActor *, bool> canExchangeCache; | ||||
| 	const Nullkiller * ai; | ||||
| 	boost::shared_mutex sync; | ||||
|  | ||||
| public: | ||||
| 	HeroExchangeMap(const HeroActor * actor, const Nullkiller * ai); | ||||
| 	~HeroExchangeMap(); | ||||
|  | ||||
| 	HeroActor * exchange(const ChainActor * other); | ||||
| 	bool canExchange(const ChainActor * other); | ||||
| 	HeroActor * tryExchange(const ChainActor * other); | ||||
|  | ||||
| private: | ||||
| 	HeroExchangeArmy * pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const; | ||||
| @@ -113,10 +111,8 @@ public: | ||||
| 	HeroActor(const CGHeroInstance * hero, HeroRole heroRole, uint64_t chainMask, const Nullkiller * ai); | ||||
| 	HeroActor(const ChainActor * carrier, const ChainActor * other, const HeroExchangeArmy * army, const Nullkiller * ai); | ||||
|  | ||||
| 	virtual bool canExchange(const ChainActor * other) const override; | ||||
|  | ||||
| protected: | ||||
| 	virtual ChainActor * exchange(const ChainActor * specialActor, const ChainActor * other) const override; | ||||
| 	virtual ChainActor * tryExchange(const ChainActor * specialActor, const ChainActor * other) const override; | ||||
| }; | ||||
|  | ||||
| class ObjectActor : public ChainActor | ||||
|   | ||||
| @@ -143,7 +143,7 @@ namespace AIPathfinding | ||||
| 					destination.node->layer, | ||||
| 					destinationNode->actor->resourceActor); | ||||
|  | ||||
| 				if(!questNode || questNode.get()->cost < destination.cost) | ||||
| 				if(!questNode || questNode.get()->getCost() < destination.cost) | ||||
| 				{ | ||||
| 					return false; | ||||
| 				} | ||||
|   | ||||
| @@ -502,7 +502,7 @@ void VCAI::init(std::shared_ptr<CCallback> CB) | ||||
| 	myCb->unlockGsWhenWaiting = true; | ||||
|  | ||||
| 	nullkiller->init(CB, playerID); | ||||
|  | ||||
| 	 | ||||
| 	retrieveVisitableObjs(); | ||||
| } | ||||
|  | ||||
| @@ -764,6 +764,7 @@ void VCAI::makeTurn() | ||||
| 	} | ||||
| 	catch (boost::thread_interrupted & e) | ||||
| 	{ | ||||
| 	(void)e; | ||||
| 		logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn."); | ||||
| 		return; | ||||
| 	} | ||||
| @@ -1195,7 +1196,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) | ||||
| 			logAi->error("Hero %s cannot reach %s.", h->name, dst.toString()); | ||||
| 			return true; | ||||
| 		} | ||||
| 		int i = path.nodes.size() - 1; | ||||
| 		int i = (int)path.nodes.size() - 1; | ||||
|  | ||||
| 		auto getObj = [&](int3 coord, bool ignoreHero) | ||||
| 		{ | ||||
| @@ -1391,12 +1392,12 @@ void VCAI::tryRealize(Goals::Trade & g) //trade | ||||
|  | ||||
| 				int toGive, toGet; | ||||
| 				m->getOffer(res, g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE); | ||||
| 				toGive = toGive * (it->resVal / toGive); //round down | ||||
| 				toGive = static_cast<int>(toGive * (it->resVal / toGive)); //round down | ||||
| 				//TODO trade only as much as needed | ||||
| 				if (toGive) //don't try to sell 0 resources | ||||
| 				{ | ||||
| 					cb->trade(obj, EMarketMode::RESOURCE_RESOURCE, res, g.resID, toGive); | ||||
| 					accquiredResources = toGet * (it->resVal / toGive); | ||||
| 					accquiredResources = static_cast<int>(toGet * (it->resVal / toGive)); | ||||
| 					logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName()); | ||||
| 				} | ||||
| 				if (cb->getResourceAmount((Res::ERes)g.resID) >= g.value) | ||||
| @@ -1482,6 +1483,7 @@ void VCAI::finish() | ||||
| { | ||||
| 	//we want to lock to avoid multiple threads from calling makingTurn->join() at same time | ||||
| 	boost::lock_guard<boost::mutex> multipleCleanupGuard(turnInterruptionMutex); | ||||
|  | ||||
| 	if(makingTurn) | ||||
| 	{ | ||||
| 		makingTurn->interrupt(); | ||||
| @@ -1617,7 +1619,7 @@ void AIStatus::removeQuery(QueryID ID) | ||||
| int AIStatus::getQueriesCount() | ||||
| { | ||||
| 	boost::unique_lock<boost::mutex> lock(mx); | ||||
| 	return remainingQueries.size(); | ||||
| 	return static_cast<int>(remainingQueries.size()); | ||||
| } | ||||
|  | ||||
| void AIStatus::startedTurn() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user