diff --git a/core/src/main/java/aima/core/search/basic/uninformed/GraphSearch.java b/core/src/main/java/aima/core/search/basic/uninformed/GraphSearch.java index 4a42f267f0..3a81d56d99 100644 --- a/core/src/main/java/aima/core/search/basic/uninformed/GraphSearch.java +++ b/core/src/main/java/aima/core/search/basic/uninformed/GraphSearch.java @@ -1,19 +1,11 @@ package aima.core.search.basic.uninformed; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; -import java.util.Set; - -import aima.core.search.api.Node; -import aima.core.search.api.NodeFactory; -import aima.core.search.api.Problem; -import aima.core.search.api.SearchController; -import aima.core.search.api.SearchForActionsFunction; +import aima.core.search.api.*; import aima.core.search.basic.support.BasicNodeFactory; import aima.core.search.basic.support.BasicSearchController; +import java.util.*; + /** * Artificial Intelligence A Modern Approach (4th Edition): Figure ??, page ??. *
@@ -31,84 +23,84 @@ * expand the chosen node, adding the resulting nodes to the frontier * only if not in the frontier or explored set * - * + *

* Figure ?? An informal description of the general graph-search algorithm. * - * * @author Ciaran O'Reilly */ public class GraphSearch implements SearchForActionsFunction { - + + protected NodeFactory nodeFactory = new BasicNodeFactory<>(); + protected SearchController searchController = new BasicSearchController(); + + public GraphSearch() { + //empty constructor + } + // function GRAPH-SEARCH(problem) returns a solution, or failure @Override public List apply(Problem problem) { // initialize the frontier using the initial state of problem Queue> frontier = newFrontier(problem.initialState()); - // initialize the explored set to be empty - Set explored = newExploredSet(); + // initialize the reached table to be empty + Map> reached = new HashMap<>(); + // initialize the solution + List solution = failure(); + + // if the frontier is empty then return failure + if (frontier.isEmpty()) { + return failure(); + } // loop do - while (true) { - // if the frontier is empty then return failure - if (frontier.isEmpty()) { - return failure(); - } + while (!frontier.isEmpty()) { // choose a leaf node and remove it from the frontier - Node node = frontier.remove(); - // if the node contains a goal state then return the corresponding - // solution - if (isGoalState(node, problem)) { - return solution(node); - } - // add the node to the explored set - explored.add(node.state()); - // expand the chosen node, adding the resulting nodes to the - // frontier - for (A action : problem.actions(node.state())) { - Node child = newChildNode(problem, node, action); - // only if not in the frontier or explored set - if (!(containsState(frontier, child) || explored.contains(child.state()))) { + Node parent = frontier.remove(); + // expand the chosen node + for (Node child : expand(problem, parent)) { + // only if the child is not in reached or child is a cheaper path than reached[child.state()] + if (!reached.containsKey(child.state()) || child.pathCost() < reached.get(child.state()).pathCost()) { + // add child in reached and frontier + reached.put(child.state(), child); frontier.add(child); + // if child is a goal and is cheaper than the best solution found so far then update the solution + if (isGoalState(child, problem)) { + solution = getSolution(child); + } } } } + return solution; } - - // - // Supporting Code - protected NodeFactory nodeFactory = new BasicNodeFactory<>(); - protected SearchController searchController = new BasicSearchController(); - - public GraphSearch() { + + public List> expand(Problem problem, Node parent) { + List> nodes = new ArrayList<>(); + for (A action : problem.actions(parent.state())) { + Node node = newChildNode(problem, parent, action); + nodes.add(node); + } + return nodes; } - + public Node newChildNode(Problem problem, Node node, A action) { return nodeFactory.newChildNode(problem, node, action); } - + public Queue> newFrontier(S initialState) { Queue> frontier = new LinkedList<>(); frontier.add(nodeFactory.newRootNode(initialState)); return frontier; } - - public Set newExploredSet() { - return new HashSet<>(); - } - + public List failure() { return searchController.failure(); } - - public List solution(Node node) { + + public List getSolution(Node node) { return searchController.solution(node); } - + public boolean isGoalState(Node node, Problem problem) { return searchController.isGoalState(node, problem); } - - public boolean containsState(Queue> frontier, Node child) { - // NOTE: Not very efficient (i.e. linear in the size of the frontier) - return frontier.stream().anyMatch(frontierNode -> frontierNode.state().equals(child.state())); - } + }