|
1 |
| -// javascript-astar |
| 1 | +// javascript-astar 0.0.1 |
2 | 2 | // http://github.com/bgrins/javascript-astar
|
3 | 3 | // Freely distributable under the MIT License.
|
4 |
| -// Implements the astar search algorithm in javascript using a binary heap. |
5 |
| - |
| 4 | +// Implements the astar search algorithm in javascript using a Binary Heap. |
| 5 | +// Includes Binary Heap (with modifications) from Marijn Haverbeke. |
| 6 | +// http://eloquentjavascript.net/appendix2.html |
6 | 7 |
|
7 | 8 | (function(definition) {
|
8 | 9 | if(typeof exports === 'object') {
|
9 | 10 | module.exports = definition();
|
10 | 11 | } else if(typeof define === 'function' && define.amd) {
|
11 | 12 | define([], definition);
|
12 | 13 | } else {
|
13 |
| - window.astar = definition(); |
| 14 | + var exports = definition(); |
| 15 | + window.astar = exports.astar; |
| 16 | + window.Graph = exports.Graph; |
14 | 17 | }
|
15 | 18 | })(function() {
|
16 | 19 |
|
@@ -162,6 +165,181 @@ var astar = {
|
162 | 165 | }
|
163 | 166 | };
|
164 | 167 |
|
165 |
| -return astar; |
| 168 | +function Graph(grid) { |
| 169 | + var nodes = []; |
| 170 | + |
| 171 | + for (var x = 0; x < grid.length; x++) { |
| 172 | + nodes[x] = []; |
| 173 | + |
| 174 | + for (var y = 0, row = grid[x]; y < row.length; y++) { |
| 175 | + nodes[x][y] = new GraphNode(x, y, row[y]); |
| 176 | + } |
| 177 | + } |
| 178 | + |
| 179 | + this.input = grid; |
| 180 | + this.nodes = nodes; |
| 181 | +} |
| 182 | + |
| 183 | +Graph.prototype.toString = function() { |
| 184 | + var graphString = "\n"; |
| 185 | + var nodes = this.nodes; |
| 186 | + var rowDebug, row, y, l; |
| 187 | + for (var x = 0, len = nodes.length; x < len; x++) { |
| 188 | + rowDebug = ""; |
| 189 | + row = nodes[x]; |
| 190 | + for (y = 0, l = row.length; y < l; y++) { |
| 191 | + rowDebug += row[y].type + " "; |
| 192 | + } |
| 193 | + graphString = graphString + rowDebug + "\n"; |
| 194 | + } |
| 195 | + return graphString; |
| 196 | +}; |
| 197 | + |
| 198 | +function GraphNode(x,y,type) { |
| 199 | + this.data = { }; |
| 200 | + this.x = x; |
| 201 | + this.y = y; |
| 202 | + this.pos = { |
| 203 | + x: x, |
| 204 | + y: y |
| 205 | + }; |
| 206 | + this.type = type; |
| 207 | +} |
| 208 | + |
| 209 | +GraphNode.prototype.toString = function() { |
| 210 | + return "[" + this.x + " " + this.y + "]"; |
| 211 | +}; |
| 212 | + |
| 213 | +GraphNode.prototype.isWall = function() { |
| 214 | + return this.type === 0; |
| 215 | +}; |
| 216 | + |
| 217 | +function BinaryHeap(scoreFunction){ |
| 218 | + this.content = []; |
| 219 | + this.scoreFunction = scoreFunction; |
| 220 | +} |
| 221 | + |
| 222 | +BinaryHeap.prototype = { |
| 223 | + push: function(element) { |
| 224 | + // Add the new element to the end of the array. |
| 225 | + this.content.push(element); |
| 226 | + |
| 227 | + // Allow it to sink down. |
| 228 | + this.sinkDown(this.content.length - 1); |
| 229 | + }, |
| 230 | + pop: function() { |
| 231 | + // Store the first element so we can return it later. |
| 232 | + var result = this.content[0]; |
| 233 | + // Get the element at the end of the array. |
| 234 | + var end = this.content.pop(); |
| 235 | + // If there are any elements left, put the end element at the |
| 236 | + // start, and let it bubble up. |
| 237 | + if (this.content.length > 0) { |
| 238 | + this.content[0] = end; |
| 239 | + this.bubbleUp(0); |
| 240 | + } |
| 241 | + return result; |
| 242 | + }, |
| 243 | + remove: function(node) { |
| 244 | + var i = this.content.indexOf(node); |
| 245 | + |
| 246 | + // When it is found, the process seen in 'pop' is repeated |
| 247 | + // to fill up the hole. |
| 248 | + var end = this.content.pop(); |
| 249 | + |
| 250 | + if (i !== this.content.length - 1) { |
| 251 | + this.content[i] = end; |
| 252 | + |
| 253 | + if (this.scoreFunction(end) < this.scoreFunction(node)) { |
| 254 | + this.sinkDown(i); |
| 255 | + } |
| 256 | + else { |
| 257 | + this.bubbleUp(i); |
| 258 | + } |
| 259 | + } |
| 260 | + }, |
| 261 | + size: function() { |
| 262 | + return this.content.length; |
| 263 | + }, |
| 264 | + rescoreElement: function(node) { |
| 265 | + this.sinkDown(this.content.indexOf(node)); |
| 266 | + }, |
| 267 | + sinkDown: function(n) { |
| 268 | + // Fetch the element that has to be sunk. |
| 269 | + var element = this.content[n]; |
| 270 | + |
| 271 | + // When at 0, an element can not sink any further. |
| 272 | + while (n > 0) { |
| 273 | + |
| 274 | + // Compute the parent element's index, and fetch it. |
| 275 | + var parentN = ((n + 1) >> 1) - 1, |
| 276 | + parent = this.content[parentN]; |
| 277 | + // Swap the elements if the parent is greater. |
| 278 | + if (this.scoreFunction(element) < this.scoreFunction(parent)) { |
| 279 | + this.content[parentN] = element; |
| 280 | + this.content[n] = parent; |
| 281 | + // Update 'n' to continue at the new position. |
| 282 | + n = parentN; |
| 283 | + } |
| 284 | + |
| 285 | + // Found a parent that is less, no need to sink any further. |
| 286 | + else { |
| 287 | + break; |
| 288 | + } |
| 289 | + } |
| 290 | + }, |
| 291 | + bubbleUp: function(n) { |
| 292 | + // Look up the target element and its score. |
| 293 | + var length = this.content.length, |
| 294 | + element = this.content[n], |
| 295 | + elemScore = this.scoreFunction(element); |
| 296 | + |
| 297 | + while(true) { |
| 298 | + // Compute the indices of the child elements. |
| 299 | + var child2N = (n + 1) << 1, child1N = child2N - 1; |
| 300 | + // This is used to store the new position of the element, |
| 301 | + // if any. |
| 302 | + var swap = null; |
| 303 | + var child1Score; |
| 304 | + // If the first child exists (is inside the array)... |
| 305 | + if (child1N < length) { |
| 306 | + // Look it up and compute its score. |
| 307 | + var child1 = this.content[child1N]; |
| 308 | + child1Score = this.scoreFunction(child1); |
| 309 | + |
| 310 | + // If the score is less than our element's, we need to swap. |
| 311 | + if (child1Score < elemScore){ |
| 312 | + swap = child1N; |
| 313 | + } |
| 314 | + } |
| 315 | + |
| 316 | + // Do the same checks for the other child. |
| 317 | + if (child2N < length) { |
| 318 | + var child2 = this.content[child2N], |
| 319 | + child2Score = this.scoreFunction(child2); |
| 320 | + if (child2Score < (swap === null ? elemScore : child1Score)) { |
| 321 | + swap = child2N; |
| 322 | + } |
| 323 | + } |
| 324 | + |
| 325 | + // If the element needs to be moved, swap it, and continue. |
| 326 | + if (swap !== null) { |
| 327 | + this.content[n] = this.content[swap]; |
| 328 | + this.content[swap] = element; |
| 329 | + n = swap; |
| 330 | + } |
| 331 | + |
| 332 | + // Otherwise, we are done. |
| 333 | + else { |
| 334 | + break; |
| 335 | + } |
| 336 | + } |
| 337 | + } |
| 338 | +}; |
| 339 | + |
| 340 | +return { |
| 341 | + astar: astar, |
| 342 | + Graph: Graph |
| 343 | +}; |
166 | 344 |
|
167 | 345 | });
|
0 commit comments