public static Optional <Path> FindPath(MapPawn mapPawn)
    {
        HexCell start = mapPawn.Location;
        HexCell dest  = mapPawn.Destination;

        // For node n, cameFrom[n] is the node immediately preceding it on the cheapest path from start
        // to n currently known.
        Dictionary <HexCell, PathNode> cameFrom = MapPool <HexCell, PathNode> .GLGet();

        // For node n, gScore[n] is the cost of the cheapest path from start to n currently known.
        Dictionary <HexCell, int> gScore = MapPool <HexCell, int> .GLGet();

        gScore[start] = 0;

        // For node n, fScore[n] = gScore[n] + h(n). fScore[n] represents our current best guess as to
        // how short a path from start to finish can be if it goes through n.
        int startFScore = Heuristic(start, dest);

        // The set of discovered nodes that may need to be (re-)expanded.
        PriorityQueue <HexCell> openSet = ObjectPool <PriorityQueue <HexCell> > .GLGet();

        openSet.Add(start, startFScore);

        // The set of nodes for which a shortest path has already been found
        HashSet <HexCell> closedSet = HashSetPool <HexCell> .GLGet();

        while (!openSet.IsEmpty())
        {
            HexCell current = (HexCell)openSet.Poll();  // Explicit cast because we know this can never be null
            if (current == dest)
            {
                ObjectPool <PriorityQueue <HexCell> > .GLRestore(openSet);

                MapPool <HexCell, int> .GLRestore(gScore);

                HashSetPool <HexCell> .GLRestore(closedSet);

                return(ReconstructPath(cameFrom, current));
            }

            closedSet.Add(current);

            for (HexDirection dir = HexDirection.NE; dir <= HexDirection.NW; dir++)
            {
                HexCell neighbor = current.GetNeighbor(dir);

                if (neighbor == null || closedSet.Contains(neighbor))
                {
                    continue;                                                   // Dont waste time with already evaluated nodes
                }
                if (!mapPawn.CanTransverse(current, neighbor, dir))
                {
                    continue;                                                   // We cannot go there
                }
                int transversalCost  = mapPawn.TransversalCost(current, neighbor);
                int tentative_gScore = gScore[current] + transversalCost;

                int neighbor_gScore = gScore.ContainsKey(neighbor) ? gScore[neighbor] : int.MaxValue;
                if (tentative_gScore < neighbor_gScore)
                {
                    cameFrom[neighbor] = new PathNode(current, transversalCost);
                    gScore[neighbor]   = tentative_gScore;
                    int fScore = tentative_gScore + Heuristic(neighbor, dest);

                    if (!openSet.Update(neighbor, fScore))
                    {
                        openSet.Add(neighbor, fScore);
                    }
                }
            }
        }

        // Open set is empty but goal was never reached
        ObjectPool <PriorityQueue <HexCell> > .GLRestore(openSet);

        MapPool <HexCell, PathNode> .GLRestore(cameFrom);

        MapPool <HexCell, int> .GLRestore(gScore);

        HashSetPool <HexCell> .GLRestore(closedSet);

        return(null);
    }