mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	AI: optimizae explore logically, reduce amount of scanned tiles during full map scan for new exploration point
This commit is contained in:
		| @@ -148,11 +148,6 @@ std::vector<AIPath> AIhelper::getPathsToTile(HeroPtr hero, int3 tile) | |||||||
| 	return pathfindingManager->getPathsToTile(hero, tile); | 	return pathfindingManager->getPathsToTile(hero, tile); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool AIhelper::isTileAccessible(HeroPtr hero, int3 tile) |  | ||||||
| { |  | ||||||
| 	return pathfindingManager->isTileAccessible(hero, tile); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void AIhelper::resetPaths() | void AIhelper::resetPaths() | ||||||
| { | { | ||||||
| 	pathfindingManager->resetPaths(); | 	pathfindingManager->resetPaths(); | ||||||
|   | |||||||
| @@ -60,9 +60,14 @@ public: | |||||||
| 	Goals::TGoalVec howToVisitTile(int3 tile) override; | 	Goals::TGoalVec howToVisitTile(int3 tile) override; | ||||||
| 	Goals::TGoalVec howToVisitObj(ObjectIdRef obj) override; | 	Goals::TGoalVec howToVisitObj(ObjectIdRef obj) override; | ||||||
| 	std::vector<AIPath> getPathsToTile(HeroPtr hero, int3 tile) override; | 	std::vector<AIPath> getPathsToTile(HeroPtr hero, int3 tile) override; | ||||||
| 	bool isTileAccessible(HeroPtr hero, int3 tile) override; |  | ||||||
| 	void resetPaths() override; | 	void resetPaths() override; | ||||||
|  |  | ||||||
|  | 	STRONG_INLINE | ||||||
|  | 	bool isTileAccessible(const HeroPtr & hero, const int3 & tile) | ||||||
|  | 	{ | ||||||
|  | 		return pathfindingManager->isTileAccessible(hero, tile); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| private: | private: | ||||||
| 	bool notifyGoalCompleted(Goals::TSubgoal goal) override; | 	bool notifyGoalCompleted(Goals::TSubgoal goal) override; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,6 +26,208 @@ extern FuzzyHelper * fh; | |||||||
|  |  | ||||||
| using namespace Goals; | using namespace Goals; | ||||||
|  |  | ||||||
|  | namespace Goals | ||||||
|  | { | ||||||
|  | 	struct ExplorationHelper | ||||||
|  | 	{ | ||||||
|  | 		HeroPtr hero; | ||||||
|  | 		int sightRadius; | ||||||
|  | 		float bestValue; | ||||||
|  | 		TSubgoal bestGoal; | ||||||
|  | 		VCAI * aip; | ||||||
|  | 		CCallback * cbp; | ||||||
|  | 		const TeamState * ts; | ||||||
|  | 		int3 ourPos; | ||||||
|  | 		bool allowDeadEndCancellation; | ||||||
|  | 		bool allowGatherArmy; | ||||||
|  |  | ||||||
|  | 		ExplorationHelper(HeroPtr h, bool gatherArmy) | ||||||
|  | 		{ | ||||||
|  | 			cbp = cb.get(); | ||||||
|  | 			aip = ai.get(); | ||||||
|  | 			hero = h; | ||||||
|  | 			ts = cbp->getPlayerTeam(ai->playerID); | ||||||
|  | 			sightRadius = hero->getSightRadius(); | ||||||
|  | 			bestGoal = sptr(Goals::Invalid()); | ||||||
|  | 			bestValue = 0; | ||||||
|  | 			ourPos = h->convertPosition(h->pos, false); | ||||||
|  | 			allowDeadEndCancellation = true; | ||||||
|  | 			allowGatherArmy = gatherArmy; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		void scanSector(int scanRadius) | ||||||
|  | 		{ | ||||||
|  | 			for(int x = ourPos.x - scanRadius; x <= ourPos.x + scanRadius; x++) | ||||||
|  | 			{ | ||||||
|  | 				for(int y = ourPos.y - scanRadius; y <= ourPos.y + scanRadius; y++) | ||||||
|  | 				{ | ||||||
|  | 					int3 tile = int3(x, y, ourPos.z); | ||||||
|  |  | ||||||
|  | 					if(cbp->isInTheMap(tile) && ts->fogOfWarMap[tile.x][tile.y][tile.z]) | ||||||
|  | 					{ | ||||||
|  | 						scanTile(tile); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		void scanMap() | ||||||
|  | 		{ | ||||||
|  | 			int3 mapSize = cbp->getMapSize(); | ||||||
|  | 			int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y); | ||||||
|  |  | ||||||
|  | 			std::vector<int3> from; | ||||||
|  | 			std::vector<int3> to; | ||||||
|  |  | ||||||
|  | 			from.reserve(perimeter); | ||||||
|  | 			to.reserve(perimeter); | ||||||
|  |  | ||||||
|  | 			foreach_tile_pos([&](const int3 & pos) | ||||||
|  | 			{ | ||||||
|  | 				if(ts->fogOfWarMap[pos.x][pos.y][pos.z]) | ||||||
|  | 				{ | ||||||
|  | 					bool hasInvisibleNeighbor = false; | ||||||
|  |  | ||||||
|  | 					foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour) | ||||||
|  | 					{ | ||||||
|  | 						if(!ts->fogOfWarMap[neighbour.x][neighbour.y][neighbour.z]) | ||||||
|  | 						{ | ||||||
|  | 							hasInvisibleNeighbor = true; | ||||||
|  | 						} | ||||||
|  | 					}); | ||||||
|  |  | ||||||
|  | 					if(hasInvisibleNeighbor) | ||||||
|  | 						from.push_back(pos); | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  |  | ||||||
|  | 			logAi->debug("Exploration scan visible area perimeter for hero %s", hero.name); | ||||||
|  |  | ||||||
|  | 			for(const int3 & tile : from) | ||||||
|  | 			{ | ||||||
|  | 				scanTile(tile); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if(!bestGoal->invalid()) | ||||||
|  | 			{ | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			allowDeadEndCancellation = false; | ||||||
|  |  | ||||||
|  | 			for(int i = 0; i < sightRadius; i++) | ||||||
|  | 			{ | ||||||
|  | 				getVisibleNeighbours(from, to); | ||||||
|  | 				vstd::concatenate(from, to); | ||||||
|  | 				vstd::removeDuplicates(from); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			logAi->debug("Exploration scan all possible tiles for hero %s", hero.name); | ||||||
|  |  | ||||||
|  | 			for(const int3 & tile : from) | ||||||
|  | 			{ | ||||||
|  | 				scanTile(tile); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		void scanTile(const int3 & tile) | ||||||
|  | 		{ | ||||||
|  | 			if(tile == ourPos | ||||||
|  | 				|| !aip->ah->isTileAccessible(hero, tile)) //shouldn't happen, but it does | ||||||
|  | 				return; | ||||||
|  |  | ||||||
|  | 			int tilesDiscovered = howManyTilesWillBeDiscovered(tile); | ||||||
|  | 			if(!tilesDiscovered) | ||||||
|  | 				return; | ||||||
|  |  | ||||||
|  | 			auto waysToVisit = aip->ah->howToVisitTile(hero, tile, allowGatherArmy); | ||||||
|  | 			for(auto goal : waysToVisit) | ||||||
|  | 			{ | ||||||
|  | 				if(goal->evaluationContext.movementCost <= 0.0) // should not happen | ||||||
|  | 					continue; | ||||||
|  |  | ||||||
|  | 				float ourValue = (float)tilesDiscovered * tilesDiscovered / goal->evaluationContext.movementCost; | ||||||
|  |  | ||||||
|  | 				if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much | ||||||
|  | 				{ | ||||||
|  | 					auto obj = cb->getTopObj(tile); | ||||||
|  |  | ||||||
|  | 					// picking up resources does not yield any exploration at all. | ||||||
|  | 					// if it blocks the way to some explorable tile AIPathfinder will take care of it | ||||||
|  | 					if(obj && obj->blockVisit) | ||||||
|  | 					{ | ||||||
|  | 						continue; | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					if(isSafeToVisit(hero, tile)) | ||||||
|  | 					{ | ||||||
|  | 						bestGoal = goal; | ||||||
|  | 						bestValue = ourValue; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const | ||||||
|  | 		{ | ||||||
|  | 			for(const int3 & tile : tiles) | ||||||
|  | 			{ | ||||||
|  | 				foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour) | ||||||
|  | 				{ | ||||||
|  | 					if(ts->fogOfWarMap[neighbour.x][neighbour.y][neighbour.z]) | ||||||
|  | 					{ | ||||||
|  | 						out.push_back(neighbour); | ||||||
|  | 					} | ||||||
|  | 				}); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		int howManyTilesWillBeDiscovered( | ||||||
|  | 			const int3 & pos) const | ||||||
|  | 		{ | ||||||
|  | 			int ret = 0; | ||||||
|  | 			for(int x = pos.x - sightRadius; x <= pos.x + sightRadius; x++) | ||||||
|  | 			{ | ||||||
|  | 				for(int y = pos.y - sightRadius; y <= pos.y + sightRadius; y++) | ||||||
|  | 				{ | ||||||
|  | 					int3 npos = int3(x, y, pos.z); | ||||||
|  | 					if(cbp->isInTheMap(npos) | ||||||
|  | 						&& pos.dist2d(npos) - 0.5 < sightRadius | ||||||
|  | 						&& !ts->fogOfWarMap[npos.x][npos.y][npos.z]) | ||||||
|  | 					{ | ||||||
|  | 						if(allowDeadEndCancellation | ||||||
|  | 							&& !hasReachableNeighbor(npos)) | ||||||
|  | 						{ | ||||||
|  | 							continue; | ||||||
|  | 						} | ||||||
|  |  | ||||||
|  | 						ret++; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			return ret; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		bool hasReachableNeighbor(const int3 &pos) const | ||||||
|  | 		{ | ||||||
|  | 			for(crint3 dir : int3::getDirs()) | ||||||
|  | 			{ | ||||||
|  | 				int3 tile = pos + dir; | ||||||
|  | 				if(cbp->isInTheMap(tile)) | ||||||
|  | 				{ | ||||||
|  | 					auto isAccessible = aip->ah->isTileAccessible(hero, tile); | ||||||
|  |  | ||||||
|  | 					if(isAccessible) | ||||||
|  | 						return true; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  |  | ||||||
| bool Explore::operator==(const Explore & other) const | bool Explore::operator==(const Explore & other) const | ||||||
| { | { | ||||||
| 	return other.hero.h == hero.h && other.allowGatherArmy == allowGatherArmy; | 	return other.hero.h == hero.h && other.allowGatherArmy == allowGatherArmy; | ||||||
| @@ -38,18 +240,7 @@ std::string Explore::completeMessage() const | |||||||
|  |  | ||||||
| TSubgoal Explore::whatToDoToAchieve() | TSubgoal Explore::whatToDoToAchieve() | ||||||
| { | { | ||||||
| 	auto ret = fh->chooseSolution(getAllPossibleSubgoals()); | 	return fh->chooseSolution(getAllPossibleSubgoals()); | ||||||
| 	if(hero) //use best step for this hero |  | ||||||
| 	{ |  | ||||||
| 		return ret; |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		if(ret->hero.get(true)) |  | ||||||
| 			return sptr(Explore().sethero(ret->hero.h)); //choose this hero and then continue with him |  | ||||||
| 		else |  | ||||||
| 			return ret; //other solutions, like buying hero from tavern |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| TGoalVec Explore::getAllPossibleSubgoals() | TGoalVec Explore::getAllPossibleSubgoals() | ||||||
| @@ -143,6 +334,8 @@ TGoalVec Explore::getAllPossibleSubgoals() | |||||||
| 	{ | 	{ | ||||||
| 		for(auto h : heroes) | 		for(auto h : heroes) | ||||||
| 		{ | 		{ | ||||||
|  | 			logAi->trace("Exploration searching for a new point for hero %s", h->name); | ||||||
|  |  | ||||||
| 			TSubgoal goal = explorationNewPoint(h); | 			TSubgoal goal = explorationNewPoint(h); | ||||||
|  |  | ||||||
| 			if(goal->invalid()) | 			if(goal->invalid()) | ||||||
| @@ -180,197 +373,43 @@ bool Explore::fulfillsMe(TSubgoal goal) | |||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool Explore::hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const | TSubgoal Explore::explorationBestNeighbour(int3 hpos, HeroPtr h) const | ||||||
| { | { | ||||||
| 	for(crint3 dir : int3::getDirs()) | 	ExplorationHelper scanResult(h, allowGatherArmy); | ||||||
| 	{ |  | ||||||
| 		int3 tile = pos + dir; |  | ||||||
| 		if(cbp->isInTheMap(tile)) |  | ||||||
| 		{ |  | ||||||
| 			auto isAccessible = vcai->ah->isTileAccessible(hero, tile); |  | ||||||
|  |  | ||||||
| 			if(isAccessible) |  | ||||||
| 				return true; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int Explore::howManyTilesWillBeDiscovered( |  | ||||||
| 	const int3 & pos, |  | ||||||
| 	int radious, |  | ||||||
| 	CCallback * cbp, |  | ||||||
| 	const TeamState * ts, |  | ||||||
| 	VCAI * aip, |  | ||||||
| 	HeroPtr h) 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 |  | ||||||
| 				&& !ts->fogOfWarMap[npos.x][npos.y][npos.z] |  | ||||||
| 				&& hasReachableNeighbor(npos, h, cbp, aip)) |  | ||||||
| 			{ |  | ||||||
| 				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(); |  | ||||||
| 	const TeamState * ts = cbp->getPlayerTeam(ai->playerID); |  | ||||||
|  |  | ||||||
| 	for(crint3 dir : int3::getDirs()) | 	for(crint3 dir : int3::getDirs()) | ||||||
| 	{ | 	{ | ||||||
| 		int3 tile = hpos + dir; | 		int3 tile = hpos + dir; | ||||||
| 		if(cb->isInTheMap(tile)) | 		if(cb->isInTheMap(tile)) | ||||||
| 		{ | 		{ | ||||||
| 			if(isBlockVisitObj(tile)) | 			scanResult.scanTile(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, ts, aip, h); |  | ||||||
|  |  | ||||||
| 				if(tilesDiscovered > 0) |  | ||||||
| 					dstToRevealedTiles[tile] = tilesDiscovered / distance; |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if(dstToRevealedTiles.empty()) //yes, it DID happen! | 	return scanResult.bestGoal; | ||||||
| 		return sptr(Invalid()); |  | ||||||
|  |  | ||||||
| 	auto paths = cb->getPathsInfo(h.get()); |  | ||||||
|  |  | ||||||
| 	auto best = dstToRevealedTiles.begin(); |  | ||||||
| 	for(auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++) |  | ||||||
| 	{ |  | ||||||
| 		const CGPathNode * pn = paths->getPathInfo(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 | TSubgoal Explore::explorationNewPoint(HeroPtr h) const | ||||||
| { | { | ||||||
| 	int radius = h->getSightRadius(); | 	ExplorationHelper scanResult(h, allowGatherArmy); | ||||||
| 	CCallback * cbp = cb.get(); |  | ||||||
| 	VCAI *aip = ai.get(); |  | ||||||
| 	const TeamState * ts = cbp->getPlayerTeam(aip->playerID); |  | ||||||
|  |  | ||||||
| 	int3 mapSize = cbp->getMapSize(); | 	scanResult.scanSector(10); | ||||||
| 	int perimiter = 2 * radius * (mapSize.x + mapSize.y); |  | ||||||
|  |  | ||||||
| 	std::vector<int3> from; | 	if(!scanResult.bestGoal->invalid()) | ||||||
| 	std::vector<int3> to; |  | ||||||
|  |  | ||||||
| 	from.reserve(perimiter); |  | ||||||
| 	to.reserve(perimiter); |  | ||||||
|  |  | ||||||
| 	foreach_tile_pos([&](const int3 & pos) |  | ||||||
| 	{ | 	{ | ||||||
| 		if(ts->fogOfWarMap[pos.x][pos.y][pos.z]) | 		return scanResult.bestGoal; | ||||||
| 		{ |  | ||||||
| 			bool hasInvisibleNeighbor = false; |  | ||||||
|  |  | ||||||
| 			foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour) |  | ||||||
| 			{ |  | ||||||
| 				if(!ts->fogOfWarMap[neighbour.x][neighbour.y][neighbour.z]) |  | ||||||
| 				{ |  | ||||||
| 					hasInvisibleNeighbor = true; |  | ||||||
| 				} |  | ||||||
| 			}); |  | ||||||
|  |  | ||||||
| 			if(hasInvisibleNeighbor) |  | ||||||
| 				from.push_back(pos); |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
|  |  | ||||||
| 	for(int i = 0; i < radius; i++) |  | ||||||
| 	{ |  | ||||||
| 		getVisibleNeighbours(from, to, cbp, ts); |  | ||||||
| 		vstd::concatenate(from, to); |  | ||||||
| 		vstd::removeDuplicates(from); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return explorationScanRange(h, from); | 	scanResult.scanMap(); | ||||||
|  |  | ||||||
|  | 	return scanResult.bestGoal; | ||||||
| } | } | ||||||
|  |  | ||||||
| TSubgoal Explore::explorationScanRange(HeroPtr h, std::vector<int3> & range) const |  | ||||||
| { |  | ||||||
| 	int radius = h->getSightRadius(); |  | ||||||
| 	CCallback * cbp = cb.get(); |  | ||||||
| 	VCAI *aip = ai.get(); |  | ||||||
| 	const TeamState * ts = cbp->getPlayerTeam(aip->playerID); |  | ||||||
|  |  | ||||||
| 	float bestValue = 0; //discovered tile to node distance ratio |  | ||||||
| 	TSubgoal bestWay = sptr(Invalid()); |  | ||||||
| 	int3 ourPos = h->convertPosition(h->pos, false); |  | ||||||
|  |  | ||||||
| 	for(const int3 & tile : range) |  | ||||||
| 	{ |  | ||||||
| 		if(tile == ourPos) //shouldn't happen, but it does |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		int tilesDiscovered = howManyTilesWillBeDiscovered(tile, radius, cbp, ts, aip, h); |  | ||||||
| 		if(!tilesDiscovered) |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		auto waysToVisit = aip->ah->howToVisitTile(h, tile, allowGatherArmy); |  | ||||||
| 		for(auto goal : waysToVisit) |  | ||||||
| 		{ |  | ||||||
| 			if(goal->evaluationContext.movementCost <= 0.0) // should not happen |  | ||||||
| 				continue; |  | ||||||
|  |  | ||||||
| 			float ourValue = (float)tilesDiscovered * tilesDiscovered / goal->evaluationContext.movementCost; |  | ||||||
|  |  | ||||||
| 			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::exploreNearestNeighbour(HeroPtr h) const | TSubgoal Explore::exploreNearestNeighbour(HeroPtr h) const | ||||||
| { | { | ||||||
| 	TimeCheck tc("where to explore"); | 	TimeCheck tc("where to explore"); | ||||||
| 	int radius = h->getSightRadius(); |  | ||||||
| 	int3 hpos = h->visitablePos(); | 	int3 hpos = h->visitablePos(); | ||||||
|  |  | ||||||
| 	//look for nearby objs -> visit them if they're close enough | 	//look for nearby objs -> visit them if they're close enough | ||||||
| @@ -406,19 +445,5 @@ TSubgoal Explore::exploreNearestNeighbour(HeroPtr h) const | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	//check if nearby tiles allow us to reveal anything - this is quick | 	//check if nearby tiles allow us to reveal anything - this is quick | ||||||
| 	return explorationBestNeighbour(hpos, radius, h); | 	return explorationBestNeighbour(hpos, h); | ||||||
| } |  | ||||||
|  |  | ||||||
| void Explore::getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out, CCallback * cbp, const TeamState * ts) const |  | ||||||
| { |  | ||||||
| 	for(const int3 & tile : tiles) |  | ||||||
| 	{ |  | ||||||
| 		foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour) |  | ||||||
| 		{ |  | ||||||
| 			if(ts->fogOfWarMap[neighbour.x][neighbour.y][neighbour.z]) |  | ||||||
| 			{ |  | ||||||
| 				out.push_back(neighbour); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,6 +17,8 @@ class FuzzyHelper; | |||||||
|  |  | ||||||
| namespace Goals | namespace Goals | ||||||
| { | { | ||||||
|  | 	struct ExplorationHelper; | ||||||
|  |  | ||||||
| 	class DLL_EXPORT Explore : public CGoal<Explore> | 	class DLL_EXPORT Explore : public CGoal<Explore> | ||||||
| 	{ | 	{ | ||||||
| 	private: | 	private: | ||||||
| @@ -49,8 +51,8 @@ namespace Goals | |||||||
| 	private: | 	private: | ||||||
| 		TSubgoal exploreNearestNeighbour(HeroPtr h) const; | 		TSubgoal exploreNearestNeighbour(HeroPtr h) const; | ||||||
| 		TSubgoal explorationNewPoint(HeroPtr h) const; | 		TSubgoal explorationNewPoint(HeroPtr h) const; | ||||||
| 		TSubgoal explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) const; | 		TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const; | ||||||
| 		TSubgoal explorationScanRange(HeroPtr h, std::vector<int3> & range) const; | 		void explorationScanTile(const int3 & tile, ExplorationHelper & scanResult) const; | ||||||
| 		bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const; | 		bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const; | ||||||
|  |  | ||||||
| 		void getVisibleNeighbours( | 		void getVisibleNeighbours( | ||||||
| @@ -59,12 +61,6 @@ namespace Goals | |||||||
| 			CCallback * cbp, | 			CCallback * cbp, | ||||||
| 			const TeamState * ts) const; | 			const TeamState * ts) const; | ||||||
|  |  | ||||||
| 		int howManyTilesWillBeDiscovered( | 		int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const; | ||||||
| 			const int3 & pos, |  | ||||||
| 			int radious, |  | ||||||
| 			CCallback * cbp, |  | ||||||
| 			const TeamState * ts, |  | ||||||
| 			VCAI * aip, |  | ||||||
| 			HeroPtr h) const; |  | ||||||
| 	}; | 	}; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -220,18 +220,18 @@ std::vector<CGPathNode *> AINodeStorage::calculateTeleportations( | |||||||
|  |  | ||||||
| 	if(source.isNodeObjectVisitable()) | 	if(source.isNodeObjectVisitable()) | ||||||
| 	{ | 	{ | ||||||
| 	auto accessibleExits = pathfinderHelper->getTeleportExits(source); | 		auto accessibleExits = pathfinderHelper->getTeleportExits(source); | ||||||
| 	auto srcNode = getAINode(source.node); | 		auto srcNode = getAINode(source.node); | ||||||
|  |  | ||||||
| 	for(auto & neighbour : accessibleExits) | 		for(auto & neighbour : accessibleExits) | ||||||
| 	{ | 		{ | ||||||
| 		auto node = getOrCreateNode(neighbour, source.node->layer, srcNode->chainMask); | 			auto node = getOrCreateNode(neighbour, source.node->layer, srcNode->chainMask); | ||||||
|  |  | ||||||
| 		if(!node) | 			if(!node) | ||||||
| 			continue; | 				continue; | ||||||
|  |  | ||||||
| 		neighbours.push_back(node.get()); | 			neighbours.push_back(node.get()); | ||||||
| 	} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if(hero->getPosition(false) == source.coord) | 	if(hero->getPosition(false) == source.coord) | ||||||
| @@ -342,19 +342,11 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode | |||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool AINodeStorage::isTileAccessible(int3 pos, const EPathfindingLayer layer) const | bool AINodeStorage::isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const | ||||||
| { | { | ||||||
| 	auto chains = nodes[pos.x][pos.y][pos.z][layer]; | 	const AIPathNode & node = nodes[pos.x][pos.y][pos.z][layer][0]; | ||||||
|  |  | ||||||
| 	for(const AIPathNode & node : chains) | 	return node.action != CGPathNode::ENodeAction::UNKNOWN; | ||||||
| 	{ |  | ||||||
| 		if(node.action != CGPathNode::ENodeAction::UNKNOWN) |  | ||||||
| 		{ |  | ||||||
| 			return true; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return false; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| std::vector<AIPath> AINodeStorage::getChainInfo(int3 pos, bool isOnLand) const | std::vector<AIPath> AINodeStorage::getChainInfo(int3 pos, bool isOnLand) const | ||||||
|   | |||||||
| @@ -114,7 +114,7 @@ public: | |||||||
| 	bool hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const; | 	bool hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const; | ||||||
| 	boost::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, int chainNumber); | 	boost::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, int chainNumber); | ||||||
| 	std::vector<AIPath> getChainInfo(int3 pos, bool isOnLand) const; | 	std::vector<AIPath> getChainInfo(int3 pos, bool isOnLand) const; | ||||||
| 	bool isTileAccessible(int3 pos, const EPathfindingLayer layer) const; | 	bool isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const; | ||||||
|  |  | ||||||
| 	void setHero(HeroPtr heroPtr); | 	void setHero(HeroPtr heroPtr); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ void AIPathfinder::init() | |||||||
| 	storageMap.clear(); | 	storageMap.clear(); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool AIPathfinder::isTileAccessible(HeroPtr hero, int3 tile) | bool AIPathfinder::isTileAccessible(const HeroPtr & hero, const int3 & tile) | ||||||
| { | { | ||||||
| 	boost::unique_lock<boost::mutex> storageLock(storageMutex); | 	boost::unique_lock<boost::mutex> storageLock(storageMutex); | ||||||
|  |  | ||||||
| @@ -61,7 +61,7 @@ std::vector<AIPath> AIPathfinder::getPathInfo(HeroPtr hero, int3 tile) | |||||||
| 	return nodeStorage->getChainInfo(tile, !tileInfo->isWater()); | 	return nodeStorage->getChainInfo(tile, !tileInfo->isWater()); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<AINodeStorage> AIPathfinder::getOrCreateStorage(HeroPtr hero) | std::shared_ptr<AINodeStorage> AIPathfinder::getOrCreateStorage(const HeroPtr & hero) | ||||||
| { | { | ||||||
| 	std::shared_ptr<AINodeStorage> nodeStorage; | 	std::shared_ptr<AINodeStorage> nodeStorage; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,11 +23,11 @@ private: | |||||||
| 	CPlayerSpecificInfoCallback * cb; | 	CPlayerSpecificInfoCallback * cb; | ||||||
| 	VCAI * ai; | 	VCAI * ai; | ||||||
|  |  | ||||||
| 	std::shared_ptr<AINodeStorage> getOrCreateStorage(HeroPtr hero); | 	std::shared_ptr<AINodeStorage> getOrCreateStorage(const HeroPtr & hero); | ||||||
| public: | public: | ||||||
| 	AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai); | 	AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai); | ||||||
| 	std::vector<AIPath> getPathInfo(HeroPtr hero, int3 tile); | 	std::vector<AIPath> getPathInfo(HeroPtr hero, int3 tile); | ||||||
| 	bool isTileAccessible(HeroPtr hero, int3 tile); | 	bool isTileAccessible(const HeroPtr & hero, const int3 & tile); | ||||||
| 	void clear(); | 	void clear(); | ||||||
| 	void init(); | 	void init(); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -107,11 +107,6 @@ std::vector<AIPath> PathfindingManager::getPathsToTile(HeroPtr hero, int3 tile) | |||||||
| 	return pathfinder->getPathInfo(hero, tile); | 	return pathfinder->getPathInfo(hero, tile); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool PathfindingManager::isTileAccessible(HeroPtr hero, int3 tile) |  | ||||||
| { |  | ||||||
| 	return pathfinder->isTileAccessible(hero, tile); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Goals::TGoalVec PathfindingManager::findPath( | Goals::TGoalVec PathfindingManager::findPath( | ||||||
| 	HeroPtr hero, | 	HeroPtr hero, | ||||||
| 	crint3 dest, | 	crint3 dest, | ||||||
|   | |||||||
| @@ -26,7 +26,6 @@ public: | |||||||
| 	virtual Goals::TGoalVec howToVisitTile(int3 tile) = 0; | 	virtual Goals::TGoalVec howToVisitTile(int3 tile) = 0; | ||||||
| 	virtual Goals::TGoalVec howToVisitObj(ObjectIdRef obj) = 0; | 	virtual Goals::TGoalVec howToVisitObj(ObjectIdRef obj) = 0; | ||||||
| 	virtual std::vector<AIPath> getPathsToTile(HeroPtr hero, int3 tile) = 0; | 	virtual std::vector<AIPath> getPathsToTile(HeroPtr hero, int3 tile) = 0; | ||||||
| 	virtual bool isTileAccessible(HeroPtr hero, int3 tile) = 0; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class DLL_EXPORT PathfindingManager : public IPathfindingManager | class DLL_EXPORT PathfindingManager : public IPathfindingManager | ||||||
| @@ -47,9 +46,14 @@ public: | |||||||
| 	Goals::TGoalVec howToVisitTile(int3 tile) override; | 	Goals::TGoalVec howToVisitTile(int3 tile) override; | ||||||
| 	Goals::TGoalVec howToVisitObj(ObjectIdRef obj) override; | 	Goals::TGoalVec howToVisitObj(ObjectIdRef obj) override; | ||||||
| 	std::vector<AIPath> getPathsToTile(HeroPtr hero, int3 tile) override; | 	std::vector<AIPath> getPathsToTile(HeroPtr hero, int3 tile) override; | ||||||
| 	bool isTileAccessible(HeroPtr hero, int3 tile) override; |  | ||||||
| 	void resetPaths() override; | 	void resetPaths() override; | ||||||
|  |  | ||||||
|  | 	STRONG_INLINE | ||||||
|  | 	bool isTileAccessible(const HeroPtr & hero, const int3 & tile) | ||||||
|  | 	{ | ||||||
|  | 		return pathfinder->isTileAccessible(hero, tile); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| private: | private: | ||||||
| 	void init(CPlayerSpecificInfoCallback * CB) override; | 	void init(CPlayerSpecificInfoCallback * CB) override; | ||||||
| 	void setAI(VCAI * AI) override; | 	void setAI(VCAI * AI) override; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user