-
Notifications
You must be signed in to change notification settings - Fork 0
/
DijkstraPathFinder.cs
125 lines (111 loc) · 5.29 KB
/
DijkstraPathFinder.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
using System.Collections.Generic;
using System.Linq;
using Greedy.Architecture;
using System.Drawing;
namespace Greedy
{
public class DijkstraData
{
public Point Previous { get; set; }
public int Price { get; set; }
}
public class PointAndPrice
{
public Point Point { get; set; }
public int Price { get; set; }
}
public class DijkstraPathFinder
{
public readonly Point[] Offsets = {new Point(-1, 0), new Point(1, 0), new Point(0, -1), new Point(0, 1)};
public HashSet<PointAndPrice> GetPossibleNextSteps(State state, HashSet<Point> visited, Point current)
{
var lastPoint = current;
var nextSteps = Offsets
.Where(offset => !visited.Contains(new Point(offset.X + lastPoint.X, offset.Y + lastPoint.Y))
&& state.InsideMap(offset + (Size) lastPoint)
&& state.CellCost[(offset + (Size) lastPoint).X, (offset + (Size) lastPoint).Y] != 0)
.Select(point => new PointAndPrice
{
Point = point + (Size) lastPoint,
Price = state.CellCost[(point + (Size) lastPoint).X, (point + (Size) lastPoint).Y]
})
.ToHashSet();
return nextSteps;
}
public PathWithCost GetPathWithCost(Dictionary<Point, DijkstraData> dict, Point target, Point start)
{
var cost = dict[target].Price;
var path = new List<Point> {target};
var point = new Point(-int.MaxValue, -int.MaxValue);
while (point != start)
{
point = dict[target].Previous;
path.Add(point);
target = point;
}
path.Reverse();
return new PathWithCost(cost, path.ToArray());
}
public IEnumerable<PathWithCost> GetPathsByDijkstra(State state, Point start, IEnumerable<Point> targets)
{
var dict = new Dictionary<Point, DijkstraData> {[start] = new DijkstraData {Price = 0, Previous = start}};
var visited = new HashSet<Point> {start};
var listTargets = targets.ToList(); // список еще не найденных сундуков
var toOpen = start; // вершина которую мы сейчас собираемся раскрывать
while (true)
{
// если в toOpen находится сундук - лениво возвращаем его
if (listTargets.Contains(toOpen))
{
yield return GetPathWithCost(dict, toOpen, start);
listTargets.Remove(toOpen);
}
if (listTargets.Count == 0) // если сундуков не осталось
yield break;
// находим всех кандидатов для след шага кроме уже посещенных точек
var listPossiblePoints = GetPossibleNextSteps(state, visited, toOpen);
// если нет ни одного кандидата куда перейти, значит мы зашли в тупик => пытаемся
// перейти на клетку из Dict с минимальной ценой, иначе нет пути
if (listPossiblePoints.Count == 0)
{
try
{
toOpen = dict
.Where(d => !visited.Contains(d.Key))
.OrderBy(d => d.Value.Price)
.First().Key;
}
catch
{
yield break;
}
}
foreach (var possiblePoint in listPossiblePoints.Where(possiblePoint =>
!dict.ContainsKey(possiblePoint.Point)))
{
dict.Add(possiblePoint.Point, new DijkstraData {Price = int.MaxValue, Previous = start});
}
// если цена с учетом перехода меньше чем записанная для этой точки, то перезаписываем
foreach (var possiblePoint in listPossiblePoints.Where(possiblePoint =>
possiblePoint.Price + dict[toOpen].Price < dict[possiblePoint.Point].Price))
{
dict[possiblePoint.Point].Price = possiblePoint.Price + dict[toOpen].Price;
dict[possiblePoint.Point].Previous = toOpen;
}
visited.Add(toOpen); // добавляем старую точку в список посещенных
// ищем новую самую дешевую точку для посещения из непосещенных, иначе нет пути
try
{
toOpen = dict
.Where(d => !visited.Contains(d.Key))
.OrderBy(d => d.Value.Price)
.First().Key;
}
catch
{
yield break;
}
}
}
}
}