/// <summary>Gets and returns the successors or predecessors of the given node, omitting paths that correspond to /// previously visited states and are not better than the existing path. /// </summary> /// <param name="parent">The state whose successors or predecessors will be returned.</param> /// <param name="statesSeen">A dictionary mapping states to the best known path to that state, or null if /// <see cref="GraphSearchBase{S,A}.EliminateDuplicateStates"/> is false. /// </param> /// <param name="reversed">If true, the predecessors of the node will be retrieved. If false, the successors.</param> /// <returns>Returns the new nodes to add to the queue.</returns> IEnumerable <Node <StateType, ActionType> > GetNodes(Node <StateType, ActionType> parent, Dictionary <StateType, Node <StateType, ActionType> > statesSeen, bool reversed) { IEnumerable <StateActionPair <StateType, ActionType> > statePairs = reversed ? BidiProblem.GetPredecessors(parent.State) : Problem.GetSuccessors(parent.State); return(statesSeen == null?GetNodes(parent, statePairs) : GetNonDuplicatedNodes(parent, statePairs, statesSeen)); }
/// <summary>Given the forward and backward chains formed by bidirectional searching, joins them together and returns /// a single forward-only solution. /// </summary> Node <StateType, ActionType> BuildBidirectionalSolution(Node <StateType, ActionType> start, Node <StateType, ActionType> end) { // 'start' contains the solution leading back to the start node, and 'end' contains the solution leading back to // the end node. both have the same state. we need to reverse the solution of the end node, and adjust the // actions, depths, and costs while (end.Parent != null) { Node <StateType, ActionType> nextEnd = end.Parent; end.Action = BidiProblem.GetSuccessorAction(end.Parent.State, end.State, end.Action); end.State = nextEnd.State; end.PathCost = start.PathCost + (end.Parent == null ? 0 : end.PathCost - end.Parent.PathCost); end.Depth = start.Depth + 1; end.Parent = start; end.HeuristicCost = nextEnd.HeuristicCost; start = end; end = nextEnd; } return(start); }
/// <summary>This method does all of the work of running a bidirectional search except for ensuring that searching /// bidirectionally is safe and starting the limiter, if any. /// </summary> protected SearchResult FinishBidirectionalSearch(StateType initialState, SearchLimiter limiter, out Node <StateType, ActionType> solution) { solution = new Node <StateType, ActionType>(); // the queues into which nodes will be placed IQueue <Node <StateType, ActionType> > leftFringe, rightFringe; // dictionaries keeping track of states that have been visited before, and the best paths to them. Dictionary <StateType, Node <StateType, ActionType> > leftStates, rightStates; // initialize the search leftFringe = CreateQueue(); rightFringe = CreateQueue(); leftStates = new Dictionary <StateType, Node <StateType, ActionType> >(); rightStates = EliminateDuplicateStates ? new Dictionary <StateType, Node <StateType, ActionType> >() : null; leftFringe.Enqueue(MakeNode(initialState)); foreach (StateType goalState in BidiProblem.GetGoalStates()) { rightFringe.Enqueue(MakeNode(goalState)); } bool limitHit = false; // keeps track of whether the search has hit some limit preventing it from exploring further while (leftFringe.Count != 0 && rightFringe.Count != 0) // while there are still nodes in both open lists { if (limiter != null && limiter.LimitReached) { limitHit = true; break; } // get a node from the forward search Node <StateType, ActionType> leftNode = leftFringe.Dequeue(); if (Problem.IsGoal(leftNode.State)) // if it's a goal, we've found a forward-only solution, so return it { solution = leftNode; return(SearchResult.Success); } // this proceeds much like standard search... if (EliminateDuplicateStates) { limitHit |= TryEnqueueNodes(leftFringe, leftNode, leftStates, false); } else if (leftNode.Depth >= DepthLimit) { limitHit = true; } else { foreach (Node <StateType, ActionType> child in GetNodes(leftNode, null, false)) { leftFringe.Enqueue(child); Node <StateType, ActionType> existingChild; if (!leftStates.TryGetValue(child.State, out existingChild) || IsNodeBetter(child, existingChild)) { leftStates[child.State] = child; } } } // now take a node from the reverse search Node <StateType, ActionType> rightNode = rightFringe.Dequeue(); // if it has been visited by the forward search, we've found a link between the two if (leftStates.TryGetValue(rightNode.State, out leftNode)) { solution = BuildBidirectionalSolution(leftNode, rightNode); // convert it into a forward-only solution return(SearchResult.Success); // and return it } limitHit |= TryEnqueueNodes(rightFringe, rightNode, rightStates, true); } // if it gets here, we couldn't find a solution return(limitHit ? SearchResult.LimitReached : SearchResult.Failed); }