/// <summary>
        /// Computes if track without revresing is long enough for given length
        /// </summary>
        /// <param name="current"></param>
        /// <param name="from"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static bool IsDirectLengthEnough(this RailTrack current, RailTrack from, double length)
        {
            if (current.logicTrack.length > length)
            {
                return(true);
            }

            bool isFromInJuction  = current.inIsConnected && current.GetAllInBranches().Any(b => b.track == from);
            bool isFromOutJuction = current.outIsConnected && current.GetAllOutBranches().Any(b => b.track == from);

            if (current.inIsConnected && !isFromInJuction)
            {
                foreach (var branch in current.GetAllInBranches())
                {
                    if (CanGoToDirectly(current, from, branch.track) && IsDirectLengthEnough(branch.track, current, length - current.logicTrack.length))
                    {
                        return(true);
                    }
                }
            }

            if (current.outIsConnected && !isFromOutJuction)
            {
                foreach (var branch in current.GetAllOutBranches())
                {
                    if (CanGoToDirectly(current, from, branch.track) && IsDirectLengthEnough(branch.track, current, length - current.logicTrack.length))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
        public static bool IsTrackOutBranch(this RailTrack current, RailTrack next)
        {
            if (!current.outIsConnected)
            {
                return(false);
            }

            return(current.GetAllOutBranches().Any(b => b.track == next));
        }
        public static bool CanGoToDirectly(this RailTrack current, RailTrack from, RailTrack to, out Junction reversingJunction)
        {
            reversingJunction = null;

            bool isInJuction  = current.inIsConnected && current.GetAllInBranches().Any(b => b.track == to);
            bool isOutJuction = current.outIsConnected && current.GetAllOutBranches().Any(b => b.track == to);

            if (current.inIsConnected)
            {
                //Terminal.Log($"IN: {from?.logicTrack.ID.FullID} -> {current.logicTrack.ID.FullID} -> {to?.logicTrack.ID.FullID}");
                if (isInJuction && CanGoThroughJunctionDirectly(current, current.inJunction, from, to))
                {
                    return(true);
                }
            }

            if (current.outIsConnected)
            {
                //Terminal.Log($"OUT: {from?.logicTrack.ID.FullID} -> {current.logicTrack.ID.FullID} -> {to?.logicTrack.ID.FullID}");
                if (isOutJuction && CanGoThroughJunctionDirectly(current, current.outJunction, from, to))
                {
                    return(true);
                }
            }

            if (isInJuction)
            {
                reversingJunction = current.inJunction;
            }

            if (isOutJuction)
            {
                reversingJunction = current.outJunction;
            }

            return(false);
        }
Beispiel #4
0
        /// <summary>
        /// A* search
        /// </summary>
        /// <param name="allowReverse"></param>
        /// <param name="carsToIgnore"></param>
        /// <param name="consistLength"></param>
        protected async System.Threading.Tasks.Task Astar(bool allowReverse, HashSet <string> carsToIgnore, double consistLength, List <TrackTransition> bannedTransitions)
        {
            await Await.BackgroundSyncContext();

            cameFrom  = new Dictionary <RailTrack, RailTrack>();
            costSoFar = new Dictionary <RailTrack, double>();

            //var queue = new PriorityQueue<RailTrack>();
            var queue = new GenericPriorityQueue <RailTrackNode, double>(10000);

            queue.Enqueue(new RailTrackNode(start), 0.0);

            cameFrom.Add(start, start);
            costSoFar.Add(start, 0.0);

            RailTrack current = null;


            while (queue.Count > 0)
            {
                current = queue.Dequeue().track;

                RailTrack prev = null;
                cameFrom.TryGetValue(current, out prev);

                string debug = $"ID: {current.logicTrack.ID.FullID} Prev: {prev?.logicTrack.ID.FullID}";

                List <RailTrack> neighbors = new List <RailTrack>();

                if (current.outIsConnected)
                {
                    neighbors.AddRange(current.GetAllOutBranches().Select(b => b.track));
                }

                if (current.inIsConnected)
                {
                    neighbors.AddRange(current.GetAllInBranches().Select(b => b.track));
                }
                string branches = DumpNodes(neighbors, current);
                debug += "\n" + $"all branches: {branches}";

#if DEBUG2
                Terminal.Log(debug);
#endif

                foreach (var neighbor in neighbors)
                {
                    if (bannedTransitions != null && bannedTransitions.All(t => t.track == current && t.nextTrack == neighbor))
                    {
                        Terminal.Log($"{current.logicTrack.ID.FullID}->{neighbor.logicTrack.ID.FullID} banned");
                        continue;
                    }

                    //if non start/end track is not free omit it
                    if (neighbor != start && neighbor != goal && !neighbor.logicTrack.IsFree(carsToIgnore))
                    {
                        Terminal.Log($"{neighbor.logicTrack.ID.FullID} not free");
                        continue;
                    }

                    //if we could go through junction directly (without reversing)
                    bool isDirect = current.CanGoToDirectly(prev, neighbor);

                    if (!allowReverse && !isDirect)
                    {
                        Terminal.Log($"{neighbor.logicTrack.ID.FullID} reverse needed");
                        continue;
                    }

                    // compute exact cost
                    //double newCost = costSoFar[current] + neighbor.logicTrack.length;
                    double newCost = costSoFar[current] + neighbor.logicTrack.length / neighbor.GetAverageSpeed();

                    if (!isDirect)
                    {
                        // if we can't fit consist on this track to reverse, drop this neighbor
                        if (prev != null && !current.IsDirectLengthEnough(prev, consistLength))
                        {
                            Terminal.Log($"{neighbor.logicTrack.ID.FullID} not long enough to reverse");
                            continue;
                        }

                        //add penalty when we must reverse

                        //newCost += 2.0 * consistLength + 30.0;
                    }

                    // If there's no cost assigned to the neighbor yet, or if the new
                    // cost is lower than the assigned one, add newCost for this neighbor
                    if (!costSoFar.ContainsKey(neighbor) || newCost < costSoFar[neighbor])
                    {
                        // If we're replacing the previous cost, remove it
                        if (costSoFar.ContainsKey(neighbor))
                        {
                            costSoFar.Remove(neighbor);
                            cameFrom.Remove(neighbor);
                        }

                        //Terminal.Log($"neighbor {neighbor.logicTrack.ID.FullID} update {newCost}");

                        costSoFar.Add(neighbor, newCost);
                        cameFrom.Add(neighbor, current);
                        double priority = newCost + Heuristic(neighbor, goal)
                                          / 20.0f; //convert distance to time (t = s / v)
                        queue.Enqueue(new RailTrackNode(neighbor), priority);
                    }
                }
            }

            await Await.UnitySyncContext();
        }