/// <summary>Returns an <c>IPath</c> for the optimal path from coordinates <c>start</c> to <c>goal</c>.</summary>
        /// <param name="start">Coordinates for the <c>last</c> step on the desired path.</param>
        /// <param name="goal">Coordinates for the <c>first</c> step on the desired path.</param>
        /// <param name="directedStepCost">Cost to extend path by hex at <c>coords</c> from hex at direction <c>hexside</c>.</param>
        /// <param name="heuristic">Returns a monotonic (ie locally admissible) cost estimate from a range value.</param>
        /// <param name="isOnboard">Returns whether the coordinates specified are "on board".</param>
        /// ///<remarks>Note that <c>heuristic</c> <b>must</b> be monotonic in order for the algorithm to perform properly.</remarks>
        /// <seealso cref="http://www.cs.trincoll.edu/~ram/cpsc352/notes/astar.html"/>
        public static IDirectedPath FindDirectedPath(
            IHex start,
            IHex goal,
            Func <IHex, Hexside, int> directedStepCost,
            Func <int, int> heuristic,
            Func <HexCoords, bool> isOnboard
            )
        {
            if (start == null)
            {
                throw new ArgumentNullException("start");
            }
            if (goal == null)
            {
                throw new ArgumentNullException("goal");
            }
            if (directedStepCost == null)
            {
                throw new ArgumentNullException("directedStepCost");
            }
            if (heuristic == null)
            {
                throw new ArgumentNullException("heuristic");
            }
            if (isOnboard == null)
            {
                throw new ArgumentNullException("isOnboard");
            }

            var queue = new DictionaryPriorityQueue <int, DirectedPath>();

            var functor = new PathQueueFunctor(start, goal, heuristic, directedStepCost, queue);

            queue.Enqueue(0, new DirectedPath(goal));

            HexKeyValuePair <int, DirectedPath> item;

            while (queue.TryDequeue(out item))
            {
                if (functor.PathFound(item))
                {
                    return(functor.Path);
                }
            }
            return(null);
        }
        /// <summary>Returns an <c>IPath</c> for the optimal path from coordinates <c>start</c> to <c>goal</c>.</summary>
        /// <param name="start">Coordinates for the <c>last</c> step on the desired path.</param>
        /// <param name="goal">Coordinates for the <c>first</c> step on the desired path.</param>
        /// <param name="stepCost">Cost to extend path by hex at <c>coords</c> from hex at direction <c>hexside</c>.</param>
        /// <param name="heuristic">Returns a monotonic (ie locally admissible) cost estimate from a range value.</param>
        /// <returns></returns>
        /// ///<remarks>Note that <c>heuristic</c> <b>must</b> be monotonic in order for the algorithm to perform properly.</remarks>
#pragma warning disable 1658, 1584
        /// <seealso cref="http://www.cs.trincoll.edu/~ram/cpsc352/notes/astar.html"/>
#pragma warning restore 1658, 1584
        public static IDirectedPath FindDirectedPathFwd(
          IHex start,
          IHex goal,
          Func<IHex, Hexside, double> stepCost,
          Func<int, int> heuristic
        )
        {
            if (start == null) throw new ArgumentNullException("start");
            if (goal == null) throw new ArgumentNullException("goal");
            if (stepCost == null) throw new ArgumentNullException("stepCost");

            var vectorGoal = goal.Coords.Canon - start.Coords.Canon;
            var closed = new HashSet<HexCoords>();
            var open = new HashSet<HexCoords>();
            var queue = new DictionaryPriorityQueue<double, DirectedPath>();

            queue.Enqueue(0, new DirectedPath(goal));

            HexKeyValuePair<double, DirectedPath> item;
            while (queue.TryDequeue(out item))
            {
                var path = item.Value;
                open.Add(path.PathStep.Hex.Coords);
                if (closed.Contains(path.PathStep.Hex.Coords)) continue;

                if (path.PathStep.Hex.Equals(start))
                {
                    return path;
                }

                closed.Add(path.PathStep.Hex.Coords);

                foreach (var neighbour in path.PathStep.Hex.GetNeighbourHexes()
                )
                {
                    if (!open.Contains(neighbour.Hex.Coords))
                    {
                        var cost = stepCost(neighbour.Hex, neighbour.HexsideExit);
                        if (cost > 0)
                        {
                            var newPath = path.AddStep(neighbour, cost);
                            var key = Estimate(heuristic, vectorGoal, start.Coords, neighbour.Hex.Coords,
                                            newPath.TotalCost);
                            queue.Enqueue(key, newPath);
                        }
                    }
                }
            }
            return null;
        }