astar.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. export const astar = (function() {
  2. class _OpenSet {
  3. constructor() {
  4. this._priorityQueue = buckets.PriorityQueue((a, b) => {
  5. if (a.fScore > b.fScore) {
  6. return -1;
  7. }
  8. if (a.fScore < b.fScore) {
  9. return 1;
  10. }
  11. if (a.gScore > b.gScore) {
  12. return 1;
  13. }
  14. if (a.gScore < b.gScore) {
  15. return -1;
  16. }
  17. return 0;
  18. });
  19. this._dict = {};
  20. }
  21. add(k, v) {
  22. this._priorityQueue.add(v);
  23. this._dict[k] = true;
  24. }
  25. dequeue() {
  26. const v = this._priorityQueue.dequeue();
  27. delete this._dict[v.key];
  28. return v;
  29. }
  30. peek() {
  31. return this._priorityQueue.peek();
  32. }
  33. hasKey(k) {
  34. return (k in this._dict);
  35. }
  36. size() {
  37. return this._priorityQueue.size();
  38. }
  39. };
  40. class _AStarClient {
  41. constructor(start, end, nodes) {
  42. this._start = start;
  43. this._end = end;
  44. this._nodes = nodes;
  45. this._path = null;
  46. this._astarInstance = null;
  47. }
  48. Start(instance) {
  49. this._astarInstance = instance;
  50. }
  51. get Path() {
  52. return this._path;
  53. }
  54. get started() {
  55. return this._astarInstance != null;
  56. }
  57. get finished() {
  58. if (this._path) {
  59. return true;
  60. }
  61. if (!this._astarInstance) {
  62. return false;
  63. }
  64. return this._astarInstance.finished;
  65. }
  66. CachePath() {
  67. if (!this._astarInstance) {
  68. return null;
  69. }
  70. this._path = this._astarInstance.BuildPath();
  71. this._path = this._path.map(k => this._astarInstance._nodes[k])
  72. this._astarInstance = null;
  73. }
  74. Step() {
  75. if (!this._astarInstance) {
  76. return;
  77. }
  78. this._astarInstance.Step();
  79. }
  80. };
  81. const _MAX_ASTAR = 400;
  82. class _AStarManager {
  83. constructor(nodes, costFunction, weightFunction) {
  84. this._nodes = nodes;
  85. this._costFunction = costFunction;
  86. this._weightFunction = weightFunction;
  87. this._live = [];
  88. this._clients = [];
  89. }
  90. Step() {
  91. for (let c of this._clients) {
  92. if (!c._astarInstance && !c.finished && this._live.length < _MAX_ASTAR) {
  93. const a = new _AStar(this._nodes, c._start, c._end, this._costFunction, this._weightFunction);
  94. c.Start(a);
  95. this._live.push(c);
  96. }
  97. }
  98. for (let c of this._live) {
  99. c.Step();
  100. if (c.finished) {
  101. c.CachePath();
  102. }
  103. }
  104. this._live = this._live.filter(c => !c.finished);
  105. }
  106. CreateClient(start, end) {
  107. const c = new _AStarClient(start, end, this._nodes);
  108. this._clients.push(c);
  109. return c;
  110. }
  111. }
  112. class _AStar {
  113. constructor(nodes, start, end, costFunction, weightFunction) {
  114. this._start = start;
  115. this._end = end;
  116. this._nodes = nodes;
  117. this._costFunction = costFunction;
  118. this._weightFunction = weightFunction;
  119. this._finished = false;
  120. this._steps = 0;
  121. this._data = {};
  122. this._data[start] = {
  123. key: start,
  124. gScore: 0,
  125. fScore: costFunction(start, end),
  126. cameFrom: null,
  127. };
  128. this._open = new _OpenSet();
  129. this._open.add(start, this._data[start]);
  130. }
  131. get finished() {
  132. return this._finished;
  133. }
  134. BuildInProgressPath() {
  135. const lowestF = this._open.peek();
  136. const path = [lowestF.key];
  137. while (true) {
  138. const n = this._data[path[path.length - 1]];
  139. if (n.cameFrom == null) {
  140. break;
  141. }
  142. path.push(n.cameFrom);
  143. }
  144. return path.reverse();
  145. }
  146. BuildPath() {
  147. if (!this.finished) {
  148. return this.BuildInProgressPath();
  149. }
  150. const path = [this._end];
  151. while (true) {
  152. const n = this._data[path[path.length - 1]];
  153. if (n.cameFrom == null) {
  154. break;
  155. }
  156. path.push(n.cameFrom);
  157. }
  158. return path.reverse();
  159. }
  160. Step() {
  161. if (this.finished) {
  162. return;
  163. }
  164. if (this._open.size() > 0) {
  165. this._steps += 1;
  166. const curNode = this._open.dequeue();
  167. const k = curNode.key;
  168. if (k == this._end) {
  169. this._finished = true;
  170. return;
  171. }
  172. for (const e of this._nodes[k].edges) {
  173. // Lazily instantiate graph instead of in constructor.
  174. if (!(e in this._data)) {
  175. this._data[e] = {
  176. key: k,
  177. gScore: Number.MAX_VALUE,
  178. fScore: Number.MAX_VALUE,
  179. cameFrom: null,
  180. };
  181. }
  182. const gScore = this._data[k].gScore + this._weightFunction(k, e);
  183. if (gScore < this._data[e].gScore) {
  184. this._data[e] = {
  185. key: e,
  186. gScore: gScore,
  187. fScore: gScore + this._costFunction(this._end, e),
  188. cameFrom: k,
  189. };
  190. if (!this._open.hasKey(e)) {
  191. this._open.add(e, this._data[e]);
  192. }
  193. }
  194. }
  195. }
  196. }
  197. };
  198. return {
  199. AStarManager: _AStarManager,
  200. };
  201. })();