astar.h 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. #ifndef __ASTAR_H
  2. #define __ASTAR_H
  3. #include <vector>
  4. #include <algorithm>
  5. namespace astar {
  6. struct Path {
  7. unsigned int w;
  8. unsigned int h;
  9. unsigned int ox;
  10. unsigned int oy;
  11. unsigned int dx;
  12. unsigned int dy;
  13. float diagonalCost;
  14. enum dir_t { NORTH_WEST, NORTH, NORTH_EAST, WEST, NONE, EAST, SOUTH_WEST, SOUTH, SOUTH_EAST };
  15. std::vector<float> grid; // wxh djikstra distance grid (covered distance)
  16. std::vector<float> heur; // wxh A* score grid (covered distance + estimated remaining distance)
  17. std::vector<dir_t> prev; // wxh 'previous' grid : direction to the previous cell
  18. std::vector<size_t> heap; // min_heap used in the algorithm. stores the offset in grid/heur (offset=x+y*w)
  19. std::vector< std::pair<unsigned int, unsigned int> > path;
  20. Path() : w(0), h(0) {}
  21. void init(unsigned int _w, unsigned int _h) {
  22. if (w == _w && h == _h)
  23. return;
  24. w = _w;
  25. h = _h;
  26. grid.resize(w*h);
  27. heur.resize(w*h);
  28. prev.resize(w*h);
  29. }
  30. template <typename FUNC>
  31. bool compute(unsigned int _ox, unsigned int _oy, unsigned int _dx, unsigned int _dy,
  32. float _diagCost, unsigned int cutoff, FUNC walk_cost) {
  33. ox = _ox;
  34. oy = _oy;
  35. dx = _dx;
  36. dy = _dy;
  37. diagonalCost = _diagCost;
  38. path.clear();
  39. heap.clear();
  40. if (ox == dx && oy == dy)
  41. return true;
  42. if (ox >= w || dx >= w || oy >= h || dy >= h) {
  43. return false;
  44. }
  45. grid.assign(w*h, 0.0);
  46. prev.assign(w*h, NONE);
  47. heur.assign(w*h, 0.0);
  48. heur[ox + oy*w] = 1.0;
  49. heap.push_back(ox + oy*w);
  50. _set_cells(cutoff, walk_cost);
  51. if (grid[dx + dy * w] == 0)
  52. return false;
  53. static int dirx[] = { -1, 0, 1, -1, 0, 1, -1, 0, 1 };
  54. static int diry[] = { -1, -1, -1, 0, 0, 0, 1, 1, 1 };
  55. path.push_back(std::make_pair(dx, dy));
  56. while (1) {
  57. dir_t step = prev[dx + dy*w];
  58. dx -= dirx[step];
  59. dy -= diry[step];
  60. if (dx == ox && dy == oy)
  61. break;
  62. path.push_back(std::make_pair(dx, dy));
  63. }
  64. return true;
  65. }
  66. bool walk(unsigned int& rx, unsigned int& ry) {
  67. if (path.empty())
  68. return false;
  69. rx = path.back().first;
  70. ry = path.back().second;
  71. path.pop_back();
  72. return true;
  73. }
  74. private:
  75. template <typename FUNC>
  76. void _set_cells(unsigned int cutoff, FUNC _walk_cost) {
  77. unsigned int stepstaken = 0;
  78. while (grid[dx + dy*w] == 0 && heap.size() > 0) {
  79. stepstaken++;
  80. if (stepstaken > cutoff)
  81. break;
  82. size_t offset = heap[0];
  83. std::pop_heap(heap.begin(), heap.end(), [this](size_t a, size_t b) { return (heur[a] > heur[b]); });
  84. heap.pop_back();
  85. unsigned int x = offset % w;
  86. unsigned int y = offset / w;
  87. double distance = grid[offset];
  88. unsigned int imax = (diagonalCost == 0.0f ? 4 : 8);
  89. for (unsigned int i = 0; i < imax; i++) {
  90. static int idirx[] = { 0, -1, 1, 0, -1, 1, -1, 1 };
  91. static int idiry[] = { -1, 0, 0, 1, -1, -1, 1, 1 };
  92. static dir_t prevdirs[] = {
  93. NORTH, WEST, EAST, SOUTH, NORTH_WEST, NORTH_EAST, SOUTH_WEST, SOUTH_EAST
  94. };
  95. int cx = x + idirx[i];
  96. int cy = y + idiry[i];
  97. if (cx >= 0 && cy >= 0 && (unsigned int)cx < w && (unsigned int)cy < h) {
  98. float walk_cost = _walk_cost(x, y, cx, cy);
  99. if (walk_cost <= 0)
  100. continue;
  101. float covered = distance + walk_cost * (i>=4 ? diagonalCost : 1.0f);
  102. size_t coffset = cx + cy*w;
  103. float previousCovered = grid[coffset];
  104. if (previousCovered == 0) {
  105. float remaining = ::sqrt((cx-dx)*(cx-dx) + (cy-dy)*(cy-dy));
  106. grid[coffset] = covered;
  107. heur[coffset] = covered + remaining;
  108. prev[coffset] = prevdirs[i];
  109. heap.push_back(coffset);
  110. std::push_heap(heap.begin(), heap.end(),
  111. [this](size_t a, size_t b) { return (heur[a] > heur[b]); });
  112. } else if (previousCovered > covered) {
  113. grid[coffset] = covered;
  114. heur[coffset] -= (previousCovered - covered);
  115. prev[coffset] = prevdirs[i];
  116. std::make_heap(heap.begin(), heap.end(),
  117. [this](size_t a, size_t b) { return (heur[a] > heur[b]); });
  118. }
  119. }
  120. }
  121. }
  122. }
  123. };
  124. }
  125. #endif