RmgPath.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*
  2. * RmgPath.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 "RmgPath.h"
  12. #include <boost/heap/priority_queue.hpp> //A*
  13. VCMI_LIB_NAMESPACE_BEGIN
  14. using namespace rmg;
  15. const std::function<float(const int3 &, const int3 &)> Path::DEFAULT_MOVEMENT_FUNCTION =
  16. [](const int3 & src, const int3 & dst)
  17. {
  18. return 1.f;
  19. };
  20. //A* priority queue
  21. using TDistance = std::pair<int3, float>;
  22. struct NodeComparer
  23. {
  24. bool operator()(const TDistance & lhs, const TDistance & rhs) const
  25. {
  26. return (rhs.second < lhs.second);
  27. }
  28. };
  29. boost::heap::priority_queue<TDistance, boost::heap::compare<NodeComparer>> createPriorityQueue()
  30. {
  31. return boost::heap::priority_queue<TDistance, boost::heap::compare<NodeComparer>>();
  32. }
  33. Path::Path(const Area & area): dArea(&area)
  34. {
  35. }
  36. Path::Path(const Area & area, const int3 & src): dArea(&area)
  37. {
  38. dPath.add(src);
  39. }
  40. Path & Path::operator= (const Path & path)
  41. {
  42. //do not modify area
  43. dPath = path.dPath;
  44. return *this;
  45. }
  46. bool Path::valid() const
  47. {
  48. return !dPath.empty();
  49. }
  50. Path Path::invalid()
  51. {
  52. return Path({});
  53. }
  54. Path Path::search(const Tileset & dst, bool straight, std::function<float(const int3 &, const int3 &)> moveCostFunction) const
  55. {
  56. //A* algorithm taken from Wiki http://en.wikipedia.org/wiki/A*_search_algorithm
  57. if(!dArea)
  58. return Path::invalid();
  59. if(dst.empty()) // Skip construction of same area
  60. return Path(*dArea);
  61. auto resultArea = *dArea + dst;
  62. Path result(resultArea);
  63. int3 src = rmg::Area(dst).nearest(dPath);
  64. result.connect(src);
  65. Tileset closed; // The set of nodes already evaluated.
  66. auto open = createPriorityQueue(); // The set of tentative nodes to be evaluated, initially containing the start node
  67. std::map<int3, int3> cameFrom; // The map of navigated nodes.
  68. std::map<int3, float> distances;
  69. cameFrom[src] = int3(-1, -1, -1); //first node points to finish condition
  70. distances[src] = 0;
  71. open.push(std::make_pair(src, 0.f));
  72. // Cost from start along best known path.
  73. while(!open.empty())
  74. {
  75. auto node = open.top();
  76. open.pop();
  77. int3 currentNode = node.first;
  78. closed.insert(currentNode);
  79. if(dPath.contains(currentNode)) //we reached connection, stop
  80. {
  81. // Trace the path using the saved parent information and return path
  82. int3 backTracking = currentNode;
  83. while (cameFrom[backTracking].valid())
  84. {
  85. result.dPath.add(backTracking);
  86. backTracking = cameFrom[backTracking];
  87. }
  88. return result;
  89. }
  90. else
  91. {
  92. auto computeTileScore = [&open, &closed, &cameFrom, &currentNode, &distances, &moveCostFunction, &result](const int3& pos) -> void
  93. {
  94. if(closed.count(pos))
  95. return;
  96. if(!result.dArea->contains(pos))
  97. return;
  98. float movementCost = moveCostFunction(currentNode, pos);
  99. float distance = distances[currentNode] + movementCost; //we prefer to use already free paths
  100. int bestDistanceSoFar = std::numeric_limits<int>::max();
  101. auto it = distances.find(pos);
  102. if(it != distances.end())
  103. bestDistanceSoFar = static_cast<int>(it->second);
  104. if(distance < bestDistanceSoFar)
  105. {
  106. cameFrom[pos] = currentNode;
  107. open.push(std::make_pair(pos, distance));
  108. distances[pos] = distance;
  109. }
  110. };
  111. auto dirs = int3::getDirs();
  112. std::vector<int3> neighbors(dirs.begin(), dirs.end());
  113. if(straight)
  114. neighbors.assign(rmg::dirs4.begin(), rmg::dirs4.end());
  115. for(auto & i : neighbors)
  116. {
  117. computeTileScore(currentNode + i);
  118. }
  119. }
  120. }
  121. result.dPath.clear();
  122. return result;
  123. }
  124. Path Path::search(const int3 & dst, bool straight, std::function<float(const int3 &, const int3 &)> moveCostFunction) const
  125. {
  126. return search(Tileset{dst}, straight, std::move(moveCostFunction));
  127. }
  128. Path Path::search(const Area & dst, bool straight, std::function<float(const int3 &, const int3 &)> moveCostFunction) const
  129. {
  130. return search(dst.getTiles(), straight, std::move(moveCostFunction));
  131. }
  132. Path Path::search(const Path & dst, bool straight, std::function<float(const int3 &, const int3 &)> moveCostFunction) const
  133. {
  134. assert(dst.dArea == dArea);
  135. return search(dst.dPath, straight, std::move(moveCostFunction));
  136. }
  137. void Path::connect(const int3 & path)
  138. {
  139. dPath.add(path);
  140. }
  141. void Path::connect(const Tileset & path)
  142. {
  143. Area a(path);
  144. dPath.unite(a);
  145. }
  146. void Path::connect(const Area & path)
  147. {
  148. dPath.unite(path);
  149. }
  150. void Path::connect(const Path & path)
  151. {
  152. dPath.unite(path.dPath);
  153. }
  154. const Area & Path::getPathArea() const
  155. {
  156. return dPath;
  157. }
  158. Path::MoveCostFunction Path::createCurvedCostFunction(const Area & border)
  159. {
  160. // Capture by value to ensure the Area object persists
  161. return [border = border](const int3& src, const int3& dst) -> float
  162. {
  163. // Route main roads far from border
  164. float ret = dst.dist2d(src);
  165. float dist = border.distanceSqr(dst);
  166. if(dist > 1.0f)
  167. {
  168. ret /= dist;
  169. }
  170. return ret;
  171. };
  172. }
  173. VCMI_LIB_NAMESPACE_END