import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import net.datastructures.*; /** * Uses A* search - key value is current path length plus L1 distance * to the target. * * Solves the given maze using a search that depends on cost estimates * for the final path length. Returns the final maze SequenceItem, which * has links back to all intermediate SequenceItems. * * To guarantee optimality of the solution, the estimate of total cost * must be both admissible (never overestimate the true cost) * and monotone (cost estimate after a move is never less that the cost estimate * before the move). The monotone assumption is needed because this search * function uses a closed set. The estimate above meets both criteria. * * @author Scot Drysdale * @see Sequencer */ public class AStarSequencer implements Sequencer { // Priority queue with replace-key capabilities. private AdaptablePriorityQueue pq; /* The open and closed sets for A* search. * We have found the best path to the things in closed. * Items in open are in the priority queue, and are mapped to the * priority queue entry in case it needs updating. */ private Set closed; private Map> open; private int targetRow, targetCol; // row and column of the target /** * Constructor. targetX and targetY will be initialized by setTarget */ public AStarSequencer() { pq = new HeapAdaptablePriorityQueue(); closed = new HashSet(); open = new HashMap>(); } /** *

{@inheritDoc}

* Uses L1 distance to target plus current path length as key

* previous must be a AStarSequenceItem, although the interface for add only * requires a SequenceItem. */ public void add(int row, int col, SequenceItem previous) { AStarSequenceItem prev = (AStarSequenceItem) previous; int pathLength; // Path length to this item if (prev == null) pathLength = 1; else pathLength = prev.getPathLength() + 1; // One longer than pred. // Get the A* total path length estimate int distEst = pathLength + Math.abs(row-getTargetRow()) + Math.abs(col-getTargetCol()); AStarSequenceItem newItem = new AStarSequenceItem(row, col, prev, pathLength); // If the maze item is in the closed set can ignore it. if(!closed.contains(newItem)) { // Try to get an earlier version of this SequenceItem Entry oldEntry = open.get(newItem); if(oldEntry == null) { // First time this maze position discovered? Entry newEntry = pq.insert(distEst, newItem); open.put(newItem, newEntry); } else { // Found this position before. Have we found a better path to it? AStarSequenceItem oldItem = oldEntry.getValue(); // Note that the maze position is the same, so distance to target is the same if(pathLength < oldItem.getPathLength()) { // Update the pq entry to reflect the new distance and value pq.replaceKey(oldEntry, distEst); pq.replaceValue(oldEntry, newItem); } } } } /** * Gets the next item in the sequencer. Updates open and closed sets. * @return the next obj from the sequencer and remove it */ public SequenceItem next() { Entry nextEntry = pq.removeMin(); AStarSequenceItem item = nextEntry.getValue(); // Move item from open set to closed set open.remove(item); closed.add(item); return item; } /** * Does this sequencer have more items? * @return true iff this sequencer is has more items */ public boolean hasNext() { return !pq.isEmpty(); } /** * Save the target location *

{@inheritDoc} */ public void setTarget(int row, int col) { targetRow = row; targetCol = col; } /** * Get the target row * @return the target row */ public int getTargetRow() { return targetRow; } /** * Get the target column * @return the target column */ public int getTargetCol() { return targetCol; } }