Skip to content

Instantly share code, notes, and snippets.

@Langerz82
Last active February 17, 2021 20:47
Show Gist options
  • Save Langerz82/36cbcce94ce1521fbcd53f800700783a to your computer and use it in GitHub Desktop.
Save Langerz82/36cbcce94ce1521fbcd53f800700783a to your computer and use it in GitHub Desktop.
Advanced A* (A-Star) algorithm for a path finder. This does pixel based path finding, for map grids and is an advance on the original authors code.
define(function() {
var AStar = (function () {
/**
* Advanced A* (A-Star) algorithm for a path finder.
* This does pixel based path finding, for map grids and is an advance
* on the original authors code.
*
* @originalAuthor Andrea Giammarchi
* @revisedAuthor Langerz82
* @license Mit Style License.
*/
var asGridCellSize = 16;
var asCols = 0;
var asRows = 0;
function diagonalSuccessors($N, $S, $E, $W, N, S, E, W, grid, result, i) {
if($N) {
$E && !grid[N][E] && (result[i++] = {x:E, y:N});
$W && !grid[N][W] && (result[i++] = {x:W, y:N});
}
if($S){
$E && !grid[S][E] && (result[i++] = {x:E, y:S});
$W && !grid[S][W] && (result[i++] = {x:W, y:S});
}
return result;
}
function diagonalSuccessorsFree($N, $S, $E, $W, N, S, E, W, grid, result, i) {
$N = N > -1;
$S = S < asRows;
$E = E < asCols;
$W = W > -1;
if($E) {
$N && !grid[N][E] && (result[i++] = {x:E, y:N});
$S && !grid[S][E] && (result[i++] = {x:E, y:S});
}
if($W) {
$N && !grid[N][W] && (result[i++] = {x:W, y:N});
$S && !grid[S][W] && (result[i++] = {x:W, y:S});
}
return result;
}
function nothingToDo($N, $S, $E, $W, N, S, E, W, grid, result, i) {
return result;
}
function successors(find, x, y, grid){
var result = [],
i = 0;
if (x <= -1 || x >= asCols || y <= -1 || y >= asRows)
return [];
var mx=x%1, my=y%1;
var X1 = (mx == 0) ? x : Math.floor(x),
X2 = (mx == 0) ? x : Math.ceil(x),
Y1 = (my == 0) ? y : Math.floor(y),
Y2 = (my == 0) ? y : Math.ceil(y),
Ny = (Y1 - 1),
Ex = (X2 + 1),
Sy = (Y2 + 1),
Wx = (X1 - 1),
$N, $S, $E, $W;
if (mx == 0)
{
if (x >= 0 && x <= (asCols-1))
{
$N = Ny >= 0 && !grid[Ny][x];
$S = Sy <= (asRows-1) && !grid[Sy][x];
}
}
else if (X1 >= 0 || X2 <= (asCols-1)) {
$N = Ny >= 0 && !grid[Ny][X1] && !grid[Ny][X2];
$S = Sy <= (asRows-1) && !grid[Sy][X1] && !grid[Sy][X2];
}
if (my == 0)
{
if (y >= 0 && y <= (asRows-1))
{
$E = Ex <= (asCols-1) && !grid[y][Ex];
$W = Wx >= 0 && !grid[y][Wx];
}
}
else if (Y1 >= 0 || Y2 <= (asRows-1)) {
$E = Ex <= asCols && !grid[Y1][Ex] && !grid[Y2][Ex];
$W = Wx >= 0 && !grid[Y1][Wx] && !grid[Y2][Wx];
}
Ny = (my == 0) ? y-1 : Y1;
Sy = (my == 0) ? y+1 : Y2;
Ex = (mx == 0) ? x+1 : X2;
Wx = (mx == 0) ? x-1 : X1;
$N && (result[i++] = {x:x, y:Ny});
$E && (result[i++] = {x:Ex, y:y});
$S && (result[i++] = {x:x, y:Sy});
$W && (result[i++] = {x:Wx, y:y});
return find($N, $S, $E, $W, Ny, Sy, Ex, Wx, grid, result, i);
}
function diagonal(start, end, f1, f2) {
return f2(f1(start.x - end.x), f1(start.y - end.y));
}
function euclidean(start, end, f1, f2) {
var x = start.x - end.x,
y = start.y - end.y;
return f2(x * x + y * y);
}
function manhattan(start, end, f1, f2) {
return f1(start.x - end.x) + f1(start.y - end.y);
}
function getRelativeCoords(c1, c2)
{
var x, y;
if ((c2[0] - c1[0]) > 0)
x = Math.floor(c1[0]/asGridCellSize);
else {
x = Math.ceil(c1[0]/asGridCellSize);
}
if ((c2[1] - c1[1]) > 0)
y = Math.floor(c1[1]/asGridCellSize);
else {
y = Math.ceil(c1[1]/asGridCellSize);
}
return [x, y];
}
function AStar(grid, start, end, f) {
asCols = grid[0].length;
asRows = grid.length;
var rcs = [start[0]/asGridCellSize, start[1]/asGridCellSize];
if (grid[Math.floor(rcs[1])][Math.floor(rcs[0])] ||
grid[Math.ceil(rcs[1])][Math.ceil(rcs[0])])
{
return null;
}
var start2 = {x:rcs[0], y:rcs[1],
f:0, g:0, v:rcs[0] + rcs[1]*asCols};
var limit = (asCols * asRows),
f1 = Math.abs,
f2 = Math.max,
list = {},
result = [],
open = [start2],
length = 1,
adj, distance, find, i, j, max, min, current, next;
var rce = [end[0]/asGridCellSize, end[1]/asGridCellSize];
var rce2 = getRelativeCoords(end, start);
if (grid[rce2[1]][rce2[0]])
return null;
// Check if path points outside of grid, if so exit function.
if (rcs[0] < 0 || rcs[0] >= asCols ||
rcs[1] < 0 || rcs[1] >= asRows ||
rce[0] < 0 || rce[0] >= asCols ||
rce[1] < 0 || rce[1] >= asRows)
{
return null;
}
var end2 = {x:rce2[0], y:rce2[1],
f:0, g:0, v:rce2[0] + rce2[1]*asCols};
switch (f) {
case "Diagonal":
find = diagonalSuccessors;
case "DiagonalFree":
distance = diagonal;
break;
case "Euclidean":
find = diagonalSuccessors;
case "EuclideanFree":
f2 = Math.sqrt;
distance = euclidean;
break;
default:
distance = manhattan;
find = nothingToDo;
break;
}
find || (find = diagonalSuccessorsFree);
do {
max = limit;
min = 0;
for(i = 0; i < length; ++i) {
if((f = open[i].f) < max) {
max = f;
min = i;
}
}
current = open.splice(min, 1)[0];
var diffEndX = Math.abs(current.x - rce[0]),
diffEndY = Math.abs(current.y - rce[1]);
if (diffEndX < 1 && diffEndY < 1)
{
current.x = rce[0];
current.y = rce[1];
current.v = end2.v;
if (!current.hasOwnProperty('p'))
break;
// This section joins the end of the path to the found path
// only if it's different values but in the same grid cell.
var tmp = {};
var insertTemp = false;
tmp.x = rce[0];
tmp.y = rce[1];
var diffX = Math.abs(rce[0]-current.p.x),
diffY = Math.abs(rce[1]-current.p.y);
if (diffX > 0 && diffX < 1)
{
tmp.x = current.p.x;
insertTemp = true;
}
else if (diffY > 0 && diffY < 1)
{
tmp.y = current.p.y;
insertTemp = true;
}
if (insertTemp) {
tmp.v = (tmp.x) + (tmp.y) * asCols;
tmp.p = current.p;
current.p = tmp;
}
}
if (current.v != end2.v) {
--length;
var diffEndX = Math.abs(current.x - rce[0]),
diffEndY = Math.abs(current.y - rce[1]);
if (diffEndX < 1 && diffEndY == 0)
{
current.x = rce[0];
}
else if (diffEndX == 0 && diffEndY < 1)
{
current.y = rce[1];
}
next = successors(find, current.x, current.y, grid);
for(i = 0, j = next.length; i < j; ++i){
(adj = next[i]).p = current;
adj.f = adj.g = 0;
adj.v = (adj.x) + (adj.y) * asCols;
if(!(adj.v in list)){
adj.f = (adj.g = current.g + distance(adj, current, f1, f2)) + distance(adj, end2, f1, f2);
open[length++] = adj;
list[adj.v] = 1;
}
}
} else {
i = 0;
length = 0;
do {
result[i++] = [current.x*asGridCellSize,
current.y*asGridCellSize];
} while (current = current.p);
result.reverse();
}
} while (length);
log.info(JSON.stringify(result));
// Remove redundant coords in the result list.
for (var i=2; i < (result.length); ++i)
{
if ((Math.abs(result[i-2][0] - result[i][0]) >= asGridCellSize &&
Math.abs(result[i-2][1] - result[i][1]) == 0) ||
(Math.abs(result[i-2][1] - result[i][1]) >= asGridCellSize &&
Math.abs(result[i-2][0] - result[i][0]) == 0))
{
result.splice(--i,1);
}
}
log.info(JSON.stringify(result));
return result;
}
return {AStar};
}());
return AStar;
});
@Langerz82
Copy link
Author

Updated. - Minor change to adjacent grid cells.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment