コード例 #1
0
ファイル: CbsNode.cs プロジェクト: kylevedder/mapf
        /// <summary>
        /// Finds the first conflict (timewise) for all the given plans, or declares this node as a goal.
        /// Assumes all agents are initially on the same timestep (no OD).
        /// </summary>
        private void FindConflict()
        {
            this.conflict = null;
            if (this.allSingleAgentPlans.Length == 1)
            {
                return;
            }
            int maxPlanSize = this.allSingleAgentPlans.Max <SinglePlan>(plan => plan.GetSize());

            // Check in every time step that the plans do not collide
            for (int time = 1; time < maxPlanSize; time++)
            {
                // Check all pairs of groups if they are conflicting at the given time step
                for (int i = 0; i < allSingleAgentPlans.Length; i++)
                {
                    for (int j = i + 1; j < allSingleAgentPlans.Length; j++)
                    {
                        if (allSingleAgentPlans[i].IsColliding(time, allSingleAgentPlans[j]))
                        {
                            int initialTimeStep = this.problem.m_vAgents[0].lastMove.time; // To account for solving partially solved problems.
                                                                                           // This assumes the makespan of all the agents is the same.
                            Move first  = allSingleAgentPlans[i].GetLocationAt(time);
                            Move second = allSingleAgentPlans[j].GetLocationAt(time);
                            this.conflict = new CbsConflict(i, j, first, second, time + initialTimeStep);
                            return;
                        }
                    }
                }
            }
        }
コード例 #2
0
ファイル: ClassicAStar.cs プロジェクト: doratzmon/WeightedCBS
        ///// <summary>
        /////
        ///// </summary>
        ///// <param name="agentIndex"></param>
        ///// <param name="fromNode"></param>
        ///// <returns>Whether the shuffle succeeded</returns>
        //bool RMStarShuffleIndividualPath(CbsConflict conflict, bool agentA, WorldState fromNode)
        //{
        //    int agentIndex = agentA ? conflict.agentAIndex : conflict.agentBIndex;
        //    //WorldState node = fromNode.individualMStarPlanBases[agentIndex];
        //    WorldState node = fromNode;

        //    if (this.mstarPlanBasesToTheirConstraints.ContainsKey(node) == false)
        //        this.mstarPlanBasesToTheirConstraints[node] = new HashSet<CbsConstraint>[this.instance.GetNumOfAgents()];
        //    if (this.mstarPlanBasesToTheirConstraints[node][agentIndex] == null)
        //        this.mstarPlanBasesToTheirConstraints[node][agentIndex] = new HashSet<CbsConstraint>();

        //    return solveOneAgentForMstar(node, conflict, agentA);
        //}

        //protected bool solveOneAgentForMstar(WorldState node, CbsConflict conflict, bool agentA)
        //{
        //    int agentIndex = agentA ? conflict.agentAIndex : conflict.agentBIndex;
        //    HashSet_U<CbsConstraint> constraints = null;
        //    HashSet<CbsConstraint> newConstraints = null;
        //    int oldMaxCost = int.MaxValue;
        //    if (this.instance.parameters.ContainsKey(CBS_LocalConflicts.CONSTRAINTS))
        //        constraints = (HashSet_U<CbsConstraint>)this.instance.parameters[CBS_LocalConflicts.CONSTRAINTS];
        //    else
        //    {
        //        constraints = new HashSet_U<CbsConstraint>();
        //        this.instance.parameters[CBS_LocalConflicts.CONSTRAINTS] = constraints;
        //    }

        //    if (this.instance.parameters.ContainsKey(CBS_LocalConflicts.CAT) == false)
        //        this.instance.parameters[CBS_LocalConflicts.CAT] = new Dictionary_U<TimedMove, int>(); // Indicate TO CBS that another level is running above it

        //    if (this.debug)
        //    {
        //        Debug.Print("Planning for agent index: " + agentIndex + " in node: " + node);
        //    }

        //    newConstraints = this.mstarPlanBasesToTheirConstraints[node][agentIndex];
        //    CbsConstraint newConstraint = new CbsConstraint(conflict, this.instance, agentA);
        //    newConstraints.Add(newConstraint);
        //    constraints.Join(newConstraints);

        //    if (this.debug)
        //    {
        //        Debug.Print("Constraints: ");
        //        foreach (var constraint in newConstraints)
        //        {
        //            Debug.Print(constraint.ToString());
        //        }
        //    }

        //    oldMaxCost = this.maxCost;
        //    //this.instance.parameters[IndependenceDetection.MAXIMUM_COST_KEY] = this.mstarPlanBasesToTheirPlans[node][agentIndex].GetSize() - 1;
        //    this.instance.parameters[IndependenceDetection.MAXIMUM_COST_KEY] = node.individualMStarPlans[agentIndex].GetSize() - 1;

        //    bool success = solveOneAgentForMstar(node, agentIndex);

        //    constraints.Separate(newConstraints);
        //    this.instance.parameters[IndependenceDetection.MAXIMUM_COST_KEY] = oldMaxCost;
        //    this.instance.parameters.Remove(CBS_LocalConflicts.CAT);

        //    return success;
        //}

        //protected bool solveOneAgentForMstar(WorldState node, int agentIndex)
        //{
        //    AgentState[] thisAgentOnly = new AgentState[1];
        //    thisAgentOnly[0] = node.allAgentsState[agentIndex];
        //    var subProblem = this.instance.Subproblem(thisAgentOnly);

        //    ClassicAStar astar = new ClassicAStar(this.heuristic);
        //    ICbsSolver solver = new CBS_LocalConflicts(astar, astar); // Uses a precomputed solution if possible
        //    solver.Setup(subProblem, this.runner);
        //    bool success = solver.Solve();

        //    if (success)
        //    {
        //        //this.mstarPlanBasesToTheirPlans[node][agentIndex] = solver.GetSinglePlans()[0];
        //        node.individualMStarPlans[agentIndex] = solver.GetSinglePlans()[0];
        //    }
        //    // else nothing. Don't null the old plan yet - it might be saved by a successful replan of the other agent

        //    return success;
        //}

        /// <summary>
        /// NOT the algorithm in the M* journal paper.
        /// They want each node to propagate its entire collision set, not just the new conflict that began the process.
        /// This implementation may be suitable for the M* algorithm as appears in the paper,
        /// but it isn't suitable when we want to backpropagate from a closed list hit,
        /// because then we don't have a specific conflict to propagate.
        /// </summary>
        /// <param name="conflict"></param>
        /// <param name="fromNode">
        /// Not the node where the collision happened, because it was never generated.
        /// The node from where the colliding moves were made.
        /// </param>
        void RMStarCollisionBackPropagation(CbsConflict conflict, WorldState fromNode)
        {
            if (this.debug)
            {
                Debug.Print("Back prop!!");
            }
            var queue = new Queue <WorldState>();

            queue.Enqueue(fromNode);

            while (queue.Count != 0)
            {
                var node = queue.Dequeue();

                bool onlyUnitedNow = node.collisionSets.Union(conflict.agentAIndex, conflict.agentBIndex);

                if (onlyUnitedNow)
                {
                    if (this.debug)
                    {
                        Console.WriteLine("Re-opening node {0} with an updated collision set", node);
                    }
                    this.reinsertIntoOpenList(node);

                    foreach (var next in node.backPropagationSet)
                    {
                        queue.Enqueue(next);
                    }
                }
            }
        }
コード例 #3
0
ファイル: CBS_IDA.cs プロジェクト: kylevedder/mapf
 protected void addToGlobalConflictCount(CbsConflict conflict)
 {
     if (conflict != null)
     {
         globalConflictsCounter[Math.Max(conflict.agentA, conflict.agentB)][Math.Min(conflict.agentA, conflict.agentB)]++;
     }
 }
コード例 #4
0
ファイル: CbsConstraint.cs プロジェクト: doratzmon/k_robust
        }                                                                             // Nonsense values until Init, just allocate move

        public CbsConstraint(CbsConflict conflict, ProblemInstance instance, bool agentA, Run.ConstraintPolicy constraintPolicy = Run.ConstraintPolicy.Single, int constraintRange = 0)
        {
            Move move;
            int  agentNum;
            int  minTime;

            this.constaintRange = Math.Abs(conflict.timeStepAgentB - conflict.timeStepAgentA);
            if (constraintPolicy == Run.ConstraintPolicy.Range)
            {
                minTime = Math.Min(conflict.timeStepAgentA, conflict.timeStepAgentB);
            }
            else if (constraintPolicy == Run.ConstraintPolicy.DoubleRange)
            {
                minTime = conflict.timeStepAgentA;
            }
            else
            {
                if (agentA)
                {
                    minTime = conflict.timeStepAgentA;
                }
                else
                {
                    minTime = conflict.timeStepAgentB;
                }
            }
            if (agentA)
            {
                move      = conflict.agentAmove;
                agentNum  = instance.m_vAgents[conflict.agentAIndex].agent.agentNum;
                this.move = new TimedMove(move, minTime);
            }
            else
            {
                move      = conflict.agentBmove;
                agentNum  = instance.m_vAgents[conflict.agentBIndex].agent.agentNum;
                this.move = new TimedMove(move, minTime);
            }

            this.agentNum = (byte)agentNum;


            if (conflict.vertex)
            {
                this.move.direction = Move.Direction.NO_DIRECTION;
            }
        }
コード例 #5
0
        }                                                                             // Nonsense values until Init, just allocate move

        public CbsConstraint(CbsConflict conflict, ProblemInstance instance, bool agentA)
        {
            Move move;
            int  agentNum;
            int  minTime;

            if (agentA)
            {
                minTime = conflict.timeStepAgentA;
            }
            else
            {
                minTime = conflict.timeStepAgentB;
            }

            if (agentA)
            {
                move      = conflict.agentAmove;
                agentNum  = instance.m_vAgents[conflict.agentAIndex].agent.agentNum;
                this.move = new TimedMove(move, minTime);
            }
            else
            {
                move      = conflict.agentBmove;
                agentNum  = instance.m_vAgents[conflict.agentBIndex].agent.agentNum;
                this.move = new TimedMove(move, minTime);
            }

            this.agentNum = (byte)agentNum;


            if (conflict.vertex)
            {
                this.move.direction = Move.Direction.NO_DIRECTION;
            }
        }
コード例 #6
0
ファイル: CBS_IDA.cs プロジェクト: kylevedder/mapf
        public virtual bool Expand(CbsNode node, CbsConflict conflict)
        {
            if (runner.ElapsedMilliseconds() > Constants.MAX_TIME)
            {
                return(false);
            }
            highLevelExpanded++;
            if (conflict == null)
            {
                this.totalCost = node.totalCost;
                this.goalNode  = node;
                this.solution  = node.CalculateJointPlan();
                this.Clear();
                return(true);
            }
            CbsNode       toAdd;
            CbsConstraint con2       = new CbsConstraint(conflict, instance, false);
            CbsConstraint con1       = new CbsConstraint(conflict, instance, true);
            byte          stepLength = 0;

            if (conflict.vertex)
            {
                stepLength = 1;
            }
            bool ok1 = false, ok2 = false;

            if (node.totalCost + conflict.timeStep + stepLength - node.PathLength(conflict.agentA) <= fBound)
            {
                ok1 = true;
                if (node.DoesMustConstraintAllow(con1))
                {
                    toAdd = new CbsNode(node, con1, conflict.agentA);
                    toAdd.SetMustConstraint(con2);

                    if (toAdd.Replan3b(conflict.agentA, Math.Max(minCost, conflict.timeStep)))
                    {
                        this.highLevelGenerated++;
                        if (toAdd.totalCost <= fBound)
                        {
                            if (Expand(toAdd, toAdd.GetConflict()))
                            {
                                return(true);
                            }
                        }
                        else if (toAdd.totalCost < nextF)
                        {
                            nextF = toAdd.totalCost;
                        }
                    }
                }
            }

            if (node.totalCost + conflict.timeStep + stepLength - node.PathLength(conflict.agentB) <= fBound)
            {
                ok2 = true;
                if (node.DoesMustConstraintAllow(con2))
                {
                    toAdd = new CbsNode(node, con2, conflict.agentB);
                    toAdd.SetMustConstraint(con1);

                    if (toAdd.Replan3b(conflict.agentB, Math.Max(minCost, conflict.timeStep)))
                    {
                        this.highLevelGenerated++;
                        if (toAdd.totalCost <= fBound)
                        {
                            if (Expand(toAdd, toAdd.GetConflict()))
                            {
                                return(true);
                            }
                        }
                        else if (toAdd.totalCost < nextF)
                        {
                            nextF = toAdd.totalCost;
                        }
                    }
                }
            }

            if (ok1 && ok2)
            {
                toAdd = new CbsNode(node, con1, conflict.agentA);
                if (toAdd.Replan3b(conflict.agentA, Math.Max(minCost, conflict.timeStep)))
                {
                    if (toAdd.totalCost <= fBound)
                    {
                        toAdd = new CbsNode(toAdd, con2, conflict.agentB);
                        if (toAdd.Replan(conflict.agentB, Math.Max(minCost, conflict.timeStep)))
                        // FIXME: Should this really use the regular Replan() or was this a typo?
                        {
                            this.highLevelGenerated++;
                            if (toAdd.totalCost <= fBound)
                            {
                                if (Expand(toAdd, toAdd.GetConflict()))
                                {
                                    return(true);
                                }
                            }
                            else if (toAdd.totalCost < nextF)
                            {
                                nextF = toAdd.totalCost;
                            }
                        }
                    }
                }
            }
            return(false);
        }
コード例 #7
0
ファイル: CBS.cs プロジェクト: doratzmon/WeightedCBS
 protected virtual void addToGlobalConflictCount(CbsConflict conflict)
 {
 }
コード例 #8
0
ファイル: CBS.cs プロジェクト: doratzmon/WeightedCBS
        protected CbsNode ConstraintExpand(CbsNode node, bool doLeftChild, out int closedListHitChildCost)
        {
            CbsConflict conflict = node.GetConflict();
            int         conflictingAgentIndex = doLeftChild? conflict.agentAIndex : conflict.agentBIndex;

            CbsNode.ExpansionState expansionsState           = doLeftChild ? node.agentAExpansion : node.agentBExpansion;
            CbsNode.ExpansionState otherChildExpansionsState = doLeftChild ? node.agentBExpansion : node.agentAExpansion;
            string agentSide = doLeftChild? "left" : "right";
            int    planSize  = node.allSingleAgentPlans[conflictingAgentIndex].GetSize();
            int    groupSize = node.GetGroupSize(conflictingAgentIndex);

            closedListHitChildCost = -1;

            if ((Constants.Variant == Constants.ProblemVariant.ORIG &&
                 expansionsState == CbsNode.ExpansionState.NOT_EXPANDED && conflict.vertex == true &&
                 conflict.timeStep >= node.allSingleAgentCosts[conflictingAgentIndex] &&             // TODO: Can't just check whether the node is at its goal - the plan may involve it passing through its goal and returning to it later because of preexisting constraints.
                 node.h < conflict.timeStep + 1 - node.allSingleAgentCosts[conflictingAgentIndex] && // Otherwise we won't be increasing its h and there would be no reason to delay expansion
                 groupSize == 1) ||                                                                  // Otherwise an agent in the group can be forced to take a longer route without increasing the group's cost because another agent would be able to take a shorter route.
                (Constants.Variant == Constants.ProblemVariant.NEW &&
                 expansionsState == CbsNode.ExpansionState.NOT_EXPANDED && conflict.vertex == true &&
                 ((conflict.timeStep > planSize - 1 && node.h < 2) ||
                  (conflict.timeStep == planSize - 1 && node.h < 1)) &&
                 groupSize == 1)) // Otherwise an agent in the group can be forced to take a longer route without increasing the group's cost because another agent would be able to take a shorter route.
            // Conflict happens when or after the agent reaches its goal, and the agent is in a single-agent group.
            // With multi-agent groups, banning the goal doesn't guarantee a higher cost solution,
            // since if an agent is forced to take a longer route it may enable another agent in the group
            // to take a shorter route, getting an alternative solution of the same cost
            // The child would cost a lot because:
            // A) All WAIT moves in the goal before leaving it now add to the g (if we're in the original problem variant).
            // B) We force the low level to compute a path longer than the optimal,
            //    and with a bad suprise towards the end in the form of a constraint,
            //    so the low-level's SIC heuristic performs poorly.
            // C) We're banning the GOAL from all directions (since this is a vertex conflict),
            //    so any alternative plan will at least cost 1 more.
            //    We're ignoring edge conflicts because they can only happen at the goal when reaching it,
            //    and aren't guaranteed to increase the cost because the goal can still be possibly reached from another edge.
            {
                if (otherChildExpansionsState == CbsNode.ExpansionState.DEFERRED)
                {
                    throw new Exception("Unexpected: Expansion of both children deffered, but this is a vertex conflict so that means the targets for the two agents are equal, which is illegal");
                }

                if (debug)
                {
                    Debug.WriteLine("Skipping " + agentSide + " child for now");
                }
                if (doLeftChild)
                {
                    node.agentAExpansion = CbsNode.ExpansionState.DEFERRED;
                }
                else
                {
                    node.agentBExpansion = CbsNode.ExpansionState.DEFERRED;
                }
                // Add the minimal delta in the child's cost:
                // since we're banning the goal at conflict.timeStep, it must at least do conflict.timeStep+1 steps
                if (Constants.Variant == Constants.ProblemVariant.ORIG)
                {
                    node.h = (ushort)(conflict.timeStep + 1 - node.allSingleAgentCosts[conflictingAgentIndex]);
                }
                else if (Constants.Variant == Constants.ProblemVariant.NEW)
                {
                    if (conflict.timeStep > planSize - 1) // Agent will need to step out and step in to the goal, at least
                    {
                        node.h = 2;
                    }
                    else // Conflict is just when agent enters the goal, it'll have to at least wait one timestep.
                    {
                        node.h = 1;
                    }
                }
                return(node);
            }
            else if (expansionsState != CbsNode.ExpansionState.EXPANDED)
            // Agent expansion already skipped in the past or not forcing it from its goal - finally generate the child:
            {
                if (debug)
                {
                    Debug.WriteLine("Generating " + agentSide + " child");
                }

                if (doLeftChild)
                {
                    node.agentAExpansion = CbsNode.ExpansionState.EXPANDED;
                }
                else
                {
                    node.agentBExpansion = CbsNode.ExpansionState.EXPANDED;
                }

                var     newConstraint = new CbsConstraint(conflict, instance, doLeftChild);
                CbsNode child         = new CbsNode(node, newConstraint, conflictingAgentIndex);

                if (closedList.ContainsKey(child) == false)
                {
                    int minCost = -1;
                    //if (this.useMddHeuristic)
                    //    minCost = node.GetGroupCost(conflictingAgentIndex) + 1;
                    bool success = child.Replan(conflictingAgentIndex, this.minDepth, null, -1, minCost); // The node takes the max between minDepth and the max time over all constraints.

                    if (success == false)
                    {
                        return(null); // A timeout probably occured
                    }
                    if (debug)
                    {
                        Debug.WriteLine("Child hash: " + child.GetHashCode());
                        Debug.WriteLine("Child cost: " + child.totalCost);
                        Debug.WriteLine("Child min ops to solve: " + child.minOpsToSolve);
                        Debug.WriteLine("Child num of agents that conflict: " + child.totalInternalAgentsThatConflict);
                        Debug.WriteLine("Child num of internal conflicts: " + child.totalConflictsBetweenInternalAgents);
                        Debug.WriteLine("");
                    }

                    if (child.totalCost < node.totalCost && groupSize == 1) // Catch the error early
                    {
                        child.Print();
                        Debug.WriteLine("Child plan: (cost {0})", child.allSingleAgentCosts[conflictingAgentIndex]);
                        child.allSingleAgentPlans[conflictingAgentIndex].PrintPlan();
                        Debug.WriteLine("Parent plan: (cost {0})", node.allSingleAgentCosts[conflictingAgentIndex]);
                        node.allSingleAgentPlans[conflictingAgentIndex].PrintPlan();
                        Debug.Assert(false, "Single agent node with lower cost than parent! " + child.totalCost + " < " + node.totalCost);
                    }

                    return(child);
                }
                else
                {
                    this.closedListHits++;
                    closedListHitChildCost = this.closedList[child].totalCost;
                    if (debug)
                    {
                        Debug.WriteLine("Child already in closed list!");
                    }
                }
            }
            else
            {
                if (debug)
                {
                    Debug.WriteLine("Child already generated before");
                }
            }

            return(null);
        }
コード例 #9
0
ファイル: CBS.cs プロジェクト: doratzmon/WeightedCBS
        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <param name="children"></param>
        /// <param name="adoptBy">If not given, adoption is done by expanded node</param>
        /// <returns>true if adopted - need to rerun this method, ignoring the returned children from this call, bacause adoption was performed</returns>
        protected bool ExpandImpl(CbsNode node, out IList <CbsNode> children, out bool reinsertParent)
        {
            CbsConflict conflict = node.GetConflict();

            children = new List <CbsNode>();

            CbsNode child;

            reinsertParent = false;
            int  closedListHitChildCost;
            bool leftSameCost  = false; // To quiet the compiler
            bool rightSameCost = false;


            // Generate left child:
            child = ConstraintExpand(node, true, out closedListHitChildCost);
            if (child != null)
            {
                if (child == node) // Expansion deferred
                {
                    reinsertParent = true;
                }
                else // New child
                {
                    children.Add(child);
                    leftSameCost = child.totalCost == node.totalCost;
                }
            }
            else  // A timeout occured, or the child was already in the closed list.
            {
                if (closedListHitChildCost != -1)
                {
                    leftSameCost = closedListHitChildCost == node.totalCost;
                }
            }

            if (runner.ElapsedMilliseconds() > Constants.MAX_TIME)
            {
                return(false);
            }

            // Generate right child:
            child = ConstraintExpand(node, false, out closedListHitChildCost);
            if (child != null)
            {
                if (child == node) // Expansion deferred
                {
                    reinsertParent = true;
                }
                else // New child
                {
                    children.Add(child);
                    rightSameCost = child.totalCost == node.totalCost;
                }
            }
            else  // A timeout occured, or the child was already in the closed list.
            {
                if (closedListHitChildCost != -1)
                {
                    rightSameCost = closedListHitChildCost == node.totalCost;
                }
            }


            return(false);
        }
コード例 #10
0
        public virtual void Expand(CbsNode node)
        {
            CbsConflict conflict = node.GetConflict();
            CbsNode     child;

            if (this.mergeThreshold != -1 && ShouldMerge(node))
            {
                child = new CbsNode(node, node.agentsGroupAssignment[conflict.agentA], node.agentsGroupAssignment[conflict.agentB]);
                if (closedList.ContainsKey(child) == false) // We may have already merged these agents in the parent
                {
                    if (debug)
                    {
                        Debug.WriteLine("Merging agents {0} and {1}", conflict.agentA, conflict.agentB);
                    }
                    closedList.Add(child, child);
                    bool success = child.Replan(conflict.agentA, this.minDepth); // or agentB. Doesn't matter - they're in the same group.
                    if (debug)
                    {
                        Debug.WriteLine("New cost: " + child.totalCost);
                        var constraints = child.GetConstraints();
                        Debug.WriteLine(constraints.Count + " Remaining internal constraints:");
                        foreach (CbsConstraint constraint in constraints)
                        {
                            Debug.WriteLine(constraint);
                        }
                        var externalConstraints = (HashSet_U <CbsConstraint>) this.instance.parameters[CBS_LocalConflicts.CONSTRAINTS];
                        Debug.WriteLine(externalConstraints.Count.ToString() + " external constraints: ");
                        foreach (CbsConstraint constraint in externalConstraints)
                        {
                            Debug.WriteLine(constraint);
                        }
                        Debug.WriteLine("New conflict: " + child.GetConflict());
                        Debug.Write("New agent group assignments: ");
                        for (int i = 0; i < child.agentsGroupAssignment.Length; i++)
                        {
                            Debug.Write(" " + child.agentsGroupAssignment[i]);
                        }
                        Debug.WriteLine("");
                        Debug.Write("Single agent costs: ");
                        for (int i = 0; i < child.allSingleAgentCosts.Length; i++)
                        {
                            Debug.Write(" " + child.allSingleAgentCosts[i]);
                        }
                        Debug.WriteLine("");
                        child.CalculateJointPlan().PrintPlan();
                        Debug.WriteLine("");
                        Debug.WriteLine("");
                    }
                    this.maxSizeGroup = Math.Max(this.maxSizeGroup, child.replanSize);

                    if (success == false) // A timeout probably occured
                    {
                        return;
                    }

                    if (node.totalCost <= maxCost) // FIXME: Code dup with other new node creations
                    {
                        openList.Add(child);
                        this.highLevelGenerated++;
                        this.addToGlobalConflictCount(child.GetConflict());
                    }
                }
                else
                {
                    closedListHits++;
                }
                return;
            }

            // Expand node, possibly partially:
            // Generate left child:
            int agentAGroupSize = node.GetGroupSize(node.agentsGroupAssignment[conflict.agentA]);

            if (node.agentAExpansion == CbsNode.ExpansionState.NOT_EXPANDED && conflict.vertex == true &&
                conflict.timeStep >= node.allSingleAgentCosts[conflict.agentA] && // TODO: Consider checking directly whether at the time of the conflict the agent would be at its goal according to the node.singleAgentPlans. It may be more readable.
                agentAGroupSize == 1)
            // Conflict happens when or after agent A reaches its goal, and agent A is in a single agent group.
            // With multi-agent groups, banning the goal doesn't guarantee a higher cost solution,
            // since if an agent is forced to take a longer route it may enable another agent in the group
            // to take a shorter route, getting an alternative solution of the same cost
            // The left child would cost a lot because:
            // A) All WAIT moves in the goal before leaving it now add to the g.
            // B) We force the low level to compute a path longer than the optimal,
            //    and with a bad suprise towards the end in the form of a constraint,
            //    so the low-level's SIC heuristic performs poorly.
            // C) We're banning the GOAL from all directions (since this is a vertex conflict),
            //    so any alternative plan will at least cost 1 more.
            //    We're ignoring edge conflicts because they can only happen at the goal when reaching it,
            //    and aren't guaranteed to increase the cost because the goal can still be possibly reached from another edge.
            {
                if (debug)
                {
                    Debug.WriteLine("Skipping left child");
                }
                node.agentAExpansion = CbsNode.ExpansionState.DEFERRED;
                int agentAOldCost = node.allSingleAgentCosts[conflict.agentA];
                // Add the minimal delta in the child's cost:
                // since we're banning the goal at conflict.timeStep, it must at least do conflict.timeStep+1 steps
                node.totalCost += (ushort)(conflict.timeStep + 1 - agentAOldCost);
                openList.Add(node); // Re-insert node into open list with higher cost, don't re-increment global conflict counts
                this.partialExpansions++;
            }
            else if (node.agentAExpansion != CbsNode.ExpansionState.EXPANDED)
            // Agent A expansion already skipped in the past or not forcing A from its goal - finally generate the child:
            {
                if (debug)
                {
                    Debug.WriteLine("Generating left child");
                }
                var newConstraint = new CbsConstraint(conflict, instance, true);
                child = new CbsNode(node, newConstraint, conflict.agentA);

                if (closedList.ContainsKey(child) == false)
                {
                    closedList.Add(child, child);
                    if (child.Replan(conflict.agentA, this.minDepth)) // The node takes the max between minDepth and the max time over all constraints.
                    {
                        if (child.totalCost < node.totalCost && agentAGroupSize == 1)
                        {
                            Debug.WriteLine("");
                            Debug.Write("Single agent costs: ");
                            for (int i = 0; i < child.allSingleAgentCosts.Length; i++)
                            {
                                Debug.Write(" " + child.allSingleAgentCosts[i]);
                            }
                            Debug.WriteLine("");
                            //Debug.WriteLine("Offending plan:");
                            //child.CalculateJointPlan().PrintPlan();
                            Debug.WriteLine("Chlid plan: (cost {0})", child.allSingleAgentCosts[conflict.agentA]);
                            child.allSingleAgentPlans[conflict.agentA].PrintPlan();
                            Debug.WriteLine("Parent plan: (cost {0})", node.allSingleAgentCosts[conflict.agentA]);
                            node.allSingleAgentPlans[conflict.agentA].PrintPlan();
                            Debug.Assert(false, "Single agent node with lower cost than parent! " + child.totalCost + " < " + node.totalCost);
                        }
                        if (child.totalCost <= this.maxCost)
                        {
                            openList.Add(child);
                            this.highLevelGenerated++;
                            addToGlobalConflictCount(child.GetConflict());
                            if (debug)
                            {
                                Debug.WriteLine("Child cost: " + child.totalCost);
                                Debug.WriteLine("Child hash: " + child.GetHashCode());
                                Debug.WriteLine("");
                            }
                        }
                    }
                    else // A timeout probably occured
                    {
                        return;
                    }
                }
                else
                {
                    this.closedListHits++;
                }
                node.agentAExpansion = CbsNode.ExpansionState.EXPANDED;
            }

            // Generate right child:
            int agentBGroupSize = node.GetGroupSize(node.agentsGroupAssignment[conflict.agentB]);

            if (node.agentBExpansion == CbsNode.ExpansionState.NOT_EXPANDED && conflict.vertex == true &&
                conflict.timeStep >= node.allSingleAgentCosts[conflict.agentB] &&
                agentBGroupSize == 1) // Again, skip expansion
            {
                if (debug)
                {
                    Debug.WriteLine("Skipping right child");
                }
                if (node.agentAExpansion == CbsNode.ExpansionState.DEFERRED)
                {
                    throw new Exception("Unexpected: Expansion of both children differed, but this is a vertex conflict so that means the targets for the two agents are equal, which is illegal");
                }

                node.agentBExpansion = CbsNode.ExpansionState.DEFERRED;
                int agentBOldCost = node.allSingleAgentCosts[conflict.agentB];
                node.totalCost += (ushort)(conflict.timeStep + 1 - agentBOldCost);
                openList.Add(node); // Re-insert node into open list with higher cost, don't re-increment global conflict counts
                this.partialExpansions++;
                // TODO: Code duplication with agentA. Make this into a function.
            }
            else if (node.agentBExpansion != CbsNode.ExpansionState.EXPANDED)
            {
                if (debug)
                {
                    Debug.WriteLine("Generating right child");
                }
                var newConstraint = new CbsConstraint(conflict, instance, false);
                child = new CbsNode(node, newConstraint, conflict.agentB);

                if (closedList.ContainsKey(child) == false)
                {
                    closedList.Add(child, child);
                    if (child.Replan(conflict.agentB, this.minDepth)) // The node takes the max between minDepth and the max time over all constraints.
                    {
                        if (child.totalCost < node.totalCost && node.agentAExpansion == CbsNode.ExpansionState.EXPANDED &&
                            agentBGroupSize == 1)
                        {
                            Debug.WriteLine("");
                            Debug.Write("Single agent costs: ");
                            for (int i = 0; i < child.allSingleAgentCosts.Length; i++)
                            {
                                Debug.Write(" " + child.allSingleAgentCosts[i]);
                            }
                            Debug.WriteLine("");
                            //Debug.WriteLine("Offending plan:");
                            //child.CalculateJointPlan().PrintPlan();
                            Debug.WriteLine("Chlid plan: (cost {0})", child.allSingleAgentCosts[conflict.agentB]);
                            child.allSingleAgentPlans[conflict.agentB].PrintPlan();
                            Debug.WriteLine("Parent plan: (cost {0})", node.allSingleAgentCosts[conflict.agentB]);
                            node.allSingleAgentPlans[conflict.agentB].PrintPlan();
                            Debug.Assert(false, "Single agent node with lower cost than parent! " + child.totalCost + " < " + node.totalCost);
                        }
                        if (child.totalCost <= this.maxCost)
                        {
                            openList.Add(child);
                            this.highLevelGenerated++;
                            addToGlobalConflictCount(child.GetConflict());
                            if (debug)
                            {
                                Debug.WriteLine("Child cost: " + child.totalCost);
                                Debug.WriteLine("Child hash: " + child.GetHashCode());
                                Debug.WriteLine("");
                            }
                        }
                    }
                    else // A timeout probably occured
                    {
                        return;
                    }
                }
                else
                {
                    this.closedListHits++;
                }
                node.agentBExpansion = CbsNode.ExpansionState.EXPANDED;
            }
        }