lojix/search/src/main/com/thesett/aima/search/impl/MaxStepsAlgorithm.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 if this has not already been done.
        // This will only be done on the first call to this method, as enqueueStartStates sets a flag when it is
        // done. Subsequent searches continue from where the previous search left off. Have to call reset on
        // the search method to really start the search again from the start states.
        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();

        // Keep running until the queue becomes empty or a goal state is found.
        while (!queue.isEmpty())
        {
            // Extract or peek at 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.
            if (!headNode.isExpanded())
            {
                headNode.expandSuccessors(queue, reverseEnqueue);
            }

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

            // Only goal check leaves, or nodes already expanded. (The expanded flag will be set on leaves anyway).
            if (currentNode.isExpanded())
            {
                // If required, remove the node to goal check from the queue.
                currentNode = peekAtHead ? queue.remove() : headNode;

                // Check if the current node is a goal state.
                if (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)
            {
                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 so return null
        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 if this has not already been done.
        // This will only be done on the first call to this method, as enqueueStartStates sets a flag when it is
        // done. Subsequent searches continue from where the previous search left off. Have to call reset on
        // the search method to really start the search again from the start states.
        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();

        // Keep running until the queue becomes empty or a goal state is found.
        while (!queue.isEmpty())
        {
            // Extract or peek at 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.
            if (!headNode.isExpanded())
            {
                headNode.expandSuccessors(queue, reverseEnqueue);
            }

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

            // Only goal check leaves, or nodes already expanded. (The expanded flag will be set on leaves anyway).
            if (currentNode.isExpanded())
            {
                // If required, remove the node to goal check from the queue.
                currentNode = peekAtHead ? queue.remove() : headNode;

                // Check if the current node is a goal state.
                if (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)
            {
                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 so return null
        return null;
    }
Search until a goal state is found or the maximum allowed number of steps is reached.

@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 SearchNotExhaustiveException If the search terminates prematurely because
the maximum number of steps is reached.