001    /*
002     * Created on Sep 8, 2004
003     *
004     */
005    package aima.search.uninformed;
006    
007    import java.util.ArrayList;
008    import java.util.List;
009    
010    import aima.search.framework.Node;
011    import aima.search.framework.NodeExpander;
012    import aima.search.framework.Problem;
013    import aima.search.framework.Search;
014    import aima.search.framework.SearchUtils;
015    
016    /**
017     * Artificial Intelligence A Modern Approach (2nd Edition): Figure 3.13, page
018     * 77.
019     * 
020     * <code>
021     * function DEPTH-LIMITED-SEARCH(problem, limit) returns a solution, or failure/cutoff
022     *   return RECURSIVE-DLS(MAKE-NODE(INITIAL-STATE[problem]), problem, limit)
023     *   
024     * function RECURSIVE-DLS(node, problem, limit) returns a solution, or failure/cutoff
025     *   cutoff_occurred? <- false
026     *   if GOAL-TEST[problem](STATE[node]) then return SOLUTION(node)
027     *   else if DEPTH[node] = limit then return cutoff
028     *   else for each successor in EXPAND(node, problem) do
029     *     result <- RECURSIVE-DLS(successor, problem, limit)
030     *     if result = cutoff then cutoff_occurred? <- true
031     *     else if result != failure then return result
032     *   if cutoff_occurred? then return cutoff else return failure
033     * </code>
034     * Figure 3.13 A recursive implementation of depth-limited search.
035     */
036    
037    /**
038     * @author Ravi Mohan
039     * 
040     */
041    public class DepthLimitedSearch extends NodeExpander implements Search {
042            private static String PATH_COST = "pathCost";
043    
044            private final int limit;
045    
046            public DepthLimitedSearch(int limit) {
047                    this.limit = limit;
048    
049            }
050    
051            // function DEPTH-LIMITED-SEARCH(problem, limit) returns a solution, or
052            // failure/cutoff
053            public List search(Problem p) throws Exception {
054                    clearInstrumentation();
055                    // return RECURSIVE-DLS(MAKE-NODE(INITIAL-STATE[problem]), problem,
056                    // limit)
057                    return recursiveDLS(new Node(p.getInitialState()), p, limit);
058            }
059    
060            // function RECURSIVE-DLS(node, problem, limit) returns a solution, or
061            // failure/cutoff
062            private List recursiveDLS(Node node, Problem problem, int limit) {
063                    // cutoff_occurred? <- false
064                    boolean cutOffOccured = false;
065                    // if GOAL-TEST[problem](STATE[node]) then return SOLUTION(node)
066                    if (problem.isGoalState(node.getState())) {
067                            setPathCost(node.getPathCost());
068                            return SearchUtils.actionsFromNodes(node.getPathFromRoot());
069                            // else if DEPTH[node] = limit then return cutoff
070                    } else if (node.getDepth() == limit) {
071                            return createCutOffResult();
072                    } else {
073                            // else for each successor in EXPAND(node, problem) do
074                            List children = expandNode(node, problem);
075                            for (int i = 0; i < children.size(); i++) {
076                                    Node child = (Node) children.get(i);
077                                    // result <- RECURSIVE-DLS(successor, problem, limit)
078                                    List result = recursiveDLS(child, problem, limit);
079                                    // if result = cutoff then cutoff_occurred? <- true
080                                    if (cutoffResult(result)) {
081                                            cutOffOccured = true;
082                                            // else if result != failure then return result
083                                    } else if (!(failure(result))) {
084                                            return result;
085                                    }
086                            }
087                            // if cutoff_occurred? then return cutoff else return failure
088                            if (cutOffOccured) {
089                                    return createCutOffResult();
090                            } else {
091                                    return new ArrayList();
092                            }
093    
094                    }
095    
096            }
097    
098            @Override
099            public void clearInstrumentation() {
100                    super.clearInstrumentation();
101                    metrics.set(PATH_COST, 0);
102            }
103    
104            public double getPathCost() {
105                    return metrics.getDouble(PATH_COST);
106            }
107    
108            public void setPathCost(Double pathCost) {
109                    metrics.set(PATH_COST, pathCost);
110            }
111    
112            private boolean failure(List result) {
113    
114                    return result.size() == 0;
115            }
116    
117            private boolean cutoffResult(List result) {
118    
119                    return result.size() == 1 && result.get(0).equals("cutoff");
120            }
121    
122            private List createCutOffResult() {
123                    List result = new ArrayList();
124                    result.add("cutoff");
125                    return result;
126            }
127    
128    }