back to devpro   download
// (C) Andrea Giammarchi
//	Special thanks to Alessandro Crugnola [www.sephiroth.it]

function AStar(	// A* algorithm for a path finder

	Grid,	// Array Grid bi dimensional [[N,N], [N,N]]
	Start,	// Array Start Point [x, y]
	Goal,	// Array End Point [x, y]
	Find	// [String] Distance function "Diagonal" | "DiagonalFree" * | "Euclidean" | "EuclideanFree" * | "Manhattan"
		// default choosed function: Manhattan
		// Free suffix means that algorithm doesn't care about closed squares
		//	---------
		//	|0	1|
		//	|1	0|
		//	---------
		// In this example [x:0, y:0] to [x:1, y:1] is a valid choice

){

// Generic AStar private functions, used to find a path

	// init function, sets Find string as dedicated function to search diagonal nodes
	function AStar(){
		switch(Find){
			case	"Diagonal":
			case	"Euclidean":
					Find = DiagonalSuccessors;
					break;
			case 	"DiagonalFree":
			case	"EuclideanFree":
					Find = DiagonalSuccessors$;
					break;
			default:
					Find = function(){};
					break;
		};
	};
	
	// returns boolean value (Grid cell is available and open)
	function $Grid(x, y){
		return Grid[y][x] === 0;
	};

	// Node function, returns a new object with Node properties
	function Node(Parent, Point){
		return {
			Parent:Parent,
			value:Point.x + (Point.y * cols),
			x:Point.x,
			y:Point.y,
			f:0,
			g:0
		};
	};

	// Path function, executes AStar algorithm operations
	function Path(){
		var	$Start = Node(null, {x:Start[0], y:Start[1]}),	// Start Point
			$Goal = Node(null, {x:Goal[0], y:Goal[1]}),	// Goal Point
			AStar = new Array(limit),			// Every undefined Grid cell
			Open = [$Start], Closed = [], result = [],	// open and closed list and result array
			$Successors, $Node, $Path,			// nodes referers
			length, max, min, i, j;				// integer variables
		while(length = Open.length) {
			max = limit;
			min = -1;
			for(i = 0; i < length; i++) {
				if(Open[i].f < max) {
					max = Open[i].f;
					min = i;
				}
			};
			$Node = Open.splice(min, 1)[0];
			if($Node.value === $Goal.value) {
				$Path = Closed[Closed.push($Node) - 1];
				do {
					result.push([$Path.x, $Path.y]);
				}while($Path = $Path.Parent);
				AStar = Closed = Open = [];
				result.reverse();
			} else {
				$Successors = Successors($Node.x, $Node.y);
				for(i = 0, j = $Successors.length; i < j; i++){
					$Path = Node($Node, $Successors[i]);
					if(!AStar[$Path.value]){
						$Path.g = $Node.g + Distance($Successors[i], $Node);
						$Path.f = $Path.g + Distance($Successors[i], $Goal);
						Open.push($Path);
						AStar[$Path.value] = true;
					};
				};
				Closed.push($Node);
			};
		};
		return result;
	};

// Successors functions, used to find adjacent available cells

	// returns every available North, South, East or West cell
	//
	//	-----------------
	//	|	0	 |
	//	|0	P	1|
	//	|	0	 |
	//	-----------------
	//
	// In this example will returns each point around P except for [x:2, y:1]
	// Diagonal points are verified only if Distance function is not Manhattan
	function Successors(x, y){
		var	N = y - 1,
			S = y + 1,
			E = x + 1,
			W = x - 1,
			$N = N > -1 && $Grid(x, N),
			$S = S < rows && $Grid(x, S),
			$E = E < cols && $Grid(E, y),
			$W = W > -1 && $Grid(W, y),
			result = [];
		if($N)
			result.push({x:x, y:N});
		if($E)
			result.push({x:E, y:y});
		if($S)
			result.push({x:x, y:S});
		if($W)
			result.push({x:W, y:y});
		Find($N, $S, $E, $W, N, S, E, W, result);
		return result;
	};
	
	// returns every available North East, South East, South West or North West cell
	//
	//	-----------------
	//	|0	0	0|
	//	|1	P	0|
	//	|0	1	0|
	//	-----------------
	//
	// In this example will returns each point around P except for [x:0, y:2]
	// because South and West cells close movement
	function DiagonalSuccessors($N, $S, $E, $W, N, S, E, W, result){
		if($N) {
			if($E && $Grid(E, N))
				result.push({x:E, y:N});
			if($W && $Grid(W, N))
				result.push({x:W, y:N});
		};
		if($S){
			if($E && $Grid(E, S))
				result.push({x:E, y:S});
			if($W && $Grid(W, S))
				result.push({x:W, y:S});
		};
	};

	// returns every available North East, South East, South West or North West cell
	//
	//	-----------------
	//	|0	0	0|
	//	|1	P	0|
	//	|0	1	0|
	//	-----------------
	//
	// In this example will returns each point around P with [x:0, y:2] too.
	function DiagonalSuccessors$($N, $S, $E, $W, N, S, E, W, result){
		$N = N > -1;
		$S = S < rows;
		$E = E < cols;
		$W = W > -1;
		if($E) {
			if($N && $Grid(E, N))
				result.push({x:E, y:N});
			if($S && $Grid(E, S))
				result.push({x:E, y:S});
		};
		if($W) {
			if($N && $Grid(W, N))
				result.push({x:W, y:N});
			if($S && $Grid(W, S))
				result.push({x:W, y:S});
		};
	};

// Distance functions, returns ideal distance from 2 points

	function Diagonal(Point, Goal){		// diagonal movement
		return max(abs(Point.x - Goal.x), abs(Point.y - Goal.y));
	};
	
	function Euclidean(Point, Goal){	// diagonal movement using Euclide (AC = sqrt(AB^2 + BC^2))
						//	where AB = x2 - x1 and BC = y2 - y1 and AC will be [x3, y3]
		return sqrt(pow(Point.x - Goal.x, 2) + pow(Point.y - Goal.y, 2));
	};
	
	function Manhattan(Point, Goal){	// linear movement
		return abs(Point.x - Goal.x) + abs(Point.y - Goal.y);
	};


// Private scope variables
	var
		// Math lib shortcuts
		abs =	Math.abs,
		max =	Math.max,
		pow =	Math.pow,
		sqrt =	Math.sqrt,

		cols =	Grid[0].length,	// Grid width
		rows =	Grid.length,	// Grid height
		limit =	cols * rows,	// Maximum choices

		// [choosed function] or Manhattan function to know distance between 2 Points
		Distance = {
			Diagonal:Diagonal,
			DiagonalFree:Diagonal,
			Euclidean:Euclidean,
			EuclideanFree:Euclidean,
			Manhattan:Manhattan
		}[Find] || Manhattan;
	
	return Path(AStar());
};
back to devpro   download
Creative Commons License