lojix/search/src/main/com/thesett/aima/search/impl/BoundedAlgorithm.java
public SearchNode search(QueueSearchState<O, T> initSearch, Collection<T> startStates, int maxSteps,
        int searchSteps) throws SearchNotExhaustiveException
    {
        // Initialize the queue with the start states set up in search nodes.
        Queue<SearchNode<O, T>> queue = initSearch.enqueueStartStates(startStates);

        // Get the goal predicate configured as part of the enqueueing start states process.
        UnaryPredicate<T> goalPredicate = initSearch.getGoalPredicate();

        // Flag used to indicate whether there are unexplored successor states known to exist beyond the max depth
        // fringe.
        boolean beyondFringe = false;

        // Reset the minimum beyond the fringe boundary value.
        minBeyondBound = Float.POSITIVE_INFINITY;

        // Keep running until the queue becomes empty or a goal state is found.
        while (!queue.isEmpty())
        {
            // Extract the head element from the queue.
            SearchNode<O, T> headNode = peekAtHead ? queue.peek() : queue.remove();

            // Expand the successors into the queue whether the current node is a goal state or not.
            // This prepares the queue for subsequent searches, ensuring that goal states do not block
            // subsequent goal states that exist beyond them.
            // Add the successors to the queue provided that they are below or at the maximum bounded property.

            // Get all the successor states.
            Queue<SearchNode<O, T>> successors = new LinkedList<SearchNode<O, T>>();

            headNode.expandSuccessors(successors, reverseEnqueue);

            // Loop over all the successor states checking how they stand with respect to the bounded property.
            for (SearchNode<O, T> successor : successors)
            {
                // Get the value of the bound property for the successor node.
                float boundProperty = boundPropertyExtractor.getBoundProperty(successor);

                // Check if the successor is below or on the bound.
                if (boundProperty <= maxBound)
                {
                    // Add it to the queue to be searched.
                    queue.offer(successor);
                }

                // The successor state is above the bound.
                else
                {
                    // Set the flag to indicate that there is at least one search node known to exist beyond the
                    // bound.
                    beyondFringe = true;

                    // Compare to the best minimum beyond the bound property found so far to see if
                    // this is a new minimum and update the minimum to the new minimum if so.
                    minBeyondBound = (boundProperty < minBeyondBound) ? boundProperty : minBeyondBound;
                }
            }

            // Get the node to be goal checked, either the head node or the new top of queue, depending on the
            // peek at head flag.
            SearchNode<O, T> currentNode = peekAtHead ? queue.remove() : headNode;

            // Check if the current node is a goal state.
            // Only goal check leaves, or nodes already expanded. (The expanded flag will be set on leaves anyway).
            if (currentNode.isExpanded() && goalPredicate.evaluate(currentNode.getState()))
            {
                return currentNode;
            }

            // Check if there is a maximum number of steps limit and increase the step count and check the limit if so.
            if (maxSteps > 0)
            {
                // Increase the search step count because a goal test was performed.
                searchSteps++;

                // Update the search state with the number of steps taken so far.
                initSearch.setStepsTaken(searchSteps);

                if (searchSteps >= maxSteps)
                {
                    // The maximum number of steps has been reached, however if the queue is now empty then the search
                    // has just completed within the maximum. Check if the queue is empty and return null if so.
                    if (queue.isEmpty())
                    {
                        return null;
                    }

                    // Quit without a solution as the max number of steps has been reached but because there are still
                    // more states in the queue then raise a search failure exception.
                    else
                    {
                        throw new SearchNotExhaustiveException("Maximum number of steps reached.", null);
                    }
                }
            }
        }

        // No goal state was found. Check if there known successors beyond the max depth fringe and if so throw
        // a SearchNotExhaustiveException to indicate that the search failed rather than exhausted the search space.
        if (beyondFringe)
        {
            throw new MaxBoundException("Max bound reached.", null);
        }
        else
        {
            // The search space was exhausted so return null to indicate that no goal could be found.
            return null;
        }
    }
public SearchNode search(QueueSearchState<O, T> initSearch, Collection<T> startStates, int maxSteps,
        int searchSteps) throws SearchNotExhaustiveException
    {
        // Initialize the queue with the start states set up in search nodes.
        Queue<SearchNode<O, T>> queue = initSearch.enqueueStartStates(startStates);

        // Get the goal predicate configured as part of the enqueueing start states process.
        UnaryPredicate<T> goalPredicate = initSearch.getGoalPredicate();

        // Flag used to indicate whether there are unexplored successor states known to exist beyond the max depth
        // fringe.
        boolean beyondFringe = false;

        // Reset the minimum beyond the fringe boundary value.
        minBeyondBound = Float.POSITIVE_INFINITY;

        // Keep running until the queue becomes empty or a goal state is found.
        while (!queue.isEmpty())
        {
            // Extract the head element from the queue.
            SearchNode<O, T> headNode = peekAtHead ? queue.peek() : queue.remove();

            // Expand the successors into the queue whether the current node is a goal state or not.
            // This prepares the queue for subsequent searches, ensuring that goal states do not block
            // subsequent goal states that exist beyond them.
            // Add the successors to the queue provided that they are below or at the maximum bounded property.

            // Get all the successor states.
            Queue<SearchNode<O, T>> successors = new LinkedList<SearchNode<O, T>>();

            headNode.expandSuccessors(successors, reverseEnqueue);

            // Loop over all the successor states checking how they stand with respect to the bounded property.
            for (SearchNode<O, T> successor : successors)
            {
                // Get the value of the bound property for the successor node.
                float boundProperty = boundPropertyExtractor.getBoundProperty(successor);

                // Check if the successor is below or on the bound.
                if (boundProperty <= maxBound)
                {
                    // Add it to the queue to be searched.
                    queue.offer(successor);
                }

                // The successor state is above the bound.
                else
                {
                    // Set the flag to indicate that there is at least one search node known to exist beyond the
                    // bound.
                    beyondFringe = true;

                    // Compare to the best minimum beyond the bound property found so far to see if
                    // this is a new minimum and update the minimum to the new minimum if so.
                    minBeyondBound = (boundProperty < minBeyondBound) ? boundProperty : minBeyondBound;
                }
            }

            // Get the node to be goal checked, either the head node or the new top of queue, depending on the
            // peek at head flag.
            SearchNode<O, T> currentNode = peekAtHead ? queue.remove() : headNode;

            // Check if the current node is a goal state.
            // Only goal check leaves, or nodes already expanded. (The expanded flag will be set on leaves anyway).
            if (currentNode.isExpanded() && goalPredicate.evaluate(currentNode.getState()))
            {
                return currentNode;
            }

            // Check if there is a maximum number of steps limit and increase the step count and check the limit if so.
            if (maxSteps > 0)
            {
                // Increase the search step count because a goal test was performed.
                searchSteps++;

                // Update the search state with the number of steps taken so far.
                initSearch.setStepsTaken(searchSteps);

                if (searchSteps >= maxSteps)
                {
                    // The maximum number of steps has been reached, however if the queue is now empty then the search
                    // has just completed within the maximum. Check if the queue is empty and return null if so.
                    if (queue.isEmpty())
                    {
                        return null;
                    }

                    // Quit without a solution as the max number of steps has been reached but because there are still
                    // more states in the queue then raise a search failure exception.
                    else
                    {
                        throw new SearchNotExhaustiveException("Maximum number of steps reached.", null);
                    }
                }
            }
        }

        // No goal state was found. Check if there known successors beyond the max depth fringe and if so throw
        // a SearchNotExhaustiveException to indicate that the search failed rather than exhausted the search space.
        if (beyondFringe)
        {
            throw new MaxBoundException("Max bound reached.", null);
        }
        else
        {
            // The search space was exhausted so return null to indicate that no goal could be found.
            return null;
        }
    }
Searches all SearchNodes less than the maximum bound for some property of the nodes.

@param  initSearch  The algorithm to turn the start states into the intial set of search nodes, of the correct
type to begin the search with.
@param  startStates The set of start states to begin the search with.
@param  maxSteps    The maximum number of steps to search for before giving up. If this is 0 or less then no
maximum number is enforced.
@param  searchSteps The number of steps taken so far. This may be greater than 0 if this search is run
iteratively and previous iterations of it have already been run.

@return The first {@link SearchNode} corresponding to a goal state that is found or null if none can be found
after exhaustive searching.

@throws SearchNotExhaustiveException If the search terminates prematurely because the maximum number of steps is
reached or because the maximum bound is reached and no more nodes can be
found that are below it.