mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-12 02:28:11 +02:00
NKAI: graph add battle layer
This commit is contained in:
parent
047e076d05
commit
b236384356
@ -224,7 +224,11 @@ void ObjectClusterizer::clusterize()
|
||||
ai->memory->visitableObjs.begin(),
|
||||
ai->memory->visitableObjs.end());
|
||||
|
||||
#if NKAI_TRACE_LEVEL == 0
|
||||
parallel_for(blocked_range<size_t>(0, objs.size()), [&](const blocked_range<size_t> & r)
|
||||
#else
|
||||
blocked_range<size_t> r(0, objs.size());
|
||||
#endif
|
||||
{
|
||||
auto priorityEvaluator = ai->priorityEvaluators->acquire();
|
||||
|
||||
@ -255,9 +259,9 @@ void ObjectClusterizer::clusterize()
|
||||
}
|
||||
|
||||
std::sort(paths.begin(), paths.end(), [](const AIPath & p1, const AIPath & p2) -> bool
|
||||
{
|
||||
return p1.movementCost() < p2.movementCost();
|
||||
});
|
||||
{
|
||||
return p1.movementCost() < p2.movementCost();
|
||||
});
|
||||
|
||||
if(vstd::contains(ignoreObjects, obj->ID))
|
||||
{
|
||||
@ -348,7 +352,11 @@ void ObjectClusterizer::clusterize()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if NKAI_TRACE_LEVEL == 0
|
||||
});
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
|
||||
logAi->trace("Near objects count: %i", nearObjects.objects.size());
|
||||
logAi->trace("Far objects count: %i", farObjects.objects.size());
|
||||
|
@ -42,6 +42,8 @@ void Nullkiller::init(std::shared_ptr<CCallback> cb, PlayerColor playerID)
|
||||
this->cb = cb;
|
||||
this->playerID = playerID;
|
||||
|
||||
baseGraph.reset();
|
||||
|
||||
priorityEvaluator.reset(new PriorityEvaluator(this));
|
||||
priorityEvaluators.reset(
|
||||
new SharedPool<PriorityEvaluator>(
|
||||
|
@ -39,12 +39,18 @@ void AdventureSpellCast::accept(AIGateway * ai)
|
||||
if(hero->mana < hero->getSpellCost(spell))
|
||||
throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getNameTranslated());
|
||||
|
||||
if(spellID == SpellID::TOWN_PORTAL && town && town->visitingHero)
|
||||
throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->getNameTranslated());
|
||||
|
||||
if(town && spellID == SpellID::TOWN_PORTAL)
|
||||
{
|
||||
ai->selectedObject = town->id;
|
||||
|
||||
if(town->visitingHero && town->tempOwner == ai->playerID && !town->getUpperArmy()->stacksCount())
|
||||
{
|
||||
ai->myCb->swapGarrisonHero(town);
|
||||
}
|
||||
|
||||
if(town->visitingHero)
|
||||
throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->getNameTranslated());
|
||||
}
|
||||
|
||||
auto wait = cb->waitTillRealize;
|
||||
|
@ -1123,14 +1123,14 @@ void AINodeStorage::calculateTownPortal(
|
||||
{
|
||||
for(const CGTownInstance * targetTown : towns)
|
||||
{
|
||||
// TODO: allow to hide visiting hero in garrison
|
||||
if(targetTown->visitingHero && maskMap.find(targetTown->visitingHero.get()) != maskMap.end())
|
||||
if(targetTown->visitingHero
|
||||
&& targetTown->getUpperArmy()->stacksCount()
|
||||
&& maskMap.find(targetTown->visitingHero.get()) != maskMap.end())
|
||||
{
|
||||
auto basicMask = maskMap.at(targetTown->visitingHero.get());
|
||||
bool heroIsInChain = (actor->chainMask & basicMask) != 0;
|
||||
bool sameActorInTown = actor->chainMask == basicMask;
|
||||
|
||||
if(sameActorInTown || !heroIsInChain)
|
||||
if(!sameActorInTown)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,8 @@
|
||||
#pragma once
|
||||
|
||||
#define NKAI_PATHFINDER_TRACE_LEVEL 0
|
||||
#define NKAI_TRACE_LEVEL 2
|
||||
#define NKAI_GRAPH_TRACE_LEVEL 0
|
||||
#define NKAI_TRACE_LEVEL 0
|
||||
|
||||
#include "../../../lib/pathfinder/CGPathNode.h"
|
||||
#include "../../../lib/pathfinder/INodeStorage.h"
|
||||
|
@ -48,9 +48,10 @@ std::vector<AIPath> AIPathfinder::getPathInfo(const int3 & tile, bool includeGra
|
||||
{
|
||||
for(auto hero : cb->getHeroesInfo())
|
||||
{
|
||||
auto & graph = heroGraphs.at(hero->id);
|
||||
auto graph = heroGraphs.find(hero->id);
|
||||
|
||||
graph.addChainInfo(info, tile, hero, ai);
|
||||
if(graph != heroGraphs.end())
|
||||
graph->second.addChainInfo(info, tile, hero, ai);
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,10 +141,10 @@ void AIPathfinder::updateGraphs(std::map<const CGHeroInstance *, HeroRole> heroe
|
||||
parallel_for(blocked_range<size_t>(0, heroesVector.size()), [&](const blocked_range<size_t> & r)
|
||||
{
|
||||
for(auto i = r.begin(); i != r.end(); i++)
|
||||
heroGraphs[heroesVector[i]->id].calculatePaths(heroesVector[i], ai);
|
||||
heroGraphs.at(heroesVector[i]->id).calculatePaths(heroesVector[i], ai);
|
||||
});
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
#if NKAI_GRAPH_TRACE_LEVEL >= 1
|
||||
for(auto hero : heroes)
|
||||
{
|
||||
heroGraphs[hero.first->id].dumpToLog();
|
||||
|
@ -30,7 +30,7 @@ namespace AIPathfinding
|
||||
std::vector<std::shared_ptr<IPathfindingRule>> rules = {
|
||||
std::make_shared<AILayerTransitionRule>(cb, ai, nodeStorage),
|
||||
std::make_shared<DestinationActionRule>(),
|
||||
std::make_shared<AIMovementToDestinationRule>(nodeStorage),
|
||||
std::make_shared<AIMovementToDestinationRule>(nodeStorage, allowBypassObjects),
|
||||
std::make_shared<MovementCostRule>(),
|
||||
std::make_shared<AIPreviousNodeRule>(nodeStorage),
|
||||
std::make_shared<AIMovementAfterDestinationRule>(cb, nodeStorage, allowBypassObjects)
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "../../../CCallback.h"
|
||||
#include "../../../lib/mapping/CMap.h"
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "../../../lib/logging/VisualLogger.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
@ -64,6 +65,14 @@ void ObjectGraph::updateGraph(const Nullkiller * ai)
|
||||
|
||||
foreach_tile_pos(cb.get(), [&](const CPlayerSpecificInfoCallback * cb, const int3 & pos)
|
||||
{
|
||||
if(nodes.find(pos) != nodes.end())
|
||||
return;
|
||||
|
||||
auto guarded = ai->cb->getGuardingCreaturePosition(pos).valid();
|
||||
|
||||
if(guarded)
|
||||
return;
|
||||
|
||||
auto paths = ai->pathfinder->getPathInfo(pos);
|
||||
|
||||
for(AIPath & path1 : paths)
|
||||
@ -76,9 +85,25 @@ void ObjectGraph::updateGraph(const Nullkiller * ai)
|
||||
auto obj1 = actorObjectMap[path1.targetHero];
|
||||
auto obj2 = actorObjectMap[path2.targetHero];
|
||||
|
||||
nodes[obj1->visitablePos()].connections[obj2->visitablePos()].update(
|
||||
auto danger = ai->pathfinder->getStorage()->evaluateDanger(obj2->visitablePos(), path1.targetHero, true);
|
||||
|
||||
auto updated = nodes[obj1->visitablePos()].connections[obj2->visitablePos()].update(
|
||||
path1.movementCost() + path2.movementCost(),
|
||||
path1.getPathDanger() + path2.getPathDanger());
|
||||
danger);
|
||||
|
||||
#if NKAI_GRAPH_TRACE_LEVEL >= 2
|
||||
if(updated)
|
||||
{
|
||||
logAi->trace(
|
||||
"Connected %s[%s] -> %s[%s] through [%s], cost %2f",
|
||||
obj1->getObjectName(), obj1->visitablePos().toString(),
|
||||
obj2->getObjectName(), obj2->visitablePos().toString(),
|
||||
pos.toString(),
|
||||
path1.movementCost() + path2.movementCost());
|
||||
}
|
||||
#else
|
||||
(void)updated;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -87,6 +112,10 @@ void ObjectGraph::updateGraph(const Nullkiller * ai)
|
||||
{
|
||||
delete h.first;
|
||||
}
|
||||
|
||||
#if NKAI_GRAPH_TRACE_LEVEL >= 1
|
||||
dumpToLog("graph");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ObjectGraph::addObject(const CGObjectInstance * obj)
|
||||
@ -104,7 +133,7 @@ void ObjectGraph::connectHeroes(const Nullkiller * ai)
|
||||
}
|
||||
}
|
||||
|
||||
for(auto node : nodes)
|
||||
for(auto & node : nodes)
|
||||
{
|
||||
auto pos = node.first;
|
||||
auto paths = ai->pathfinder->getPathInfo(pos);
|
||||
@ -124,6 +153,29 @@ void ObjectGraph::connectHeroes(const Nullkiller * ai)
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectGraph::dumpToLog(std::string visualKey) const
|
||||
{
|
||||
logVisual->updateWithLock(visualKey, [&](IVisualLogBuilder & logBuilder)
|
||||
{
|
||||
for(auto & tile : nodes)
|
||||
{
|
||||
for(auto & node : tile.second.connections)
|
||||
{
|
||||
#if NKAI_GRAPH_TRACE_LEVEL >= 2
|
||||
logAi->trace(
|
||||
"%s -> %s: %f !%d",
|
||||
node.first.toString(),
|
||||
tile.first.toString(),
|
||||
node.second.cost,
|
||||
node.second.danger);
|
||||
#endif
|
||||
|
||||
logBuilder.addLine(node.first, tile.first);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool GraphNodeComparer::operator()(const GraphPathNodePointer & lhs, const GraphPathNodePointer & rhs) const
|
||||
{
|
||||
return pathNodes.at(lhs.coord)[lhs.nodeType].cost > pathNodes.at(rhs.coord)[rhs.nodeType].cost;
|
||||
@ -134,6 +186,7 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
|
||||
graph = *ai->baseGraph;
|
||||
graph.connectHeroes(ai);
|
||||
|
||||
visualKey = std::to_string(ai->playerID) + ":" + targetHero->getNameTranslated();
|
||||
pathNodes.clear();
|
||||
|
||||
GraphNodeComparer cmp(pathNodes);
|
||||
@ -153,11 +206,17 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
|
||||
|
||||
graph.iterateConnections(pos.coord, [&](int3 target, ObjectLink o)
|
||||
{
|
||||
auto targetPointer = GraphPathNodePointer(target, pos.nodeType);
|
||||
auto graphNode = graph.getNode(target);
|
||||
auto targetNodeType = o.danger ? GrapthPathNodeType::BATTLE : pos.nodeType;
|
||||
|
||||
auto targetPointer = GraphPathNodePointer(target, targetNodeType);
|
||||
auto & targetNode = getNode(targetPointer);
|
||||
|
||||
if(targetNode.tryUpdate(pos, node, o))
|
||||
{
|
||||
if(graph.getNode(target).objTypeID == Obj::HERO)
|
||||
return;
|
||||
|
||||
if(targetNode.isInQueue)
|
||||
{
|
||||
pq.increase(targetNode.handle);
|
||||
@ -174,21 +233,28 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
|
||||
|
||||
void GraphPaths::dumpToLog() const
|
||||
{
|
||||
for(auto & tile : pathNodes)
|
||||
{
|
||||
for(auto & node : tile.second)
|
||||
logVisual->updateWithLock(visualKey, [&](IVisualLogBuilder & logBuilder)
|
||||
{
|
||||
if(!node.previous.valid())
|
||||
continue;
|
||||
for(auto & tile : pathNodes)
|
||||
{
|
||||
for(auto & node : tile.second)
|
||||
{
|
||||
if(!node.previous.valid())
|
||||
continue;
|
||||
|
||||
logAi->trace(
|
||||
"%s -> %s: %f !%d",
|
||||
node.previous.coord.toString(),
|
||||
tile.first.toString(),
|
||||
node.cost,
|
||||
node.danger);
|
||||
}
|
||||
}
|
||||
#if NKAI_GRAPH_TRACE_LEVEL >= 2
|
||||
logAi->trace(
|
||||
"%s -> %s: %f !%d",
|
||||
node.previous.coord.toString(),
|
||||
tile.first.toString(),
|
||||
node.cost,
|
||||
node.danger);
|
||||
#endif
|
||||
|
||||
logBuilder.addLine(node.previous.coord, tile.first);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool GraphPathNode::tryUpdate(const GraphPathNodePointer & pos, const GraphPathNode & prev, const ObjectLink & link)
|
||||
@ -197,6 +263,11 @@ bool GraphPathNode::tryUpdate(const GraphPathNodePointer & pos, const GraphPathN
|
||||
|
||||
if(newCost < cost)
|
||||
{
|
||||
if(nodeType < pos.nodeType)
|
||||
{
|
||||
logAi->error("Linking error");
|
||||
}
|
||||
|
||||
previous = pos;
|
||||
danger = prev.danger + link.danger;
|
||||
cost = newCost;
|
||||
@ -214,7 +285,7 @@ void GraphPaths::addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHe
|
||||
if(nodes == pathNodes.end())
|
||||
return;
|
||||
|
||||
for(auto & node : (*nodes).second)
|
||||
for(auto & node : nodes->second)
|
||||
{
|
||||
if(!node.reachable())
|
||||
continue;
|
||||
@ -236,6 +307,9 @@ void GraphPaths::addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHe
|
||||
|
||||
auto currentNode = currentTile->second[current.nodeType];
|
||||
|
||||
if(currentNode.cost == 0)
|
||||
break;
|
||||
|
||||
allowBattle = allowBattle || currentNode.nodeType == GrapthPathNodeType::BATTLE;
|
||||
vstd::amax(danger, currentNode.danger);
|
||||
vstd::amax(cost, currentNode.cost);
|
||||
|
@ -23,19 +23,24 @@ struct ObjectLink
|
||||
float cost = 100000; // some big number
|
||||
uint64_t danger = 0;
|
||||
|
||||
void update(float newCost, uint64_t newDanger)
|
||||
bool update(float newCost, uint64_t newDanger)
|
||||
{
|
||||
if(cost > newCost)
|
||||
{
|
||||
cost = newCost;
|
||||
danger = newDanger;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct ObjectNode
|
||||
{
|
||||
ObjectInstanceID objID;
|
||||
MapObjectID objTypeID;
|
||||
bool objectExists;
|
||||
std::unordered_map<int3, ObjectLink> connections;
|
||||
|
||||
@ -43,6 +48,7 @@ struct ObjectNode
|
||||
{
|
||||
objectExists = true;
|
||||
objID = obj->id;
|
||||
objTypeID = obj->ID;
|
||||
}
|
||||
};
|
||||
|
||||
@ -54,15 +60,21 @@ public:
|
||||
void updateGraph(const Nullkiller * ai);
|
||||
void addObject(const CGObjectInstance * obj);
|
||||
void connectHeroes(const Nullkiller * ai);
|
||||
void dumpToLog(std::string visualKey) const;
|
||||
|
||||
template<typename Func>
|
||||
void iterateConnections(const int3 & pos, Func fn)
|
||||
{
|
||||
for(auto connection : nodes.at(pos).connections)
|
||||
for(auto & connection : nodes.at(pos).connections)
|
||||
{
|
||||
fn(connection.first, connection.second);
|
||||
}
|
||||
}
|
||||
|
||||
const ObjectNode & getNode(int3 tile) const
|
||||
{
|
||||
return nodes.at(tile);
|
||||
}
|
||||
};
|
||||
|
||||
struct GraphPathNode;
|
||||
@ -134,6 +146,7 @@ class GraphPaths
|
||||
{
|
||||
ObjectGraph graph;
|
||||
GraphNodeStorage pathNodes;
|
||||
std::string visualKey;
|
||||
|
||||
public:
|
||||
void calculatePaths(const CGHeroInstance * targetHero, const Nullkiller * ai);
|
||||
@ -143,7 +156,11 @@ public:
|
||||
private:
|
||||
GraphPathNode & getNode(const GraphPathNodePointer & pos)
|
||||
{
|
||||
return pathNodes[pos.coord][pos.nodeType];
|
||||
auto & node = pathNodes[pos.coord][pos.nodeType];
|
||||
|
||||
node.nodeType = pos.nodeType;
|
||||
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -48,7 +48,7 @@ namespace AIPathfinding
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(!allowBypassObjects)
|
||||
return;
|
||||
|
||||
|
@ -14,8 +14,10 @@ namespace NKAI
|
||||
{
|
||||
namespace AIPathfinding
|
||||
{
|
||||
AIMovementToDestinationRule::AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage)
|
||||
: nodeStorage(nodeStorage)
|
||||
AIMovementToDestinationRule::AIMovementToDestinationRule(
|
||||
std::shared_ptr<AINodeStorage> nodeStorage,
|
||||
bool allowBypassObjects)
|
||||
: nodeStorage(nodeStorage), allowBypassObjects(allowBypassObjects)
|
||||
{
|
||||
}
|
||||
|
||||
@ -37,15 +39,30 @@ namespace AIPathfinding
|
||||
return;
|
||||
}
|
||||
|
||||
if(blocker == BlockingReason::SOURCE_GUARDED && nodeStorage->getAINode(source.node)->actor->allowBattle)
|
||||
if(blocker == BlockingReason::SOURCE_GUARDED)
|
||||
{
|
||||
auto actor = nodeStorage->getAINode(source.node)->actor;
|
||||
|
||||
if(!allowBypassObjects)
|
||||
{
|
||||
if (source.node->getCost() == 0)
|
||||
return;
|
||||
|
||||
// when actor represents moster graph node, we need to let him escape monster
|
||||
if(!destination.guarded && cb->getGuardingCreaturePosition(source.coord) == actor->initialPosition)
|
||||
return;
|
||||
}
|
||||
|
||||
if(actor->allowBattle)
|
||||
{
|
||||
#if NKAI_PATHFINDER_TRACE_LEVEL >= 1
|
||||
logAi->trace(
|
||||
"Bypass src guard while moving from %s to %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
logAi->trace(
|
||||
"Bypass src guard while moving from %s to %s",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString());
|
||||
#endif
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
destination.blocked = true;
|
||||
|
@ -24,9 +24,10 @@ namespace AIPathfinding
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<AINodeStorage> nodeStorage;
|
||||
bool allowBypassObjects;
|
||||
|
||||
public:
|
||||
AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage);
|
||||
AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage, bool allowBypassObjects);
|
||||
|
||||
virtual void process(
|
||||
const PathNodeInfo & source,
|
||||
|
@ -21,7 +21,9 @@ void VisualLogger::updateWithLock(std::string channel, std::function<void(IVisua
|
||||
|
||||
mapLines[channel].clear();
|
||||
|
||||
func(VisualLogBuilder(mapLines[channel]));
|
||||
VisualLogBuilder builder(mapLines[channel]);
|
||||
|
||||
func(builder);
|
||||
}
|
||||
|
||||
void VisualLogger::visualize(ILogVisualizer & visulizer)
|
||||
|
Loading…
Reference in New Issue
Block a user