// inspired by http://www.redblobgames.com/pathfinding/a-star/introduction.html
        public virtual PathResult pathFromTo(Tile start, Tile goal, HeuristicPathFinder heuristic, bool playersCanBlockPath = false)
        {
            HexRegion region = GameControl.gameSession.mapGenerator.getRegion() as HexRegion;

            return(pathFromTo(region, start, goal, heuristic, playersCanBlockPath));
        }
        public virtual PathResult pathFromTo(HexRegion region, Tile start, Tile goal, HeuristicPathFinder heuristic, bool playersCanBlockPath = false)
        {
            PathResult pathResult = new PathResult();

            PathTile goalPt = new PathTile(goal);

            // set up lists
            PriorityQueue <PathTile>       frontier = new PriorityQueue <PathTile>();
            Dictionary <Vector2, PathTile> explored = new Dictionary <Vector2, PathTile>();
            Dictionary <Vector2, PathTile> previous = new Dictionary <Vector2, PathTile>();
            Dictionary <Vector2, float>    costs    = new Dictionary <Vector2, float>();

            PathTile crt;

            crt       = new PathTile(start);
            crt.depth = 0;

            frontier.Enqueue(crt, 0);
            previous[crt.tile.index] = null;
            costs[crt.tile.index]    = 0;

            // start pathfinding
            while (!frontier.IsEmpty())
            {
                // get current
                crt = frontier.Dequeue();

                // record that the tile was explored
                explored[crt.tile.index] = crt;

                if (crt.CompareTo(goalPt))
                {
                    // reached goal; search complete
                    pathResult.reachedGoal = true;
                    pathResult.pathCost    = costs[crt.tile.index];
                    break;
                }

                // get neighbor tiles
                List <PathTile> neighbors = new List <PathTile>();
                foreach (Tile neighborTile in region.getTileNeighbors(crt.tile.index))
                {
                    PathTile neighbor = new PathTile(neighborTile);
                    //neighborPt.cost = crt.cost + costBetween(crt, neighborPt);
                    neighbor.depth = crt.depth + 1;
                    neighbors.Add(neighbor);
                }

                // add neighbor tiles to search
                float cost, priority;
                foreach (PathTile neighbor in neighbors)
                {
                    // check if exceeding max depth
                    if (neighbor.depth > maxDepth)
                    {
                        break;
                    }

                    // compute cost
                    float _cost = costBetween(crt, neighbor);

                    // check if path is blocked by another player
                    if (playersCanBlockPath && GameControl.gameSession.checkForPlayersAt(neighbor.tile) != null)
                    {
                        if (!neighbor.CompareTo(goalPt))    // ensures that you can move to a tile with an enemy
                        {
                            _cost = float.PositiveInfinity; // set highest cost to signify that the tile is unreachable
                        }
                    }

                    cost = costs[crt.tile.index] + _cost;

                    if (cost <= maxCost)
                    {
                        if (!costs.ContainsKey(neighbor.tile.index) || cost < costs[neighbor.tile.index])
                        {
                            costs[neighbor.tile.index] = cost;

                            // compute heuristic priority
                            priority  = cost + heuristic.heuristic(neighbor, goalPt);
                            priority -= neighbor.depth * heuristicDepthInfluence; // makes so that tiles closest to goal are more eagerly explored

                            frontier.Enqueue(neighbor, priority);

                            previous[neighbor.tile.index] = crt;
                        }
                    }
                }
            }

            // build list of tiles on path if goal was reached
            if (pathResult.reachedGoal)
            {
                pathResult.addPathtile(goalPt);

                crt = previous[goal.index];

                while (crt != null)
                {
                    pathResult.addPathtile(crt);
                    crt = previous[crt.tile.index];
                }
            }

            foreach (PathTile pt in explored.Values)
            {
                pathResult.addExploredPathtile(pt);
            }

            return(pathResult);
        }