mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	AI: tweak explore to work with new pathfinding.
This commit is contained in:
		| @@ -403,46 +403,6 @@ bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater) | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| int3 whereToExplore(HeroPtr h) | ||||
| { | ||||
| 	TimeCheck tc("where to explore"); | ||||
| 	int radius = h->getSightRadius(); | ||||
| 	int3 hpos = h->visitablePos(); | ||||
|  | ||||
| 	//look for nearby objs -> visit them if they're close enouh | ||||
| 	const int DIST_LIMIT = 3; | ||||
| 	const int MP_LIMIT = DIST_LIMIT * 150; // aproximate cost of diagonal movement | ||||
|  | ||||
| 	std::vector<const CGObjectInstance *> nearbyVisitableObjs; | ||||
| 	for(int x = hpos.x - DIST_LIMIT; x <= hpos.x + DIST_LIMIT; ++x) //get only local objects instead of all possible objects on the map | ||||
| 	{ | ||||
| 		for(int y = hpos.y - DIST_LIMIT; y <= hpos.y + DIST_LIMIT; ++y) | ||||
| 		{ | ||||
| 			for(auto obj : cb->getVisitableObjs(int3(x, y, hpos.z), false)) | ||||
| 			{ | ||||
| 				if(ai->isGoodForVisit(obj, h, MP_LIMIT)) | ||||
| 				{ | ||||
| 					nearbyVisitableObjs.push_back(obj); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	vstd::removeDuplicates(nearbyVisitableObjs); //one object may occupy multiple tiles | ||||
| 	boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get())); | ||||
| 	if(nearbyVisitableObjs.size()) | ||||
| 		return nearbyVisitableObjs.back()->visitablePos(); | ||||
|  | ||||
| 	try //check if nearby tiles allow us to reveal anything - this is quick | ||||
| 	{ | ||||
| 		return ai->explorationBestNeighbour(hpos, radius, h); | ||||
| 	} | ||||
| 	catch(cannotFulfillGoalException & e) | ||||
| 	{ | ||||
| 		//perform exhaustive search | ||||
| 		return ai->explorationNewPoint(h); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should be handled by pathfinder | ||||
| { | ||||
| 	if(cb->getTile(tileToHit)->topVisitableId() != Obj::BORDER_GATE) | ||||
| @@ -461,51 +421,6 @@ bool isBlockVisitObj(const int3 & pos) | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp) | ||||
| { | ||||
| 	for(crint3 dir : int3::getDirs()) | ||||
| 	{ | ||||
| 		int3 tile = pos + dir; | ||||
| 		if(cbp->isInTheMap(tile) && cbp->getPathsInfo(hero.get())->getPathInfo(tile)->reachable()) | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp, HeroPtr hero) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	for(int x = pos.x - radious; x <= pos.x + radious; x++) | ||||
| 	{ | ||||
| 		for(int y = pos.y - radious; y <= pos.y + radious; y++) | ||||
| 		{ | ||||
| 			int3 npos = int3(x, y, pos.z); | ||||
| 			if(cbp->isInTheMap(npos) && pos.dist2d(npos) - 0.5 < radious && !cbp->isVisible(npos)) | ||||
| 			{ | ||||
| 				if(hasReachableNeighbor(npos, hero, cbp)) | ||||
| 					ret++; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) | ||||
| { | ||||
| 	for(const int3 & tile : tiles) | ||||
| 	{ | ||||
| 		foreach_neighbour(tile, [&](int3 neighbour) | ||||
| 		{ | ||||
| 			if(cb->isVisible(neighbour)) | ||||
| 				out.push_back(neighbour); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| creInfo infoFromDC(const dwellingContent & dc) | ||||
| { | ||||
| 	creInfo ci; | ||||
|   | ||||
| @@ -159,9 +159,6 @@ void foreach_tile_pos(CCallback * cbp, std::function<void(CCallback * cbp, const | ||||
| void foreach_neighbour(const int3 & pos, std::function<void(const int3 & pos)> foo); | ||||
| void foreach_neighbour(CCallback * cbp, const int3 & pos, std::function<void(CCallback * cbp, const int3 & pos)> foo); // avoid costly retrieval of thread-specific pointer | ||||
|  | ||||
| int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp, HeroPtr hero); | ||||
| void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out); | ||||
|  | ||||
| bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater); | ||||
| bool isBlockedBorderGate(int3 tileToHit); | ||||
| bool isBlockVisitObj(const int3 & pos); | ||||
| @@ -181,7 +178,6 @@ bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2); | ||||
| bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2); | ||||
| 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); | ||||
|  | ||||
| class CDistanceSorter | ||||
|   | ||||
| @@ -318,8 +318,15 @@ void HeroMovementGoalEngineBase::setSharedFuzzyVariables(Goals::AbstractGoal & g | ||||
| { | ||||
| 	float turns = calculateTurnDistanceInputValue(goal); | ||||
| 	float missionImportanceData = 0; | ||||
|  | ||||
| 	if(vstd::contains(ai->lockedHeroes, goal.hero)) | ||||
| 	{ | ||||
| 		missionImportanceData = ai->lockedHeroes[goal.hero]->priority; | ||||
| 	} | ||||
| 	else if(goal.parent) | ||||
| 	{ | ||||
| 		missionImportanceData = goal.parent->priority; | ||||
| 	} | ||||
|  | ||||
| 	float strengthRatioData = 10.0f; //we are much stronger than enemy | ||||
| 	ui64 danger = evaluateDanger(goal.tile, goal.hero.h); | ||||
| @@ -410,6 +417,9 @@ VisitTileEngine::VisitTileEngine() //so far no VisitTile-specific variables that | ||||
|  | ||||
| float VisitTileEngine::evaluate(Goals::VisitTile & goal) | ||||
| { | ||||
| 	// for now any visit tile is usually much more in priority then visit obj so lets reduce it | ||||
| 	const int scale = 2; | ||||
|  | ||||
| 	//we assume that hero is already set and we want to choose most suitable one for the mission | ||||
| 	if(!goal.hero) | ||||
| 		return 0; | ||||
| @@ -421,7 +431,8 @@ float VisitTileEngine::evaluate(Goals::VisitTile & goal) | ||||
| 	try | ||||
| 	{ | ||||
| 		engine.process(); | ||||
| 		goal.priority = value->getValue(); | ||||
|  | ||||
| 		goal.priority = value->getValue() / scale; | ||||
| 	} | ||||
| 	catch(fl::Exception & fe) | ||||
| 	{ | ||||
|   | ||||
| @@ -78,6 +78,11 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank) | ||||
|  | ||||
| float FuzzyHelper::evaluate(Goals::VisitTile & g) | ||||
| { | ||||
| 	if(g.parent) | ||||
| 	{ | ||||
| 		g.parent->accept(this); | ||||
| 	} | ||||
|  | ||||
| 	return visitTileEngine.evaluate(g); | ||||
| } | ||||
|  | ||||
| @@ -108,6 +113,11 @@ float FuzzyHelper::evaluate(Goals::CompleteQuest & g) | ||||
|  | ||||
| float FuzzyHelper::evaluate(Goals::VisitObj & g) | ||||
| { | ||||
| 	if(g.parent) | ||||
| 	{ | ||||
| 		g.parent->accept(this); | ||||
| 	} | ||||
|  | ||||
| 	return visitObjEngine.evaluate(g); | ||||
| } | ||||
| float FuzzyHelper::evaluate(Goals::VisitHero & g) | ||||
|   | ||||
| @@ -19,7 +19,6 @@ | ||||
| #include "../../../lib/CPathfinder.h" | ||||
| #include "../../../lib/StringConstants.h" | ||||
|  | ||||
|  | ||||
| extern boost::thread_specific_ptr<CCallback> cb; | ||||
| extern boost::thread_specific_ptr<VCAI> ai; | ||||
| extern FuzzyHelper * fh; | ||||
| @@ -132,29 +131,18 @@ TGoalVec Explore::getAllPossibleSubgoals() | ||||
| 			vstd::concatenate(ret, waysToVisitObj); | ||||
| 		} | ||||
|  | ||||
| 		int3 t = whereToExplore(h); | ||||
| 		if(t.valid()) | ||||
| 		TSubgoal goal = howToExplore(h); | ||||
|  | ||||
| 		if(goal->invalid()) | ||||
| 		{ | ||||
| 			ret.push_back(sptr(VisitTile(t).sethero(h))); | ||||
| 			ai->markHeroUnableToExplore(h); //there is no freely accessible tile, do not poll this hero anymore | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			//FIXME: possible issues when gathering army to break | ||||
| 			if(hero.h == h || //exporation is assigned to this hero | ||||
| 				(!hero && h == primaryHero)) //not assigned to specific hero, let our main hero do the job | ||||
| 			{ | ||||
| 				t = ai->explorationDesperate(h);  //check this only ONCE, high cost | ||||
| 				if (t.valid()) //don't waste time if we are completely blocked | ||||
| 				{ | ||||
| 					auto waysToVisitTile = ai->ah->howToVisitTile(h, t); | ||||
|  | ||||
| 					vstd::concatenate(ret, waysToVisitTile); | ||||
| 					continue; | ||||
| 				} | ||||
| 			} | ||||
| 			ai->markHeroUnableToExplore(h); //there is no freely accessible tile, do not poll this hero anymore | ||||
| 			ret.push_back(goal); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//we either don't have hero yet or none of heroes can explore | ||||
| 	if((!hero || ret.empty()) && ai->canRecruitAnyHero()) | ||||
| 		ret.push_back(sptr(RecruitHero())); | ||||
| @@ -179,3 +167,220 @@ bool Explore::fulfillsMe(TSubgoal goal) | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool Explore::hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const | ||||
| { | ||||
| 	for(crint3 dir : int3::getDirs()) | ||||
| 	{ | ||||
| 		int3 tile = pos + dir; | ||||
| 		if(cbp->isInTheMap(tile)) | ||||
| 		{ | ||||
| 			auto paths = vcai->ah->getPathsToTile(hero, tile); | ||||
|  | ||||
| 			return paths.size(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| int Explore::howManyTilesWillBeDiscovered( | ||||
| 	const int3 & pos,  | ||||
| 	int radious,  | ||||
| 	CCallback * cbp,  | ||||
| 	HeroPtr hero,  | ||||
| 	std::function<bool (const int3 &)> filter) const | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	for(int x = pos.x - radious; x <= pos.x + radious; x++) | ||||
| 	{ | ||||
| 		for(int y = pos.y - radious; y <= pos.y + radious; y++) | ||||
| 		{ | ||||
| 			int3 npos = int3(x, y, pos.z); | ||||
| 			if(cbp->isInTheMap(npos)  | ||||
| 				&& pos.dist2d(npos) - 0.5 < radious  | ||||
| 				&& !cbp->isVisible(npos) | ||||
| 				&& filter(npos)) | ||||
| 			{ | ||||
| 				ret++; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| TSubgoal Explore::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) const | ||||
| { | ||||
| 	std::map<int3, int> dstToRevealedTiles; | ||||
| 	VCAI * aip = ai.get(); | ||||
| 	CCallback * cbp = cb.get(); | ||||
|  | ||||
| 	for(crint3 dir : int3::getDirs()) | ||||
| 	{ | ||||
| 		int3 tile = hpos + dir; | ||||
| 		if(cb->isInTheMap(tile)) | ||||
| 		{ | ||||
| 			if(isBlockVisitObj(tile)) | ||||
| 				continue; | ||||
|  | ||||
| 			if(isSafeToVisit(h, tile) && ai->isAccessibleForHero(tile, h)) | ||||
| 			{ | ||||
| 				auto distance = hpos.dist2d(tile); // diagonal movement opens more tiles but spends more mp | ||||
| 				int tilesDiscovered = howManyTilesWillBeDiscovered(tile, radius, cbp, h, [&](int3 neighbor) -> bool {  | ||||
| 					return hasReachableNeighbor(neighbor, h, cbp, aip); | ||||
| 				}); | ||||
|  | ||||
| 				dstToRevealedTiles[tile] = tilesDiscovered / distance; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(dstToRevealedTiles.empty()) //yes, it DID happen! | ||||
| 		return sptr(Invalid()); | ||||
|  | ||||
| 	auto best = dstToRevealedTiles.begin(); | ||||
| 	for(auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++) | ||||
| 	{ | ||||
| 		const CGPathNode * pn = cb->getPathsInfo(h.get())->getPathInfo(i->first); | ||||
| 		//const TerrainTile *t = cb->getTile(i->first); | ||||
| 		if(best->second < i->second && pn->reachable() && pn->accessible == CGPathNode::ACCESSIBLE) | ||||
| 			best = i; | ||||
| 	} | ||||
|  | ||||
| 	if(best->second) | ||||
| 		return sptr(VisitTile(best->first).sethero(h)); | ||||
|  | ||||
| 	return sptr(Invalid()); | ||||
| } | ||||
|  | ||||
| TSubgoal Explore::explorationNewPoint(HeroPtr h) const | ||||
| { | ||||
| 	int radius = h->getSightRadius(); | ||||
| 	CCallback * cbp = cb.get(); | ||||
| 	VCAI *aip = ai.get(); | ||||
|  | ||||
| 	std::vector<std::vector<int3>> tiles; //tiles[distance_to_fow] | ||||
| 	tiles.resize(radius); | ||||
|  | ||||
| 	foreach_tile_pos([&](const int3 & pos) | ||||
| 	{ | ||||
| 		if(!cbp->isVisible(pos)) | ||||
| 			tiles[0].push_back(pos); | ||||
| 	}); | ||||
|  | ||||
| 	float bestValue = 0; //discovered tile to node distance ratio | ||||
| 	TSubgoal bestWay = sptr(Invalid()); | ||||
| 	int3 ourPos = h->convertPosition(h->pos, false); | ||||
|  | ||||
| 	for(int i = 1; i < radius; i++) | ||||
| 	{ | ||||
| 		getVisibleNeighbours(tiles[i - 1], tiles[i], cbp); | ||||
| 		vstd::removeDuplicates(tiles[i]); | ||||
|  | ||||
| 		for(const int3 & tile : tiles[i]) | ||||
| 		{ | ||||
| 			if(tile == ourPos) //shouldn't happen, but it does | ||||
| 				continue; | ||||
| 						 | ||||
| 			auto waysToVisit = aip->ah->howToVisitTile(h, tile); | ||||
| 			for(auto goal : waysToVisit) | ||||
| 			{ | ||||
| 				if(goal->evaluationContext.movementCost == 0) // should not happen | ||||
| 					continue; | ||||
| 				 | ||||
| 				int tilesDiscovered = howManyTilesWillBeDiscovered(tile, radius, cbp, h, [](int3 neighbor) -> bool { return true; }); | ||||
| 				float ourValue = (float) tilesDiscovered / goal->evaluationContext.movementCost; //+1 prevents erratic jumps | ||||
|  | ||||
| 				if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much | ||||
| 				{ | ||||
| 					auto obj = cb->getTopObj(tile); | ||||
| 					if(obj && obj->blockVisit && !isObjectRemovable(obj)) //we can't stand on that object | ||||
| 					{ | ||||
| 						continue; | ||||
| 					} | ||||
|  | ||||
| 					if(isSafeToVisit(h, tile)) | ||||
| 					{ | ||||
| 						bestWay = goal; | ||||
| 						bestValue = ourValue; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if(!bestWay->invalid()) | ||||
| 		{ | ||||
| 			return bestWay; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return bestWay; | ||||
| } | ||||
|  | ||||
| TSubgoal Explore::howToExplore(HeroPtr h) const | ||||
| { | ||||
| 	TimeCheck tc("where to explore"); | ||||
| 	int radius = h->getSightRadius(); | ||||
| 	int3 hpos = h->visitablePos(); | ||||
|  | ||||
| 	//look for nearby objs -> visit them if they're close enouh | ||||
| 	const int DIST_LIMIT = 3; | ||||
| 	const int MP_LIMIT = DIST_LIMIT * 150; // aproximate cost of diagonal movement | ||||
|  | ||||
| 	std::vector<const CGObjectInstance *> nearbyVisitableObjs; | ||||
| 	for(int x = hpos.x - DIST_LIMIT; x <= hpos.x + DIST_LIMIT; ++x) //get only local objects instead of all possible objects on the map | ||||
| 	{ | ||||
| 		for(int y = hpos.y - DIST_LIMIT; y <= hpos.y + DIST_LIMIT; ++y) | ||||
| 		{ | ||||
| 			for(auto obj : cb->getVisitableObjs(int3(x, y, hpos.z), false)) | ||||
| 			{ | ||||
| 				if(ai->isGoodForVisit(obj, h, MP_LIMIT)) | ||||
| 				{ | ||||
| 					nearbyVisitableObjs.push_back(obj); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	vstd::removeDuplicates(nearbyVisitableObjs); //one object may occupy multiple tiles | ||||
| 	boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get())); | ||||
|  | ||||
| 	if(nearbyVisitableObjs.size()) | ||||
| 	{ | ||||
| 		TSubgoal pickupNearestObj = fh->chooseSolution(ai->ah->howToVisitObj(h, nearbyVisitableObjs.back(), false)); | ||||
|  | ||||
| 		if(!pickupNearestObj->invalid()) | ||||
| 		{ | ||||
| 			return pickupNearestObj; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//check if nearby tiles allow us to reveal anything - this is quick | ||||
| 	TSubgoal result = explorationBestNeighbour(hpos, radius, h); | ||||
| 	 | ||||
| 	if(result->invalid()) | ||||
| 	{ | ||||
| 		//perform exhaustive search | ||||
| 		result = explorationNewPoint(h); | ||||
| 	} | ||||
|  | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| void Explore::getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out, CCallback * cbp) const | ||||
| { | ||||
| 	for(const int3 & tile : tiles) | ||||
| 	{ | ||||
| 		foreach_neighbour(tile, [&](int3 neighbour) | ||||
| 		{ | ||||
| 			if(cbp->isVisible(neighbour)) | ||||
| 			{ | ||||
| 				auto tile = cbp->getTile(neighbour); | ||||
|  | ||||
| 				if(tile && !tile->blocked) | ||||
| 					out.push_back(neighbour); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -36,5 +36,18 @@ namespace Goals | ||||
| 		std::string completeMessage() const override; | ||||
| 		bool fulfillsMe(TSubgoal goal) override; | ||||
| 		virtual bool operator==(const Explore & other) const override; | ||||
|  | ||||
| 	private: | ||||
| 		TSubgoal howToExplore(HeroPtr h) const; | ||||
| 		TSubgoal explorationNewPoint(HeroPtr h) const; | ||||
| 		TSubgoal explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) const; | ||||
| 		bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const; | ||||
| 		void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out, CCallback * cbp) const; | ||||
| 		int howManyTilesWillBeDiscovered( | ||||
| 			const int3 & pos, | ||||
| 			int radious, | ||||
| 			CCallback * cbp, | ||||
| 			HeroPtr hero,  | ||||
| 			std::function<bool(const int3 &)> filter) const; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
							
								
								
									
										193
									
								
								AI/VCAI/VCAI.cpp
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								AI/VCAI/VCAI.cpp
									
									
									
									
									
								
							| @@ -1531,33 +1531,42 @@ void VCAI::wander(HeroPtr h) | ||||
| 				vstd::concatenate(targetObjectGoals, ah->howToVisitObj(h, destination, false)); | ||||
| 			} | ||||
|  | ||||
| 			auto bestObjectGoal = fh->chooseSolution(targetObjectGoals); | ||||
|  | ||||
| 			//wander should not cause heroes to be reserved - they are always considered free | ||||
| 			if(bestObjectGoal->goalType == Goals::VISIT_OBJ) | ||||
| 			if(targetObjectGoals.size()) | ||||
| 			{ | ||||
| 				auto chosenObject = cb->getObjInstance(ObjectInstanceID(bestObjectGoal->objid)); | ||||
| 				if(chosenObject != nullptr) | ||||
| 					logAi->debug("Of all %d destinations, object %s at pos=%s seems nice", dests.size(), chosenObject->getObjectName(), chosenObject->pos.toString()); | ||||
| 				auto bestObjectGoal = fh->chooseSolution(targetObjectGoals); | ||||
|  | ||||
| 				//wander should not cause heroes to be reserved - they are always considered free | ||||
| 				if(bestObjectGoal->goalType == Goals::VISIT_OBJ) | ||||
| 				{ | ||||
| 					auto chosenObject = cb->getObjInstance(ObjectInstanceID(bestObjectGoal->objid)); | ||||
| 					if(chosenObject != nullptr) | ||||
| 						logAi->debug("Of all %d destinations, object %s at pos=%s seems nice", dests.size(), chosenObject->getObjectName(), chosenObject->pos.toString()); | ||||
| 				} | ||||
| 				else | ||||
| 					logAi->debug("Trying to realize goal of type %s as part of wandering.", bestObjectGoal->name()); | ||||
|  | ||||
| 				try | ||||
| 				{ | ||||
| 					decomposeGoal(bestObjectGoal)->accept(this); | ||||
| 				} | ||||
| 				catch(const goalFulfilledException & e) | ||||
| 				{ | ||||
| 					if(e.goal->goalType == Goals::EGoals::VISIT_TILE || e.goal->goalType == Goals::EGoals::VISIT_OBJ) | ||||
| 						continue; | ||||
|  | ||||
| 					throw e; | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 				logAi->debug("Trying to realize goal of type %s as part of wandering.", bestObjectGoal->name()); | ||||
|  | ||||
| 			try | ||||
| 			{ | ||||
| 				decomposeGoal(bestObjectGoal)->accept(this); | ||||
| 			} | ||||
| 			catch(const goalFulfilledException & e) | ||||
| 			{ | ||||
| 				if(e.goal->goalType == Goals::EGoals::VISIT_TILE || e.goal->goalType == Goals::EGoals::VISIT_OBJ) | ||||
| 					continue; | ||||
|  | ||||
| 				throw e; | ||||
| 				logAi->debug("Nowhere more to go..."); | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			visitTownIfAny(h); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	visitTownIfAny(h); //in case hero is just sitting in town | ||||
| } | ||||
|  | ||||
| @@ -2472,154 +2481,6 @@ void VCAI::buildArmyIn(const CGTownInstance * t) | ||||
| 	moveCreaturesToHero(t); | ||||
| } | ||||
|  | ||||
| int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) | ||||
| { | ||||
| 	std::map<int3, int> dstToRevealedTiles; | ||||
|  | ||||
| 	for(crint3 dir : int3::getDirs()) | ||||
| 	{ | ||||
| 		int3 tile = hpos + dir; | ||||
| 		if(cb->isInTheMap(tile)) | ||||
| 		{ | ||||
| 			if(isBlockVisitObj(tile)) | ||||
| 				continue; | ||||
|  | ||||
| 			if(isSafeToVisit(h, tile) && isAccessibleForHero(tile, h)) | ||||
| 			{ | ||||
| 				auto distance = hpos.dist2d(tile); // diagonal movement opens more tiles but spends more mp | ||||
| 				dstToRevealedTiles[tile] = howManyTilesWillBeDiscovered(tile, radius, cb.get(), h) / distance; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(dstToRevealedTiles.empty()) //yes, it DID happen! | ||||
| 		throw cannotFulfillGoalException("No neighbour will bring new discoveries!"); | ||||
|  | ||||
| 	auto best = dstToRevealedTiles.begin(); | ||||
| 	for(auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++) | ||||
| 	{ | ||||
| 		const CGPathNode * pn = cb->getPathsInfo(h.get())->getPathInfo(i->first); | ||||
| 		//const TerrainTile *t = cb->getTile(i->first); | ||||
| 		if(best->second < i->second && pn->reachable() && pn->accessible == CGPathNode::ACCESSIBLE) | ||||
| 			best = i; | ||||
| 	} | ||||
|  | ||||
| 	if(best->second) | ||||
| 		return best->first; | ||||
|  | ||||
| 	throw cannotFulfillGoalException("No neighbour will bring new discoveries!"); | ||||
| } | ||||
|  | ||||
| int3 VCAI::explorationNewPoint(HeroPtr h) | ||||
| { | ||||
| 	int radius = h->getSightRadius(); | ||||
| 	CCallback * cbp = cb.get(); | ||||
| 	const CGHeroInstance * hero = h.get(); | ||||
|  | ||||
| 	std::vector<std::vector<int3>> tiles; //tiles[distance_to_fow] | ||||
| 	tiles.resize(radius); | ||||
|  | ||||
| 	foreach_tile_pos([&](const int3 & pos) | ||||
| 	{ | ||||
| 		if(!cbp->isVisible(pos)) | ||||
| 			tiles[0].push_back(pos); | ||||
| 	}); | ||||
|  | ||||
| 	float bestValue = 0; //discovered tile to node distance ratio | ||||
| 	int3 bestTile(-1, -1, -1); | ||||
| 	int3 ourPos = h->convertPosition(h->pos, false); | ||||
|  | ||||
| 	for(int i = 1; i < radius; i++) | ||||
| 	{ | ||||
| 		getVisibleNeighbours(tiles[i - 1], tiles[i]); | ||||
| 		vstd::removeDuplicates(tiles[i]); | ||||
|  | ||||
| 		for(const int3 & tile : tiles[i]) | ||||
| 		{ | ||||
| 			if(tile == ourPos) //shouldn't happen, but it does | ||||
| 				continue; | ||||
| 			if(!cb->getPathsInfo(hero)->getPathInfo(tile)->reachable()) //this will remove tiles that are guarded by monsters (or removable objects) | ||||
| 				continue; | ||||
|  | ||||
| 			CGPath path; | ||||
| 			cb->getPathsInfo(hero)->getPath(path, tile); | ||||
| 			float ourValue = (float)howManyTilesWillBeDiscovered(tile, radius, cbp, h) / (path.nodes.size() + 1); //+1 prevents erratic jumps | ||||
|  | ||||
| 			if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much | ||||
| 			{ | ||||
| 				auto obj = cb->getTopObj(tile); | ||||
| 				if (obj) | ||||
| 					if (obj->blockVisit && !isObjectRemovable(obj)) //we can't stand on that object | ||||
| 						continue; | ||||
| 				if(isSafeToVisit(h, tile)) | ||||
| 				{ | ||||
| 					bestTile = tile; | ||||
| 					bestValue = ourValue; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return bestTile; | ||||
| } | ||||
|  | ||||
| int3 VCAI::explorationDesperate(HeroPtr h) | ||||
| { | ||||
| 	int radius = h->getSightRadius(); | ||||
|  | ||||
| 	std::vector<std::vector<int3>> tiles; //tiles[distance_to_fow] | ||||
| 	tiles.resize(radius); | ||||
|  | ||||
| 	CCallback * cbp = cb.get(); | ||||
|  | ||||
| 	foreach_tile_pos([&](const int3 & pos) | ||||
| 	{ | ||||
| 		if(!cbp->isVisible(pos)) | ||||
| 			tiles[0].push_back(pos); | ||||
| 	}); | ||||
|  | ||||
| 	ui64 lowestDanger = -1; | ||||
| 	int3 bestTile(-1, -1, -1); | ||||
|  | ||||
| 	for(int i = 1; i < radius; i++) | ||||
| 	{ | ||||
| 		getVisibleNeighbours(tiles[i - 1], tiles[i]); | ||||
| 		vstd::removeDuplicates(tiles[i]); | ||||
|  | ||||
| 		for(const int3 & tile : tiles[i]) | ||||
| 		{ | ||||
| 			if(cbp->getTile(tile)->blocked) //does it shorten the time? | ||||
| 				continue; | ||||
| 			if(!howManyTilesWillBeDiscovered(tile, radius, cbp, h)) //avoid costly checks of tiles that don't reveal much | ||||
| 				continue; | ||||
|  | ||||
| 			auto paths = ah->getPathsToTile(h, tile); | ||||
| 			for(auto path : paths) | ||||
| 			{ | ||||
| 				auto t = path.firstTileToGet(); | ||||
|  | ||||
| 				if(t == bestTile) | ||||
| 					continue; | ||||
|  | ||||
| 				auto obj = cb->getTopObj(t); | ||||
| 				if (obj) | ||||
| 					if (obj->blockVisit && !isObjectRemovable(obj)) //we can't stand on object or remove it | ||||
| 						continue; | ||||
|  | ||||
| 				ui64 ourDanger = path.getTotalDanger(h); | ||||
| 				if(ourDanger < lowestDanger) | ||||
| 				{ | ||||
| 					if(!ourDanger) //at least one safe place found | ||||
| 						return t; | ||||
|  | ||||
| 					bestTile = t; | ||||
| 					lowestDanger = ourDanger; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return bestTile; | ||||
| } | ||||
|  | ||||
| void VCAI::checkHeroArmy(HeroPtr h) | ||||
| { | ||||
| 	auto it = lockedHeroes.find(h); | ||||
|   | ||||
| @@ -131,9 +131,6 @@ public: | ||||
| 	void tryRealize(Goals::Invalid & g); | ||||
| 	void tryRealize(Goals::AbstractGoal & g); | ||||
|  | ||||
| 	int3 explorationBestNeighbour(int3 hpos, int radius, HeroPtr h); | ||||
| 	int3 explorationNewPoint(HeroPtr h); | ||||
| 	int3 explorationDesperate(HeroPtr h); | ||||
| 	bool isTileNotReserved(const CGHeroInstance * h, int3 t) const; //the tile is not occupied by allied hero and the object is not reserved | ||||
|  | ||||
| 	std::string getBattleAIName() const override; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user