瀏覽代碼

NKAI: object graph second layer not operational yet

Andrii Danylchenko 1 年之前
父節點
當前提交
a090441672

+ 1 - 0
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -168,6 +168,7 @@ void Nullkiller::updateAiState(int pass, bool fast)
 
 		PathfinderSettings cfg;
 		cfg.useHeroChain = useHeroChain;
+		cfg.allowBypassObjects = true;
 
 		if(scanDepth == ScanDepth::SMALL)
 		{

+ 1 - 1
AI/Nullkiller/Pathfinding/AINodeStorage.h

@@ -11,7 +11,7 @@
 #pragma once
 
 #define NKAI_PATHFINDER_TRACE_LEVEL 0
-#define NKAI_TRACE_LEVEL 0
+#define NKAI_TRACE_LEVEL 2
 
 #include "../../../lib/pathfinder/CGPathNode.h"
 #include "../../../lib/pathfinder/INodeStorage.h"

+ 5 - 0
AI/Nullkiller/Pathfinding/AIPathfinder.h

@@ -49,6 +49,11 @@ public:
 	void updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes, PathfinderSettings pathfinderSettings);
 	void updateGraphs(std::map<const CGHeroInstance *, HeroRole> heroes);
 	void init();
+
+	std::shared_ptr<AINodeStorage>getStorage()
+	{
+		return storage;
+	}
 };
 
 }

+ 97 - 46
AI/Nullkiller/Pathfinding/ObjectGraph.cpp

@@ -111,9 +111,6 @@ void ObjectGraph::connectHeroes(const Nullkiller * ai)
 
 		for(AIPath & path : paths)
 		{
-			if(path.turn() == 0)
-				continue;
-
 			auto heroPos = path.targetHero->visitablePos();
 
 			nodes[pos].connections[heroPos].update(
@@ -127,9 +124,9 @@ void ObjectGraph::connectHeroes(const Nullkiller * ai)
 	}
 }
 
-bool GraphNodeComparer::operator()(int3 lhs, int3 rhs) const
+bool GraphNodeComparer::operator()(const GraphPathNodePointer & lhs, const GraphPathNodePointer & rhs) const
 {
-	return pathNodes.at(lhs).cost > pathNodes.at(rhs).cost;
+	return pathNodes.at(lhs.coord)[lhs.nodeType].cost > pathNodes.at(rhs.coord)[rhs.nodeType].cost;
 }
 
 void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkiller * ai)
@@ -142,21 +139,22 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
 	GraphNodeComparer cmp(pathNodes);
 	GraphPathNode::TFibHeap pq(cmp);
 
-	pathNodes[targetHero->visitablePos()].cost = 0;
-	pq.emplace(targetHero->visitablePos());
+	pathNodes[targetHero->visitablePos()][GrapthPathNodeType::NORMAL].cost = 0;
+	pq.emplace(GraphPathNodePointer(targetHero->visitablePos(), GrapthPathNodeType::NORMAL));
 
 	while(!pq.empty())
 	{
-		int3 pos = pq.top();
+		GraphPathNodePointer pos = pq.top();
 		pq.pop();
 
-		auto node = pathNodes[pos];
+		auto & node = getNode(pos);
 
 		node.isInQueue = false;
 
-		graph.iterateConnections(pos, [&](int3 target, ObjectLink o)
+		graph.iterateConnections(pos.coord, [&](int3 target, ObjectLink o)
 			{
-				auto & targetNode = pathNodes[target];
+				auto targetPointer = GraphPathNodePointer(target, pos.nodeType);
+				auto & targetNode = getNode(targetPointer);
 
 				if(targetNode.tryUpdate(pos, node, o))
 				{
@@ -166,7 +164,7 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
 					}
 					else
 					{
-						targetNode.handle = pq.emplace(target);
+						targetNode.handle = pq.emplace(targetPointer);
 						targetNode.isInQueue = true;
 					}
 				}
@@ -176,61 +174,114 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
 
 void GraphPaths::dumpToLog() const
 {
-	for(auto & node : pathNodes)
+	for(auto & tile : pathNodes)
 	{
-		logAi->trace(
-			"%s -> %s: %f !%d",
-			node.second.previous.toString(),
-			node.first.toString(),
-			node.second.cost,
-			node.second.danger);
+		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);
+		}
 	}
 }
 
+bool GraphPathNode::tryUpdate(const GraphPathNodePointer & pos, const GraphPathNode & prev, const ObjectLink & link)
+{
+	auto newCost = prev.cost + link.cost;
+
+	if(newCost < cost)
+	{
+		previous = pos;
+		danger = prev.danger + link.danger;
+		cost = newCost;
+
+		return true;
+	}
+
+	return false;
+}
+
 void GraphPaths::addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHeroInstance * hero, const Nullkiller * ai) const
 {
-	auto node = pathNodes.find(tile);
+	auto nodes = pathNodes.find(tile);
 
-	if(node == pathNodes.end() || !node->second.reachable())
+	if(nodes == pathNodes.end())
 		return;
 
-	std::vector<int3> tilesToPass;
+	for(auto & node : (*nodes).second)
+	{
+		if(!node.reachable())
+			continue;
 
-	uint64_t danger = node->second.danger;
-	float cost = node->second.cost;;
+		std::vector<int3> tilesToPass;
 
-	while(node != pathNodes.end() && node->second.cost > 1)
-	{
-		vstd::amax(danger, node->second.danger);
+		uint64_t danger = node.danger;
+		float cost = node.cost;
+		bool allowBattle = false;
 
-		tilesToPass.push_back(node->first);
-		node = pathNodes.find(node->second.previous);
-	}
+		auto current = GraphPathNodePointer(nodes->first, node.nodeType);
 
-	if(tilesToPass.empty())
-		return;
+		while(true)
+		{
+			auto currentTile = pathNodes.find(current.coord);
 
-	auto entryPaths = ai->pathfinder->getPathInfo(tilesToPass.back());
+			if(currentTile == pathNodes.end())
+				break;
 
-	for(auto & path : entryPaths)
-	{
-		if(path.targetHero != hero)
+			auto currentNode = currentTile->second[current.nodeType];
+
+			allowBattle = allowBattle || currentNode.nodeType == GrapthPathNodeType::BATTLE;
+			vstd::amax(danger, currentNode.danger);
+			vstd::amax(cost, currentNode.cost);
+
+			tilesToPass.push_back(current.coord);
+
+			if(currentNode.cost < 2)
+				break;
+
+			current = currentNode.previous;
+		}
+
+		if(tilesToPass.empty())
 			continue;
 
-		for(auto graphTile : tilesToPass)
+		auto entryPaths = ai->pathfinder->getPathInfo(tilesToPass.back());
+
+		for(auto & path : entryPaths)
 		{
-			AIPathNodeInfo n;
+			if(path.targetHero != hero)
+				continue;
 
-			n.coord = graphTile;
-			n.cost = cost;
-			n.turns = static_cast<ui8>(cost) + 1; // just in case lets select worst scenario
-			n.danger = danger;
-			n.targetHero = hero;
+			for(auto graphTile = tilesToPass.rbegin(); graphTile != tilesToPass.rend(); graphTile++)
+			{
+				AIPathNodeInfo n;
 
-			path.nodes.insert(path.nodes.begin(), n);
-		}
+				n.coord = *graphTile;
+				n.cost = cost;
+				n.turns = static_cast<ui8>(cost) + 1; // just in case lets select worst scenario
+				n.danger = danger;
+				n.targetHero = hero;
+
+				for(auto & node : path.nodes)
+				{
+					node.parentIndex++;
+				}
 
-		paths.push_back(path);
+				path.nodes.insert(path.nodes.begin(), n);
+			}
+
+			path.armyLoss += ai->pathfinder->getStorage()->evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), danger);
+			path.targetObjectDanger = ai->pathfinder->getStorage()->evaluateDanger(tile, path.targetHero, !allowBattle);
+			path.targetObjectArmyLoss = ai->pathfinder->getStorage()->evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), path.targetObjectDanger);
+
+			paths.push_back(path);
+		}
 	}
 }
 

+ 42 - 21
AI/Nullkiller/Pathfinding/ObjectGraph.h

@@ -67,28 +67,57 @@ public:
 
 struct GraphPathNode;
 
+enum GrapthPathNodeType
+{
+	NORMAL,
+
+	BATTLE,
+
+	LAST
+};
+
+struct GraphPathNodePointer
+{
+	int3 coord = int3(-1);
+	GrapthPathNodeType nodeType = GrapthPathNodeType::NORMAL;
+
+	GraphPathNodePointer() = default;
+
+	GraphPathNodePointer(int3 coord, GrapthPathNodeType type)
+		:coord(coord), nodeType(type)
+	{ }
+
+	bool valid() const
+	{
+		return coord.valid();
+	}
+};
+
+typedef std::unordered_map<int3, GraphPathNode[GrapthPathNodeType::LAST]> GraphNodeStorage;
+
 class GraphNodeComparer
 {
-	const std::unordered_map<int3, GraphPathNode> & pathNodes;
+	const GraphNodeStorage & pathNodes;
 
 public:
-	GraphNodeComparer(const std::unordered_map<int3, GraphPathNode> & pathNodes)
+	GraphNodeComparer(const GraphNodeStorage & pathNodes)
 		:pathNodes(pathNodes)
 	{
 	}
 
-	bool operator()(int3 lhs, int3 rhs) const;
+	bool operator()(const GraphPathNodePointer & lhs, const GraphPathNodePointer & rhs) const;
 };
 
 struct GraphPathNode
 {
 	const float BAD_COST = 100000;
 
-	int3 previous = int3(-1);
+	GrapthPathNodeType nodeType = GrapthPathNodeType::NORMAL;
+	GraphPathNodePointer previous;
 	float cost = BAD_COST;
 	uint64_t danger = 0;
 
-	using TFibHeap = boost::heap::fibonacci_heap<int3, boost::heap::compare<GraphNodeComparer>>;
+	using TFibHeap = boost::heap::fibonacci_heap<GraphPathNodePointer, boost::heap::compare<GraphNodeComparer>>;
 
 	TFibHeap::handle_type handle;
 	bool isInQueue = false;
@@ -98,32 +127,24 @@ struct GraphPathNode
 		return cost < BAD_COST;
 	}
 
-	bool tryUpdate(const int3 & pos, const GraphPathNode & prev, const ObjectLink & link)
-	{
-		auto newCost = prev.cost + link.cost;
-
-		if(newCost < cost)
-		{
-			previous = pos;
-			danger = prev.danger + link.danger;
-			cost = newCost;
-
-			return true;
-		}
-
-		return false;
-	}
+	bool tryUpdate(const GraphPathNodePointer & pos, const GraphPathNode & prev, const ObjectLink & link);
 };
 
 class GraphPaths
 {
 	ObjectGraph graph;
-	std::unordered_map<int3, GraphPathNode> pathNodes;
+	GraphNodeStorage pathNodes;
 
 public:
 	void calculatePaths(const CGHeroInstance * targetHero, const Nullkiller * ai);
 	void addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHeroInstance * hero, const Nullkiller * ai) const;
 	void dumpToLog() const;
+
+private:
+	GraphPathNode & getNode(const GraphPathNodePointer & pos)
+	{
+		return pathNodes[pos.coord][pos.nodeType];
+	}
 };
 
 }