Skip to content

Diagonal modes #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
Expand All @@ -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.
Expand Down
78 changes: 75 additions & 3 deletions astar.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
}
})(function() {

var DIAGONAL_MODE = {
ALWAYS: 1,
NEVER: 2,
ONE_OBSTACLE: 3,
NO_OBSTACLES: 4
};

function pathTo(node) {
var curr = node;
var path = [];
Expand All @@ -34,6 +41,8 @@ function getHeap() {
}

var astar = {
DIAGONAL_MODE: DIAGONAL_MODE,

/**
* Perform an A* Search on a graph given a start and end node.
* @param {Graph} graph
Expand Down Expand Up @@ -157,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_MODE.ALWAYS;
} else {
this.diagonal = DIAGONAL_MODE.NEVER;
}
} else {
this.diagonal = options.diagonal;
}

this.grid = [];
for (var x = 0; x < gridIn.length; x++) {
this.grid[x] = [];
Expand Down Expand Up @@ -215,7 +236,12 @@ Graph.prototype.neighbors = function(node) {
ret.push(grid[x][y + 1]);
}

if (this.diagonal) {
if (this.diagonal === DIAGONAL_MODE.NEVER) {
return ret;
}

if (this.diagonal === DIAGONAL_MODE.ALWAYS) {

// Southwest
if (grid[x - 1] && grid[x - 1][y - 1]) {
ret.push(grid[x - 1][y - 1]);
Expand All @@ -237,6 +263,52 @@ Graph.prototype.neighbors = function(node) {
}
}

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)) {
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_MODE.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;
};

Expand Down Expand Up @@ -401,4 +473,4 @@ return {
Graph: Graph
};

});
});
40 changes: 39 additions & 1 deletion test/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down