package vooga.towerdefense.model.AStar;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
/**
* An implementation of the A* pathfinding algorithm
*
* Referenced From http://code.google.com/p/jianwikis/wiki/AStarAlgorithmForPathPlanning
* @author Jian Fang
* @author Jimmy Longley
*
*/
public class AStar {
private AreaMap map;
private AStarHeuristic heuristic;
/**
* closedList The list of Nodes not searched yet, sorted by their distance to the goal as guessed by our heuristic.
*/
private ArrayList<Node> closedList;
private SortedNodeList openList;
private ArrayList<Point> shortestPath;
public AStar(AreaMap map, AStarHeuristic heuristic) {
this.map = map;
this.heuristic = heuristic;
closedList = new ArrayList<Node>();
openList = new SortedNodeList();
}
public ArrayList<Point> calcShortestPath(int startX, int startY, int goalX, int goalY) {
//this.startX = startX;
//this.startY = startY;
//this.goalX = goalX;
//this.goalY = goalY;
//mark start and goal node
map.setStartLocation(startX, startY);
map.setGoalLocation(goalX, goalY);
//Check if the goal node is also an obstacle (if it is, it is impossible to find a path there)
if (map.getNode(goalX, goalY).isObstacle) {
return null;
}
map.getStartNode().setDistanceFromStart(0);
closedList.clear();
openList.clear();
openList.add(map.getStartNode());
//while we haven't reached the goal yet
while(openList.size() != 0) {
//get the first Node from non-searched Node list, sorted by lowest distance from our goal as guessed by our heuristic
Node current = openList.getFirst();
// check if our current Node location is the goal Node. If it is, we are done.
if(current.getX() == map.getGoalLocationX() && current.getY() == map.getGoalLocationY()) {
return reconstructPath(current);
}
//move current Node to the closed (already searched) list
openList.remove(current);
closedList.add(current);
//go through all the current Nodes neighbors and calculate if one should be our next step
for(Node neighbor : current.getNeighborList()) {
boolean neighborIsBetter;
//if we have already searched this Node, don't bother and continue to the next one
if (closedList.contains(neighbor))
continue;
//also just continue if the neighbor is an obstacle
if (!neighbor.isObstacle) {
// calculate how long the path is if we choose this neighbor as the next step in the path
float neighborDistanceFromStart = (current.getDistanceFromStart() + map.getDistanceBetween(current, neighbor));
//add neighbor to the open list if it is not there
if(!openList.contains(neighbor)) {
openList.add(neighbor);
neighborIsBetter = true;
//if neighbor is closer to start it could also be better
} else if(neighborDistanceFromStart < current.getDistanceFromStart()) {
neighborIsBetter = true;
} else {
neighborIsBetter = false;
}
// set neighbors parameters if it is better
if (neighborIsBetter) {
neighbor.setPreviousNode(current);
neighbor.setDistanceFromStart(neighborDistanceFromStart);
neighbor.setHeuristicDistanceFromGoal(heuristic.getEstimatedDistanceToGoal(neighbor.getPoint(), map.getGoalPoint()));
}
}
}
}
return null;
}
private ArrayList<Point> reconstructPath(Node node) {
ArrayList<Point> path = new ArrayList<Point>();
while(!(node.getPreviousNode() == null)) {
path.add(0,node.getPoint());
node = node.getPreviousNode();
}
this.shortestPath = path;
return path;
}
private class SortedNodeList {
private ArrayList<Node> list = new ArrayList<Node>();
public Node getFirst() {
return list.get(0);
}
public void clear() {
list.clear();
}
public void add(Node node) {
list.add(node);
Collections.sort(list);
}
public void remove(Node n) {
list.remove(n);
}
public int size() {
return list.size();
}
public boolean contains(Node n) {
return list.contains(n);
}
}
}