package aima.core.search.framework; import java.util.Collections; import java.util.List; import aima.core.agent.Action; import aima.core.util.CancelableThread; import aima.core.util.datastructure.Queue; /** * @author Ravi Mohan * @author Ciaran O'Reilly * @author Mike Stampone */ public abstract class QueueSearch extends NodeExpander { public static final String METRIC_QUEUE_SIZE = "queueSize"; public static final String METRIC_MAX_QUEUE_SIZE = "maxQueueSize"; public static final String METRIC_PATH_COST = "pathCost"; // // private Queue<Node> frontier = null; private boolean checkGoalBeforeAddingToFrontier = false; public boolean isFailure(List<Action> result) { return 0 == result.size(); } /** * Returns a list of actions to the goal if the goal was found, a list * containing a single NoOp Action if already at the goal, or an empty list * if the goal could not be found. * * @param problem * the search problem * @param frontier * the collection of nodes that are waiting to be expanded * * @return a list of actions to the goal if the goal was found, a list * containing a single NoOp Action if already at the goal, or an * empty list if the goal could not be found. */ public List<Action> search(Problem problem, Queue<Node> frontier) { this.frontier = frontier; clearInstrumentation(); // initialize the frontier using the initial state of the problem Node root = new Node(problem.getInitialState()); if (isCheckGoalBeforeAddingToFrontier()) { if (SearchUtils.isGoalState(problem, root)) { return SearchUtils.actionsFromNodes(root.getPathFromRoot()); } } frontier.insert(root); setQueueSize(frontier.size()); while (!(frontier.isEmpty()) && !CancelableThread.currIsCanceled()) { // choose a leaf node and remove it from the frontier Node nodeToExpand = popNodeFromFrontier(); setQueueSize(frontier.size()); // Only need to check the nodeToExpand if have not already // checked before adding to the frontier if (!isCheckGoalBeforeAddingToFrontier()) { // if the node contains a goal state then return the // corresponding solution if (SearchUtils.isGoalState(problem, nodeToExpand)) { setPathCost(nodeToExpand.getPathCost()); return SearchUtils.actionsFromNodes(nodeToExpand .getPathFromRoot()); } } // expand the chosen node, adding the resulting nodes to the // frontier for (Node fn : getResultingNodesToAddToFrontier(nodeToExpand, problem)) { if (isCheckGoalBeforeAddingToFrontier()) { if (SearchUtils.isGoalState(problem, fn)) { setPathCost(fn.getPathCost()); return SearchUtils.actionsFromNodes(fn .getPathFromRoot()); } } frontier.insert(fn); } setQueueSize(frontier.size()); } // if the frontier is empty then return failure return failure(); } public boolean isCheckGoalBeforeAddingToFrontier() { return checkGoalBeforeAddingToFrontier; } public void setCheckGoalBeforeAddingToFrontier( boolean checkGoalBeforeAddingToFrontier) { this.checkGoalBeforeAddingToFrontier = checkGoalBeforeAddingToFrontier; } /** * Removes and returns the node at the head of the frontier. * * @return the node at the head of the frontier. */ public Node popNodeFromFrontier() { return frontier.pop(); } public boolean removeNodeFromFrontier(Node toRemove) { return frontier.remove(toRemove); } public abstract List<Node> getResultingNodesToAddToFrontier( Node nodeToExpand, Problem p); @Override public void clearInstrumentation() { super.clearInstrumentation(); metrics.set(METRIC_QUEUE_SIZE, 0); metrics.set(METRIC_MAX_QUEUE_SIZE, 0); metrics.set(METRIC_PATH_COST, 0); } public int getQueueSize() { return metrics.getInt("queueSize"); } public void setQueueSize(int queueSize) { metrics.set(METRIC_QUEUE_SIZE, queueSize); int maxQSize = metrics.getInt(METRIC_MAX_QUEUE_SIZE); if (queueSize > maxQSize) { metrics.set(METRIC_MAX_QUEUE_SIZE, queueSize); } } public int getMaxQueueSize() { return metrics.getInt(METRIC_MAX_QUEUE_SIZE); } public double getPathCost() { return metrics.getDouble(METRIC_PATH_COST); } public void setPathCost(Double pathCost) { metrics.set(METRIC_PATH_COST, pathCost); } // // PRIVATE METHODS // private List<Action> failure() { return Collections.emptyList(); } }