From 473250e223ecf60887fd7165f6b21830ed2e0a02 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Sat, 21 Dec 2013 17:34:59 +0000 Subject: [PATCH] - Removed slow and buggy part of exploration code - Various tweaks and performance improvements. remove_if on long vector is a bad idea. --- AI/VCAI/AIUtility.cpp | 27 +++-------------------- AI/VCAI/Fuzzy.cpp | 4 +++- AI/VCAI/Goals.cpp | 46 +++++++++++++++++++-------------------- AI/VCAI/VCAI.cpp | 50 +++++++++++++++++++++---------------------- lib/mapping/CMap.h | 2 +- 5 files changed, 53 insertions(+), 76 deletions(-) diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index fd8902b1c..a37481b21 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -184,12 +184,12 @@ bool compareDanger(const CGObjectInstance *lhs, const CGObjectInstance *rhs) bool isSafeToVisit(HeroPtr h, crint3 tile) { const ui64 heroStrength = h->getTotalStrength(), - dangerStrength = evaluateDanger(tile, *h); + dangerStrength = evaluateDanger(tile, *h); if(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; } else @@ -240,28 +240,7 @@ int3 whereToExplore(HeroPtr h) catch(cannotFulfillGoalException &e) { std::vector > tiles; //tiles[distance_to_fow] - try - { - return ai->explorationNewPoint(radius, h, tiles); - } - catch(cannotFulfillGoalException &e) - { - std::map > 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? - } + return ai->explorationNewPoint(radius, h, tiles); } } diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index 6c8963ed6..0880ecbdb 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -351,7 +351,7 @@ void FuzzyHelper::initVisitTile() 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.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)); //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)); + //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 vt.rules.addRule (new fl::MamdaniRule("if strengthRatio is LOW and heroStrength is MEDIUM then Value is LOW", engine)); //do not cancel important goals diff --git a/AI/VCAI/Goals.cpp b/AI/VCAI/Goals.cpp index b4c1c1b60..afdcdea3e 100644 --- a/AI/VCAI/Goals.cpp +++ b/AI/VCAI/Goals.cpp @@ -273,9 +273,13 @@ TSubgoal VisitHero::whatToDoToAchieve() 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 - settile(pos).setisElementar(true); - return sptr (*this); + if (hero->pos == pos) + logAi->errorStream() << "Hero " << hero.name << " tries to visit himself."; + else + { + settile(pos).setisElementar(true); + return sptr (*this); + } } return sptr (Goals::Invalid()); } @@ -395,28 +399,22 @@ TGoalVec Explore::getAllPossibleSubgoals() }); } - auto objs = ai->visitableObjs; //try to use buildings that uncover map - erase_if(objs, [&](const CGObjectInstance *obj) -> bool + //try to use buildings that uncover map + std::vector objs; + for (auto obj : ai->visitableObjs) { - if (vstd::contains(ai->alreadyVisited, obj)) - return true; - switch (obj->ID.num) + if (!vstd::contains(ai->alreadyVisited, obj)) { - case Obj::REDWOOD_OBSERVATORY: - case Obj::PILLAR_OF_FIRE: - case Obj::CARTOGRAPHER: - case Obj::SUBTERRANEAN_GATE: //TODO: check ai->knownSubterraneanGates - //case Obj::MONOLITH1: - //case obj::MONOLITH2: - //case obj::MONOLITH3: - //case Obj::WHIRLPOOL: - return false; //do not erase - break; - default: - return true; + switch (obj->ID.num) + { + case Obj::REDWOOD_OBSERVATORY: + case Obj::PILLAR_OF_FIRE: + case Obj::CARTOGRAPHER: + case Obj::SUBTERRANEAN_GATE: //TODO: check ai->knownSubterraneanGates + objs.push_back (obj); + } } - }); - + } for (auto h : heroes) { for (auto obj : objs) //double loop, performance risk? @@ -428,8 +426,8 @@ TGoalVec Explore::getAllPossibleSubgoals() } int3 t = whereToExplore(h); - if (t.z != -1) //no safe tile to explore - we need to break! - ret.push_back (sptr (Goals::VisitTile(t).sethero(h))); + if (t.z != -1) //no valid tile was found + 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 ret.push_back (sptr(Goals::RecruitHero())); diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 1d38ef1bb..ef029a322 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -1096,36 +1096,31 @@ std::vector VCAI::getPossibleDestinations(HeroPtr h) std::vector possibleDestinations; for(const CGObjectInstance *obj : visitableObjs) { - if(isAccessibleForHero(obj->visitablePos(), h) && !obj->wasVisited(playerID) && - (obj->tempOwner != playerID || isWeeklyRevisitable(obj))) //flag or get weekly resources / creatures + const int3 pos = obj->visitablePos(); + 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); + } } - boost::sort(possibleDestinations, isCloser); - possibleDestinations.erase(boost::remove_if(possibleDestinations, [&](const CGObjectInstance *obj) -> bool { - const int3 pos = obj->visitablePos(); - 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 + const CGObjectInstance *topObj = cb->getVisitableObjs(obj->visitablePos()).back(); //it may be hero visiting this obj //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 if(topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES) return true; return false; - }),possibleDestinations.end()); + }), possibleDestinations.end()); + + boost::sort(possibleDestinations, isCloser); return possibleDestinations; } @@ -2004,12 +1999,11 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) std::map dstToRevealedTiles; for(crint3 dir : dirs) 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(); - best->second *= cb->getPathInfo(best->first)->reachable(); - best->second *= cb->getPathInfo(best->first)->accessible == CGPathNode::ACCESSIBLE; - for(auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++) + for (auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++) { const CGPathNode *pn = cb->getPathInfo(i->first); //const TerrainTile *t = cb->getTile(i->first); @@ -2042,13 +2036,17 @@ int3 VCAI::explorationNewPoint(int radius, HeroPtr h, std::vectorgetPathInfo(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 diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 753278122..049f236e7 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -354,7 +354,6 @@ public: void checkForObjectives(); ui32 checksum; - /// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground std::vector rumors; std::vector disposedHeroes; std::vector > predefinedHeroes; @@ -383,6 +382,7 @@ public: unique_ptr editManager; private: + /// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground TerrainTile*** terrain; public: