mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Merge pull request #4220 from IvanSavenko/ai_optimize
[1.5.4] AI optimizations
This commit is contained in:
commit
11a3da3f4f
@ -649,7 +649,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
||||
auto ratio = static_cast<float>(danger) / hero->getTotalStrength();
|
||||
|
||||
answer = topObj->id == goalObjectID; // no if we do not aim to visit this object
|
||||
logAi->trace("Query hook: %s(%s) by %s danger ratio %f", target.toString(), topObj->getObjectName(), hero.name, ratio);
|
||||
logAi->trace("Query hook: %s(%s) by %s danger ratio %f", target.toString(), topObj->getObjectName(), hero.name(), ratio);
|
||||
|
||||
if(cb->getObj(goalObjectID, false))
|
||||
{
|
||||
@ -1574,7 +1574,7 @@ void AIGateway::requestActionASAP(std::function<void()> whatToDo)
|
||||
|
||||
void AIGateway::lostHero(HeroPtr h)
|
||||
{
|
||||
logAi->debug("I lost my hero %s. It's best to forget and move on.", h.name);
|
||||
logAi->debug("I lost my hero %s. It's best to forget and move on.", h.name());
|
||||
}
|
||||
|
||||
void AIGateway::answerQuery(QueryID queryID, int selection)
|
||||
|
@ -67,7 +67,6 @@ HeroPtr::HeroPtr(const CGHeroInstance * H)
|
||||
}
|
||||
|
||||
h = H;
|
||||
name = h->getNameTranslated();
|
||||
hid = H->id;
|
||||
// infosCount[ai->playerID][hid]++;
|
||||
}
|
||||
@ -89,6 +88,14 @@ bool HeroPtr::operator<(const HeroPtr & rhs) const
|
||||
return hid < rhs.hid;
|
||||
}
|
||||
|
||||
std::string HeroPtr::name() const
|
||||
{
|
||||
if (h)
|
||||
return h->getNameTextID();
|
||||
else
|
||||
return "<NO HERO>";
|
||||
}
|
||||
|
||||
const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const
|
||||
{
|
||||
return get(cb, doWeExpectNull);
|
||||
|
@ -87,8 +87,7 @@ struct DLL_EXPORT HeroPtr
|
||||
ObjectInstanceID hid;
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
std::string name() const;
|
||||
|
||||
HeroPtr();
|
||||
HeroPtr(const CGHeroInstance * H);
|
||||
@ -117,7 +116,6 @@ public:
|
||||
{
|
||||
handler & h;
|
||||
handler & hid;
|
||||
handler & name;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -176,7 +176,7 @@ int HeroManager::selectBestSkill(const HeroPtr & hero, const std::vector<Seconda
|
||||
|
||||
logAi->trace(
|
||||
"Hero %s is proposed to learn %d with score %f",
|
||||
hero.name,
|
||||
hero.name(),
|
||||
skills[i].toEnum(),
|
||||
score);
|
||||
}
|
||||
@ -204,6 +204,7 @@ float HeroManager::getFightingStrengthCached(const CGHeroInstance * hero) const
|
||||
{
|
||||
auto cached = knownFightingStrength.find(hero->id);
|
||||
|
||||
//FIXME: fallback to hero->getFightingStrength() is VERY slow on higher difficulties (no object graph? map reveal?)
|
||||
return cached != knownFightingStrength.end() ? cached->second : hero->getFightingStrength();
|
||||
}
|
||||
|
||||
|
@ -212,7 +212,7 @@ void CaptureObjectsBehavior::decomposeObjects(
|
||||
vstd::concatenate(tasksLocal, getVisitGoals(paths, nullkiller, objToVisit, specificObjects));
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(sync);
|
||||
std::lock_guard<std::mutex> lock(sync); // FIXME: consider using tbb::parallel_reduce instead to avoid mutex overhead
|
||||
vstd::concatenate(result, tasksLocal);
|
||||
});
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ namespace Goals
|
||||
{
|
||||
objid = obj->id.getNum();
|
||||
tile = obj->visitablePos();
|
||||
name = obj->getObjectName();
|
||||
name = obj->typeName;
|
||||
}
|
||||
|
||||
bool operator==(const CaptureObject & other) const override;
|
||||
|
@ -26,7 +26,7 @@ ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance *
|
||||
if(obj)
|
||||
{
|
||||
objid = obj->id.getNum();
|
||||
targetName = obj->getObjectName() + tile.toString();
|
||||
targetName = obj->typeName + tile.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -106,7 +106,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->error("Hero %s was lost. Exit hero chain.", heroPtr.name);
|
||||
logAi->error("Hero %s was lost. Exit hero chain.", heroPtr.name());
|
||||
|
||||
return;
|
||||
}
|
||||
@ -143,7 +143,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->error("Hero %s was lost trying to execute special action. Exit hero chain.", heroPtr.name);
|
||||
logAi->error("Hero %s was lost trying to execute special action. Exit hero chain.", heroPtr.name());
|
||||
|
||||
return;
|
||||
}
|
||||
@ -204,7 +204,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
{
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->error("Hero %s was lost. Exit hero chain.", heroPtr.name);
|
||||
logAi->error("Hero %s was lost. Exit hero chain.", heroPtr.name());
|
||||
|
||||
return;
|
||||
}
|
||||
@ -250,7 +250,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
{
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->debug("Hero %s was killed while attempting to reach %s", heroPtr.name, node->coord.toString());
|
||||
logAi->debug("Hero %s was killed while attempting to reach %s", heroPtr.name(), node->coord.toString());
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -70,11 +70,8 @@ bool ExplorationHelper::scanMap()
|
||||
int3 mapSize = cbp->getMapSize();
|
||||
int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y);
|
||||
|
||||
std::vector<int3> from;
|
||||
std::vector<int3> to;
|
||||
|
||||
from.reserve(perimeter);
|
||||
to.reserve(perimeter);
|
||||
std::vector<int3> edgeTiles;
|
||||
edgeTiles.reserve(perimeter);
|
||||
|
||||
foreach_tile_pos([&](const int3 & pos)
|
||||
{
|
||||
@ -91,13 +88,13 @@ bool ExplorationHelper::scanMap()
|
||||
});
|
||||
|
||||
if(hasInvisibleNeighbor)
|
||||
from.push_back(pos);
|
||||
edgeTiles.push_back(pos);
|
||||
}
|
||||
});
|
||||
|
||||
logAi->debug("Exploration scan visible area perimeter for hero %s", hero->getNameTranslated());
|
||||
|
||||
for(const int3 & tile : from)
|
||||
for(const int3 & tile : edgeTiles)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
@ -108,19 +105,36 @@ bool ExplorationHelper::scanMap()
|
||||
}
|
||||
|
||||
allowDeadEndCancellation = false;
|
||||
|
||||
for(int i = 0; i < sightRadius; i++)
|
||||
{
|
||||
getVisibleNeighbours(from, to);
|
||||
vstd::concatenate(from, to);
|
||||
vstd::removeDuplicates(from);
|
||||
}
|
||||
|
||||
logAi->debug("Exploration scan all possible tiles for hero %s", hero->getNameTranslated());
|
||||
|
||||
for(const int3 & tile : from)
|
||||
boost::multi_array<ui8, 3> potentialTiles = *ts->fogOfWarMap;
|
||||
std::vector<int3> tilesToExploreFrom = edgeTiles;
|
||||
|
||||
// WARNING: POTENTIAL BUG
|
||||
// AI attempts to move to any tile within sight radius to reveal some new tiles
|
||||
// however sight radius is circular, while this method assumes square radius
|
||||
// standing on the edge of a square will NOT reveal tile in opposite corner
|
||||
for(int i = 0; i < sightRadius; i++)
|
||||
{
|
||||
scanTile(tile);
|
||||
std::vector<int3> newTilesToExploreFrom;
|
||||
|
||||
for(const int3 & tile : tilesToExploreFrom)
|
||||
{
|
||||
foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if(potentialTiles[neighbour.z][neighbour.x][neighbour.y])
|
||||
{
|
||||
newTilesToExploreFrom.push_back(neighbour);
|
||||
potentialTiles[neighbour.z][neighbour.x][neighbour.y] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
for(const int3 & tile : newTilesToExploreFrom)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
|
||||
std::swap(tilesToExploreFrom, newTilesToExploreFrom);
|
||||
}
|
||||
|
||||
return !bestGoal->invalid();
|
||||
@ -172,20 +186,6 @@ void ExplorationHelper::scanTile(const int3 & tile)
|
||||
}
|
||||
}
|
||||
|
||||
void ExplorationHelper::getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const
|
||||
{
|
||||
for(const int3 & tile : tiles)
|
||||
{
|
||||
foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if((*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y])
|
||||
{
|
||||
out.push_back(neighbour);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int ExplorationHelper::howManyTilesWillBeDiscovered(const int3 & pos) const
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -46,7 +46,6 @@ public:
|
||||
private:
|
||||
void scanTile(const int3 & tile);
|
||||
bool hasReachableNeighbor(const int3 & pos) const;
|
||||
void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -320,11 +320,9 @@ void AINodeStorage::calculateNeighbours(
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
const CPathfinderHelper * pathfinderHelper)
|
||||
{
|
||||
std::vector<int3> accessibleNeighbourTiles;
|
||||
NeighbourTilesVector accessibleNeighbourTiles;
|
||||
|
||||
result.clear();
|
||||
accessibleNeighbourTiles.reserve(8);
|
||||
|
||||
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
||||
|
||||
const AIPathNode * srcNode = getAINode(source.node);
|
||||
|
@ -23,6 +23,8 @@ constexpr int NKAI_GRAPH_TRACE_LEVEL = 0;
|
||||
#include "Actions/SpecialAction.h"
|
||||
#include "Actors.h"
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
namespace AIPathfinding
|
||||
@ -85,7 +87,9 @@ struct AIPathNodeInfo
|
||||
|
||||
struct AIPath
|
||||
{
|
||||
std::vector<AIPathNodeInfo> nodes;
|
||||
using NodesVector = boost::container::small_vector<AIPathNodeInfo, 16>;
|
||||
|
||||
NodesVector nodes;
|
||||
uint64_t targetObjectDanger;
|
||||
uint64_t armyLoss;
|
||||
uint64_t targetObjectArmyLoss;
|
||||
|
@ -141,7 +141,8 @@ namespace AIPathfinding
|
||||
{
|
||||
SpellID summonBoat = SpellID::SUMMON_BOAT;
|
||||
|
||||
return hero->getSpellCost(summonBoat.toSpell());
|
||||
// FIXME: this should be hero->getSpellCost, however currently queries to bonus system are too slow
|
||||
return summonBoat.toSpell()->getCost(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
|
||||
|
||||
targetNode.specialAction = compositeAction;
|
||||
|
||||
auto targetGraphNode = graph.getNode(target);
|
||||
const auto & targetGraphNode = graph.getNode(target);
|
||||
|
||||
if(targetGraphNode.objID.hasValue())
|
||||
{
|
||||
|
@ -162,10 +162,9 @@ void AINodeStorage::calculateNeighbours(
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
const CPathfinderHelper * pathfinderHelper)
|
||||
{
|
||||
std::vector<int3> accessibleNeighbourTiles;
|
||||
NeighbourTilesVector accessibleNeighbourTiles;
|
||||
|
||||
result.clear();
|
||||
accessibleNeighbourTiles.reserve(8);
|
||||
|
||||
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
||||
|
||||
|
@ -85,6 +85,9 @@ void BonusList::stackBonuses()
|
||||
|
||||
int BonusList::totalValue() const
|
||||
{
|
||||
if (bonuses.empty())
|
||||
return 0;
|
||||
|
||||
struct BonusCollection
|
||||
{
|
||||
int base = 0;
|
||||
@ -96,63 +99,65 @@ int BonusList::totalValue() const
|
||||
int indepMax = std::numeric_limits<int>::min();
|
||||
};
|
||||
|
||||
auto percent = [](int64_t base, int64_t percent) -> int {
|
||||
return static_cast<int>(std::clamp<int64_t>((base * (100 + percent)) / 100, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()));
|
||||
auto percent = [](int base, int percent) -> int {
|
||||
return (static_cast<int64_t>(base) * (100 + percent)) / 100;
|
||||
};
|
||||
std::array <BonusCollection, vstd::to_underlying(BonusSource::NUM_BONUS_SOURCE)> sources = {};
|
||||
BonusCollection any;
|
||||
|
||||
BonusCollection accumulated;
|
||||
bool hasIndepMax = false;
|
||||
bool hasIndepMin = false;
|
||||
|
||||
std::array<int, vstd::to_underlying(BonusSource::NUM_BONUS_SOURCE)> percentToSource = {};
|
||||
|
||||
for(const auto & b : bonuses)
|
||||
{
|
||||
switch(b->valType)
|
||||
{
|
||||
case BonusValueType::BASE_NUMBER:
|
||||
sources[vstd::to_underlying(b->source)].base += b->val;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_ALL:
|
||||
sources[vstd::to_underlying(b->source)].percentToAll += b->val;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_BASE:
|
||||
sources[vstd::to_underlying(b->source)].percentToBase += b->val;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_SOURCE:
|
||||
sources[vstd::to_underlying(b->source)].percentToSource += b->val;
|
||||
break;
|
||||
percentToSource[vstd::to_underlying(b->source)] += b->val;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_TARGET_TYPE:
|
||||
sources[vstd::to_underlying(b->targetSourceType)].percentToSource += b->val;
|
||||
break;
|
||||
case BonusValueType::ADDITIVE_VALUE:
|
||||
sources[vstd::to_underlying(b->source)].additive += b->val;
|
||||
break;
|
||||
case BonusValueType::INDEPENDENT_MAX:
|
||||
hasIndepMax = true;
|
||||
vstd::amax(sources[vstd::to_underlying(b->source)].indepMax, b->val);
|
||||
break;
|
||||
case BonusValueType::INDEPENDENT_MIN:
|
||||
hasIndepMin = true;
|
||||
vstd::amin(sources[vstd::to_underlying(b->source)].indepMin, b->val);
|
||||
percentToSource[vstd::to_underlying(b->targetSourceType)] += b->val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(const auto & src : sources)
|
||||
{
|
||||
any.base += percent(src.base, src.percentToSource);
|
||||
any.percentToBase += percent(src.percentToBase, src.percentToSource);
|
||||
any.percentToAll += percent(src.percentToAll, src.percentToSource);
|
||||
any.additive += percent(src.additive, src.percentToSource);
|
||||
if(hasIndepMin)
|
||||
vstd::amin(any.indepMin, percent(src.indepMin, src.percentToSource));
|
||||
if(hasIndepMax)
|
||||
vstd::amax(any.indepMax, percent(src.indepMax, src.percentToSource));
|
||||
}
|
||||
any.base = percent(any.base, any.percentToBase);
|
||||
any.base += any.additive;
|
||||
auto valFirst = percent(any.base ,any.percentToAll);
|
||||
|
||||
if(hasIndepMin && hasIndepMax && any.indepMin < any.indepMax)
|
||||
any.indepMax = any.indepMin;
|
||||
for(const auto & b : bonuses)
|
||||
{
|
||||
int sourceIndex = vstd::to_underlying(b->source);
|
||||
int valModified = percent(b->val, percentToSource[sourceIndex]);
|
||||
|
||||
switch(b->valType)
|
||||
{
|
||||
case BonusValueType::BASE_NUMBER:
|
||||
accumulated.base += valModified;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_ALL:
|
||||
accumulated.percentToAll += valModified;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_BASE:
|
||||
accumulated.percentToBase += valModified;
|
||||
break;
|
||||
case BonusValueType::ADDITIVE_VALUE:
|
||||
accumulated.additive += valModified;
|
||||
break;
|
||||
case BonusValueType::INDEPENDENT_MAX:
|
||||
hasIndepMax = true;
|
||||
vstd::amax(accumulated.indepMax, valModified);
|
||||
break;
|
||||
case BonusValueType::INDEPENDENT_MIN:
|
||||
hasIndepMin = true;
|
||||
vstd::amin(accumulated.indepMin, valModified);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
accumulated.base = percent(accumulated.base, accumulated.percentToBase);
|
||||
accumulated.base += accumulated.additive;
|
||||
auto valFirst = percent(accumulated.base ,accumulated.percentToAll);
|
||||
|
||||
if(hasIndepMin && hasIndepMax && accumulated.indepMin < accumulated.indepMax)
|
||||
accumulated.indepMax = accumulated.indepMin;
|
||||
|
||||
const int notIndepBonuses = static_cast<int>(std::count_if(bonuses.cbegin(), bonuses.cend(), [](const std::shared_ptr<Bonus>& b)
|
||||
{
|
||||
@ -160,9 +165,9 @@ int BonusList::totalValue() const
|
||||
}));
|
||||
|
||||
if(notIndepBonuses)
|
||||
return std::clamp(valFirst, any.indepMax, any.indepMin);
|
||||
return std::clamp(valFirst, accumulated.indepMax, accumulated.indepMin);
|
||||
|
||||
return hasIndepMin ? any.indepMin : hasIndepMax ? any.indepMax : 0;
|
||||
return hasIndepMin ? accumulated.indepMin : hasIndepMax ? accumulated.indepMax : 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> BonusList::getFirst(const CSelector &select)
|
||||
|
@ -157,7 +157,6 @@ void CLogger::log(ELogLevel::ELogLevel level, const boost::format & fmt) const
|
||||
|
||||
ELogLevel::ELogLevel CLogger::getLevel() const
|
||||
{
|
||||
TLockGuard _(mx);
|
||||
return level;
|
||||
}
|
||||
|
||||
|
@ -1231,7 +1231,7 @@ void RemoveObject::applyGs(CGameState *gs)
|
||||
|
||||
gs->map->instanceNames.erase(obj->instanceName);
|
||||
gs->map->objects[objectID.getNum()].dellNull();
|
||||
gs->map->calculateGuardingGreaturePositions();
|
||||
gs->map->calculateGuardingGreaturePositions();//FIXME: excessive, update only affected tiles
|
||||
}
|
||||
|
||||
static int getDir(const int3 & src, const int3 & dst)
|
||||
|
@ -49,7 +49,7 @@ bool CPathfinderHelper::canMoveFromNode(const PathNodeInfo & source) const
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPathfinderHelper::calculateNeighbourTiles(std::vector<int3> & result, const PathNodeInfo & source) const
|
||||
void CPathfinderHelper::calculateNeighbourTiles(NeighbourTilesVector & result, const PathNodeInfo & source) const
|
||||
{
|
||||
result.clear();
|
||||
|
||||
@ -239,9 +239,9 @@ void CPathfinder::calculatePaths()
|
||||
logAi->trace("CPathfinder finished with %s iterations", std::to_string(counter));
|
||||
}
|
||||
|
||||
std::vector<int3> CPathfinderHelper::getAllowedTeleportChannelExits(const TeleportChannelID & channelID) const
|
||||
TeleporterTilesVector CPathfinderHelper::getAllowedTeleportChannelExits(const TeleportChannelID & channelID) const
|
||||
{
|
||||
std::vector<int3> allowedExits;
|
||||
TeleporterTilesVector allowedExits;
|
||||
|
||||
for(const auto & objId : getTeleportChannelExits(channelID, hero->tempOwner))
|
||||
{
|
||||
@ -262,9 +262,9 @@ std::vector<int3> CPathfinderHelper::getAllowedTeleportChannelExits(const Telepo
|
||||
return allowedExits;
|
||||
}
|
||||
|
||||
std::vector<int3> CPathfinderHelper::getCastleGates(const PathNodeInfo & source) const
|
||||
TeleporterTilesVector CPathfinderHelper::getCastleGates(const PathNodeInfo & source) const
|
||||
{
|
||||
std::vector<int3> allowedExits;
|
||||
TeleporterTilesVector allowedExits;
|
||||
|
||||
auto towns = getPlayerState(hero->tempOwner)->towns;
|
||||
for(const auto & town : towns)
|
||||
@ -279,9 +279,9 @@ std::vector<int3> CPathfinderHelper::getCastleGates(const PathNodeInfo & source)
|
||||
return allowedExits;
|
||||
}
|
||||
|
||||
std::vector<int3> CPathfinderHelper::getTeleportExits(const PathNodeInfo & source) const
|
||||
TeleporterTilesVector CPathfinderHelper::getTeleportExits(const PathNodeInfo & source) const
|
||||
{
|
||||
std::vector<int3> teleportationExits;
|
||||
TeleporterTilesVector teleportationExits;
|
||||
|
||||
const auto * objTeleport = dynamic_cast<const CGTeleport *>(source.nodeObject);
|
||||
if(isAllowedTeleportEntrance(objTeleport))
|
||||
@ -578,7 +578,7 @@ int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer & layer) const
|
||||
void CPathfinderHelper::getNeighbours(
|
||||
const TerrainTile & srcTile,
|
||||
const int3 & srcCoord,
|
||||
std::vector<int3> & vec,
|
||||
NeighbourTilesVector & vec,
|
||||
const boost::logic::tribool & onLand,
|
||||
const bool limitCoastSailing) const
|
||||
{
|
||||
@ -702,8 +702,8 @@ int CPathfinderHelper::getMovementCost(
|
||||
constexpr auto maxCostOfOneStep = static_cast<int>(175 * M_SQRT2); // diagonal move on Swamp - 247 MP
|
||||
if(checkLast && left > 0 && left <= maxCostOfOneStep) //it might be the last tile - if no further move possible we take all move points
|
||||
{
|
||||
std::vector<int3> vec;
|
||||
vec.reserve(8); //optimization
|
||||
NeighbourTilesVector vec;
|
||||
|
||||
getNeighbours(*dt, dst, vec, ct->terType->isLand(), true);
|
||||
for(const auto & elem : vec)
|
||||
{
|
||||
|
@ -13,12 +13,23 @@
|
||||
#include "../IGameCallback.h"
|
||||
#include "../bonuses/BonusEnum.h"
|
||||
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CGWhirlpool;
|
||||
struct TurnInfo;
|
||||
struct PathfinderOptions;
|
||||
|
||||
// Optimized storage - tile can have 0-8 neighbour tiles
|
||||
// static_vector uses fixed, preallocated storage (capacity) and dynamic size
|
||||
// this avoid dynamic allocations on huge number of neighbour list queries
|
||||
using NeighbourTilesVector = boost::container::static_vector<int3, 8>;
|
||||
|
||||
// Optimized storage to minimize dynamic allocations - most of teleporters have only one exit, but some may have more (premade maps, castle gates)
|
||||
using TeleporterTilesVector = boost::container::small_vector<int3, 4>;
|
||||
|
||||
class DLL_LINKAGE CPathfinder
|
||||
{
|
||||
public:
|
||||
@ -87,22 +98,22 @@ public:
|
||||
bool hasBonusOfType(BonusType type) const;
|
||||
int getMaxMovePoints(const EPathfindingLayer & layer) const;
|
||||
|
||||
std::vector<int3> getCastleGates(const PathNodeInfo & source) const;
|
||||
TeleporterTilesVector getCastleGates(const PathNodeInfo & source) const;
|
||||
bool isAllowedTeleportEntrance(const CGTeleport * obj) const;
|
||||
std::vector<int3> getAllowedTeleportChannelExits(const TeleportChannelID & channelID) const;
|
||||
TeleporterTilesVector getAllowedTeleportChannelExits(const TeleportChannelID & channelID) const;
|
||||
bool addTeleportTwoWay(const CGTeleport * obj) const;
|
||||
bool addTeleportOneWay(const CGTeleport * obj) const;
|
||||
bool addTeleportOneWayRandom(const CGTeleport * obj) const;
|
||||
bool addTeleportWhirlpool(const CGWhirlpool * obj) const;
|
||||
bool canMoveBetween(const int3 & a, const int3 & b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility)
|
||||
|
||||
void calculateNeighbourTiles(std::vector<int3> & result, const PathNodeInfo & source) const;
|
||||
std::vector<int3> getTeleportExits(const PathNodeInfo & source) const;
|
||||
void calculateNeighbourTiles(NeighbourTilesVector & result, const PathNodeInfo & source) const;
|
||||
TeleporterTilesVector getTeleportExits(const PathNodeInfo & source) const;
|
||||
|
||||
void getNeighbours(
|
||||
const TerrainTile & srcTile,
|
||||
const int3 & srcCoord,
|
||||
std::vector<int3> & vec,
|
||||
NeighbourTilesVector & vec,
|
||||
const boost::logic::tribool & onLand,
|
||||
const bool limitCoastSailing) const;
|
||||
|
||||
|
@ -40,7 +40,7 @@ void NodeStorage::initialize(const PathfinderOptions & options, const CGameState
|
||||
{
|
||||
for(pos.y=0; pos.y < sizes.y; ++pos.y)
|
||||
{
|
||||
const TerrainTile tile = gs->map->getTile(pos);
|
||||
const TerrainTile & tile = gs->map->getTile(pos);
|
||||
if(tile.terType->isWater())
|
||||
{
|
||||
resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
|
||||
@ -67,10 +67,9 @@ void NodeStorage::calculateNeighbours(
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
const CPathfinderHelper * pathfinderHelper)
|
||||
{
|
||||
std::vector<int3> accessibleNeighbourTiles;
|
||||
NeighbourTilesVector accessibleNeighbourTiles;
|
||||
|
||||
result.clear();
|
||||
accessibleNeighbourTiles.reserve(8);
|
||||
|
||||
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user