topical media & game development
mobile-query-three-plugins-simplemaze-vendor-pathfinding-browser.js / js
____ _ _ _____ _ _ _ _
| _ \ __ _| |_| |__ | ___(_)_ __ __| (_)_ __ __ _ (_)___
| |_) / _` | __| '_ \| |_ | | '_ \ / _` | | '_ \ / _` | | / __|
| __/ (_| | |_| | | | _| | | | | | (_| | | | | | (_| |_ | __ \
|_| __,_|__|_| |_|_| |_|_| |_|__,_|_|_| |_|__, (_)/ |___/
|___/ |__/
github.com/qiao/PathFinding.js
var PF = (function() {var require = function (file, cwd) {
var resolved = require.resolve(file, cwd || '/');
var mod = require.modules[resolved];
if (!mod) throw new Error(
'Failed to resolve module ' + file + ', tried ' + resolved
);
var res = mod._cached ? mod._cached : mod();
return res;
}
require.paths = [];
require.modules = {};
require.extensions = [".js",".coffee"];
require._core = {
'assert': true,
'events': true,
'fs': true,
'path': true,
'vm': true
};
require.resolve = (function () {
return function (x, cwd) {
if (!cwd) cwd = '/';
if (require._core[x]) return x;
var path = require.modules.path();
cwd = path.resolve('/', cwd);
var y = cwd || '/';
if (x.match(/^(?:\.\.?\/|\/)/)) {
var m = loadAsFileSync(path.resolve(y, x))
|| loadAsDirectorySync(path.resolve(y, x));
if (m) return m;
}
var n = loadNodeModulesSync(x, y);
if (n) return n;
throw new Error("Cannot find module '" + x + "'");
function loadAsFileSync (x) {
if (require.modules[x]) {
return x;
}
for (var i = 0; i < require.extensions.length; i++) {
var ext = require.extensions[i];
if (require.modules[x + ext]) return x + ext;
}
}
function loadAsDirectorySync (x) {
x = x.replace(/\/+)|\/)?((?:.+?)?(\.[^.]*)?)
A node in grid.
This class holds some basic information about a node and custom
attributes may be added, depending on the algorithms' needs.
@constructor
parameter: {number} x - The x coordinate of the node on the grid.
parameter: {number} y - The y coordinate of the node on the grid.
parameter: {boolean} [walkable] - Whether this node is walkable.
function Node(x, y, walkable) {
The x coordinate of the node on the grid.
@type number
this.x = x;
The y coordinate of the node on the grid.
@type number
this.y = y;
Whether this node can be walked through.
@type boolean
this.walkable = (walkable === undefined ? true : walkable);
};
module.exports = Node;
});
require.define("/core/Grid.js", function (require, module, exports, __dirname, __filename) {
var Node = require('./Node');
The Grid class, which serves as the encapsulation of the layout of the nodes.
@constructor
parameter: {number} width Number of columns of the grid.
parameter: {number} height Number of rows of the grid.
parameter: {Array.>} [matrix] - A 0-1 matrix
representing the walkable status of the nodes(0 or false for walkable).
If the matrix is not supplied, all the nodes will be walkable.
function Grid(width, height, matrix) {
The number of columns of the grid.
@type number
this.width = width;
The number of rows of the grid.
@type number
this.height = height;
A 2D array of nodes.
this.nodes = this._buildNodes(width, height, matrix);
}
Build and return the nodes.
@private
parameter: {number} width
parameter: {number} height
parameter: {Array.>} [matrix] - A 0-1 matrix representing
the walkable status of the nodes.
see: Grid
Grid.prototype._buildNodes = function(width, height, matrix) {
var i, j,
nodes = new Array(height),
row;
for (i = 0; i < height; ++i) {
nodes[i] = new Array(width);
for (j = 0; j < width; ++j) {
nodes[i][j] = new Node(j, i);
}
}
if (matrix === undefined) {
return nodes;
}
if (matrix.length !== height || matrix[0].length !== width) {
throw new Error('Matrix size does not fit');
}
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
if (matrix[i][j]) {
// 0, false, null will be walkable
// while others will be un-walkable
nodes[i][j].walkable = false;
}
}
}
return nodes;
};
Grid.prototype.getNodeAt = function(x, y) {
return this.nodes[y][x];
};
Determine whether the node at the given position is walkable.
(Also returns false if the position is outside the grid.)
parameter: {number} x - The x coordinate of the node.
parameter: {number} y - The y coordinate of the node.
returns: {boolean} - The walkability of the node.
Grid.prototype.isWalkableAt = function(x, y) {
return this.isInside(x, y) && this.nodes[y][x].walkable;
};
Determine whether the position is inside the grid.
XXX: `grid.isInside(x, y)` is wierd to read.
It should be `(x, y) is inside grid`, but I failed to find a better
name for this method.
parameter: {number} x
parameter: {number} y
returns: {boolean}
Grid.prototype.isInside = function(x, y) {
return (x >= 0 && x < this.width) && (y >= 0 && y < this.height);
};
Set whether the node on the given position is walkable.
NOTE: throws exception if the coordinate is not inside the grid.
parameter: {number} x - The x coordinate of the node.
parameter: {number} y - The y coordinate of the node.
parameter: {boolean} walkable - Whether the position is walkable.
Grid.prototype.setWalkableAt = function(x, y, walkable) {
this.nodes[y][x].walkable = walkable;
};
Get the neighbors of the given node.
offsets diagonalOffsets:
+---+---+---+ +---+---+---+
| | 0 | | | 0 | | 1 |
+---+---+---+ +---+---+---+
| 3 | | 1 | | | | |
+---+---+---+ +---+---+---+
| | 2 | | | 3 | | 2 |
+---+---+---+ +---+---+---+
When allowDiagonal is true, if offsets[i] is valid, then
diagonalOffsets[i] and
diagonalOffsets[(i + 1) % 4] is valid.
parameter: {Node} node
parameter: {boolean} allowDiagonal
parameter: {boolean} dontCrossCorners
Grid.prototype.getNeighbors = function(node, allowDiagonal, dontCrossCorners) {
var x = node.x,
y = node.y,
neighbors = [],
s0 = false, d0 = false,
s1 = false, d1 = false,
s2 = false, d2 = false,
s3 = false, d3 = false,
nodes = this.nodes;
// ↑
if (this.isWalkableAt(x, y - 1)) {
neighbors.push(nodes[y - 1][x]);
s0 = true;
}
// →
if (this.isWalkableAt(x + 1, y)) {
neighbors.push(nodes[y][x + 1]);
s1 = true;
}
// ↓
if (this.isWalkableAt(x, y + 1)) {
neighbors.push(nodes[y + 1][x]);
s2 = true;
}
// ←
if (this.isWalkableAt(x - 1, y)) {
neighbors.push(nodes[y][x - 1]);
s3 = true;
}
if (!allowDiagonal) {
return neighbors;
}
if (dontCrossCorners) {
d0 = s3 && s0;
d1 = s0 && s1;
d2 = s1 && s2;
d3 = s2 && s3;
} else {
d0 = s3 || s0;
d1 = s0 || s1;
d2 = s1 || s2;
d3 = s2 || s3;
}
// ↖
if (d0 && this.isWalkableAt(x - 1, y - 1)) {
neighbors.push(nodes[y - 1][x - 1]);
}
// ↗
if (d1 && this.isWalkableAt(x + 1, y - 1)) {
neighbors.push(nodes[y - 1][x + 1]);
}
// ↘
if (d2 && this.isWalkableAt(x + 1, y + 1)) {
neighbors.push(nodes[y + 1][x + 1]);
}
// ↙
if (d3 && this.isWalkableAt(x - 1, y + 1)) {
neighbors.push(nodes[y + 1][x - 1]);
}
return neighbors;
};
Get a clone of this grid.
returns: {Grid} Cloned grid.
Grid.prototype.clone = function() {
var i, j,
width = this.width,
height = this.height,
thisNodes = this.nodes,
newGrid = new Grid(width, height),
newNodes = new Array(height),
row;
for (i = 0; i < height; ++i) {
newNodes[i] = new Array(width);
for (j = 0; j < width; ++j) {
newNodes[i][j] = new Node(j, i, thisNodes[i][j].walkable);
}
}
newGrid.nodes = newNodes;
return newGrid;
};
module.exports = Grid;
});
require.define("/core/Heap.js", function (require, module, exports, __dirname, __filename) {
// From github.com/qiao/heap.js
// Generated by CoffeeScript 1.3.1
(function() {
var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
floor = Math.floor, min = Math.min;
/*
Default comparison function to be used
*/
defaultCmp = function(x, y) {
if (x < y) {
return -1;
}
if (x > y) {
return 1;
}
return 0;
};
/*
Insert item x in list a, and keep it sorted assuming a is sorted.
If x is already in a, insert it to the right of the rightmost x.
Optional args lo (default 0) and hi (default a.length) bound the slice
of a to be searched.
*/
insort = function(a, x, lo, hi, cmp) {
var mid;
if (lo == null) {
lo = 0;
}
if (cmp == null) {
cmp = defaultCmp;
}
if (lo < 0) {
throw new Error('lo must be non-negative');
}
if (hi == null) {
hi = a.length;
}
while (cmp(lo, hi) < 0) {
mid = floor((lo + hi) / 2);
if (cmp(x, a[mid]) < 0) {
hi = mid;
} else {
lo = mid + 1;
}
}
return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);
};
/*
Push item onto heap, maintaining the heap invariant.
*/
heappush = function(array, item, cmp) {
if (cmp == null) {
cmp = defaultCmp;
}
array.push(item);
return _siftdown(array, 0, array.length - 1, cmp);
};
/*
Pop the smallest item off the heap, maintaining the heap invariant.
*/
heappop = function(array, cmp) {
var lastelt, returnitem;
if (cmp == null) {
cmp = defaultCmp;
}
lastelt = array.pop();
if (array.length) {
returnitem = array[0];
array[0] = lastelt;
_siftup(array, 0, cmp);
} else {
returnitem = lastelt;
}
return returnitem;
};
/*
Pop and return the current smallest value, and add the new item.
This is more efficient than heappop() followed by heappush(), and can be
more appropriate when using a fixed size heap. Note that the value
returned may be larger than item! That constrains reasonable use of
this routine unless written as part of a conditional replacement:
if item > array[0]
item = heapreplace(array, item)
*/
heapreplace = function(array, item, cmp) {
var returnitem;
if (cmp == null) {
cmp = defaultCmp;
}
returnitem = array[0];
array[0] = item;
_siftup(array, 0, cmp);
return returnitem;
};
/*
Fast version of a heappush followed by a heappop.
*/
heappushpop = function(array, item, cmp) {
var _ref;
if (cmp == null) {
cmp = defaultCmp;
}
if (array.length && cmp(array[0], item) < 0) {
_ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
_siftup(array, 0, cmp);
}
return item;
};
/*
Transform list into a heap, in-place, in O(array.length) time.
*/
heapify = function(array, cmp) {
var i, _i, _j, _len, _ref, _ref1, _results, _results1;
if (cmp == null) {
cmp = defaultCmp;
}
_ref1 = (function() {
_results1 = [];
for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }
return _results1;
}).apply(this).reverse();
_results = [];
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
i = _ref1[_i];
_results.push(_siftup(array, i, cmp));
}
return _results;
};
/*
Update the position of the given item in the heap.
This function should be called every time the item is being modified.
*/
updateItem = function(array, item, cmp) {
var pos;
if (cmp == null) {
cmp = defaultCmp;
}
pos = array.indexOf(item);
_siftdown(array, 0, pos, cmp);
return _siftup(array, pos, cmp);
};
/*
Find the n largest elements in a dataset.
*/
nlargest = function(array, n, cmp) {
var elem, result, _i, _len, _ref;
if (cmp == null) {
cmp = defaultCmp;
}
result = array.slice(0, n);
if (!result.length) {
return result;
}
heapify(result, cmp);
_ref = array.slice(n);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
elem = _ref[_i];
heappushpop(result, elem, cmp);
}
return result.sort(cmp).reverse();
};
/*
Find the n smallest elements in a dataset.
*/
nsmallest = function(array, n, cmp) {
var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results;
if (cmp == null) {
cmp = defaultCmp;
}
if (n * 10 <= array.length) {
result = array.slice(0, n).sort(cmp);
if (!result.length) {
return result;
}
los = result[result.length - 1];
_ref = array.slice(n);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
elem = _ref[_i];
if (cmp(elem, los) < 0) {
insort(result, elem, 0, null, cmp);
result.pop();
los = result[result.length - 1];
}
}
return result;
}
heapify(array, cmp);
_results = [];
for (i = _j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
_results.push(heappop(array, cmp));
}
return _results;
};
_siftdown = function(array, startpos, pos, cmp) {
var newitem, parent, parentpos;
if (cmp == null) {
cmp = defaultCmp;
}
newitem = array[pos];
while (pos > startpos) {
parentpos = (pos - 1) >> 1;
parent = array[parentpos];
if (cmp(newitem, parent) < 0) {
array[pos] = parent;
pos = parentpos;
continue;
}
break;
}
return array[pos] = newitem;
};
_siftup = function(array, pos, cmp) {
var childpos, endpos, newitem, rightpos, startpos;
if (cmp == null) {
cmp = defaultCmp;
}
endpos = array.length;
startpos = pos;
newitem = array[pos];
childpos = 2 * pos + 1;
while (childpos < endpos) {
rightpos = childpos + 1;
if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
childpos = rightpos;
}
array[pos] = array[childpos];
pos = childpos;
childpos = 2 * pos + 1;
}
array[pos] = newitem;
return _siftdown(array, startpos, pos, cmp);
};
Heap = (function() {
Heap.name = 'Heap';
Heap.push = heappush;
Heap.pop = heappop;
Heap.replace = heapreplace;
Heap.pushpop = heappushpop;
Heap.heapify = heapify;
Heap.nlargest = nlargest;
Heap.nsmallest = nsmallest;
function Heap(cmp) {
this.cmp = cmp != null ? cmp : defaultCmp;
this.nodes = [];
}
Heap.prototype.push = function(x) {
return heappush(this.nodes, x, this.cmp);
};
Heap.prototype.pop = function() {
return heappop(this.nodes, this.cmp);
};
Heap.prototype.peek = function() {
return this.nodes[0];
};
Heap.prototype.contains = function(x) {
return this.nodes.indexOf(x) !== -1;
};
Heap.prototype.replace = function(x) {
return heapreplace(this.nodes, x, this.cmp);
};
Heap.prototype.pushpop = function(x) {
return heappushpop(this.nodes, x, this.cmp);
};
Heap.prototype.heapify = function() {
return heapify(this.nodes, this.cmp);
};
Heap.prototype.updateItem = function(x) {
return updateItem(this.nodes, x, this.cmp);
};
Heap.prototype.clear = function() {
return this.nodes = [];
};
Heap.prototype.empty = function() {
return this.nodes.length === 0;
};
Heap.prototype.size = function() {
return this.nodes.length;
};
Heap.prototype.clone = function() {
var heap;
heap = new Heap();
heap.nodes = this.nodes.slice(0);
return heap;
};
Heap.prototype.toArray = function() {
return this.nodes.slice(0);
};
Heap.prototype.insert = Heap.prototype.push;
Heap.prototype.remove = Heap.prototype.pop;
Heap.prototype.top = Heap.prototype.peek;
Heap.prototype.front = Heap.prototype.peek;
Heap.prototype.has = Heap.prototype.contains;
Heap.prototype.copy = Heap.prototype.clone;
return Heap;
})();
if (typeof module !== "undefined" && module !== null ? module.exports : void 0) {
module.exports = Heap;
} else {
window.Heap = Heap;
}
}).call(this);
});
require.define("/core/Util.js", function (require, module, exports, __dirname, __filename) {
Backtrace according to the parent records and return the path.
(including both start and end nodes)
parameter: {Node} node End node
returns: {Array.>} the path
function backtrace(node) {
var path = [[node.x, node.y]];
while (node.parent) {
node = node.parent;
path.push([node.x, node.y]);
}
return path.reverse();
}
exports.backtrace = backtrace;
Backtrace from start and end node, and return the path.
(including both start and end nodes)
parameter: {Node}
parameter: {Node}
function biBacktrace(nodeA, nodeB) {
var pathA = backtrace(nodeA),
pathB = backtrace(nodeB);
return pathA.concat(pathB.reverse());
}
exports.biBacktrace = biBacktrace;
Compute the length of the path.
parameter: {Array.>} path The path
returns: {number} The length of the path
function pathLength(path) {
var i, sum = 0, a, b, dx, dy;
for (i = 1; i < path.length; ++i) {
a = path[i - 1];
b = path[i];
dx = a[0] - b[0];
dy = a[1] - b[1];
sum += Math.sqrt(dx * dx + dy * dy);
}
return sum;
}
exports.pathLength = pathLength;
Given the start and end coordinates, return all the coordinates lying
on the line formed by these coordinates, based on Bresenham's algorithm.
http://en.wikipedia.org/wiki/Bresenham's_line_algorithm#Simplification
parameter: {number} x0 Start x coordinate
parameter: {number} y0 Start y coordinate
parameter: {number} x1 End x coordinate
parameter: {number} y1 End y coordinate
returns: {Array.>} The coordinates on the line
function getLine(x0, y0, x1, y1) {
var abs = Math.abs,
line = [],
sx, sy, dx, dy, err, e2;
dx = abs(x1 - x0);
dy = abs(y1 - y0);
sx = (x0 < x1) ? 1 : -1;
sy = (y0 < y1) ? 1 : -1;
err = dx - dy;
while (true) {
line.push([x0, y0]);
if (x0 === x1 && y0 === y1) {
break;
}
e2 = 2 * err;
if (e2 > -dy) {
err = err - dy;
x0 = x0 + sx;
}
if (e2 < dx) {
err = err + dx;
y0 = y0 + sy;
}
}
return line;
}
exports.getLine = getLine;
Smoothen the give path.
The original path will not be modified; a new path will be returned.
parameter: {PF.Grid} grid
parameter: {Array.>} path The path
returns: {Array.>} Smoothened path
function smoothenPath(grid, path) {
var len = path.length,
x0 = path[0][0], // path start x
y0 = path[0][1], // path start y
x1 = path[len - 1][0], // path end x
y1 = path[len - 1][1], // path end y
sx, sy, // current start coordinate
ex, ey, // current end coordinate
lx, ly, // last valid end coordinate
newPath,
i, j, coord, line, testCoord, blocked;
sx = x0;
sy = y0;
lx = path[1][0];
ly = path[1][1];
newPath = [[sx, sy]];
for (i = 2; i < len; ++i) {
coord = path[i];
ex = coord[0];
ey = coord[1];
line = getLine(sx, sy, ex, ey);
blocked = false;
for (j = 1; j < line.length; ++j) {
testCoord = line[j];
if (!grid.isWalkableAt(testCoord[0], testCoord[1])) {
blocked = true;
newPath.push([lx, ly]);
sx = lx;
sy = ly;
break;
}
}
if (!blocked) {
lx = ex;
ly = ey;
}
}
newPath.push([x1, y1]);
return newPath;
}
exports.smoothenPath = smoothenPath;
});
require.define("/core/Heuristic.js", function (require, module, exports, __dirname, __filename) {
@namespace PF.Heuristic
@description A collection of heuristic functions.
module.exports = {
Manhattan distance.
parameter: {number} dx - Difference in x.
parameter: {number} dy - Difference in y.
returns: {number} dx + dy
manhattan: function(dx, dy) {
return dx + dy;
},
Euclidean distance.
parameter: {number} dx - Difference in x.
parameter: {number} dy - Difference in y.
returns: {number} sqrt(dx * dx + dy * dy)
euclidean: function(dx, dy) {
return Math.sqrt(dx * dx + dy * dy);
},
Chebyshev distance.
parameter: {number} dx - Difference in x.
parameter: {number} dy - Difference in y.
returns: {number} max(dx, dy)
chebyshev: function(dx, dy) {
return Math.max(dx, dy);
}
};
});
require.define("/finders/AStarFinder.js", function (require, module, exports, __dirname, __filename) {
var Heap = require('../core/Heap');
var Util = require('../core/Util');
var Heuristic = require('../core/Heuristic');
A* path-finder.
based upon github.com/bgrins/javascript-astar
@constructor
parameter: {object} opt
parameter: {boolean} opt.allowDiagonal Whether diagonal movement is allowed.
parameter: {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners.
parameter: {function} opt.heuristic Heuristic function to estimate the distance
(defaults to manhattan).
function AStarFinder(opt) {
opt = opt || {};
this.allowDiagonal = opt.allowDiagonal;
this.dontCrossCorners = opt.dontCrossCorners;
this.heuristic = opt.heuristic || Heuristic.manhattan;
}
Find and return the the path.
returns: {Array.<[number, number]>} The path, including both start and
end positions.
AStarFinder.prototype.findPath = function(startX, startY, endX, endY, grid) {
var openList = new Heap(function(nodeA, nodeB) {
return nodeA.f - nodeB.f;
}),
startNode = grid.getNodeAt(startX, startY),
endNode = grid.getNodeAt(endX, endY),
heuristic = this.heuristic,
allowDiagonal = this.allowDiagonal,
dontCrossCorners = this.dontCrossCorners,
abs = Math.abs, SQRT2 = Math.SQRT2,
node, neighbors, neighbor, i, l, x, y, ng;
// set the `g` and `f` value of the start node to be 0
startNode.g = 0;
startNode.f = 0;
// push the start node into the open list
openList.push(startNode);
startNode.opened = true;
// while the open list is not empty
while (!openList.empty()) {
// pop the position of node which has the minimum `f` value.
node = openList.pop();
node.closed = true;
// if reached the end position, construct the path and return it
if (node === endNode) {
return Util.backtrace(endNode);
}
// get neigbours of the current node
neighbors = grid.getNeighbors(node, allowDiagonal, dontCrossCorners);
for (i = 0, l = neighbors.length; i < l; ++i) {
neighbor = neighbors[i];
if (neighbor.closed) {
continue;
}
x = neighbor.x;
y = neighbor.y;
// get the distance between current node and the neighbor
// and calculate the next g score
ng = node.g + ((x - node.x === 0 || y - node.y === 0) ? 1 : SQRT2);
// check if the neighbor has not been inspected yet, or
// can be reached with smaller cost from the current node
if (!neighbor.opened || ng < neighbor.g) {
neighbor.g = ng;
neighbor.h = neighbor.h || heuristic(abs(x - endX), abs(y - endY));
neighbor.f = neighbor.g + neighbor.h;
neighbor.parent = node;
if (!neighbor.opened) {
openList.push(neighbor);
neighbor.opened = true;
} else {
// the neighbor can be reached with smaller cost.
// Since its f value has been updated, we have to
// update its position in the open list
openList.updateItem(neighbor);
}
}
} // end for each neighbor
} // end while not open list empty
// fail to find the path
return [];
};
module.exports = AStarFinder;
});
require.define("/finders/BestFirstFinder.js", function (require, module, exports, __dirname, __filename) {
var AStarFinder = require('./AStarFinder');
Best-First-Search path-finder.
@constructor
@extends AStarFinder
parameter: {object} opt
parameter: {boolean} opt.allowDiagonal Whether diagonal movement is allowed.
parameter: {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners.
parameter: {function} opt.heuristic Heuristic function to estimate the distance
(defaults to manhattan).
function BestFirstFinder(opt) {
AStarFinder.call(this, opt);
var orig = this.heuristic;
this.heuristic = function(dx, dy) {
return orig(dx, dy) * 1000000;
};
};
BestFirstFinder.prototype = new AStarFinder();
BestFirstFinder.prototype.constructor = BestFirstFinder;
module.exports = BestFirstFinder;
});
require.define("/finders/BreadthFirstFinder.js", function (require, module, exports, __dirname, __filename) {
var Util = require('../core/Util');
Breadth-First-Search path finder.
@constructor
parameter: {object} opt
parameter: {boolean} opt.allowDiagonal Whether diagonal movement is allowed.
parameter: {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners.
function BreadthFirstFinder(opt) {
opt = opt || {};
this.allowDiagonal = opt.allowDiagonal;
this.dontCrossCorners = opt.dontCrossCorners;
}
Find and return the the path.
returns: {Array.<[number, number]>} The path, including both start and
end positions.
BreadthFirstFinder.prototype.findPath = function(startX, startY, endX, endY, grid) {
var openList = [],
allowDiagonal = this.allowDiagonal,
dontCrossCorners = this.dontCrossCorners,
startNode = grid.getNodeAt(startX, startY),
endNode = grid.getNodeAt(endX, endY),
neighbors, neighbor, node, i, l;
// push the start pos into the queue
openList.push(startNode);
startNode.opened = true;
// while the queue is not empty
while (openList.length) {
// take the front node from the queue
node = openList.shift();
node.closed = true;
// reached the end position
if (node === endNode) {
return Util.backtrace(endNode);
}
neighbors = grid.getNeighbors(node, allowDiagonal, dontCrossCorners);
for (i = 0, l = neighbors.length; i < l; ++i) {
neighbor = neighbors[i];
// skip this neighbor if it has been inspected before
if (neighbor.closed || neighbor.opened) {
continue;
}
openList.push(neighbor);
neighbor.opened = true;
neighbor.parent = node;
}
}
// fail to find the path
return [];
};
module.exports = BreadthFirstFinder;
});
require.define("/finders/DijkstraFinder.js", function (require, module, exports, __dirname, __filename) {
var AStarFinder = require('./AStarFinder');
Dijkstra path-finder.
@constructor
@extends AStarFinder
parameter: {object} opt
parameter: {boolean} opt.allowDiagonal Whether diagonal movement is allowed.
parameter: {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners.
function DijkstraFinder(opt) {
AStarFinder.call(this, opt);
this.heuristic = function(dx, dy) {
return 0;
};
}
DijkstraFinder.prototype = new AStarFinder();
DijkstraFinder.prototype.constructor = DijkstraFinder;
module.exports = DijkstraFinder;
});
require.define("/finders/BiAStarFinder.js", function (require, module, exports, __dirname, __filename) {
var Heap = require('../core/Heap');
var Util = require('../core/Util');
var Heuristic = require('../core/Heuristic');
A* path-finder.
based upon github.com/bgrins/javascript-astar
@constructor
parameter: {object} opt
parameter: {boolean} opt.allowDiagonal Whether diagonal movement is allowed.
parameter: {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners.
parameter: {function} opt.heuristic Heuristic function to estimate the distance
(defaults to manhattan).
function BiAStarFinder(opt) {
opt = opt || {};
this.allowDiagonal = opt.allowDiagonal;
this.dontCrossCorners = opt.dontCrossCorners;
this.heuristic = opt.heuristic || Heuristic.manhattan;
}
Find and return the the path.
returns: {Array.<[number, number]>} The path, including both start and
end positions.
BiAStarFinder.prototype.findPath = function(startX, startY, endX, endY, grid) {
var cmp = function(nodeA, nodeB) {
return nodeA.f - nodeB.f;
},
startOpenList = new Heap(cmp),
endOpenList = new Heap(cmp),
startNode = grid.getNodeAt(startX, startY),
endNode = grid.getNodeAt(endX, endY),
heuristic = this.heuristic,
allowDiagonal = this.allowDiagonal,
dontCrossCorners = this.dontCrossCorners,
abs = Math.abs, SQRT2 = Math.SQRT2,
node, neighbors, neighbor, i, l, x, y, ng,
BY_START = 1, BY_END = 2;
// set the `g` and `f` value of the start node to be 0
// and push it into the start open list
startNode.g = 0;
startNode.f = 0;
startOpenList.push(startNode);
startNode.opened = BY_START;
// set the `g` and `f` value of the end node to be 0
// and push it into the open open list
endNode.g = 0;
endNode.f = 0;
endOpenList.push(endNode);
endNode.opened = BY_END;
// while both the open lists are not empty
while (!startOpenList.empty() && !endOpenList.empty()) {
// pop the position of start node which has the minimum `f` value.
node = startOpenList.pop();
node.closed = true;
// get neigbours of the current node
neighbors = grid.getNeighbors(node, allowDiagonal, dontCrossCorners);
for (i = 0, l = neighbors.length; i < l; ++i) {
neighbor = neighbors[i];
if (neighbor.closed) {
continue;
}
if (neighbor.opened === BY_END) {
return Util.biBacktrace(node, neighbor);
}
x = neighbor.x;
y = neighbor.y;
// get the distance between current node and the neighbor
// and calculate the next g score
ng = node.g + ((x - node.x === 0 || y - node.y === 0) ? 1 : SQRT2);
// check if the neighbor has not been inspected yet, or
// can be reached with smaller cost from the current node
if (!neighbor.opened || ng < neighbor.g) {
neighbor.g = ng;
neighbor.h = neighbor.h || heuristic(abs(x - endX), abs(y - endY));
neighbor.f = neighbor.g + neighbor.h;
neighbor.parent = node;
if (!neighbor.opened) {
startOpenList.push(neighbor);
neighbor.opened = BY_START;
} else {
// the neighbor can be reached with smaller cost.
// Since its f value has been updated, we have to
// update its position in the open list
startOpenList.updateItem(neighbor);
}
}
} // end for each neighbor
// pop the position of end node which has the minimum `f` value.
node = endOpenList.pop();
node.closed = true;
// get neigbours of the current node
neighbors = grid.getNeighbors(node, allowDiagonal, dontCrossCorners);
for (i = 0, l = neighbors.length; i < l; ++i) {
neighbor = neighbors[i];
if (neighbor.closed) {
continue;
}
if (neighbor.opened === BY_START) {
return Util.biBacktrace(neighbor, node);
}
x = neighbor.x;
y = neighbor.y;
// get the distance between current node and the neighbor
// and calculate the next g score
ng = node.g + ((x - node.x === 0 || y - node.y === 0) ? 1 : SQRT2);
// check if the neighbor has not been inspected yet, or
// can be reached with smaller cost from the current node
if (!neighbor.opened || ng < neighbor.g) {
neighbor.g = ng;
neighbor.h = neighbor.h || heuristic(abs(x - startX), abs(y - startY));
neighbor.f = neighbor.g + neighbor.h;
neighbor.parent = node;
if (!neighbor.opened) {
endOpenList.push(neighbor);
neighbor.opened = BY_END;
} else {
// the neighbor can be reached with smaller cost.
// Since its f value has been updated, we have to
// update its position in the open list
endOpenList.updateItem(neighbor);
}
}
} // end for each neighbor
} // end while not open list empty
// fail to find the path
return [];
};
module.exports = BiAStarFinder;
});
require.define("/finders/BiBestFirstFinder.js", function (require, module, exports, __dirname, __filename) {
var BiAStarFinder = require('./BiAStarFinder');
Bi-direcitional Best-First-Search path-finder.
@constructor
@extends BiAStarFinder
parameter: {object} opt
parameter: {boolean} opt.allowDiagonal Whether diagonal movement is allowed.
parameter: {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners.
parameter: {function} opt.heuristic Heuristic function to estimate the distance
(defaults to manhattan).
function BiBestFirstFinder(opt) {
BiAStarFinder.call(this, opt);
var orig = this.heuristic;
this.heuristic = function(dx, dy) {
return orig(dx, dy) * 1000000;
};
}
BiBestFirstFinder.prototype = new BiAStarFinder();
BiBestFirstFinder.prototype.constructor = BiBestFirstFinder;
module.exports = BiBestFirstFinder;
});
require.define("/finders/BiBreadthFirstFinder.js", function (require, module, exports, __dirname, __filename) {
var Util = require('../core/Util');
Bi-directional Breadth-First-Search path finder.
@constructor
parameter: {object} opt
parameter: {boolean} opt.allowDiagonal Whether diagonal movement is allowed.
parameter: {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners.
function BiBreadthFirstFinder(opt) {
opt = opt || {};
this.allowDiagonal = opt.allowDiagonal;
this.dontCrossCorners = opt.dontCrossCorners;
}
Find and return the the path.
returns: {Array.<[number, number]>} The path, including both start and
end positions.
BiBreadthFirstFinder.prototype.findPath = function(startX, startY, endX, endY, grid) {
var startNode = grid.getNodeAt(startX, startY),
endNode = grid.getNodeAt(endX, endY),
startOpenList = [], endOpenList = [],
neighbors, neighbor, node,
allowDiagonal = this.allowDiagonal,
dontCrossCorners = this.dontCrossCorners,
BY_START = 0, BY_END = 1,
i, l;
// push the start and end nodes into the queues
startOpenList.push(startNode);
startNode.opened = true;
startNode.by = BY_START;
endOpenList.push(endNode);
endNode.opened = true;
endNode.by = BY_END;
// while both the queues are not empty
while (startOpenList.length && endOpenList.length) {
// expand start open list
node = startOpenList.shift();
node.closed = true;
neighbors = grid.getNeighbors(node, allowDiagonal, dontCrossCorners);
for (i = 0, l = neighbors.length; i < l; ++i) {
neighbor = neighbors[i];
if (neighbor.closed) {
continue;
}
if (neighbor.opened) {
// if this node has been inspected by the reversed search,
// then a path is found.
if (neighbor.by === BY_END) {
return Util.biBacktrace(node, neighbor);
}
continue;
}
startOpenList.push(neighbor);
neighbor.parent = node;
neighbor.opened = true;
neighbor.by = BY_START;
}
// expand end open list
node = endOpenList.shift();
node.closed = true;
neighbors = grid.getNeighbors(node, allowDiagonal, dontCrossCorners);
for (i = 0, l = neighbors.length; i < l; ++i) {
neighbor = neighbors[i];
if (neighbor.closed) {
continue;
}
if (neighbor.opened) {
if (neighbor.by === BY_START) {
return Util.biBacktrace(neighbor, node);
}
continue;
}
endOpenList.push(neighbor);
neighbor.parent = node;
neighbor.opened = true;
neighbor.by = BY_END;
}
}
// fail to find the path
return [];
};
module.exports = BiBreadthFirstFinder;
});
require.define("/finders/BiDijkstraFinder.js", function (require, module, exports, __dirname, __filename) {
var BiAStarFinder = require('./BiAStarFinder');
Bi-directional Dijkstra path-finder.
@constructor
@extends BiAStarFinder
parameter: {object} opt
parameter: {boolean} opt.allowDiagonal Whether diagonal movement is allowed.
parameter: {boolean} opt.dontCrossCorners Disallow diagonal movement touching block corners.
function BiDijkstraFinder(opt) {
BiAStarFinder.call(this, opt);
this.heuristic = function(dx, dy) {
return 0;
};
}
BiDijkstraFinder.prototype = new BiAStarFinder();
BiDijkstraFinder.prototype.constructor = BiDijkstraFinder;
module.exports = BiDijkstraFinder;
});
require.define("/finders/JumpPointFinder.js", function (require, module, exports, __dirname, __filename) {
author: aniero / github.com/aniero
var Heap = require('../core/Heap');
var Util = require('../core/Util');
var Heuristic = require('../core/Heuristic');
Path finder using the Jump Point Search algorithm
parameter: {object} opt
parameter: {function} opt.heuristic Heuristic function to estimate the distance
(defaults to manhattan).
function JumpPointFinder(opt) {
opt = opt || {};
this.heuristic = opt.heuristic || Heuristic.manhattan;
}
Find and return the the path.
returns: {Array.<[number, number]>} The path, including both start and
end positions.
JumpPointFinder.prototype.findPath = function(startX, startY, endX, endY, grid) {
var openList = this.openList = new Heap(function(nodeA, nodeB) {
return nodeA.f - nodeB.f;
}),
startNode = this.startNode = grid.getNodeAt(startX, startY),
endNode = this.endNode = grid.getNodeAt(endX, endY), node;
this.grid = grid;
// set the `g` and `f` value of the start node to be 0
startNode.g = 0;
startNode.f = 0;
// push the start node into the open list
openList.push(startNode);
startNode.opened = true;
// while the open list is not empty
while (!openList.empty()) {
// pop the position of node which has the minimum `f` value.
node = openList.pop();
node.closed = true;
if (node === endNode) {
return Util.backtrace(endNode);
}
this._identifySuccessors(node);
}
// fail to find the path
return [];
};
Identify successors for the given node. Runs a jump point search in the
direction of each available neighbor, adding any points found to the open
list.
@protected
JumpPointFinder.prototype._identifySuccessors = function(node) {
var grid = this.grid,
heuristic = this.heuristic,
openList = this.openList,
endX = this.endNode.x,
endY = this.endNode.y,
neighbors, neighbor,
jumpPoint, i, l,
x = node.x, y = node.y,
jx, jy, dx, dy, d, ng, jumpNode,
abs = Math.abs, max = Math.max;
neighbors = this._findNeighbors(node);
for(i = 0, l = neighbors.length; i < l; ++i) {
neighbor = neighbors[i];
jumpPoint = this._jump(neighbor[0], neighbor[1], x, y);
if (jumpPoint) {
jx = jumpPoint[0];
jy = jumpPoint[1];
jumpNode = grid.getNodeAt(jx, jy);
if (jumpNode.closed) {
continue;
}
// include distance, as parent may not be immediately adjacent:
d = Heuristic.euclidean(abs(jx - x), abs(jy - y));
ng = node.g + d; // next `g` value
if (!jumpNode.opened || ng < jumpNode.g) {
jumpNode.g = ng;
jumpNode.h = jumpNode.h || heuristic(abs(jx - endX), abs(jy - endY));
jumpNode.f = jumpNode.g + jumpNode.h;
jumpNode.parent = node;
if (!jumpNode.opened) {
openList.push(jumpNode);
jumpNode.opened = true;
} else {
openList.updateItem(jumpNode);
}
}
}
}
};
Search recursively in the direction (parent -> child), stopping only when a
jump point is found.
@protected
returns: {Array.<[number, number]>} The x, y coordinate of the jump point
found, or null if not found
JumpPointFinder.prototype._jump = function(x, y, px, py) {
var grid = this.grid,
dx = x - px, dy = y - py, jx, jy;
if (!grid.isWalkableAt(x, y)) {
return null;
}
else if (grid.getNodeAt(x, y) === this.endNode) {
return [x, y];
}
// check for forced neighbors
// along the diagonal
if (dx !== 0 && dy !== 0) {
if ((grid.isWalkableAt(x - dx, y + dy) && !grid.isWalkableAt(x - dx, y)) ||
(grid.isWalkableAt(x + dx, y - dy) && !grid.isWalkableAt(x, y - dy))) {
return [x, y];
}
}
// horizontally/vertically
else {
if( dx !== 0 ) { // moving along x
if((grid.isWalkableAt(x + dx, y + 1) && !grid.isWalkableAt(x, y + 1)) ||
(grid.isWalkableAt(x + dx, y - 1) && !grid.isWalkableAt(x, y - 1))) {
return [x, y];
}
}
else {
if((grid.isWalkableAt(x + 1, y + dy) && !grid.isWalkableAt(x + 1, y)) ||
(grid.isWalkableAt(x - 1, y + dy) && !grid.isWalkableAt(x - 1, y))) {
return [x, y];
}
}
}
// when moving diagonally, must check for vertical/horizontal jump points
if (dx !== 0 && dy !== 0) {
jx = this._jump(x + dx, y, x, y);
jy = this._jump(x, y + dy, x, y);
if (jx || jy) {
return [x, y];
}
}
// moving diagonally, must make sure one of the vertical/horizontal
// neighbors is open to allow the path
if (grid.isWalkableAt(x + dx, y) || grid.isWalkableAt(x, y + dy)) {
return this._jump(x + dx, y + dy, x, y);
} else {
return null;
}
};
Find the neighbors for the given node. If the node has a parent,
prune the neighbors based on the jump point search algorithm, otherwise
return all available neighbors.
returns: {Array.<[number, number]>} The neighbors found.
JumpPointFinder.prototype._findNeighbors = function(node) {
var parent = node.parent,
x = node.x, y = node.y,
grid = this.grid,
px, py, nx, ny, dx, dy,
neighbors = [], neighborNodes, neighborNode, i, l;
// directed pruning: can ignore most neighbors, unless forced.
if (parent) {
px = parent.x;
py = parent.y;
// get the normalized direction of travel
dx = (x - px) / Math.max(Math.abs(x - px), 1);
dy = (y - py) / Math.max(Math.abs(y - py), 1);
// search diagonally
if (dx !== 0 && dy !== 0) {
if (grid.isWalkableAt(x, y + dy)) {
neighbors.push([x, y + dy]);
}
if (grid.isWalkableAt(x + dx, y)) {
neighbors.push([x + dx, y]);
}
if (grid.isWalkableAt(x, y + dy) || grid.isWalkableAt(x + dx, y)) {
neighbors.push([x + dx, y + dy]);
}
if (!grid.isWalkableAt(x - dx, y) && grid.isWalkableAt(x, y + dy)) {
neighbors.push([x - dx, y + dy]);
}
if (!grid.isWalkableAt(x, y - dy) && grid.isWalkableAt(x + dx, y)) {
neighbors.push([x + dx, y - dy]);
}
}
// search horizontally/vertically
else {
if(dx === 0) {
if (grid.isWalkableAt(x, y + dy)) {
if (grid.isWalkableAt(x, y + dy)) {
neighbors.push([x, y + dy]);
}
if (!grid.isWalkableAt(x + 1, y)) {
neighbors.push([x + 1, y + dy]);
}
if (!grid.isWalkableAt(x - 1, y)) {
neighbors.push([x - 1, y + dy]);
}
}
}
else {
if (grid.isWalkableAt(x + dx, y)) {
if (grid.isWalkableAt(x + dx, y)) {
neighbors.push([x + dx, y]);
}
if (!grid.isWalkableAt(x, y + 1)) {
neighbors.push([x + dx, y + 1]);
}
if (!grid.isWalkableAt(x, y - 1)) {
neighbors.push([x + dx, y - 1]);
}
}
}
}
}
// return all neighbors
else {
neighborNodes = grid.getNeighbors(node, true);
for (i = 0, l = neighborNodes.length; i < l; ++i) {
neighborNode = neighborNodes[i];
neighbors.push([neighborNode.x, neighborNode.y]);
}
}
return neighbors;
};
module.exports = JumpPointFinder;
});
require.define("/PathFinding.js", function (require, module, exports, __dirname, __filename) {
module.exports = {
'Node' : require('./core/Node'),
'Grid' : require('./core/Grid'),
'Heap' : require('./core/Heap'),
'Util' : require('./core/Util'),
'Heuristic' : require('./core/Heuristic'),
'AStarFinder' : require('./finders/AStarFinder'),
'BestFirstFinder' : require('./finders/BestFirstFinder'),
'BreadthFirstFinder' : require('./finders/BreadthFirstFinder'),
'DijkstraFinder' : require('./finders/DijkstraFinder'),
'BiAStarFinder' : require('./finders/BiAStarFinder'),
'BiBestFirstFinder' : require('./finders/BiBestFirstFinder'),
'BiBreadthFirstFinder' : require('./finders/BiBreadthFirstFinder'),
'BiDijkstraFinder' : require('./finders/BiDijkstraFinder'),
'JumpPointFinder' : require('./finders/JumpPointFinder')
};
});
require("/PathFinding.js");
return require("/PathFinding");})();
(C) Æliens
04/09/2009
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.