001    package aima.search.informed;
002    
003    import java.util.List;
004    
005    import aima.search.framework.Node;
006    import aima.search.framework.NodeExpander;
007    import aima.search.framework.Problem;
008    import aima.search.framework.Search;
009    import aima.search.framework.SearchUtils;
010    
011    /**
012     * Artificial Intelligence A Modern Approach (2nd Edition): Figure 4.11, page 112.
013     * 
014     * <code>
015     * function HILL-CLIMBING(problem) returns a state that is a local maximum
016     *   inputs: problem, a problem
017     *   local variables: current, a node
018     *                    neighbor, a node
019     *                    
020     *   current <- MAKE-NODE(INITIAL-STATE[problem])
021     *   loop do
022     *     neighbor <- a highest-valued successor of current
023     *     if VALUE[neighbor] <= VALUE[current] then return STATE[current]
024     *     current <- neighbor
025     * </code>
026     * Figure 4.11 The hill-climbing search algorithm (steepest ascent version), which is the
027     * most basic local search technique. At each step the current node is replaced by the best
028     * neighbor; in this version, that means the neighbor with the highest VALUE, but if a heuristic
029     * cost estimate h is used, we would find the neighbor with the lowest h.
030     */
031    
032    /**
033     * @author Ravi Mohan
034     * 
035     */
036    public class HillClimbingSearch extends NodeExpander implements Search {
037    
038            public enum SearchOutcome {
039                    FAILURE, SOLUTION_FOUND
040            };
041    
042            private SearchOutcome outcome = SearchOutcome.FAILURE;
043    
044            private Object lastState = null;
045    
046            public HillClimbingSearch() {
047            }
048    
049            // function HILL-CLIMBING(problem) returns a state that is a local maximum
050            // inputs: problem, a problem
051            public List search(Problem p) throws Exception {
052                    clearInstrumentation();
053                    outcome = SearchOutcome.FAILURE;
054                    lastState = null;
055                    // local variables: current, a node
056                    // neighbor, a node
057                    // current <- MAKE-NODE(INITIAL-STATE[problem])
058                    Node current = new Node(p.getInitialState());
059                    Node neighbor = null;
060                    // loop do
061                    while (true) {
062                            List children = expandNode(current, p);
063                            // neighbor <- a highest-valued successor of current
064                            neighbor = getHighestValuedNodeFrom(children, p);
065    
066                            // if VALUE[neighbor] <= VALUE[current] then return STATE[current]
067                            if ((neighbor == null)
068                                            || (getValue(p, neighbor) <= getValue(p, current))) {
069                                    if (p.isGoalState(current.getState())) {
070                                            outcome = SearchOutcome.SOLUTION_FOUND;
071                                    }
072                                    lastState = current.getState();
073                                    return SearchUtils.actionsFromNodes(current.getPathFromRoot());
074                            }
075                            // current <- neighbor
076                            current = neighbor;
077                    }
078            }
079    
080            public SearchOutcome getOutcome() {
081                    return outcome;
082            }
083    
084            public Object getLastSearchState() {
085                    return lastState;
086            }
087    
088            private Node getHighestValuedNodeFrom(List children, Problem p) {
089                    double highestValue = Double.NEGATIVE_INFINITY;
090                    Node nodeWithHighestValue = null;
091                    for (int i = 0; i < children.size(); i++) {
092                            Node child = (Node) children.get(i);
093                            double value = getValue(p, child);
094                            if (value > highestValue) {
095                                    highestValue = value;
096                                    nodeWithHighestValue = child;
097                            }
098                    }
099                    return nodeWithHighestValue;
100            }
101    
102            private double getValue(Problem p, Node n) {
103                    return -1 * getHeuristic(p, n); // assumption greater heuristic value =>
104                    // HIGHER on hill; 0 == goal state;
105            }
106    
107            private double getHeuristic(Problem p, Node aNode) {
108                    return p.getHeuristicFunction().getHeuristicValue(aNode.getState());
109            }
110    }