private void     ExpandHex(IDirectedPath path, Hexside hexside)
        {
            var here  = path.PathStep.Coords;
            var there = here.GetNeighbour(hexside);

            if (!ClosedSet.Contains(there))
            {
                TryStepCost(here, hexside).IfHasValueDo(cost => {
                    if (path.TotalCost + cost < BestSoFar || !OpenSet.ContainsKey(there))
                    {
                        Heuristic(there).IfHasValueDo(heuristic => {
                            var key     = path.TotalCost + cost + heuristic;
                            var newPath = path.AddStep(there, HexsideDirection(hexside), cost);

                            PathfinderExtensions.TraceFindPathEnqueue(there, key, 0);

                            if (!OpenSet.TryGetValue(there, out var oldPath))
                            {
                                OpenSet.Add(there, newPath);
                                Queue.Enqueue(key, newPath);
                            }
                            else if (newPath.TotalCost < oldPath.TotalCost)
                            {
                                OpenSet.Remove(there);
                                OpenSet.Add(there, newPath);
                                Queue.Enqueue(key, newPath);
                            }

                            SetBestSoFar(newPath, PartnerPath(there));
                        });
                    }
                });
            }
        }
        public bool     IsFinished()
        {
            if (Queue.TryDequeue(out var item))
            {
                var path   = item.Value;
                var coords = path.PathStep.Coords;

                OpenSet.Remove(coords);
                if (!ClosedSet.Contains(coords))
                {
                    PathfinderExtensions.TraceFindPathDequeue(GetType().Name, coords, path, item.Key, 0);

                    if (item.Key < BestSoFar)
                    {
                        Partner.Heuristic(coords).IfHasValueDo(heuristic => {
                            if (path.TotalCost + Partner.FrontierMinimum() - heuristic < BestSoFar)
                            {
                                Hexside.ForEach(hexside => ExpandHex(path, hexside));
                            }
                        });
                    }
                    ClosedSet.Add(coords);
                }
                return(!Queue.Any());
            }
            return(true);
        }
        /// <inheritdoc/>
        public void SetBestSoFar(IDirectedPath pathRev, IDirectedPath pathFwd)
        {
            if (pathFwd.TotalCost + pathRev.TotalCost < BestSoFar)
            {
                _pathRev  = pathRev;
                _pathFwd  = pathFwd;
                BestSoFar = _pathRev.TotalCost + _pathFwd.TotalCost;

                PathfinderExtensions.TraceFindPathDetailBestSoFar(pathFwd, pathRev, BestSoFar);
            }
        }
        /// <param name="pathHalves"></param>
        /// <param name="isForward"></param>
        public AltPathfinder(IPathHalves pathHalves, bool isForward)
        {
            IsForward   = isForward;
            ClosedSet   = pathHalves.ClosedSet;
            StartCoords = pathHalves.Start;
            GoalCoords  = pathHalves.Goal;
            Landmarks   = pathHalves.Board.Landmarks;
            OpenSet     = new Dictionary <HexCoords, IDirectedPath>();
            Queue       = HotPriorityQueue.New <IDirectedPath>(0, 256);
            TryStepCost = IsForward ? (StepCost)pathHalves.Board.TryEntryCost : pathHalves.Board.TryExitCost;
            PathHalves  = pathHalves;

            PathfinderExtensions.TraceFindPathDetailDirection(Direction, GoalCoords - StartCoords);

            StartPath(IsForward ? StartCoords : GoalCoords);
        }
        /// <inheritdoc/>
        public Maybe <IDirectedPath> GetPath(AltPathfinder pathfinderFwd, AltPathfinder pathfinderRev)
        {
            pathfinderFwd.Partner = pathfinderRev;
            pathfinderRev.Partner = pathfinderFwd;
            var pathfinder = pathfinderFwd;

            // Until done: alternate searching from each direction and calling the other direction
            while (!pathfinder.IsFinished())
            {
                pathfinder = pathfinder.Partner;
            }

            PathfinderExtensions.TraceFindPathDone(ClosedSet.Count);

            return(_pathFwd.MergePaths(_pathRev));
        }