mirror of
https://github.com/vcmi/vcmi.git
synced 2026-06-19 22:57:37 +02:00
Merge remote-tracking branch 'remotes/nullkiller/ai-refactor-exploration-according-to-new-pathfinder' into develop
This commit is contained in:
@@ -403,46 +403,6 @@ bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
|
|||||||
return false;
|
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
|
bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should be handled by pathfinder
|
||||||
{
|
{
|
||||||
if(cb->getTile(tileToHit)->topVisitableId() != Obj::BORDER_GATE)
|
if(cb->getTile(tileToHit)->topVisitableId() != Obj::BORDER_GATE)
|
||||||
@@ -461,51 +421,6 @@ bool isBlockVisitObj(const int3 & pos)
|
|||||||
return false;
|
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 infoFromDC(const dwellingContent & dc)
|
||||||
{
|
{
|
||||||
creInfo ci;
|
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(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
|
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 canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater);
|
||||||
bool isBlockedBorderGate(int3 tileToHit);
|
bool isBlockedBorderGate(int3 tileToHit);
|
||||||
bool isBlockVisitObj(const int3 & pos);
|
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);
|
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
|
||||||
ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t);
|
ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t);
|
||||||
ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t);
|
ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t);
|
||||||
int3 whereToExplore(HeroPtr h);
|
|
||||||
uint32_t distanceToTile(const CGHeroInstance * hero, int3 pos);
|
uint32_t distanceToTile(const CGHeroInstance * hero, int3 pos);
|
||||||
|
|
||||||
class CDistanceSorter
|
class CDistanceSorter
|
||||||
|
|||||||
@@ -148,6 +148,11 @@ 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,6 +60,7 @@ 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;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -318,8 +318,15 @@ void HeroMovementGoalEngineBase::setSharedFuzzyVariables(Goals::AbstractGoal & g
|
|||||||
{
|
{
|
||||||
float turns = calculateTurnDistanceInputValue(goal);
|
float turns = calculateTurnDistanceInputValue(goal);
|
||||||
float missionImportanceData = 0;
|
float missionImportanceData = 0;
|
||||||
|
|
||||||
if(vstd::contains(ai->lockedHeroes, goal.hero))
|
if(vstd::contains(ai->lockedHeroes, goal.hero))
|
||||||
|
{
|
||||||
missionImportanceData = ai->lockedHeroes[goal.hero]->priority;
|
missionImportanceData = ai->lockedHeroes[goal.hero]->priority;
|
||||||
|
}
|
||||||
|
else if(goal.parent)
|
||||||
|
{
|
||||||
|
missionImportanceData = goal.parent->priority;
|
||||||
|
}
|
||||||
|
|
||||||
float strengthRatioData = 10.0f; //we are much stronger than enemy
|
float strengthRatioData = 10.0f; //we are much stronger than enemy
|
||||||
ui64 danger = evaluateDanger(goal.tile, goal.hero.h);
|
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)
|
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
|
//we assume that hero is already set and we want to choose most suitable one for the mission
|
||||||
if(!goal.hero)
|
if(!goal.hero)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -421,7 +431,8 @@ float VisitTileEngine::evaluate(Goals::VisitTile & goal)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
engine.process();
|
engine.process();
|
||||||
goal.priority = value->getValue();
|
|
||||||
|
goal.priority = value->getValue() / scale;
|
||||||
}
|
}
|
||||||
catch(fl::Exception & fe)
|
catch(fl::Exception & fe)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -78,6 +78,11 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
|
|||||||
|
|
||||||
float FuzzyHelper::evaluate(Goals::VisitTile & g)
|
float FuzzyHelper::evaluate(Goals::VisitTile & g)
|
||||||
{
|
{
|
||||||
|
if(g.parent)
|
||||||
|
{
|
||||||
|
g.parent->accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
return visitTileEngine.evaluate(g);
|
return visitTileEngine.evaluate(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +113,11 @@ float FuzzyHelper::evaluate(Goals::CompleteQuest & g)
|
|||||||
|
|
||||||
float FuzzyHelper::evaluate(Goals::VisitObj & g)
|
float FuzzyHelper::evaluate(Goals::VisitObj & g)
|
||||||
{
|
{
|
||||||
|
if(g.parent)
|
||||||
|
{
|
||||||
|
g.parent->accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
return visitObjEngine.evaluate(g);
|
return visitObjEngine.evaluate(g);
|
||||||
}
|
}
|
||||||
float FuzzyHelper::evaluate(Goals::VisitHero & g)
|
float FuzzyHelper::evaluate(Goals::VisitHero & g)
|
||||||
|
|||||||
+266
-24
@@ -18,7 +18,7 @@
|
|||||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||||
#include "../../../lib/CPathfinder.h"
|
#include "../../../lib/CPathfinder.h"
|
||||||
#include "../../../lib/StringConstants.h"
|
#include "../../../lib/StringConstants.h"
|
||||||
|
#include "../../../lib/CPlayerState.h"
|
||||||
|
|
||||||
extern boost::thread_specific_ptr<CCallback> cb;
|
extern boost::thread_specific_ptr<CCallback> cb;
|
||||||
extern boost::thread_specific_ptr<VCAI> ai;
|
extern boost::thread_specific_ptr<VCAI> ai;
|
||||||
@@ -28,7 +28,7 @@ using namespace Goals;
|
|||||||
|
|
||||||
bool Explore::operator==(const Explore & other) const
|
bool Explore::operator==(const Explore & other) const
|
||||||
{
|
{
|
||||||
return other.hero.h == hero.h;
|
return other.hero.h == hero.h && other.allowGatherArmy == allowGatherArmy;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Explore::completeMessage() const
|
std::string Explore::completeMessage() const
|
||||||
@@ -127,34 +127,36 @@ TGoalVec Explore::getAllPossibleSubgoals()
|
|||||||
{
|
{
|
||||||
for(auto obj : objs) //double loop, performance risk?
|
for(auto obj : objs) //double loop, performance risk?
|
||||||
{
|
{
|
||||||
auto waysToVisitObj = ai->ah->howToVisitObj(h, obj);
|
auto waysToVisitObj = ai->ah->howToVisitObj(h, obj, allowGatherArmy);
|
||||||
|
|
||||||
vstd::concatenate(ret, waysToVisitObj);
|
vstd::concatenate(ret, waysToVisitObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
int3 t = whereToExplore(h);
|
TSubgoal goal = exploreNearestNeighbour(h);
|
||||||
if(t.valid())
|
|
||||||
{
|
|
||||||
ret.push_back(sptr(VisitTile(t).sethero(h)));
|
|
||||||
}
|
|
||||||
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);
|
if(!goal->invalid())
|
||||||
continue;
|
{
|
||||||
}
|
ret.push_back(goal);
|
||||||
}
|
|
||||||
ai->markHeroUnableToExplore(h); //there is no freely accessible tile, do not poll this hero anymore
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(ret.empty())
|
||||||
|
{
|
||||||
|
for(auto h : heroes)
|
||||||
|
{
|
||||||
|
TSubgoal goal = explorationNewPoint(h);
|
||||||
|
|
||||||
|
if(goal->invalid())
|
||||||
|
{
|
||||||
|
ai->markHeroUnableToExplore(h); //there is no freely accessible tile, do not poll this hero anymore
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret.push_back(goal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//we either don't have hero yet or none of heroes can explore
|
//we either don't have hero yet or none of heroes can explore
|
||||||
if((!hero || ret.empty()) && ai->canRecruitAnyHero())
|
if((!hero || ret.empty()) && ai->canRecruitAnyHero())
|
||||||
ret.push_back(sptr(RecruitHero()));
|
ret.push_back(sptr(RecruitHero()));
|
||||||
@@ -163,7 +165,6 @@ TGoalVec Explore::getAllPossibleSubgoals()
|
|||||||
{
|
{
|
||||||
throw goalFulfilledException(sptr(Explore().sethero(hero)));
|
throw goalFulfilledException(sptr(Explore().sethero(hero)));
|
||||||
}
|
}
|
||||||
//throw cannotFulfillGoalException("Cannot explore - no possible ways found!");
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -179,3 +180,244 @@ bool Explore::fulfillsMe(TSubgoal goal)
|
|||||||
}
|
}
|
||||||
return false;
|
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 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())
|
||||||
|
{
|
||||||
|
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, ts, aip, h);
|
||||||
|
|
||||||
|
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();
|
||||||
|
const TeamState * ts = cbp->getPlayerTeam(aip->playerID);
|
||||||
|
|
||||||
|
int3 mapSize = cbp->getMapSize();
|
||||||
|
int perimiter = 2 * radius * (mapSize.x + mapSize.y);
|
||||||
|
|
||||||
|
std::vector<int3> from;
|
||||||
|
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])
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) // 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
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nearbyVisitableObjs.size())
|
||||||
|
{
|
||||||
|
vstd::removeDuplicates(nearbyVisitableObjs); //one object may occupy multiple tiles
|
||||||
|
boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get()));
|
||||||
|
|
||||||
|
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
|
||||||
|
return explorationBestNeighbour(hpos, radius, 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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
-2
@@ -19,12 +19,21 @@ namespace Goals
|
|||||||
{
|
{
|
||||||
class DLL_EXPORT Explore : public CGoal<Explore>
|
class DLL_EXPORT Explore : public CGoal<Explore>
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
bool allowGatherArmy;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Explore()
|
Explore(bool allowGatherArmy)
|
||||||
: CGoal(Goals::EXPLORE)
|
: CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy)
|
||||||
{
|
{
|
||||||
priority = 1;
|
priority = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Explore()
|
||||||
|
: Explore(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Explore(HeroPtr h)
|
Explore(HeroPtr h)
|
||||||
: CGoal(Goals::EXPLORE)
|
: CGoal(Goals::EXPLORE)
|
||||||
{
|
{
|
||||||
@@ -36,5 +45,26 @@ namespace Goals
|
|||||||
std::string completeMessage() const override;
|
std::string completeMessage() const override;
|
||||||
bool fulfillsMe(TSubgoal goal) override;
|
bool fulfillsMe(TSubgoal goal) override;
|
||||||
virtual bool operator==(const Explore & other) const override;
|
virtual bool operator==(const Explore & other) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TSubgoal exploreNearestNeighbour(HeroPtr h) const;
|
||||||
|
TSubgoal explorationNewPoint(HeroPtr h) const;
|
||||||
|
TSubgoal explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) const;
|
||||||
|
TSubgoal explorationScanRange(HeroPtr h, std::vector<int3> & range) 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 TeamState * ts) const;
|
||||||
|
|
||||||
|
int howManyTilesWillBeDiscovered(
|
||||||
|
const int3 & pos,
|
||||||
|
int radious,
|
||||||
|
CCallback * cbp,
|
||||||
|
const TeamState * ts,
|
||||||
|
VCAI * aip,
|
||||||
|
HeroPtr h) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,8 +193,10 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
|
|||||||
|
|
||||||
if(ret.empty())
|
if(ret.empty())
|
||||||
{
|
{
|
||||||
|
const bool allowGatherArmy = false;
|
||||||
|
|
||||||
if(hero == ai->primaryHero())
|
if(hero == ai->primaryHero())
|
||||||
ret.push_back(sptr(Explore()));
|
ret.push_back(sptr(Explore(allowGatherArmy)));
|
||||||
else
|
else
|
||||||
throw cannotFulfillGoalException("No ways to gather army");
|
throw cannotFulfillGoalException("No ways to gather army");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,6 +189,23 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AINodeStorage::isTileAccessible(int3 pos, const EPathfindingLayer layer) const
|
||||||
|
{
|
||||||
|
std::vector<AIPath> paths;
|
||||||
|
auto chains = nodes[pos.x][pos.y][pos.z][layer];
|
||||||
|
auto initialPos = hero->visitablePos();
|
||||||
|
|
||||||
|
for(const AIPathNode & node : chains)
|
||||||
|
{
|
||||||
|
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
|
||||||
{
|
{
|
||||||
std::vector<AIPath> paths;
|
std::vector<AIPath> paths;
|
||||||
|
|||||||
@@ -111,6 +111,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;
|
||||||
|
|
||||||
void setHero(HeroPtr heroPtr)
|
void setHero(HeroPtr heroPtr)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,9 +35,34 @@ void AIPathfinder::init()
|
|||||||
storageMap.clear();
|
storageMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AIPathfinder::isTileAccessible(HeroPtr hero, int3 tile)
|
||||||
|
{
|
||||||
|
boost::unique_lock<boost::mutex> storageLock(storageMutex);
|
||||||
|
|
||||||
|
std::shared_ptr<AINodeStorage> nodeStorage = getOrCreateStorage(hero);
|
||||||
|
|
||||||
|
return nodeStorage->isTileAccessible(tile, EPathfindingLayer::LAND)
|
||||||
|
|| nodeStorage->isTileAccessible(tile, EPathfindingLayer::SAIL);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<AIPath> AIPathfinder::getPathInfo(HeroPtr hero, int3 tile)
|
std::vector<AIPath> AIPathfinder::getPathInfo(HeroPtr hero, int3 tile)
|
||||||
{
|
{
|
||||||
boost::unique_lock<boost::mutex> storageLock(storageMutex);
|
boost::unique_lock<boost::mutex> storageLock(storageMutex);
|
||||||
|
|
||||||
|
std::shared_ptr<AINodeStorage> nodeStorage = getOrCreateStorage(hero);
|
||||||
|
|
||||||
|
const TerrainTile * tileInfo = cb->getTile(tile, false);
|
||||||
|
|
||||||
|
if(!tileInfo)
|
||||||
|
{
|
||||||
|
return std::vector<AIPath>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeStorage->getChainInfo(tile, !tileInfo->isWater());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<AINodeStorage> AIPathfinder::getOrCreateStorage(HeroPtr hero)
|
||||||
|
{
|
||||||
std::shared_ptr<AINodeStorage> nodeStorage;
|
std::shared_ptr<AINodeStorage> nodeStorage;
|
||||||
|
|
||||||
if(!vstd::contains(storageMap, hero))
|
if(!vstd::contains(storageMap, hero))
|
||||||
@@ -56,7 +81,7 @@ std::vector<AIPath> AIPathfinder::getPathInfo(HeroPtr hero, int3 tile)
|
|||||||
|
|
||||||
storageMap[hero] = nodeStorage;
|
storageMap[hero] = nodeStorage;
|
||||||
nodeStorage->setHero(hero.get());
|
nodeStorage->setHero(hero.get());
|
||||||
|
|
||||||
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, nodeStorage);
|
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, nodeStorage);
|
||||||
|
|
||||||
cb->calculatePaths(config, hero.get());
|
cb->calculatePaths(config, hero.get());
|
||||||
@@ -66,12 +91,6 @@ std::vector<AIPath> AIPathfinder::getPathInfo(HeroPtr hero, int3 tile)
|
|||||||
nodeStorage = storageMap.at(hero);
|
nodeStorage = storageMap.at(hero);
|
||||||
}
|
}
|
||||||
|
|
||||||
const TerrainTile * tileInfo = cb->getTile(tile, false);
|
return nodeStorage;
|
||||||
|
|
||||||
if(!tileInfo)
|
|
||||||
{
|
|
||||||
return std::vector<AIPath>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodeStorage->getChainInfo(tile, !tileInfo->isWater());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ private:
|
|||||||
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);
|
||||||
|
std::shared_ptr<AINodeStorage> getOrCreateStorage(HeroPtr hero);
|
||||||
void clear();
|
void clear();
|
||||||
void init();
|
void init();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -105,6 +105,11 @@ 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,6 +26,7 @@ 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 PathfindingManager : public IPathfindingManager
|
class PathfindingManager : public IPathfindingManager
|
||||||
@@ -46,6 +47,7 @@ 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;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
+27
-166
@@ -1530,33 +1530,42 @@ void VCAI::wander(HeroPtr h)
|
|||||||
vstd::concatenate(targetObjectGoals, ah->howToVisitObj(h, destination, false));
|
vstd::concatenate(targetObjectGoals, ah->howToVisitObj(h, destination, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bestObjectGoal = fh->chooseSolution(targetObjectGoals);
|
if(targetObjectGoals.size())
|
||||||
|
|
||||||
//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));
|
auto bestObjectGoal = fh->chooseSolution(targetObjectGoals);
|
||||||
if(chosenObject != nullptr)
|
|
||||||
logAi->debug("Of all %d destinations, object %s at pos=%s seems nice", dests.size(), chosenObject->getObjectName(), chosenObject->pos.toString());
|
//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
|
else
|
||||||
logAi->debug("Trying to realize goal of type %s as part of wandering.", bestObjectGoal->name());
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
decomposeGoal(bestObjectGoal)->accept(this);
|
logAi->debug("Nowhere more to go...");
|
||||||
}
|
break;
|
||||||
catch(const goalFulfilledException & e)
|
|
||||||
{
|
|
||||||
if(e.goal->goalType == Goals::EGoals::VISIT_TILE || e.goal->goalType == Goals::EGoals::VISIT_OBJ)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitTownIfAny(h);
|
visitTownIfAny(h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitTownIfAny(h); //in case hero is just sitting in town
|
visitTownIfAny(h); //in case hero is just sitting in town
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2471,154 +2480,6 @@ void VCAI::buildArmyIn(const CGTownInstance * t)
|
|||||||
moveCreaturesToHero(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)
|
void VCAI::checkHeroArmy(HeroPtr h)
|
||||||
{
|
{
|
||||||
auto it = lockedHeroes.find(h);
|
auto it = lockedHeroes.find(h);
|
||||||
|
|||||||
@@ -131,9 +131,6 @@ public:
|
|||||||
void tryRealize(Goals::Invalid & g);
|
void tryRealize(Goals::Invalid & g);
|
||||||
void tryRealize(Goals::AbstractGoal & 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
|
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;
|
std::string getBattleAIName() const override;
|
||||||
|
|||||||
Reference in New Issue
Block a user