mirror of
https://github.com/vcmi/vcmi.git
synced 2025-09-16 09:26:28 +02:00
- Removed slow and buggy part of exploration code
- Various tweaks and performance improvements. remove_if on long vector is a bad idea.
This commit is contained in:
@@ -184,12 +184,12 @@ bool compareDanger(const CGObjectInstance *lhs, const CGObjectInstance *rhs)
|
|||||||
bool isSafeToVisit(HeroPtr h, crint3 tile)
|
bool isSafeToVisit(HeroPtr h, crint3 tile)
|
||||||
{
|
{
|
||||||
const ui64 heroStrength = h->getTotalStrength(),
|
const ui64 heroStrength = h->getTotalStrength(),
|
||||||
dangerStrength = evaluateDanger(tile, *h);
|
dangerStrength = evaluateDanger(tile, *h);
|
||||||
if(dangerStrength)
|
if(dangerStrength)
|
||||||
{
|
{
|
||||||
if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength)
|
if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength)
|
||||||
{
|
{
|
||||||
logAi->debugStream() << boost::format("It's, safe for %s to visit tile %s") % h->name % tile;
|
logAi->traceStream() << boost::format("It's safe for %s to visit tile %s") % h->name % tile;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -240,28 +240,7 @@ int3 whereToExplore(HeroPtr h)
|
|||||||
catch(cannotFulfillGoalException &e)
|
catch(cannotFulfillGoalException &e)
|
||||||
{
|
{
|
||||||
std::vector<std::vector<int3> > tiles; //tiles[distance_to_fow]
|
std::vector<std::vector<int3> > tiles; //tiles[distance_to_fow]
|
||||||
try
|
return ai->explorationNewPoint(radius, h, tiles);
|
||||||
{
|
|
||||||
return ai->explorationNewPoint(radius, h, tiles);
|
|
||||||
}
|
|
||||||
catch(cannotFulfillGoalException &e)
|
|
||||||
{
|
|
||||||
std::map<int, std::vector<int3> > profits;
|
|
||||||
{
|
|
||||||
TimeCheck tc("Evaluating exploration possibilities");
|
|
||||||
tiles[0].clear(); //we can't reach FoW anyway
|
|
||||||
for(auto &vt : tiles)
|
|
||||||
for(auto &tile : vt)
|
|
||||||
profits[howManyTilesWillBeDiscovered(tile, radius)].push_back(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(profits.empty())
|
|
||||||
return int3 (-1,-1,-1);
|
|
||||||
|
|
||||||
auto bestDest = profits.end();
|
|
||||||
bestDest--;
|
|
||||||
return bestDest->second.front(); //TODO which is the real best tile?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -351,7 +351,7 @@ void FuzzyHelper::initVisitTile()
|
|||||||
|
|
||||||
helper += vt.strengthRatio, vt.heroStrength, vt.tileDistance, vt.missionImportance;
|
helper += vt.strengthRatio, vt.heroStrength, vt.tileDistance, vt.missionImportance;
|
||||||
|
|
||||||
vt.strengthRatio->addTerm (new fl::ShoulderTerm("LOW", 0.3, SAFE_ATTACK_CONSTANT, true));
|
vt.strengthRatio->addTerm (new fl::ShoulderTerm("LOW", 0.9, SAFE_ATTACK_CONSTANT, true));
|
||||||
vt.strengthRatio->addTerm (new fl::ShoulderTerm("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3, false));
|
vt.strengthRatio->addTerm (new fl::ShoulderTerm("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3, false));
|
||||||
|
|
||||||
vt.heroStrength->addTerm (new fl::ShoulderTerm("LOW", 1, 2500, true)); //assumed strength of new hero from tavern
|
vt.heroStrength->addTerm (new fl::ShoulderTerm("LOW", 1, 2500, true)); //assumed strength of new hero from tavern
|
||||||
@@ -378,6 +378,8 @@ void FuzzyHelper::initVisitTile()
|
|||||||
//vt.rules.addRule (new fl::MamdaniRule("if OurShooters is MANY and EnemySpeed is LOW then Threat is very LOW", engine));
|
//vt.rules.addRule (new fl::MamdaniRule("if OurShooters is MANY and EnemySpeed is LOW then Threat is very LOW", engine));
|
||||||
//use unarmed scouts if possible
|
//use unarmed scouts if possible
|
||||||
vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is HIGH and heroStrength is LOW then Value is very HIGH", engine));
|
vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is HIGH and heroStrength is LOW then Value is very HIGH", engine));
|
||||||
|
//don't assign targets to heroes who are too weak
|
||||||
|
vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is very LOW then Value is very LOW", engine));
|
||||||
//if medium heroes can't scratch enemy, don't try to arm them
|
//if medium heroes can't scratch enemy, don't try to arm them
|
||||||
vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is LOW and heroStrength is MEDIUM then Value is LOW", engine));
|
vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is LOW and heroStrength is MEDIUM then Value is LOW", engine));
|
||||||
//do not cancel important goals
|
//do not cancel important goals
|
||||||
|
@@ -273,9 +273,13 @@ TSubgoal VisitHero::whatToDoToAchieve()
|
|||||||
|
|
||||||
if (hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
|
if (hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
|
||||||
{
|
{
|
||||||
assert (hero->pos != pos); //don't try to visit yourself
|
if (hero->pos == pos)
|
||||||
settile(pos).setisElementar(true);
|
logAi->errorStream() << "Hero " << hero.name << " tries to visit himself.";
|
||||||
return sptr (*this);
|
else
|
||||||
|
{
|
||||||
|
settile(pos).setisElementar(true);
|
||||||
|
return sptr (*this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return sptr (Goals::Invalid());
|
return sptr (Goals::Invalid());
|
||||||
}
|
}
|
||||||
@@ -395,28 +399,22 @@ TGoalVec Explore::getAllPossibleSubgoals()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto objs = ai->visitableObjs; //try to use buildings that uncover map
|
//try to use buildings that uncover map
|
||||||
erase_if(objs, [&](const CGObjectInstance *obj) -> bool
|
std::vector<const CGObjectInstance *> objs;
|
||||||
|
for (auto obj : ai->visitableObjs)
|
||||||
{
|
{
|
||||||
if (vstd::contains(ai->alreadyVisited, obj))
|
if (!vstd::contains(ai->alreadyVisited, obj))
|
||||||
return true;
|
|
||||||
switch (obj->ID.num)
|
|
||||||
{
|
{
|
||||||
case Obj::REDWOOD_OBSERVATORY:
|
switch (obj->ID.num)
|
||||||
case Obj::PILLAR_OF_FIRE:
|
{
|
||||||
case Obj::CARTOGRAPHER:
|
case Obj::REDWOOD_OBSERVATORY:
|
||||||
case Obj::SUBTERRANEAN_GATE: //TODO: check ai->knownSubterraneanGates
|
case Obj::PILLAR_OF_FIRE:
|
||||||
//case Obj::MONOLITH1:
|
case Obj::CARTOGRAPHER:
|
||||||
//case obj::MONOLITH2:
|
case Obj::SUBTERRANEAN_GATE: //TODO: check ai->knownSubterraneanGates
|
||||||
//case obj::MONOLITH3:
|
objs.push_back (obj);
|
||||||
//case Obj::WHIRLPOOL:
|
}
|
||||||
return false; //do not erase
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
for (auto h : heroes)
|
for (auto h : heroes)
|
||||||
{
|
{
|
||||||
for (auto obj : objs) //double loop, performance risk?
|
for (auto obj : objs) //double loop, performance risk?
|
||||||
@@ -428,8 +426,8 @@ TGoalVec Explore::getAllPossibleSubgoals()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int3 t = whereToExplore(h);
|
int3 t = whereToExplore(h);
|
||||||
if (t.z != -1) //no safe tile to explore - we need to break!
|
if (t.z != -1) //no valid tile was found
|
||||||
ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
|
ret.push_back (sptr (Goals::VisitTile(t).sethero(h)));
|
||||||
}
|
}
|
||||||
if (!hero && ai->canRecruitAnyHero())//if hero is assigned to that goal, no need to buy another one yet
|
if (!hero && ai->canRecruitAnyHero())//if hero is assigned to that goal, no need to buy another one yet
|
||||||
ret.push_back (sptr(Goals::RecruitHero()));
|
ret.push_back (sptr(Goals::RecruitHero()));
|
||||||
|
@@ -1096,36 +1096,31 @@ std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(HeroPtr h)
|
|||||||
std::vector<const CGObjectInstance *> possibleDestinations;
|
std::vector<const CGObjectInstance *> possibleDestinations;
|
||||||
for(const CGObjectInstance *obj : visitableObjs)
|
for(const CGObjectInstance *obj : visitableObjs)
|
||||||
{
|
{
|
||||||
if(isAccessibleForHero(obj->visitablePos(), h) && !obj->wasVisited(playerID) &&
|
const int3 pos = obj->visitablePos();
|
||||||
(obj->tempOwner != playerID || isWeeklyRevisitable(obj))) //flag or get weekly resources / creatures
|
if (isAccessibleForHero(obj->visitablePos(), h) &&
|
||||||
|
!obj->wasVisited(playerID) &&
|
||||||
|
(obj->tempOwner != playerID || isWeeklyRevisitable(obj)) && //flag or get weekly resources / creatures
|
||||||
|
isSafeToVisit(h, pos) &&
|
||||||
|
shouldVisit(h, obj) &&
|
||||||
|
!vstd::contains(alreadyVisited, obj) &&
|
||||||
|
!vstd::contains(reservedObjs, obj))
|
||||||
|
{
|
||||||
possibleDestinations.push_back(obj);
|
possibleDestinations.push_back(obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::sort(possibleDestinations, isCloser);
|
|
||||||
|
|
||||||
possibleDestinations.erase(boost::remove_if(possibleDestinations, [&](const CGObjectInstance *obj) -> bool
|
possibleDestinations.erase(boost::remove_if(possibleDestinations, [&](const CGObjectInstance *obj) -> bool
|
||||||
{
|
{
|
||||||
const int3 pos = obj->visitablePos();
|
const CGObjectInstance *topObj = cb->getVisitableObjs(obj->visitablePos()).back(); //it may be hero visiting this obj
|
||||||
if(vstd::contains(alreadyVisited, obj))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if(!isSafeToVisit(h, pos))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!shouldVisit(h, obj))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (vstd::contains(reservedObjs, obj)) //does checking for our own reserved objects make sense? here?
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const CGObjectInstance *topObj = cb->getVisitableObjs(pos).back(); //it may be hero visiting this obj
|
|
||||||
//we don't try visiting object on which allied or owned hero stands
|
//we don't try visiting object on which allied or owned hero stands
|
||||||
// -> it will just trigger exchange windows and AI will be confused that obj behind doesn't get visited
|
// -> it will just trigger exchange windows and AI will be confused that obj behind doesn't get visited
|
||||||
if(topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
|
if(topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}),possibleDestinations.end());
|
}), possibleDestinations.end());
|
||||||
|
|
||||||
|
boost::sort(possibleDestinations, isCloser);
|
||||||
|
|
||||||
return possibleDestinations;
|
return possibleDestinations;
|
||||||
}
|
}
|
||||||
@@ -2004,12 +1999,11 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h)
|
|||||||
std::map<int3, int> dstToRevealedTiles;
|
std::map<int3, int> dstToRevealedTiles;
|
||||||
for(crint3 dir : dirs)
|
for(crint3 dir : dirs)
|
||||||
if(cb->isInTheMap(hpos+dir))
|
if(cb->isInTheMap(hpos+dir))
|
||||||
dstToRevealedTiles[hpos + dir] = howManyTilesWillBeDiscovered(radius, hpos, dir) * isSafeToVisit(h, hpos + dir);
|
if (isSafeToVisit(h, hpos + dir) && isAccessibleForHero (hpos + dir, h))
|
||||||
|
dstToRevealedTiles[hpos + dir] = howManyTilesWillBeDiscovered(radius, hpos, dir);
|
||||||
|
|
||||||
auto best = dstToRevealedTiles.begin();
|
auto best = dstToRevealedTiles.begin();
|
||||||
best->second *= cb->getPathInfo(best->first)->reachable();
|
for (auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++)
|
||||||
best->second *= cb->getPathInfo(best->first)->accessible == CGPathNode::ACCESSIBLE;
|
|
||||||
for(auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++)
|
|
||||||
{
|
{
|
||||||
const CGPathNode *pn = cb->getPathInfo(i->first);
|
const CGPathNode *pn = cb->getPathInfo(i->first);
|
||||||
//const TerrainTile *t = cb->getTile(i->first);
|
//const TerrainTile *t = cb->getTile(i->first);
|
||||||
@@ -2042,13 +2036,17 @@ int3 VCAI::explorationNewPoint(int radius, HeroPtr h, std::vector<std::vector<in
|
|||||||
|
|
||||||
for(const int3 &tile : tiles[i])
|
for(const int3 &tile : tiles[i])
|
||||||
{
|
{
|
||||||
if(cb->getPathInfo(tile)->reachable() && isSafeToVisit(h, tile) && howManyTilesWillBeDiscovered(tile, radius) && !isBlockedBorderGate(tile))
|
if (cb->getTile(tile)->blocked) //does it shorten the time?
|
||||||
|
continue;
|
||||||
|
if(cb->getPathInfo(tile)->reachable() && howManyTilesWillBeDiscovered(tile, radius) &&
|
||||||
|
isSafeToVisit(h, tile) && !isBlockedBorderGate(tile))
|
||||||
{
|
{
|
||||||
return tile;
|
return tile; //return first tile that will discover anything
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw cannotFulfillGoalException("No accessible tile will bring discoveries!");
|
return int3 (-1,-1,-1);
|
||||||
|
//throw cannotFulfillGoalException("No accessible tile will bring discoveries!");
|
||||||
}
|
}
|
||||||
|
|
||||||
TResources VCAI::estimateIncome() const
|
TResources VCAI::estimateIncome() const
|
||||||
|
@@ -354,7 +354,6 @@ public:
|
|||||||
void checkForObjectives();
|
void checkForObjectives();
|
||||||
|
|
||||||
ui32 checksum;
|
ui32 checksum;
|
||||||
/// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground
|
|
||||||
std::vector<Rumor> rumors;
|
std::vector<Rumor> rumors;
|
||||||
std::vector<DisposedHero> disposedHeroes;
|
std::vector<DisposedHero> disposedHeroes;
|
||||||
std::vector<ConstTransitivePtr<CGHeroInstance> > predefinedHeroes;
|
std::vector<ConstTransitivePtr<CGHeroInstance> > predefinedHeroes;
|
||||||
@@ -383,6 +382,7 @@ public:
|
|||||||
unique_ptr<CMapEditManager> editManager;
|
unique_ptr<CMapEditManager> editManager;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground
|
||||||
TerrainTile*** terrain;
|
TerrainTile*** terrain;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Reference in New Issue
Block a user