diff --git a/astar.js b/astar.js
index ece7d08..90dc740 100644
--- a/astar.js
+++ b/astar.js
@@ -56,13 +56,11 @@ var astar = {
astar.init(graph);
options = options || {};
- var heuristic = options.heuristic || astar.heuristics.manhattan;
- var closest = options.closest || false;
+ var heuristic = options.heuristic || astar.heuristics.manhattan,
+ closest = options.closest || false;
- var openHeap = astar.heap();
-
- // set the start node to be the closest if required
- var closestNode = start;
+ var openHeap = astar.heap(),
+ closestNode = start; // set the start node to be the closest if required
start.h = heuristic(start, end);
@@ -84,7 +82,7 @@ var astar = {
// Find all neighbors for the current node.
var neighbors = graph.neighbors(currentNode);
- for(var i=0, il = neighbors.length; i < il; i++) {
+ for(var i = 0, il = neighbors.length; i < il; ++i) {
var neighbor = neighbors[i];
if(neighbor.closed || neighbor.isWall()) {
@@ -94,8 +92,8 @@ var astar = {
// The g score is the shortest distance from start to current node.
// We need to check if the path we have arrived at this neighbor is the shortest one we have seen yet.
- var gScore = currentNode.g + neighbor.getCost(currentNode);
- var beenVisited = neighbor.visited;
+ var gScore = currentNode.g + neighbor.getCost(currentNode),
+ beenVisited = neighbor.visited;
if(!beenVisited || gScore < neighbor.g) {
@@ -136,15 +134,15 @@ var astar = {
// See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
heuristics: {
manhattan: function(pos0, pos1) {
- var d1 = Math.abs (pos1.x - pos0.x);
- var d2 = Math.abs (pos1.y - pos0.y);
+ var d1 = Math.abs(pos1.x - pos0.x);
+ var d2 = Math.abs(pos1.y - pos0.y);
return d1 + d2;
},
diagonal: function(pos0, pos1) {
var D = 1;
var D2 = Math.sqrt(2);
- var d1 = Math.abs (pos1.x - pos0.x);
- var d2 = Math.abs (pos1.y - pos0.y);
+ var d1 = Math.abs(pos1.x - pos0.x);
+ var d2 = Math.abs(pos1.y - pos0.y);
return (D * (d1 + d2)) + ((D2 - (2 * D)) * Math.min(d1, d2));
}
}
@@ -322,7 +320,6 @@ BinaryHeap.prototype = {
// Update 'n' to continue at the new position.
n = parentN;
}
-
// Found a parent that is less, no need to sink any further.
else {
break;
@@ -337,11 +334,11 @@ BinaryHeap.prototype = {
while(true) {
// Compute the indices of the child elements.
- var child2N = (n + 1) << 1, child1N = child2N - 1;
- // This is used to store the new position of the element,
- // if any.
- var swap = null;
- var child1Score;
+ var child2N = (n + 1) << 1,
+ child1N = child2N - 1;
+ // This is used to store the new position of the element, if any.
+ var swap = null,
+ child1Score;
// If the first child exists (is inside the array)...
if (child1N < length) {
// Look it up and compute its score.
@@ -369,7 +366,6 @@ BinaryHeap.prototype = {
this.content[swap] = element;
n = swap;
}
-
// Otherwise, we are done.
else {
break;
diff --git a/benchmark/benchmark.js b/benchmark/benchmark.js
index 8daf24d..5bb1359 100644
--- a/benchmark/benchmark.js
+++ b/benchmark/benchmark.js
@@ -6,18 +6,18 @@ $(function() {
}
running = true;
- var graph = new Graph(grid);
- var start = graph.grid[0][0];
- var end = graph.grid[140][140];
- var results = [];
- var times = 0;
-
+ var graph = new Graph(grid),
+ start = graph.grid[0][0],
+ end = graph.grid[140][140],
+ results = [],
+ times = 0;
+
for (var i = 0; i < 1000; i++) {
- var startTime = performance ? performance.now() : new Date().getTime();
- var result = astar.search(graph, start, end);
- var endTime = performance ? performance.now() : new Date().getTime();
+ var startTime = performance ? performance.now() : new Date().getTime(),
+ result = astar.search(graph, start, end),
+ endTime = performance ? performance.now() : new Date().getTime();
times = times + (endTime - startTime);
-
+
results.push(
'
Found path with ' + result.length + ' steps. ' +
'Took ' + (endTime - startTime).toFixed(2) + ' milliseconds.'
diff --git a/demo/demo.js b/demo/demo.js
index bf0df4a..968639c 100644
--- a/demo/demo.js
+++ b/demo/demo.js
@@ -1,52 +1,47 @@
-/* demo.js http://github.com/bgrins/javascript-astar
- MIT License
+/* demo.js http://github.com/bgrins/javascript-astar
+ MIT License
- Set up the demo page for the A* Search
+ Set up the demo page for the A* Search
*/
window.log = function(){
- if(this.console){
- console.log( Array.prototype.slice.call(arguments) );
- }
+ if(this.console){
+ console.log( Array.prototype.slice.call(arguments) );
+ }
};
-var WALL = 0;
-var OPEN = 1;
+var WALL = 0,
+ OPEN = 1;
var generateRandom = function (width, height, wallFrequency) {
-
- var nodes = [];
-
- for (var x=0; x < width; x++) {
- var nodeRow = [];
- var gridRow = [];
-
- for(var y=0; y < height; y++) {
-
- var isWall = Math.floor(Math.random()*(1/wallFrequency));
- if(isWall == 0) {
- nodeRow.push(WALL);
- }
- else {
- nodeRow.push(OPEN);
- }
- }
- nodes.push(nodeRow);
+ var nodes = [];
+ for (var x = 0; x < width; x++) {
+ var nodeRow = [],
+ gridRow = [];
+
+ for (var y = 0; y < height; y++) {
+ var isWall = Math.floor(Math.random()*(1/wallFrequency));
+ if (isWall === 0) {
+ nodeRow.push(WALL);
+ }
+ else {
+ nodeRow.push(OPEN);
+ }
+ }
+ nodes.push(nodeRow);
}
-
return new Graph(nodes);
};
$(function() {
- var $grid = $("#search_grid");
- var $selectWallFrequency = $("#selectWallFrequency");
- var $selectGridSize = $("#selectGridSize");
- var $checkDebug = $("#checkDebug");
- var $searchDiagonal = $("#searchDiagonal");
- var $checkClosest = $("#checkClosest");
-
+ var $grid = $("#search_grid"),
+ $selectWallFrequency = $("#selectWallFrequency"),
+ $selectGridSize = $("#selectGridSize"),
+ $checkDebug = $("#checkDebug"),
+ $searchDiagonal = $("#searchDiagonal"),
+ $checkClosest = $("#checkClosest");
var opts = {
wallFrequency: $selectWallFrequency.val(),
@@ -59,7 +54,7 @@ $(function() {
var grid = new GraphSearch($grid, opts, astar.search);
$("#btnGenerate").click(function() {
- grid.initialize();
+ grid.initialize();
});
$selectWallFrequency.change(function() {
@@ -101,119 +96,117 @@ var css = { start: "start", finish: "finish", wall: "wall", active: "active" };
function GraphSearch($graph, options, implementation) {
this.$graph = $graph;
this.search = implementation;
- this.opts = $.extend({wallFrequency:.1, debug:true, gridSize:10}, options);
+ this.opts = $.extend({wallFrequency:0.1, debug:true, gridSize:10}, options);
this.initialize();
}
GraphSearch.prototype.setOption = function(opt) {
this.opts = $.extend(this.opts, opt);
- if(opt["debug"]||opt["debug"]==false) {
- this.drawDebugInfo(opt["debug"]);
- }
+ this.drawDebugInfo();
};
GraphSearch.prototype.initialize = function() {
-
- var self = this;
- this.grid = [];
- var nodes = [];
- var $graph = this.$graph;
-
- $graph.empty();
-
- var cellWidth = ($graph.width()/this.opts.gridSize)-2; // -2 for border
- var cellHeight = ($graph.height()/this.opts.gridSize)-2;
- var $cellTemplate = $("").addClass("grid_item").width(cellWidth).height(cellHeight);
- var startSet = false;
-
- for(var x=0;x");
-
- var nodeRow = [];
- var gridRow = [];
-
- for(var y=0;y").addClass("grid_item").width(cellWidth).height(cellHeight),
+ startSet = false;
+
+ for(var x = 0; x < this.opts.gridSize; x++) {
+ var $row = $(""),
+ nodeRow = [],
+ gridRow = [];
+
+ for(var y = 0; y < this.opts.gridSize; y++) {
+ var id = "cell_"+x+"_"+y,
+ $cell = $cellTemplate.clone();
+ $cell.attr("id", id).attr("x", x).attr("y", y);
+ $row.append($cell);
+ gridRow.push($cell);
+
+ var isWall = Math.floor(Math.random()*(1/self.opts.wallFrequency));
+ if(isWall === 0) {
+ nodeRow.push(WALL);
+ $cell.addClass(css.wall);
+ }
+ else {
var cell_weight = ($("#generateWeights").prop("checked") ? (Math.floor(Math.random() * 3)) * 2 + 1 : 1);
- nodeRow.push(cell_weight);
- $cell.addClass('weight' + cell_weight);
- if ($("#displayWeights").prop("checked")) {$cell.html(cell_weight)};
- if (!startSet) {
- $cell.addClass(css.start);
- startSet = true;
- }
- }
- }
- $graph.append($row);
-
- this.grid.push(gridRow);
- nodes.push(nodeRow);
+ nodeRow.push(cell_weight);
+ $cell.addClass('weight' + cell_weight);
+ if ($("#displayWeights").prop("checked")) {
+ $cell.html(cell_weight);
+ }
+ if (!startSet) {
+ $cell.addClass(css.start);
+ startSet = true;
+ }
+ }
+ }
+ $graph.append($row);
+
+ this.grid.push(gridRow);
+ nodes.push(nodeRow);
}
this.graph = new Graph(nodes);
// bind cell event, set start/wall positions
this.$cells = $graph.find(".grid_item");
- this.$cells.click(function() { self.cellClicked($(this)) });
+ this.$cells.click(function() {
+ self.cellClicked($(this));
+ });
};
GraphSearch.prototype.cellClicked = function($end) {
var end = this.nodeFromElement($end);
- if($end.hasClass(css.wall) || $end.hasClass(css.start)) {
- log("clicked on wall or start...", $end);
- return;
- }
+ if($end.hasClass(css.wall) || $end.hasClass(css.start)) {
+ log("clicked on wall or start...", $end);
+ return;
+ }
- this.$cells.removeClass(css.finish);
- $end.addClass("finish");
- var $start = this.$cells.filter("." + css.start);
- var start = this.nodeFromElement($start);
+ this.$cells.removeClass(css.finish);
+ $end.addClass("finish");
+ var $start = this.$cells.filter("." + css.start),
+ start = this.nodeFromElement($start);
+
+ var sTime = performance ? performance.now() : new Date().getTime();
- var sTime = performance ? performance.now() : new Date().getTime();
var path = this.search(this.graph, start, end, {
closest: this.opts.closest
});
- var fTime = performance ? performance.now() : new Date().getTime();
- var duration = (fTime-sTime).toFixed(2);
-
- if(!path || path.length == 0) {
- $("#message").text("couldn't find a path (" + duration + "ms)");
- this.animateNoPath();
- }
- else {
- $("#message").text("search took " + duration + "ms.");
- if(this.opts.debug) {
- this.drawDebugInfo(this.opts.debug);
- }
- this.animatePath(path);
- }
+ var fTime = performance ? performance.now() : new Date().getTime(),
+ duration = (fTime-sTime).toFixed(2);
+
+ if(path.length === 0) {
+ $("#message").text("couldn't find a path (" + duration + "ms)");
+ this.animateNoPath();
+ }
+ else {
+ $("#message").text("search took " + duration + "ms.");
+ this.drawDebugInfo();
+ this.animatePath(path);
+ }
};
-GraphSearch.prototype.drawDebugInfo = function(show) {
+GraphSearch.prototype.drawDebugInfo = function() {
this.$cells.html(" ");
var that = this;
- if(show) {
- that.$cells.each(function(i) {
- var node = that.nodeFromElement($(this));
- var debug = false;
+ if(this.opts.debug) {
+ that.$cells.each(function(i) {
+ var node = that.nodeFromElement($(this)),
+ debug = false;
if (node.visited) {
debug = "F: " + node.f + "
G: " + node.g + "
H: " + node.h;
}
- if (debug) {
- $(this).html(debug);
- }
- });
-
+ if (debug) {
+ $(this).html(debug);
+ }
+ });
}
};
GraphSearch.prototype.nodeFromElement = function($cell) {
@@ -222,47 +215,51 @@ GraphSearch.prototype.nodeFromElement = function($cell) {
GraphSearch.prototype.animateNoPath = function() {
var $graph = this.$graph;
var jiggle = function(lim, i) {
- if(i>=lim) { $graph.css("top", 0).css("left", 0); return; }
- if(!i) i=0;
- i++;
- $graph.css("top", Math.random()*6).css("left", Math.random()*6);
- setTimeout( function() { jiggle(lim, i) }, 5 );
+ if(i>=lim) { $graph.css("top", 0).css("left", 0); return; }
+ if(!i) i=0;
+ i++;
+ $graph.css("top", Math.random()*6).css("left", Math.random()*6);
+ setTimeout(function() {
+ jiggle(lim, i);
+ }, 5);
};
jiggle(15);
};
GraphSearch.prototype.animatePath = function(path) {
- var grid = this.grid;
- var timeout = 1000 / grid.length;
- var elementFromNode = function(node) {
- return grid[node.x][node.y];
- };
+ var grid = this.grid,
+ timeout = 1000 / grid.length,
+ elementFromNode = function(node) {
+ return grid[node.x][node.y];
+ };
var self = this;
// will add start class if final
var removeClass = function(path, i) {
- if(i>=path.length){ // finished removing path, set start positions
+ if(i >= path.length) { // finished removing path, set start positions
return setStartClass(path, i);
}
elementFromNode(path[i]).removeClass(css.active);
- setTimeout( function() { removeClass(path, i+1) }, timeout*path[i].cost);
- }
- var setStartClass = function(path, i){
- if(i === path.length){
+ setTimeout(function() {
+ removeClass(path, i+1);
+ }, timeout*path[i].getCost());
+ };
+ var setStartClass = function(path, i) {
+ if(i === path.length) {
self.$graph.find("." + css.start).removeClass(css.start);
elementFromNode(path[i-1]).addClass(css.start);
}
- }
- var addClass = function(path, i) {
- if(i>=path.length) { // Finished showing path, now remove
- return removeClass(path, 0);
- }
- elementFromNode(path[i]).addClass(css.active);
- setTimeout( function() { addClass(path, i+1) }, timeout*path[i].cost);
+ };
+ var addClass = function(path, i) {
+ if(i >= path.length) { // Finished showing path, now remove
+ return removeClass(path, 0);
+ }
+ elementFromNode(path[i]).addClass(css.active);
+ setTimeout(function() {
+ addClass(path, i+1);
+ }, timeout*path[i].getCost());
};
- addClass(path, 0)
+ addClass(path, 0);
this.$graph.find("." + css.start).removeClass(css.start);
this.$graph.find("." + css.finish).removeClass(css.finish).addClass(css.start);
};
-
-
diff --git a/test/tests.js b/test/tests.js
index ab2d822..0e3a0ba 100644
--- a/test/tests.js
+++ b/test/tests.js
@@ -92,11 +92,11 @@ function runSearch(graph, start, end, options) {
if (!(graph instanceof Graph)) {
graph = new Graph(graph);
}
- var start = graph.grid[start[0]][start[1]];
- var end = graph.grid[end[0]][end[1]];
- var sTime = new Date();
- var result = astar.search(graph, start, end, options);
- var eTime = new Date();
+ start = graph.grid[start[0]][start[1]];
+ end = graph.grid[end[0]][end[1]];
+ var sTime = new Date(),
+ result = astar.search(graph, start, end, options),
+ eTime = new Date();
return {
result: result,
text: pathToString(result),
@@ -132,7 +132,9 @@ test( "GPS Pathfinding", function() {
function CityGraph(data, links) {
this.nodes = [];
- var cities = this.cities = {};
+ this.links = links;
+ this.cities = {};
+
for (var i = 0; i < data.length; ++i) {
var city = data[i],
obj = new CityNode(city.name, city.lat, city.lng);
@@ -141,11 +143,8 @@ test( "GPS Pathfinding", function() {
this.nodes.push(obj);
}
- cities[obj.name] = obj;
+ this.cities[obj.name] = obj;
}
-
- this.cities = cities;
- this.links = links;
}
CityGraph.prototype.neighbors = function (node) {