/// <summary>
 ///     Computes the Manhattan distance from the current node to the end node.
 /// </summary>
 /// <param name="current">The current node.</param>
 /// <param name="end">The end node.</param>
 /// <returns>Returns the Manhattan distance from the current node to the end node.</returns>
 private static int Heuristic(AStarNode current, AStarNode end)
 {
     return(Math.Abs(current.X - end.X) + Math.Abs(current.Y - current.Y));
 }
        public AStarNode GetNodeNearestWaterSource(AStarNode node)
        {
            List <AStarNode> list = new List <AStarNode>();

            for (int i = 1; i < 30; i++)
            {
                AStarNode goalNode = this.GetNode(node.X + i, node.Y);

                if (goalNode is not null && goalNode.TileClear && !goalNode.IsWateringCanFillingSource())
                {
                    list.Add(goalNode);
                }

                goalNode = this.GetNode(node.X - i, node.Y);
                if (goalNode is not null && goalNode.TileClear && !goalNode.IsWateringCanFillingSource())
                {
                    list.Add(goalNode);
                }

                goalNode = this.GetNode(node.X, node.Y + i);
                if (goalNode is not null && goalNode.TileClear && !goalNode.IsWateringCanFillingSource())
                {
                    list.Add(goalNode);
                }

                goalNode = this.GetNode(node.X, node.Y - i);
                if (goalNode is not null && goalNode.TileClear && !goalNode.IsWateringCanFillingSource())
                {
                    list.Add(goalNode);
                }

                if (list.Count > 0)
                {
                    break;
                }
            }

            if (list.Count == 0)
            {
                return(null);
            }

            int   minIndex    = 0;
            float minDistance = float.MaxValue;

            for (int i = 1; i < list.Count; i++)
            {
                float distance = Vector2.Distance(ClickToMoveHelper.PlayerOffsetPosition, list[i].NodeCenterOnMap);
                if (distance < minDistance)
                {
                    minDistance = distance;
                    minIndex    = i;
                }
            }

            int x = node.X;
            int y = node.Y;

            if (list[minIndex].X != node.X)
            {
                x = list[minIndex].X <= node.X ? list[minIndex].X + 1 : list[minIndex].X - 1;
            }
            else
            {
                y = list[minIndex].Y <= node.Y ? list[minIndex].Y + 1 : list[minIndex].Y - 1;
            }

            return(this.GetNode(x, y));
        }
        public AStarPath FindPathToNeighbourDiagonalWithBubbleCheck(AStarNode startNode, AStarNode endNode)
        {
            AStarPath pathWithBubbleCheck = this.FindPathWithBubbleCheck(startNode, endNode);

            if (pathWithBubbleCheck is not null)
            {
                return(pathWithBubbleCheck);
            }

            if (endNode.FakeTileClear)
            {
                double    minDistance = double.MaxValue;
                AStarNode nearestNode = null;
                foreach (WalkDirection walkDirection in WalkDirection.DiagonalDirections)
                {
                    AStarNode node = this.GetNode(endNode.X + walkDirection.X, endNode.Y + walkDirection.Y);

                    if (node is not null && node.TileClear)
                    {
                        double distance = ClickToMoveHelper.SquaredEuclideanDistance(
                            startNode.X,
                            startNode.Y,
                            node.X,
                            node.Y);

                        if (distance < minDistance)
                        {
                            nearestNode = node;
                            minDistance = distance;
                        }
                    }
                }

                if (nearestNode is not null)
                {
                    return(this.FindPathWithBubbleCheck(startNode, nearestNode));
                }
            }

            return(null);
        }
        public AStarNode GetNearestLandNodePerpendicularToWaterSource(AStarNode nodeClicked)
        {
            AStarNode result = nodeClicked;

            AStarNode node;

            if (this.FarmerNodeOffset.X == nodeClicked.X ||
                (this.FarmerNodeOffset.Y != nodeClicked.Y && Math.Abs(nodeClicked.X - this.FarmerNodeOffset.X)
                 > Math.Abs(nodeClicked.Y - this.FarmerNodeOffset.Y)))
            {
                if (nodeClicked.Y > this.FarmerNodeOffset.Y)
                {
                    for (int i = nodeClicked.Y; i >= this.FarmerNodeOffset.Y; i--)
                    {
                        node = this.GetNode(nodeClicked.X, i);
                        if (node is not null && node.TileClear && !node.IsWateringCanFillingSource())
                        {
                            return(result);
                        }

                        result = node;
                    }
                }
                else
                {
                    for (int i = nodeClicked.Y; i <= this.FarmerNodeOffset.Y; i++)
                    {
                        node = this.GetNode(nodeClicked.X, i);
                        if (node is not null && node.TileClear && !node.IsWateringCanFillingSource())
                        {
                            return(result);
                        }

                        result = node;
                    }
                }
            }
            else if (nodeClicked.X > this.FarmerNodeOffset.X)
            {
                for (int i = nodeClicked.X; i >= this.FarmerNodeOffset.X; i--)
                {
                    node = this.GetNode(i, nodeClicked.Y);
                    if (node is not null && node.TileClear && !node.IsWateringCanFillingSource())
                    {
                        return(result);
                    }

                    result = node;
                }
            }
            else
            {
                for (int i = nodeClicked.X; i <= this.FarmerNodeOffset.X; i++)
                {
                    node = this.GetNode(i, nodeClicked.Y);
                    if (node is not null && node.TileClear && !node.IsWateringCanFillingSource())
                    {
                        return(result);
                    }

                    result = node;
                }
            }

            node = this.GetNodeNearestWaterSource(nodeClicked) ?? this.GetNodeNearestWaterSource(this.FarmerNodeOffset);

            return(node);
        }
        /// <summary>
        ///     Computes a path between the two specified nodes.
        /// </summary>
        /// <param name="startNode">The start node.</param>
        /// <param name="endNode">The end node.</param>
        /// <returns>A path from the start node to the end node.</returns>
        public AStarPath FindPath(AStarNode startNode, AStarNode endNode)
        {
            if (startNode is null || endNode is null)
            {
                return(null);
            }

            startNode.GCost        = 0;
            startNode.HCost        = AStarGraph.Heuristic(startNode, endNode);
            startNode.PreviousNode = null;

            // The set of discovered nodes that may need to be expanded.
            // Initially, only the start node is known.
            FastBinaryHeap <AStarNode> openSet = new FastBinaryHeap <AStarNode>();

            openSet.Push(startNode);

            // The set of nodes already expanded.
            HashSet <AStarNode> closedSet = new HashSet <AStarNode>();

            while (!openSet.IsEmpty())
            {
                AStarNode currentNode = openSet.Pop();

                if (currentNode == endNode)
                {
                    return(new AStarPath(startNode, endNode));
                }

                closedSet.Add(currentNode);

                foreach (AStarNode neighbour in currentNode.GetNeighbours())
                {
                    if (closedSet.Contains(neighbour) ||
                        (this.gameLocation is FarmHouse && !endNode.IsBlockingBedTile() &&
                         neighbour.IsBlockingBedTile()))
                    {
                        continue;
                    }

                    int gCost = currentNode.GCost + 1;

                    bool visited = openSet.Contains(neighbour);

                    if (gCost < neighbour.GCost || !visited)
                    {
                        // This path to neighbour is better than any previous one. Record it!
                        neighbour.GCost        = gCost;
                        neighbour.HCost        = AStarGraph.Heuristic(neighbour, endNode);
                        neighbour.PreviousNode = currentNode;

                        if (visited)
                        {
                            openSet.Heapify(neighbour);
                        }
                        else
                        {
                            openSet.Push(neighbour);
                        }
                    }
                }
            }

            // Open set is empty but goal was never reached.
            return(null);
        }