1
0
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:
DjWarmonger
2013-12-21 17:34:59 +00:00
parent 5ec3685041
commit 473250e223
5 changed files with 53 additions and 76 deletions

View File

@@ -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?
}
} }
} }

View File

@@ -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

View File

@@ -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()));

View File

@@ -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

View File

@@ -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: