ObjectGraphCalculator.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /*
  2. * ObjectGraphCalculator.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "ObjectGraphCalculator.h"
  12. #include "AIPathfinderConfig.h"
  13. #include "../../../lib/CRandomGenerator.h"
  14. #include "../../../lib/mapping/CMap.h"
  15. #include "../Engine/Nullkiller.h"
  16. #include "../../../lib/logging/VisualLogger.h"
  17. #include "Actions/QuestAction.h"
  18. #include "../pforeach.h"
  19. namespace NKAI
  20. {
  21. ObjectGraphCalculator::ObjectGraphCalculator(ObjectGraph * target, const Nullkiller * ai)
  22. :ai(ai), target(target), syncLock()
  23. {
  24. }
  25. void ObjectGraphCalculator::setGraphObjects()
  26. {
  27. for(auto obj : ai->memory->visitableObjs)
  28. {
  29. if(obj && obj->isVisitable() && obj->ID != Obj::HERO && obj->ID != Obj::EVENT)
  30. {
  31. addObjectActor(obj);
  32. }
  33. }
  34. for(auto town : ai->cb->getTownsInfo())
  35. {
  36. addObjectActor(town);
  37. }
  38. }
  39. void ObjectGraphCalculator::calculateConnections()
  40. {
  41. updatePaths();
  42. std::vector<AIPath> pathCache;
  43. foreach_tile_pos(ai->cb.get(), [this, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & pos)
  44. {
  45. calculateConnections(pos, pathCache);
  46. });
  47. removeExtraConnections();
  48. }
  49. float ObjectGraphCalculator::getNeighborConnectionsCost(const int3 & pos, std::vector<AIPath> & pathCache)
  50. {
  51. float neighborCost = std::numeric_limits<float>::max();
  52. if(NKAI_GRAPH_TRACE_LEVEL >= 2)
  53. {
  54. logAi->trace("Checking junction %s", pos.toString());
  55. }
  56. foreach_neighbour(
  57. ai->cb.get(),
  58. pos,
  59. [this, &neighborCost, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & neighbor)
  60. {
  61. ai->pathfinder->calculatePathInfo(pathCache, neighbor);
  62. auto costTotal = this->getConnectionsCost(pathCache);
  63. if(costTotal.connectionsCount > 2 && costTotal.avg < neighborCost)
  64. {
  65. neighborCost = costTotal.avg;
  66. if(NKAI_GRAPH_TRACE_LEVEL >= 2)
  67. {
  68. logAi->trace("Better node found at %s", neighbor.toString());
  69. }
  70. }
  71. });
  72. return neighborCost;
  73. }
  74. void ObjectGraphCalculator::addMinimalDistanceJunctions()
  75. {
  76. tbb::concurrent_unordered_set<int3, std::hash<int3>> junctions;
  77. pforeachTilePaths(ai->cb->getMapSize(), ai, [this, &junctions](const int3 & pos, std::vector<AIPath> & paths)
  78. {
  79. if(target->hasNodeAt(pos))
  80. return;
  81. if(ai->cb->getGuardingCreaturePosition(pos).isValid())
  82. return;
  83. ConnectionCostInfo currentCost = getConnectionsCost(paths);
  84. if(currentCost.connectionsCount <= 2)
  85. return;
  86. float neighborCost = getNeighborConnectionsCost(pos, paths);
  87. if(currentCost.avg < neighborCost)
  88. {
  89. junctions.insert(pos);
  90. }
  91. });
  92. for(auto pos : junctions)
  93. {
  94. addJunctionActor(pos);
  95. }
  96. }
  97. void ObjectGraphCalculator::updatePaths()
  98. {
  99. PathfinderSettings ps;
  100. ps.mainTurnDistanceLimit = 5;
  101. ps.scoutTurnDistanceLimit = 1;
  102. ps.allowBypassObjects = false;
  103. ai->pathfinder->updatePaths(actors, ps);
  104. }
  105. void ObjectGraphCalculator::calculateConnections(const int3 & pos, std::vector<AIPath> & pathCache)
  106. {
  107. if(target->hasNodeAt(pos))
  108. {
  109. foreach_neighbour(
  110. ai->cb.get(),
  111. pos,
  112. [this, &pos, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & neighbor)
  113. {
  114. if(target->hasNodeAt(neighbor))
  115. {
  116. ai->pathfinder->calculatePathInfo(pathCache, neighbor);
  117. for(auto & path : pathCache)
  118. {
  119. if(pos == path.targetHero->visitablePos())
  120. {
  121. target->tryAddConnection(pos, neighbor, path.movementCost(), path.getTotalDanger());
  122. }
  123. }
  124. }
  125. });
  126. auto obj = ai->cb->getTopObj(pos);
  127. if((obj && obj->ID == Obj::BOAT) || target->isVirtualBoat(pos))
  128. {
  129. ai->pathfinder->calculatePathInfo(pathCache, pos);
  130. for(AIPath & path : pathCache)
  131. {
  132. auto from = path.targetHero->visitablePos();
  133. auto fromObj = actorObjectMap[path.targetHero];
  134. auto danger = ai->dangerEvaluator->evaluateDanger(pos, path.targetHero, true);
  135. auto updated = target->tryAddConnection(
  136. from,
  137. pos,
  138. path.movementCost(),
  139. danger);
  140. if(NKAI_GRAPH_TRACE_LEVEL >= 2 && updated)
  141. {
  142. logAi->trace(
  143. "Connected %s[%s] -> %s[%s] through [%s], cost %2f",
  144. fromObj ? fromObj->getObjectName() : "J", from.toString(),
  145. "Boat", pos.toString(),
  146. pos.toString(),
  147. path.movementCost());
  148. }
  149. }
  150. }
  151. return;
  152. }
  153. auto guardPos = ai->cb->getGuardingCreaturePosition(pos);
  154. ai->pathfinder->calculatePathInfo(pathCache, pos);
  155. for(AIPath & path1 : pathCache)
  156. {
  157. for(AIPath & path2 : pathCache)
  158. {
  159. if(path1.targetHero == path2.targetHero)
  160. continue;
  161. auto pos1 = path1.targetHero->visitablePos();
  162. auto pos2 = path2.targetHero->visitablePos();
  163. if(guardPos.isValid() && guardPos != pos1 && guardPos != pos2)
  164. continue;
  165. auto obj1 = actorObjectMap[path1.targetHero];
  166. auto obj2 = actorObjectMap[path2.targetHero];
  167. auto tile1 = cb->getTile(pos1);
  168. auto tile2 = cb->getTile(pos2);
  169. if(tile2->isWater() && !tile1->isWater())
  170. {
  171. if(!cb->getTile(pos)->isWater())
  172. continue;
  173. auto startingObjIsBoat = (obj1 && obj1->ID == Obj::BOAT) || target->isVirtualBoat(pos1);
  174. if(!startingObjIsBoat)
  175. continue;
  176. }
  177. auto danger = ai->dangerEvaluator->evaluateDanger(pos2, path1.targetHero, true);
  178. auto updated = target->tryAddConnection(
  179. pos1,
  180. pos2,
  181. path1.movementCost() + path2.movementCost(),
  182. danger);
  183. if(NKAI_GRAPH_TRACE_LEVEL >= 2 && updated)
  184. {
  185. logAi->trace(
  186. "Connected %s[%s] -> %s[%s] through [%s], cost %2f",
  187. obj1 ? obj1->getObjectName() : "J", pos1.toString(),
  188. obj2 ? obj2->getObjectName() : "J", pos2.toString(),
  189. pos.toString(),
  190. path1.movementCost() + path2.movementCost());
  191. }
  192. }
  193. }
  194. }
  195. bool ObjectGraphCalculator::isExtraConnection(float direct, float side1, float side2) const
  196. {
  197. float sideRatio = (side1 + side2) / direct;
  198. return sideRatio < 1.25f && direct > side1 && direct > side2;
  199. }
  200. void ObjectGraphCalculator::removeExtraConnections()
  201. {
  202. std::vector<std::pair<int3, int3>> connectionsToRemove;
  203. for(auto & actor : temporaryActorHeroes)
  204. {
  205. auto pos = actor->visitablePos();
  206. auto & currentNode = target->getNode(pos);
  207. target->iterateConnections(pos, [this, &pos, &connectionsToRemove, &currentNode](int3 n1, ObjectLink o1)
  208. {
  209. target->iterateConnections(n1, [&pos, &o1, &currentNode, &connectionsToRemove, this](int3 n2, ObjectLink o2)
  210. {
  211. auto direct = currentNode.connections.find(n2);
  212. if(direct != currentNode.connections.end() && isExtraConnection(direct->second.cost, o1.cost, o2.cost))
  213. {
  214. connectionsToRemove.push_back({pos, n2});
  215. }
  216. });
  217. });
  218. }
  219. vstd::removeDuplicates(connectionsToRemove);
  220. for(auto & c : connectionsToRemove)
  221. {
  222. target->removeConnection(c.first, c.second);
  223. if(NKAI_GRAPH_TRACE_LEVEL >= 2)
  224. {
  225. logAi->trace("Remove ineffective connection %s->%s", c.first.toString(), c.second.toString());
  226. }
  227. }
  228. }
  229. void ObjectGraphCalculator::addObjectActor(const CGObjectInstance * obj)
  230. {
  231. auto objectActor = temporaryActorHeroes.emplace_back(std::make_unique<CGHeroInstance>(obj->cb)).get();
  232. CRandomGenerator rng;
  233. auto visitablePos = obj->visitablePos();
  234. objectActor->setOwner(ai->playerID); // lets avoid having multiple colors
  235. objectActor->initHero(rng, static_cast<HeroTypeID>(0));
  236. objectActor->pos = objectActor->convertFromVisitablePos(visitablePos);
  237. objectActor->initObj(rng);
  238. if(cb->getTile(visitablePos)->isWater())
  239. {
  240. objectActor->setBoat(temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get());
  241. }
  242. assert(objectActor->visitablePos() == visitablePos);
  243. actorObjectMap[objectActor] = obj;
  244. actors[objectActor] = obj->ID == Obj::TOWN || obj->ID == Obj::BOAT ? HeroRole::MAIN : HeroRole::SCOUT;
  245. target->addObject(obj);
  246. auto shipyard = dynamic_cast<const IShipyard *>(obj);
  247. if(shipyard && shipyard->bestLocation().isValid())
  248. {
  249. int3 virtualBoat = shipyard->bestLocation();
  250. addJunctionActor(virtualBoat, true);
  251. target->addVirtualBoat(virtualBoat, obj);
  252. }
  253. }
  254. void ObjectGraphCalculator::addJunctionActor(const int3 & visitablePos, bool isVirtualBoat)
  255. {
  256. std::lock_guard lock(syncLock);
  257. auto internalCb = temporaryActorHeroes.front()->cb;
  258. auto objectActor = temporaryActorHeroes.emplace_back(std::make_unique<CGHeroInstance>(internalCb)).get();
  259. CRandomGenerator rng;
  260. objectActor->setOwner(ai->playerID); // lets avoid having multiple colors
  261. objectActor->initHero(rng, static_cast<HeroTypeID>(0));
  262. objectActor->pos = objectActor->convertFromVisitablePos(visitablePos);
  263. objectActor->initObj(rng);
  264. if(isVirtualBoat || ai->cb->getTile(visitablePos)->isWater())
  265. {
  266. objectActor->setBoat(temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get());
  267. }
  268. assert(objectActor->visitablePos() == visitablePos);
  269. actorObjectMap[objectActor] = nullptr;
  270. actors[objectActor] = isVirtualBoat ? HeroRole::MAIN : HeroRole::SCOUT;
  271. target->registerJunction(visitablePos);
  272. }
  273. ConnectionCostInfo ObjectGraphCalculator::getConnectionsCost(std::vector<AIPath> & paths) const
  274. {
  275. std::map<int3, float> costs;
  276. for(auto & path : paths)
  277. {
  278. auto fromPos = path.targetHero->visitablePos();
  279. auto cost = costs.find(fromPos);
  280. if(cost == costs.end())
  281. {
  282. costs.emplace(fromPos, path.movementCost());
  283. }
  284. else
  285. {
  286. if(path.movementCost() < cost->second)
  287. {
  288. costs[fromPos] = path.movementCost();
  289. }
  290. }
  291. }
  292. ConnectionCostInfo result;
  293. for(auto & cost : costs)
  294. {
  295. result.totalCost += cost.second;
  296. result.connectionsCount++;
  297. }
  298. if(result.connectionsCount)
  299. {
  300. result.avg = result.totalCost / result.connectionsCount;
  301. }
  302. return result;
  303. }
  304. }