package aima.core.search.framework; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import aima.core.agent.Action; import aima.core.search.framework.problem.ActionsFunction; import aima.core.search.framework.problem.Problem; import aima.core.search.framework.problem.ResultFunction; import aima.core.search.framework.problem.StepCostFunction; /** * Instances of this class are responsible for node creation and expansion. They * compute path costs, support progress tracing, and count the number of * {@link #expand(Node, Problem)} calls. * * @author Ruediger Lunde * */ public class NodeExpander { protected boolean useParentLinks = true; /** * Modifies {@link #useParentLinks} and returns this node expander. When * using local search to search for states, parent links are not needed and * lead to unnecessary memory consumption. * * @return */ public NodeExpander useParentLinks(boolean state) { useParentLinks = state; return this; } /////////////////////////////////////////////////////////////////////// // expanding nodes /** * Factory method, which creates a root node for the specified state. */ public Node createRootNode(Object state) { return new Node(state); } /** * Computes the path cost for getting from the root node state via the * parent node state to the specified state, creates a new node for the * specified state, adds it as child of the provided parent (if * {@link #useParentLinks} is true), and returns it. */ public Node createNode(Object state, Node parent, Action action, double stepCost) { Node p = useParentLinks ? parent : null; return new Node(state, p, action, parent.getPathCost() + stepCost); } /** * Returns the children obtained from expanding the specified node in the * specified problem. * * @param node * the node to expand * @param problem * the problem the specified node is within. * * @return the children obtained from expanding the specified node in the * specified problem. */ public List<Node> expand(Node node, Problem problem) { List<Node> successors = new ArrayList<>(); ActionsFunction actionsFunction = problem.getActionsFunction(); ResultFunction resultFunction = problem.getResultFunction(); StepCostFunction stepCostFunction = problem.getStepCostFunction(); for (Action action : actionsFunction.actions(node.getState())) { Object successorState = resultFunction.result(node.getState(), action); double stepCost = stepCostFunction.c(node.getState(), action, successorState); successors.add(createNode(successorState, node, action, stepCost)); } notifyNodeListeners(node); return successors; } /////////////////////////////////////////////////////////////////////// // progress tracing /** * All node listeners added to this list get informed whenever a node is * expanded. */ private List<Consumer<Node>> nodeListeners = new ArrayList<>(); /** * Adds a listener to the list of node listeners. It is informed whenever a * node is expanded during search. */ public void addNodeListener(Consumer<Node> listener) { nodeListeners.add(listener); } /** * Removes a listener from the list of node listeners. */ public boolean removeNodeListener(Consumer<Node> listener) { return nodeListeners.remove(listener); } protected void notifyNodeListeners(Node node) { for (Consumer<Node> listener : nodeListeners) listener.accept(node); } }