package sim.app.geo.haiti; import java.util.ArrayList; import java.util.HashMap; import sim.app.geo.haiti.HaitiFood.Node; import sim.field.network.Edge; public class AStar { /** * Assumes that both the start and end location are NODES as opposed to * LOCATIONS * <p/> * @param world * @param start * @param goal * <p/> * @return */ public ArrayList<Location> astarPath(HaitiFood world, Node start, Node goal) { // initial check if (start == null || goal == null) { System.out.println("Error: invalid node provided to AStar"); } // containers for the metainformation about the Nodes relative to the // A* search HashMap<Node, AStarNodeWrapper> foundNodes = new HashMap<Node, AStarNodeWrapper>(); AStarNodeWrapper startNode = new AStarNodeWrapper(start); AStarNodeWrapper goalNode = new AStarNodeWrapper(goal); foundNodes.put(start, startNode); foundNodes.put(goal, goalNode); startNode.gx = 0; startNode.hx = heuristic(start, goal); startNode.fx = heuristic(start, goal); // A* containers: nodes to be investigated, nodes that have been investigated ArrayList<AStarNodeWrapper> closedSet = new ArrayList<AStarNodeWrapper>(), openSet = new ArrayList<AStarNodeWrapper>(); openSet.add(startNode); while (openSet.size() > 0) { // while there are reachable nodes to investigate AStarNodeWrapper x = findMin(openSet); // find the shortest path so far if (x.node == goal) { // we have found the shortest possible path to the goal! // Reconstruct the path and send it back. return reconstructPath(goalNode); } openSet.remove(x); // maintain the lists closedSet.add(x); // check all the neighbors of this location for (Edge l : x.node.links) { Node n = (Node) l.from(); if (n == x.node) { n = (Node) l.to(); } // get the A* meta information about this Node AStarNodeWrapper nextNode; if (foundNodes.containsKey(n)) { nextNode = foundNodes.get(n); } else { nextNode = new AStarNodeWrapper(n); foundNodes.put(n, nextNode); } if (closedSet.contains(nextNode)) // it has already been considered { continue; } // otherwise evaluate the cost of this node/edge combo double tentativeCost = x.gx + (Integer) l.info; boolean better = false; if (!openSet.contains(nextNode)) { openSet.add(nextNode); nextNode.hx = heuristic(n, goal); better = true; } else if (tentativeCost < nextNode.gx) { better = true; } // store A* information about this promising candidate node if (better) { nextNode.cameFrom = x; nextNode.gx = tentativeCost; nextNode.fx = nextNode.gx + nextNode.hx; } } } return null; } /** * Takes the information about the given node n and returns the path that * found it. * <p/> * @param n the end point of the path * <p/> * @return an ArrayList of nodes that lead from the given Node to the Node * from which the search began */ ArrayList<Location> reconstructPath(AStarNodeWrapper n) { ArrayList<Location> result = new ArrayList<Location>(); AStarNodeWrapper x = n; while (x.cameFrom != null) { result.add(0, x.node.loc); // add this edge to the front of the list x = x.cameFrom; } return result; } /** * Measure of the estimated distance between two Nodes. * <p/> * @return notional "distance" between the given nodes. */ double heuristic(Node x, Node y) { return x.loc.distanceTo(y.loc); } /** * Considers the list of Nodes open for consideration and returns the node * with minimum fx value * <p/> * @param set list of open Nodes * <p/> * @return */ AStarNodeWrapper findMin(ArrayList<AStarNodeWrapper> set) { double min = 100000; AStarNodeWrapper minNode = null; for (AStarNodeWrapper n : set) { if (n.fx < min) { min = n.fx; minNode = n; } } return minNode; } /** * A wrapper to contain the A* meta information about the Nodes * <p/> */ class AStarNodeWrapper { // the underlying Node associated with the metainformation Node node; // the Node from which this Node was most profitably linked AStarNodeWrapper cameFrom; double gx, hx, fx; public AStarNodeWrapper(Node n) { node = n; gx = 0; hx = 0; fx = 0; cameFrom = null; } } }