//TODO: split this method to multiple methods. public IList <IAction> Search(Problem p) { Debug.Assert(p is IBidirectionalProblem); searchOutcome = SearchOutcome.PathNotFound; ClearInstrumentation(); var op = ((IBidirectionalProblem)p).GetOriginalProblem(); var rp = ((IBidirectionalProblem)p).GetReverseProblem(); var opFrontier = new CachedStateQueue <Node>(); var rpFrontier = new CachedStateQueue <Node>(); var ogs = new GraphSearch(); var rgs = new GraphSearch(); // Ensure the instrumentation for these // are cleared down as their values // are used in calculating the overall // bidirectional Metrics. ogs.ClearInstrumentation(); rgs.ClearInstrumentation(); var opNode = new Node(op.InitialState); var rpNode = new Node(rp.InitialState); opFrontier.Insert(opNode); rpFrontier.Insert(rpNode); this.SetQueueSize(opFrontier.Size() + rpFrontier.Size()); this.SetNodesExpanded(ogs.GetNodesExpanded() + rgs.GetNodesExpanded()); while (!(opFrontier.IsEmpty() && rpFrontier.IsEmpty())) { // Determine the nodes to work with and expand their fringes // in preparation for testing whether or not the two // searches meet or one or other is at the GOAL. if (!opFrontier.IsEmpty()) { opNode = opFrontier.Pop(); opFrontier.AddAll(ogs.GetResultingNodesToAddToFrontier(opNode, op)); } else { opNode = null; } if (!rpFrontier.IsEmpty()) { rpNode = rpFrontier.Pop(); rpFrontier.AddAll(rgs.GetResultingNodesToAddToFrontier(rpNode, rp)); } else { rpNode = null; } this.SetQueueSize(opFrontier.Size() + rpFrontier.Size()); this.SetNodesExpanded(ogs.GetNodesExpanded() + rgs.GetNodesExpanded()); // // First Check if either frontier contains the other's state if (null != opNode && null != rpNode) { Node popNode = null; Node prpNode = null; if (opFrontier.ContainsNodeBasedOn(rpNode.State)) { popNode = opFrontier.GetNodeBasedOn(rpNode.State); prpNode = rpNode; } else if (rpFrontier.ContainsNodeBasedOn(opNode.State)) { popNode = opNode; prpNode = rpFrontier.GetNodeBasedOn(opNode.State); // Need to also check whether or not the nodes that // have been taken off the frontier actually represent the // same state, otherwise there are instances whereby // the searches can pass each other by } else if (opNode.State.Equals(rpNode.State)) { popNode = opNode; prpNode = rpNode; } if (null != popNode && null != prpNode) { IList <IAction> actions = this.RetrieveActions(op, rp, popNode, prpNode); // It may be the case that it is not in fact possible to // traverse from the original node to the goal node based on // the reverse path (i.e. unidirectional links: e.g. // InitialState(A)<->C<-Goal(B) ) if (null != actions) { return(actions); } } } // // Check if the original problem is at the GOAL state if (null != opNode && SearchUtils.IsGoalState(op, opNode)) { // No need to check return value for null here // as an action path discovered from the goal // is guaranteed to exist return(this.RetrieveActions(op, rp, opNode, null)); } // // Check if the reverse problem is at the GOAL state if (null != rpNode && SearchUtils.IsGoalState(rp, rpNode)) { IList <IAction> actions = this.RetrieveActions(op, rp, null, rpNode); // It may be the case that it is not in fact possible to // traverse from the original node to the goal node based on // the reverse path (i.e. unidirectional links: e.g. // InitialState(A)<-Goal(B) ) if (null != actions) { return(actions); } } } // Empty IList can indicate already at Goal // or unable to find valid Set of actions return(new List <IAction>()); }