mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-12 02:28:11 +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();
|
auto ratio = static_cast<float>(danger) / hero->getTotalStrength();
|
||||||
|
|
||||||
answer = topObj->id == goalObjectID; // no if we do not aim to visit this object
|
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))
|
if(cb->getObj(goalObjectID, false))
|
||||||
{
|
{
|
||||||
@ -1574,7 +1574,7 @@ void AIGateway::requestActionASAP(std::function<void()> whatToDo)
|
|||||||
|
|
||||||
void AIGateway::lostHero(HeroPtr h)
|
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)
|
void AIGateway::answerQuery(QueryID queryID, int selection)
|
||||||
|
@ -67,7 +67,6 @@ HeroPtr::HeroPtr(const CGHeroInstance * H)
|
|||||||
}
|
}
|
||||||
|
|
||||||
h = H;
|
h = H;
|
||||||
name = h->getNameTranslated();
|
|
||||||
hid = H->id;
|
hid = H->id;
|
||||||
// infosCount[ai->playerID][hid]++;
|
// infosCount[ai->playerID][hid]++;
|
||||||
}
|
}
|
||||||
@ -89,6 +88,14 @@ bool HeroPtr::operator<(const HeroPtr & rhs) const
|
|||||||
return hid < rhs.hid;
|
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
|
const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const
|
||||||
{
|
{
|
||||||
return get(cb, doWeExpectNull);
|
return get(cb, doWeExpectNull);
|
||||||
|
@ -87,8 +87,7 @@ struct DLL_EXPORT HeroPtr
|
|||||||
ObjectInstanceID hid;
|
ObjectInstanceID hid;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string name;
|
std::string name() const;
|
||||||
|
|
||||||
|
|
||||||
HeroPtr();
|
HeroPtr();
|
||||||
HeroPtr(const CGHeroInstance * H);
|
HeroPtr(const CGHeroInstance * H);
|
||||||
@ -117,7 +116,6 @@ public:
|
|||||||
{
|
{
|
||||||
handler & h;
|
handler & h;
|
||||||
handler & hid;
|
handler & hid;
|
||||||
handler & name;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ int HeroManager::selectBestSkill(const HeroPtr & hero, const std::vector<Seconda
|
|||||||
|
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"Hero %s is proposed to learn %d with score %f",
|
"Hero %s is proposed to learn %d with score %f",
|
||||||
hero.name,
|
hero.name(),
|
||||||
skills[i].toEnum(),
|
skills[i].toEnum(),
|
||||||
score);
|
score);
|
||||||
}
|
}
|
||||||
@ -204,6 +204,7 @@ float HeroManager::getFightingStrengthCached(const CGHeroInstance * hero) const
|
|||||||
{
|
{
|
||||||
auto cached = knownFightingStrength.find(hero->id);
|
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();
|
return cached != knownFightingStrength.end() ? cached->second : hero->getFightingStrength();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ void CaptureObjectsBehavior::decomposeObjects(
|
|||||||
vstd::concatenate(tasksLocal, getVisitGoals(paths, nullkiller, objToVisit, specificObjects));
|
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);
|
vstd::concatenate(result, tasksLocal);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ namespace Goals
|
|||||||
{
|
{
|
||||||
objid = obj->id.getNum();
|
objid = obj->id.getNum();
|
||||||
tile = obj->visitablePos();
|
tile = obj->visitablePos();
|
||||||
name = obj->getObjectName();
|
name = obj->typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const CaptureObject & other) const override;
|
bool operator==(const CaptureObject & other) const override;
|
||||||
|
@ -26,7 +26,7 @@ ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance *
|
|||||||
if(obj)
|
if(obj)
|
||||||
{
|
{
|
||||||
objid = obj->id.getNum();
|
objid = obj->id.getNum();
|
||||||
targetName = obj->getObjectName() + tile.toString();
|
targetName = obj->typeName + tile.toString();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -106,7 +106,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
|||||||
|
|
||||||
if(!heroPtr.validAndSet())
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@ -143,7 +143,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
|||||||
|
|
||||||
if(!heroPtr.validAndSet())
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@ -204,7 +204,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
|||||||
{
|
{
|
||||||
if(!heroPtr.validAndSet())
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@ -250,7 +250,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
|||||||
{
|
{
|
||||||
if(!heroPtr.validAndSet())
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -70,11 +70,8 @@ bool ExplorationHelper::scanMap()
|
|||||||
int3 mapSize = cbp->getMapSize();
|
int3 mapSize = cbp->getMapSize();
|
||||||
int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y);
|
int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y);
|
||||||
|
|
||||||
std::vector<int3> from;
|
std::vector<int3> edgeTiles;
|
||||||
std::vector<int3> to;
|
edgeTiles.reserve(perimeter);
|
||||||
|
|
||||||
from.reserve(perimeter);
|
|
||||||
to.reserve(perimeter);
|
|
||||||
|
|
||||||
foreach_tile_pos([&](const int3 & pos)
|
foreach_tile_pos([&](const int3 & pos)
|
||||||
{
|
{
|
||||||
@ -91,13 +88,13 @@ bool ExplorationHelper::scanMap()
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(hasInvisibleNeighbor)
|
if(hasInvisibleNeighbor)
|
||||||
from.push_back(pos);
|
edgeTiles.push_back(pos);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
logAi->debug("Exploration scan visible area perimeter for hero %s", hero->getNameTranslated());
|
logAi->debug("Exploration scan visible area perimeter for hero %s", hero->getNameTranslated());
|
||||||
|
|
||||||
for(const int3 & tile : from)
|
for(const int3 & tile : edgeTiles)
|
||||||
{
|
{
|
||||||
scanTile(tile);
|
scanTile(tile);
|
||||||
}
|
}
|
||||||
@ -108,19 +105,36 @@ bool ExplorationHelper::scanMap()
|
|||||||
}
|
}
|
||||||
|
|
||||||
allowDeadEndCancellation = false;
|
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());
|
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();
|
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 ExplorationHelper::howManyTilesWillBeDiscovered(const int3 & pos) const
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -46,7 +46,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
void scanTile(const int3 & tile);
|
void scanTile(const int3 & tile);
|
||||||
bool hasReachableNeighbor(const int3 & pos) const;
|
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 PathfinderConfig * pathfinderConfig,
|
||||||
const CPathfinderHelper * pathfinderHelper)
|
const CPathfinderHelper * pathfinderHelper)
|
||||||
{
|
{
|
||||||
std::vector<int3> accessibleNeighbourTiles;
|
NeighbourTilesVector accessibleNeighbourTiles;
|
||||||
|
|
||||||
result.clear();
|
result.clear();
|
||||||
accessibleNeighbourTiles.reserve(8);
|
|
||||||
|
|
||||||
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
||||||
|
|
||||||
const AIPathNode * srcNode = getAINode(source.node);
|
const AIPathNode * srcNode = getAINode(source.node);
|
||||||
|
@ -23,6 +23,8 @@ constexpr int NKAI_GRAPH_TRACE_LEVEL = 0;
|
|||||||
#include "Actions/SpecialAction.h"
|
#include "Actions/SpecialAction.h"
|
||||||
#include "Actors.h"
|
#include "Actors.h"
|
||||||
|
|
||||||
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
namespace NKAI
|
namespace NKAI
|
||||||
{
|
{
|
||||||
namespace AIPathfinding
|
namespace AIPathfinding
|
||||||
@ -85,7 +87,9 @@ struct AIPathNodeInfo
|
|||||||
|
|
||||||
struct AIPath
|
struct AIPath
|
||||||
{
|
{
|
||||||
std::vector<AIPathNodeInfo> nodes;
|
using NodesVector = boost::container::small_vector<AIPathNodeInfo, 16>;
|
||||||
|
|
||||||
|
NodesVector nodes;
|
||||||
uint64_t targetObjectDanger;
|
uint64_t targetObjectDanger;
|
||||||
uint64_t armyLoss;
|
uint64_t armyLoss;
|
||||||
uint64_t targetObjectArmyLoss;
|
uint64_t targetObjectArmyLoss;
|
||||||
|
@ -141,7 +141,8 @@ namespace AIPathfinding
|
|||||||
{
|
{
|
||||||
SpellID summonBoat = SpellID::SUMMON_BOAT;
|
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;
|
targetNode.specialAction = compositeAction;
|
||||||
|
|
||||||
auto targetGraphNode = graph.getNode(target);
|
const auto & targetGraphNode = graph.getNode(target);
|
||||||
|
|
||||||
if(targetGraphNode.objID.hasValue())
|
if(targetGraphNode.objID.hasValue())
|
||||||
{
|
{
|
||||||
|
@ -162,10 +162,9 @@ void AINodeStorage::calculateNeighbours(
|
|||||||
const PathfinderConfig * pathfinderConfig,
|
const PathfinderConfig * pathfinderConfig,
|
||||||
const CPathfinderHelper * pathfinderHelper)
|
const CPathfinderHelper * pathfinderHelper)
|
||||||
{
|
{
|
||||||
std::vector<int3> accessibleNeighbourTiles;
|
NeighbourTilesVector accessibleNeighbourTiles;
|
||||||
|
|
||||||
result.clear();
|
result.clear();
|
||||||
accessibleNeighbourTiles.reserve(8);
|
|
||||||
|
|
||||||
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
||||||
|
|
||||||
|
@ -85,6 +85,9 @@ void BonusList::stackBonuses()
|
|||||||
|
|
||||||
int BonusList::totalValue() const
|
int BonusList::totalValue() const
|
||||||
{
|
{
|
||||||
|
if (bonuses.empty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
struct BonusCollection
|
struct BonusCollection
|
||||||
{
|
{
|
||||||
int base = 0;
|
int base = 0;
|
||||||
@ -96,63 +99,65 @@ int BonusList::totalValue() const
|
|||||||
int indepMax = std::numeric_limits<int>::min();
|
int indepMax = std::numeric_limits<int>::min();
|
||||||
};
|
};
|
||||||
|
|
||||||
auto percent = [](int64_t base, int64_t percent) -> int {
|
auto percent = [](int base, int percent) -> int {
|
||||||
return static_cast<int>(std::clamp<int64_t>((base * (100 + percent)) / 100, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()));
|
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 hasIndepMax = false;
|
||||||
bool hasIndepMin = false;
|
bool hasIndepMin = false;
|
||||||
|
|
||||||
|
std::array<int, vstd::to_underlying(BonusSource::NUM_BONUS_SOURCE)> percentToSource = {};
|
||||||
|
|
||||||
for(const auto & b : bonuses)
|
for(const auto & b : bonuses)
|
||||||
{
|
{
|
||||||
switch(b->valType)
|
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:
|
case BonusValueType::PERCENT_TO_SOURCE:
|
||||||
sources[vstd::to_underlying(b->source)].percentToSource += b->val;
|
percentToSource[vstd::to_underlying(b->source)] += b->val;
|
||||||
break;
|
break;
|
||||||
case BonusValueType::PERCENT_TO_TARGET_TYPE:
|
case BonusValueType::PERCENT_TO_TARGET_TYPE:
|
||||||
sources[vstd::to_underlying(b->targetSourceType)].percentToSource += b->val;
|
percentToSource[vstd::to_underlying(b->targetSourceType)] += 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);
|
|
||||||
break;
|
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)
|
for(const auto & b : bonuses)
|
||||||
any.indepMax = any.indepMin;
|
{
|
||||||
|
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)
|
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)
|
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)
|
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
|
ELogLevel::ELogLevel CLogger::getLevel() const
|
||||||
{
|
{
|
||||||
TLockGuard _(mx);
|
|
||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1231,7 +1231,7 @@ void RemoveObject::applyGs(CGameState *gs)
|
|||||||
|
|
||||||
gs->map->instanceNames.erase(obj->instanceName);
|
gs->map->instanceNames.erase(obj->instanceName);
|
||||||
gs->map->objects[objectID.getNum()].dellNull();
|
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)
|
static int getDir(const int3 & src, const int3 & dst)
|
||||||
|
@ -49,7 +49,7 @@ bool CPathfinderHelper::canMoveFromNode(const PathNodeInfo & source) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPathfinderHelper::calculateNeighbourTiles(std::vector<int3> & result, const PathNodeInfo & source) const
|
void CPathfinderHelper::calculateNeighbourTiles(NeighbourTilesVector & result, const PathNodeInfo & source) const
|
||||||
{
|
{
|
||||||
result.clear();
|
result.clear();
|
||||||
|
|
||||||
@ -239,9 +239,9 @@ void CPathfinder::calculatePaths()
|
|||||||
logAi->trace("CPathfinder finished with %s iterations", std::to_string(counter));
|
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))
|
for(const auto & objId : getTeleportChannelExits(channelID, hero->tempOwner))
|
||||||
{
|
{
|
||||||
@ -262,9 +262,9 @@ std::vector<int3> CPathfinderHelper::getAllowedTeleportChannelExits(const Telepo
|
|||||||
return allowedExits;
|
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;
|
auto towns = getPlayerState(hero->tempOwner)->towns;
|
||||||
for(const auto & town : towns)
|
for(const auto & town : towns)
|
||||||
@ -279,9 +279,9 @@ std::vector<int3> CPathfinderHelper::getCastleGates(const PathNodeInfo & source)
|
|||||||
return allowedExits;
|
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);
|
const auto * objTeleport = dynamic_cast<const CGTeleport *>(source.nodeObject);
|
||||||
if(isAllowedTeleportEntrance(objTeleport))
|
if(isAllowedTeleportEntrance(objTeleport))
|
||||||
@ -578,7 +578,7 @@ int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer & layer) const
|
|||||||
void CPathfinderHelper::getNeighbours(
|
void CPathfinderHelper::getNeighbours(
|
||||||
const TerrainTile & srcTile,
|
const TerrainTile & srcTile,
|
||||||
const int3 & srcCoord,
|
const int3 & srcCoord,
|
||||||
std::vector<int3> & vec,
|
NeighbourTilesVector & vec,
|
||||||
const boost::logic::tribool & onLand,
|
const boost::logic::tribool & onLand,
|
||||||
const bool limitCoastSailing) const
|
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
|
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
|
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;
|
NeighbourTilesVector vec;
|
||||||
vec.reserve(8); //optimization
|
|
||||||
getNeighbours(*dt, dst, vec, ct->terType->isLand(), true);
|
getNeighbours(*dt, dst, vec, ct->terType->isLand(), true);
|
||||||
for(const auto & elem : vec)
|
for(const auto & elem : vec)
|
||||||
{
|
{
|
||||||
|
@ -13,12 +13,23 @@
|
|||||||
#include "../IGameCallback.h"
|
#include "../IGameCallback.h"
|
||||||
#include "../bonuses/BonusEnum.h"
|
#include "../bonuses/BonusEnum.h"
|
||||||
|
|
||||||
|
#include <boost/container/static_vector.hpp>
|
||||||
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
class CGWhirlpool;
|
class CGWhirlpool;
|
||||||
struct TurnInfo;
|
struct TurnInfo;
|
||||||
struct PathfinderOptions;
|
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
|
class DLL_LINKAGE CPathfinder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -87,22 +98,22 @@ public:
|
|||||||
bool hasBonusOfType(BonusType type) const;
|
bool hasBonusOfType(BonusType type) const;
|
||||||
int getMaxMovePoints(const EPathfindingLayer & layer) 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;
|
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 addTeleportTwoWay(const CGTeleport * obj) const;
|
||||||
bool addTeleportOneWay(const CGTeleport * obj) const;
|
bool addTeleportOneWay(const CGTeleport * obj) const;
|
||||||
bool addTeleportOneWayRandom(const CGTeleport * obj) const;
|
bool addTeleportOneWayRandom(const CGTeleport * obj) const;
|
||||||
bool addTeleportWhirlpool(const CGWhirlpool * 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)
|
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;
|
void calculateNeighbourTiles(NeighbourTilesVector & result, const PathNodeInfo & source) const;
|
||||||
std::vector<int3> getTeleportExits(const PathNodeInfo & source) const;
|
TeleporterTilesVector getTeleportExits(const PathNodeInfo & source) const;
|
||||||
|
|
||||||
void getNeighbours(
|
void getNeighbours(
|
||||||
const TerrainTile & srcTile,
|
const TerrainTile & srcTile,
|
||||||
const int3 & srcCoord,
|
const int3 & srcCoord,
|
||||||
std::vector<int3> & vec,
|
NeighbourTilesVector & vec,
|
||||||
const boost::logic::tribool & onLand,
|
const boost::logic::tribool & onLand,
|
||||||
const bool limitCoastSailing) const;
|
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)
|
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())
|
if(tile.terType->isWater())
|
||||||
{
|
{
|
||||||
resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
|
resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
|
||||||
@ -67,10 +67,9 @@ void NodeStorage::calculateNeighbours(
|
|||||||
const PathfinderConfig * pathfinderConfig,
|
const PathfinderConfig * pathfinderConfig,
|
||||||
const CPathfinderHelper * pathfinderHelper)
|
const CPathfinderHelper * pathfinderHelper)
|
||||||
{
|
{
|
||||||
std::vector<int3> accessibleNeighbourTiles;
|
NeighbourTilesVector accessibleNeighbourTiles;
|
||||||
|
|
||||||
result.clear();
|
result.clear();
|
||||||
accessibleNeighbourTiles.reserve(8);
|
|
||||||
|
|
||||||
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user