diff --git a/src/algorithms/search/a-star searching/A Star Searching Algorithm.js b/src/algorithms/search/a-star searching/A Star Searching Algorithm.js new file mode 100644 index 00000000..99f81430 --- /dev/null +++ b/src/algorithms/search/a-star searching/A Star Searching Algorithm.js @@ -0,0 +1,147 @@ +var astar = { + init: function(grid) { + for(var x = , xl = grid.length; x < xl; x++) { + for(var y = , yl = grid[x].length; y < yl; y++) { + var node = grid[x][y]; + node.f = ; + node.g = ; + node.h = ; + node.cost = 1; + node.visited = false; + node.closed = false; + node.parent = null; + } + } + }, + heap: function() { + return new BinaryHeap(function(node) { + return node.f; + }); + }, + search: function(grid, start, end, diagonal, heuristic) { + astar.init(grid); + heuristic = heuristic || astar.manhattan; + diagonal = !!diagonal; + + var openHeap = astar.heap(); + + openHeap.push(start); + + while(openHeap.size() > ) { + + // Grab the lowest f(x) to process next. Heap keeps this sorted for us. + var currentNode = openHeap.pop(); + + // End case -- result has been found, return the traced path. + if(currentNode === end) { + var curr = currentNode; + var ret = []; + while(curr.parent) { + ret.push(curr); + curr = curr.parent; + } + return ret.reverse(); + } + + // Normal case -- move currentNode from open to closed, process each of its neighbors. + currentNode.closed = true; + + // Find all neighbors for the current node. Optionally find diagonal neighbors as well (false by default). + var neighbors = astar.neighbors(grid, currentNode, diagonal); + + for(var i=, il = neighbors.length; i < il; i++) { + var neighbor = neighbors[i]; + + if(neighbor.closed || neighbor.isWall()) { + // Not a valid node to process, skip to next neighbor. + continue; + } + + // 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.cost; + var beenVisited = neighbor.visited; + + if(!beenVisited || gScore < neighbor.g) { + + // Found an optimal (so far) path to this node. Take score for node to see how good it is. + neighbor.visited = true; + neighbor.parent = currentNode; + neighbor.h = neighbor.h || heuristic(neighbor.pos, end.pos); + neighbor.g = gScore; + neighbor.f = neighbor.g + neighbor.h; + + if (!beenVisited) { + // Pushing to heap will put it in proper place based on the 'f' value. + openHeap.push(neighbor); + } + else { + // Already seen the node, but since it has been rescored we need to reorder it in the heap + openHeap.rescoreElement(neighbor); + } + } + } + } + + // No result was found - empty array signifies failure to find path. + return []; + }, + manhattan: function(pos0, pos1) { + // See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html + + var d1 = Math.abs (pos1.x - pos0.x); + var d2 = Math.abs (pos1.y - pos0.y); + return d1 + d2; + }, + neighbors: function(grid, node, diagonals) { + var ret = []; + var x = node.x; + var y = node.y; + + // West + if(grid[x-1] && grid[x-1][y]) { + ret.push(grid[x-1][y]); + } + + // East + if(grid[x+1] && grid[x+1][y]) { + ret.push(grid[x+1][y]); + } + + // South + if(grid[x] && grid[x][y-1]) { + ret.push(grid[x][y-1]); + } + + // North + if(grid[x] && grid[x][y+1]) { + ret.push(grid[x][y+1]); + } + + if (diagonals) { + + // Southwest + if(grid[x-1] && grid[x-1][y-1]) { + ret.push(grid[x-1][y-1]); + } + + // Southeast + if(grid[x+1] && grid[x+1][y-1]) { + ret.push(grid[x+1][y-1]); + } + + // Northwest + if(grid[x-1] && grid[x-1][y+1]) { + ret.push(grid[x-1][y+1]); + } + + // Northeast + if(grid[x+1] && grid[x+1][y+1]) { + ret.push(grid[x+1][y+1]); + } + + } + + return ret; + } +}; \ No newline at end of file diff --git a/src/algorithms/search/a-star searching/README.md b/src/algorithms/search/a-star searching/README.md new file mode 100644 index 00000000..7375aa7c --- /dev/null +++ b/src/algorithms/search/a-star searching/README.md @@ -0,0 +1,27 @@ +This is the actual implementation of the algorithm. I will do my best to explain what is going on, but feel free to just look at the source of the example, or just download astar.js. + +There are three functions that we keep track of for nodes that we look at: + +g(x): The total cost of getting to that node (pretty straightforward). If we reach a node for the first time or reach a node in less time than it currently took, then update the g(x) to the cost to reach this node. +h(x): The estimated time to reach the finish from the current node. This is also called a heuristic. We online need to update this if it is not set already, since the distance to the finish will not change even if the path we took to arrive at a node changes. Note: There are many different ways to guess how far you are from the end, I use the Manhattan distance in this implementation. +f(x): Simply g(x) + h(x). The lower the f(x), the better. Think about it like this: the best node is one that takes the least total amount of time to arrive at and to get to the end. So, a node that took only 1 step to arrive at and 5 to get to the end is more ideal than one that took 10 to arrive and and only 1 to get to the end. + + + + + +PsuedoCode: +push startNode onto openList +while(openList is not empty) { + currentNode = find lowest f in openList + if currentNode is final, return the successful path + push currentNode onto closedList and remove from openList + foreach neighbor of currentNode { + if neighbor is not in openList { + save g, h, and f then save the current parent + add neighbor to openList + } + if neighbor is in openList but the current g is better than previous g { + save g and f, then save the current parent + } + } \ No newline at end of file