mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-27 22:49:25 +02:00
AI: explore logical optimization
This commit is contained in:
@@ -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,12 +127,24 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
TSubgoal goal = howToExplore(h);
|
TSubgoal goal = exploreNearestNeighbour(h);
|
||||||
|
|
||||||
|
if(!goal->invalid())
|
||||||
|
{
|
||||||
|
ret.push_back(goal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ret.empty())
|
||||||
|
{
|
||||||
|
for(auto h : heroes)
|
||||||
|
{
|
||||||
|
TSubgoal goal = explorationNewPoint(h);
|
||||||
|
|
||||||
if(goal->invalid())
|
if(goal->invalid())
|
||||||
{
|
{
|
||||||
@@ -143,6 +155,7 @@ TGoalVec Explore::getAllPossibleSubgoals()
|
|||||||
ret.push_back(goal);
|
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())
|
||||||
@@ -152,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;
|
||||||
}
|
}
|
||||||
@@ -263,37 +275,71 @@ TSubgoal Explore::explorationNewPoint(HeroPtr h) const
|
|||||||
VCAI *aip = ai.get();
|
VCAI *aip = ai.get();
|
||||||
const TeamState * ts = cbp->getPlayerTeam(aip->playerID);
|
const TeamState * ts = cbp->getPlayerTeam(aip->playerID);
|
||||||
|
|
||||||
std::vector<std::vector<int3>> tiles; //tiles[distance_to_fow]
|
int3 mapSize = cbp->getMapSize();
|
||||||
tiles.resize(radius);
|
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)
|
foreach_tile_pos([&](const int3 & pos)
|
||||||
{
|
{
|
||||||
if(!cbp->isVisible(pos))
|
if(ts->fogOfWarMap[pos.x][pos.y][pos.z])
|
||||||
tiles[0].push_back(pos);
|
{
|
||||||
|
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
|
float bestValue = 0; //discovered tile to node distance ratio
|
||||||
TSubgoal bestWay = sptr(Invalid());
|
TSubgoal bestWay = sptr(Invalid());
|
||||||
int3 ourPos = h->convertPosition(h->pos, false);
|
int3 ourPos = h->convertPosition(h->pos, false);
|
||||||
|
|
||||||
for(int i = 1; i < radius; i++)
|
for(const int3 & tile : range)
|
||||||
{
|
|
||||||
getVisibleNeighbours(tiles[i - 1], tiles[i], cbp, ts);
|
|
||||||
vstd::removeDuplicates(tiles[i]);
|
|
||||||
|
|
||||||
for(const int3 & tile : tiles[i])
|
|
||||||
{
|
{
|
||||||
if(tile == ourPos) //shouldn't happen, but it does
|
if(tile == ourPos) //shouldn't happen, but it does
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto waysToVisit = aip->ah->howToVisitTile(h, tile);
|
int tilesDiscovered = howManyTilesWillBeDiscovered(tile, radius, cbp, ts, aip, h);
|
||||||
|
if(!tilesDiscovered)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto waysToVisit = aip->ah->howToVisitTile(h, tile, allowGatherArmy);
|
||||||
for(auto goal : waysToVisit)
|
for(auto goal : waysToVisit)
|
||||||
{
|
{
|
||||||
if(goal->evaluationContext.movementCost == 0) // should not happen
|
if(goal->evaluationContext.movementCost == 0) // should not happen
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int tilesDiscovered = howManyTilesWillBeDiscovered(tile, radius, cbp, ts, aip, h);
|
float ourValue = (float)tilesDiscovered * tilesDiscovered / goal->evaluationContext.movementCost;
|
||||||
float ourValue = (float) tilesDiscovered / goal->evaluationContext.movementCost; //+1 prevents erratic jumps
|
|
||||||
|
|
||||||
if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much
|
if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much
|
||||||
{
|
{
|
||||||
@@ -316,12 +362,11 @@ TSubgoal Explore::explorationNewPoint(HeroPtr h) const
|
|||||||
{
|
{
|
||||||
return bestWay;
|
return bestWay;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return bestWay;
|
return bestWay;
|
||||||
}
|
}
|
||||||
|
|
||||||
TSubgoal Explore::howToExplore(HeroPtr h) const
|
TSubgoal Explore::exploreNearestNeighbour(HeroPtr h) const
|
||||||
{
|
{
|
||||||
TimeCheck tc("where to explore");
|
TimeCheck tc("where to explore");
|
||||||
int radius = h->getSightRadius();
|
int radius = h->getSightRadius();
|
||||||
@@ -346,11 +391,11 @@ TSubgoal Explore::howToExplore(HeroPtr h) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(nearbyVisitableObjs.size())
|
||||||
|
{
|
||||||
vstd::removeDuplicates(nearbyVisitableObjs); //one object may occupy multiple tiles
|
vstd::removeDuplicates(nearbyVisitableObjs); //one object may occupy multiple tiles
|
||||||
boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get()));
|
boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get()));
|
||||||
|
|
||||||
if(nearbyVisitableObjs.size())
|
|
||||||
{
|
|
||||||
TSubgoal pickupNearestObj = fh->chooseSolution(ai->ah->howToVisitObj(h, nearbyVisitableObjs.back(), false));
|
TSubgoal pickupNearestObj = fh->chooseSolution(ai->ah->howToVisitObj(h, nearbyVisitableObjs.back(), false));
|
||||||
|
|
||||||
if(!pickupNearestObj->invalid())
|
if(!pickupNearestObj->invalid())
|
||||||
@@ -360,15 +405,7 @@ TSubgoal Explore::howToExplore(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
|
||||||
TSubgoal result = explorationBestNeighbour(hpos, radius, h);
|
return 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 TeamState * ts) const
|
void Explore::getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out, CCallback * cbp, const TeamState * ts) const
|
||||||
@@ -379,9 +416,6 @@ void Explore::getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<
|
|||||||
{
|
{
|
||||||
if(ts->fogOfWarMap[neighbour.x][neighbour.y][neighbour.z])
|
if(ts->fogOfWarMap[neighbour.x][neighbour.y][neighbour.z])
|
||||||
{
|
{
|
||||||
//auto tile = cbp->getTile(neighbour);
|
|
||||||
|
|
||||||
//if(tile && !tile->blocked)
|
|
||||||
out.push_back(neighbour);
|
out.push_back(neighbour);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
@@ -38,9 +47,10 @@ namespace Goals
|
|||||||
virtual bool operator==(const Explore & other) const override;
|
virtual bool operator==(const Explore & other) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TSubgoal howToExplore(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, 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;
|
bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const;
|
||||||
|
|
||||||
void getVisibleNeighbours(
|
void getVisibleNeighbours(
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user