public List <Action> search(Problem p) { assert(p is BidirectionalProblem); searchOutcome = SearchOutcome.PATH_NOT_FOUND; clearInstrumentation(); Problem op = ((BidirectionalProblem)p).getOriginalProblem(); Problem rp = ((BidirectionalProblem)p).getReverseProblem(); CachedStateQueue <Node> opFrontier = new CachedStateQueue <Node>(); CachedStateQueue <Node> rpFrontier = new CachedStateQueue <Node>(); GraphSearch ogs = new GraphSearch(); GraphSearch 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(); Node opNode = new Node(op.getInitialState()); Node rpNode = new Node(rp.getInitialState()); opFrontier.insert(opNode); rpFrontier.insert(rpNode); setQueueSize(opFrontier.Count + rpFrontier.Count); 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.AddRange(ogs.getResultingNodesToAddToFrontier(opNode, op)); } else { opNode = null; } if (!rpFrontier.isEmpty()) { rpNode = rpFrontier.pop(); rpFrontier.AddRange(rgs.getResultingNodesToAddToFrontier(rpNode, rp)); } else { rpNode = null; } setQueueSize(opFrontier.Count + rpFrontier.Count); 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.getState())) { popNode = opFrontier.getNodeBasedOn(rpNode.getState()); prpNode = rpNode; } else if (rpFrontier.containsNodeBasedOn(opNode.getState())) { popNode = opNode; prpNode = rpFrontier.getNodeBasedOn(opNode.getState()); // 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.getState().Equals(rpNode.getState())) { popNode = opNode; prpNode = rpNode; } if (null != popNode && null != prpNode) { List <Action> actions = 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(retrieveActions(op, rp, opNode, null)); } // // Check if the reverse problem is at the GOAL state if (null != rpNode && SearchUtils.isGoalState(rp, rpNode)) { List <Action> actions = 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 List can indicate already at Goal // or unable to find valid set of actions return(new List <Action>()); }