From 62b1f38c98efdb8214c2bbc5dd07aad79a5301a7 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Wed, 1 Mar 2017 22:29:38 -0500 Subject: [PATCH 1/7] Added modes and attached to export --- astar.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/astar.js b/astar.js index 8a25115..cacb3f3 100644 --- a/astar.js +++ b/astar.js @@ -17,6 +17,13 @@ } })(function() { +var DIAGONAL_MOVEMENT = { + ALWAYS: 1, + NEVER: 2, + ONE_OBSTACLE: 3, + NO_OBSTACLES: 4 +}; + function pathTo(node) { var curr = node; var path = []; @@ -34,6 +41,8 @@ function getHeap() { } var astar = { + DIAGONAL_MOVEMENT: DIAGONAL_MOVEMENT, + /** * Perform an A* Search on a graph given a start and end node. * @param {Graph} graph @@ -401,4 +410,4 @@ return { Graph: Graph }; -}); \ No newline at end of file +}); From d07825a0146afab30ec52bcd4d7f3ac6423be5d1 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Wed, 1 Mar 2017 22:30:27 -0500 Subject: [PATCH 2/7] Added legacy option check with deprecation warning --- astar.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/astar.js b/astar.js index cacb3f3..8c1f585 100644 --- a/astar.js +++ b/astar.js @@ -166,7 +166,19 @@ var astar = { function Graph(gridIn, options) { options = options || {}; this.nodes = []; - this.diagonal = !!options.diagonal; + + if (typeof options.diagonal === 'Boolean') { + console.warn('options.diagonal was specified as a Boolean. This functionality will be removed in an upcoming version. Use DIAGONAL_MODE instead.'); + + if (options.diagonal) { + this.diagonal = DIAGONAL_MOVEMENT.ALWAYS; + } else { + this.diagonal = DIAGONAL_MOVEMENT.NEVER; + } + } else { + this.diagonal = options.diagonal; + } + this.grid = []; for (var x = 0; x < gridIn.length; x++) { this.grid[x] = []; From 33bc5e354c5f69fd0bdd62e4e67683bdc06cb368 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Wed, 1 Mar 2017 22:31:44 -0500 Subject: [PATCH 3/7] Added diagonal mode processing --- astar.js | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/astar.js b/astar.js index 8c1f585..050b873 100644 --- a/astar.js +++ b/astar.js @@ -236,7 +236,12 @@ Graph.prototype.neighbors = function(node) { ret.push(grid[x][y + 1]); } - if (this.diagonal) { + if (this.diagonal === DIAGONAL_MOVEMENT.NEVER) { + return ret; + } + + if (this.diagonal === DIAGONAL_MOVEMENT.ALWAYS) { + // Southwest if (grid[x - 1] && grid[x - 1][y - 1]) { ret.push(grid[x - 1][y - 1]); @@ -258,6 +263,52 @@ Graph.prototype.neighbors = function(node) { } } + if (this.diagonal === DIAGONAL_MOVEMENT.ONE_OBSTACLE) { + + // Southwest + if (grid[x-1] && grid[x-1][y-1] && (grid[x-1][y].weight || grid[x][y-1].weight)) { + ret.push(grid[x-1][y-1]); + } + + // Southeast + if (grid[x+1] && grid[x+1][y-1] && (grid[x+1][y].weight || grid[x][y-1].weight)) { + ret.push(grid[x+1][y-1]); + } + + // Northwest + if (grid[x-1] && grid[x-1][y+1] && (grid[x-1][y].weight || grid[x][y+1].weight)) { + ret.push(grid[x-1][y+1]); + } + + // Northeast + if (grid[x+1] && grid[x+1][y+1] && (grid[x+1][y].weight || grid[x][y+1].weight)) { + ret.push(grid[x+1][y+1]); + } + } + + if (this.diagonal === DIAGONAL_MOVEMENT.NO_OBSTACLES) { + + // Southwest + if (grid[x-1] && grid[x-1][y-1] && grid[x-1][y].weight && grid[x][y-1].weight) { + ret.push(grid[x-1][y-1]); + } + + // Southeast + if (grid[x+1] && grid[x+1][y-1] && grid[x+1][y].weight && grid[x][y-1].weight) { + ret.push(grid[x+1][y-1]); + } + + // Northwest + if (grid[x-1] && grid[x-1][y+1] && grid[x-1][y].weight && grid[x][y+1].weight) { + ret.push(grid[x-1][y+1]); + } + + // Northeast + if (grid[x+1] && grid[x+1][y+1] && grid[x+1][y].weight && grid[x][y+1].weight) { + ret.push(grid[x+1][y+1]); + } + } + return ret; }; From c3d35e836698f1d5f6f7744486fe9e330a9a829b Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Wed, 1 Mar 2017 22:51:12 -0500 Subject: [PATCH 4/7] Fixed typo --- astar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/astar.js b/astar.js index 050b873..e4c2a3f 100644 --- a/astar.js +++ b/astar.js @@ -167,7 +167,7 @@ function Graph(gridIn, options) { options = options || {}; this.nodes = []; - if (typeof options.diagonal === 'Boolean') { + if (typeof options.diagonal === 'boolean') { console.warn('options.diagonal was specified as a Boolean. This functionality will be removed in an upcoming version. Use DIAGONAL_MODE instead.'); if (options.diagonal) { From 1122181b2132e02ff3d00e4c7b16f333416fe410 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Sat, 4 Mar 2017 19:35:44 -0500 Subject: [PATCH 5/7] Added tests for diagonal mode --- test/tests.js | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/test/tests.js b/test/tests.js index 0aa969e..9c5067c 100644 --- a/test/tests.js +++ b/test/tests.js @@ -54,7 +54,45 @@ test( "Pathfinding", function() { equal (result1.text, "(0,1)(1,1)(1,2)(2,2)(2,3)", "Result is expected"); }); -test( "Diagonal Pathfinding", function() { +test("Diagonal Pathfinding - Modes", function() { + var result1 = runSearch(new Graph([ + [1,1,1,1], + [1,1,0,1], + [1,0,1,1], + [1,1,1,1] + ], { diagonal: astar.DIAGONAL_MOVEMENT.NO_OBSTACLES }), [0,0], [2,2]); + + equal (result1.text, "(1,0)(2,0)(3,0)(3,1)(3,2)(2,2)", "Result is expected"); + + var result2 = runSearch(new Graph([ + [1,1,1,1], + [1,1,0,1], + [1,0,1,1], + [1,1,1,1] + ], { diagonal: astar.DIAGONAL_MOVEMENT.ONE_OBSTACLE }), [0,0], [2,2]); + + equal (result2.text, "(1,0)(2,0)(3,1)(2,2)", "Result is expected"); + + var result3 = runSearch(new Graph([ + [1,1,1,1], + [1,1,0,1], + [1,0,1,1], + [1,1,1,1] + ], { diagonal: astar.DIAGONAL_MOVEMENT.ALWAYS }), [0,0], [2,2]); + + equal (result3.text, "(1,1)(2,2)", "Result is expected"); + + var result4 = runSearch(new Graph([ + [1,1,1,1], + [1,1,0,1], + [1,0,1,1], + [1,1,1,1] + ], { diagonal: astar.DIAGONAL_MOVEMENT.NEVER }), [0,0], [2,2]); + + equal (result4.text, "(0,1)(0,2)(0,3)(1,3)(2,3)(2,2)", "Result is expected"); +}); + +test("Diagonal Pathfinding - Legacy Options", function () { var result1 = runSearch(new Graph([ [1,1,1,1], [0,1,1,0], From 057264e7e080b34288132bad7a37009ee7f04d3d Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Sat, 4 Mar 2017 19:59:14 -0500 Subject: [PATCH 6/7] Renamed DIAGONAL_MOVEMENT > DIAGONAL_MODE --- astar.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/astar.js b/astar.js index e4c2a3f..4656346 100644 --- a/astar.js +++ b/astar.js @@ -17,7 +17,7 @@ } })(function() { -var DIAGONAL_MOVEMENT = { +var DIAGONAL_MODE = { ALWAYS: 1, NEVER: 2, ONE_OBSTACLE: 3, @@ -41,7 +41,7 @@ function getHeap() { } var astar = { - DIAGONAL_MOVEMENT: DIAGONAL_MOVEMENT, + DIAGONAL_MODE: DIAGONAL_MODE, /** * Perform an A* Search on a graph given a start and end node. @@ -171,9 +171,9 @@ function Graph(gridIn, options) { console.warn('options.diagonal was specified as a Boolean. This functionality will be removed in an upcoming version. Use DIAGONAL_MODE instead.'); if (options.diagonal) { - this.diagonal = DIAGONAL_MOVEMENT.ALWAYS; + this.diagonal = DIAGONAL_MODE.ALWAYS; } else { - this.diagonal = DIAGONAL_MOVEMENT.NEVER; + this.diagonal = DIAGONAL_MODE.NEVER; } } else { this.diagonal = options.diagonal; @@ -236,11 +236,11 @@ Graph.prototype.neighbors = function(node) { ret.push(grid[x][y + 1]); } - if (this.diagonal === DIAGONAL_MOVEMENT.NEVER) { + if (this.diagonal === DIAGONAL_MODE.NEVER) { return ret; } - if (this.diagonal === DIAGONAL_MOVEMENT.ALWAYS) { + if (this.diagonal === DIAGONAL_MODE.ALWAYS) { // Southwest if (grid[x - 1] && grid[x - 1][y - 1]) { @@ -263,7 +263,7 @@ Graph.prototype.neighbors = function(node) { } } - if (this.diagonal === DIAGONAL_MOVEMENT.ONE_OBSTACLE) { + if (this.diagonal === DIAGONAL_MODE.ONE_OBSTACLE) { // Southwest if (grid[x-1] && grid[x-1][y-1] && (grid[x-1][y].weight || grid[x][y-1].weight)) { @@ -286,7 +286,7 @@ Graph.prototype.neighbors = function(node) { } } - if (this.diagonal === DIAGONAL_MOVEMENT.NO_OBSTACLES) { + if (this.diagonal === DIAGONAL_MODE.NO_OBSTACLES) { // Southwest if (grid[x-1] && grid[x-1][y-1] && grid[x-1][y].weight && grid[x][y-1].weight) { From c5910f40f4930aef6e3bec09c4447b1f21272d35 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Sat, 4 Mar 2017 19:59:49 -0500 Subject: [PATCH 7/7] Updated README to include DIAGONAL_MODE usage --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 07bcbe6..1543e63 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,8 @@ If you want just the A* search code (not the demo visualization), use code like [1,1,1,1], [0,1,1,0], [0,0,1,1] - ], { diagonal: true }); - + ], { diagonal: astar.DIAGONAL_MODE.ALWAYS }); + var start = graphDiagonal.grid[0][0]; var end = graphDiagonal.grid[1][2]; var resultWithDiagonals = astar.search(graphDiagonal, start, end, { heuristic: astar.heuristics.diagonal }); @@ -47,6 +47,17 @@ A few notes about weight values: 3. A weight cannot be between 0 and 1 (exclusive). 4. A weight can contain decimal values (greater than 1). +##Configuration +You can modify the default behaviour by passing options as the second argument to `Graph`: + +__diagonal__ — Enable diagonal movement. Values are available in `astar.DIAGONAL_MODE`. +- *NEVER:* Diable diagonal movement (default) +- *ALWAYS:* Allow diagonal movement regardless of obstacles +- *ONE_OBSTACLE:* Allow diagonal movement with at most one obstacle +- *NO_OBSTACLES:* Allow diagonal movement only when there are no obstacles + +_Note: To support legacy implementations, it is also possible to set `diagonal` to `true` or `false`._ + ### Original (slower) implementation The original version of the algorithm used a list, and was a bit clearer but much slower. It was based off the [original blog post](http://www.briangrinstead.com/blog/astar-search-algorithm-in-javascript). The code is available at: https://github.com/bgrins/javascript-astar/tree/0.0.1/original-implementation.