forked from TheAlgorithms/C-Sharp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAStar.cs
144 lines (130 loc) · 4.97 KB
/
AStar.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using System.Collections.Generic;
using Algorithms.Search.AStar;
namespace AStar
{
/// <summary>
/// Contains the code for A* Pathfinding.
/// </summary>
public static class AStar
{
/// <summary>
/// Resets the Nodes in the list.
/// </summary>
/// <param name="nodes">Resets the nodes to be used again.</param>
public static void ResetNodes(List<Node> nodes)
{
foreach (var node in nodes)
{
node.CurrentCost = 0;
node.EstimatedCost = 0;
node.Parent = null;
node.State = NodeState.UNCONSIDERED;
}
}
/// <summary>
/// Generates the Path from an (solved) node graph, before it gets reset.
/// </summary>
/// <param name="target">The node where we want to go.</param>
/// <returns>The Path to the target node.</returns>
public static List<Node> GeneratePath(Node target)
{
var ret = new List<Node>();
Node? current = target;
while (!(current is null))
{
ret.Add(current);
current = current.Parent;
}
ret.Reverse();
return ret;
}
/// <summary>
/// Computes the path from => to.
/// </summary>
/// <param name="from">Start node.</param>
/// <param name="to">end node.</param>
/// <returns>Path from start to end.</returns>
public static List<Node> Compute(Node from, Node to)
{
var done = new List<Node>();
// A priority queue that will sort our nodes based on the total cost estimate
var open = new PriorityQueue<Node>();
foreach (var node in from.ConnectedNodes)
{
// Add connecting nodes if traversable
if (node.Traversable)
{
// Calculate the Costs
node.CurrentCost = from.CurrentCost + from.DistanceTo(node) * node.TraversalCostMultiplier;
node.EstimatedCost = from.CurrentCost + node.DistanceTo(to);
// Enqueue
open.Enqueue(node);
}
}
while (true)
{
// End Condition( Path not found )
if (open.Count == 0)
{
ResetNodes(done);
ResetNodes(open.GetData());
return new List<Node>();
}
// Selecting next Element from queue
var current = open.Dequeue();
// Add it to the done list
done.Add(current);
current.State = NodeState.CLOSED;
// EndCondition( Path was found )
if (current == to)
{
var ret = GeneratePath(to); // Create the Path
// Reset all Nodes that were used.
ResetNodes(done);
ResetNodes(open.GetData());
return ret;
}
else
{
AddOrUpdateConnected(current, to, open);
}
}
}
private static void AddOrUpdateConnected(Node current, Node to, PriorityQueue<Node> queue)
{
foreach (var connected in current.ConnectedNodes)
{
if (!connected.Traversable ||
connected.State == NodeState.CLOSED)
{
continue; // Do ignore already checked and not traversable nodes.
}
// Adds a previously not "seen" node into the Queue
if (connected.State == NodeState.UNCONSIDERED)
{
connected.Parent = current;
connected.CurrentCost = current.CurrentCost + current.DistanceTo(connected) * connected.TraversalCostMultiplier;
connected.EstimatedCost = connected.CurrentCost + connected.DistanceTo(to);
connected.State = NodeState.OPEN;
queue.Enqueue(connected);
}
else if (current != connected)
{
// Updating the cost of the node if the current way is cheaper than the previous
var newCCost = current.CurrentCost + current.DistanceTo(connected);
var newTCost = newCCost + current.EstimatedCost;
if (newTCost < connected.TotalCost)
{
connected.Parent = current;
connected.CurrentCost = newCCost;
}
}
else
{
// Codacy made me do it.
throw new PathfindingException("Detected the same node twice. Confusion how this could ever happen");
}
}
}
}
}