|
@@ -60,7 +60,9 @@ public class Map
|
|
|
public void BuildGraph()
|
|
public void BuildGraph()
|
|
|
{
|
|
{
|
|
|
var startTile = At(_start)!;
|
|
var startTile = At(_start)!;
|
|
|
- _root = BuildPath(startTile, Neighbors(startTile).Single().Position.Subtract(startTile.Position), new Dictionary<Vec, PathNode>());
|
|
|
|
|
|
|
+ var map = new Dictionary<Vec, PathNode>();
|
|
|
|
|
+ BuildEdge(startTile, Neighbors(startTile).Single().Position.Subtract(startTile.Position), map);
|
|
|
|
|
+ _root = map[startTile.Position];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public int LongestPath()
|
|
public int LongestPath()
|
|
@@ -68,54 +70,98 @@ public class Map
|
|
|
Assert(_root != null);
|
|
Assert(_root != null);
|
|
|
|
|
|
|
|
var longest = 0;
|
|
var longest = 0;
|
|
|
- var queue = new Queue<(PathNode Path, int Length)>();
|
|
|
|
|
|
|
+ var visited = new HashSet<PathNode>();
|
|
|
|
|
+ var queue = new Queue<(PathNode Node, int Length)>();
|
|
|
queue.Enqueue((_root!, 0));
|
|
queue.Enqueue((_root!, 0));
|
|
|
while (queue.Count > 0)
|
|
while (queue.Count > 0)
|
|
|
{
|
|
{
|
|
|
var (current, length) = queue.Dequeue();
|
|
var (current, length) = queue.Dequeue();
|
|
|
- var nextLength = length + current.Tiles.Count + 1;
|
|
|
|
|
|
|
+ if (current.Outgoing.Count > 2)
|
|
|
|
|
+ {
|
|
|
|
|
+ throw new Exception("Graph only has T-junctions");
|
|
|
|
|
+ }
|
|
|
|
|
+ visited.Add(current);
|
|
|
|
|
|
|
|
if (current.Outgoing.Count == 0)
|
|
if (current.Outgoing.Count == 0)
|
|
|
{
|
|
{
|
|
|
- longest = int.Max(longest, nextLength);
|
|
|
|
|
|
|
+ longest = int.Max(longest, length);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- foreach (var next in current.Outgoing)
|
|
|
|
|
|
|
+ foreach (var o in current.Outgoing)
|
|
|
{
|
|
{
|
|
|
- queue.Enqueue((next, nextLength));
|
|
|
|
|
|
|
+ if (!visited.Contains(o.End))
|
|
|
|
|
+ {
|
|
|
|
|
+ queue.Enqueue((o.End, length + o.Tiles.Count + 1));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// start should not be counted (-1) and end is counted double (-1)
|
|
// start should not be counted (-1) and end is counted double (-1)
|
|
|
return longest - 2;
|
|
return longest - 2;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- private PathNode BuildPath(Tile start, Vec dir, IDictionary<Vec, PathNode> map)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ public int LongestPathIgnoreDirection()
|
|
|
{
|
|
{
|
|
|
- if (map.TryGetValue(start.Position, out PathNode? path))
|
|
|
|
|
|
|
+ Assert(_root != null);
|
|
|
|
|
+
|
|
|
|
|
+ var longest = 0;
|
|
|
|
|
+ var queue = new Queue<(PathNode Node, int Length, HashSet<PathNode> Visited)>();
|
|
|
|
|
+ queue.Enqueue((_root!, 0, new HashSet<PathNode>()));
|
|
|
|
|
+ while (queue.Count > 0)
|
|
|
{
|
|
{
|
|
|
- return path;
|
|
|
|
|
|
|
+ var (current, length, visited) = queue.Dequeue();
|
|
|
|
|
+ //Console.WriteLine(current.Id);
|
|
|
|
|
+ if (current.Outgoing.Count > 2)
|
|
|
|
|
+ {
|
|
|
|
|
+ throw new Exception("Graph only has T-junctions");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var newVisited = new HashSet<PathNode>(visited) { current };
|
|
|
|
|
+
|
|
|
|
|
+ if (current.Outgoing.Count == 0 && current != _root)
|
|
|
|
|
+ {
|
|
|
|
|
+ longest = int.Max(longest, length);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ foreach (var o in current.Outgoing.Concat(current.Incoming.Select(x => x.Invert())))
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!visited.Contains(o.End))
|
|
|
|
|
+ {
|
|
|
|
|
+ queue.Enqueue((o.End, length + o.Tiles.Count + 1, newVisited));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- var pathNode = new PathNode();
|
|
|
|
|
- pathNode.Tiles.AddRange(Follow(start, dir));
|
|
|
|
|
- map[start.Position] = pathNode;
|
|
|
|
|
|
|
|
|
|
- var arrowTile = pathNode.Tiles.Last();
|
|
|
|
|
- var crossing = At(arrowTile.Position.Add(arrowTile.ArrowDirection()))!;
|
|
|
|
|
|
|
+ // start should not be counted (-1) and end is counted double (-1)
|
|
|
|
|
+ return longest - 2;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (crossing != At(_goal))
|
|
|
|
|
|
|
+ private void BuildEdge(Tile start, Vec dir, IDictionary<Vec, PathNode> map)
|
|
|
|
|
+ {
|
|
|
|
|
+ var previous = At(start.Previous())!;
|
|
|
|
|
+ map.TryAdd(previous.Position, new PathNode());
|
|
|
|
|
+ var startNode = map[previous.Position];
|
|
|
|
|
+
|
|
|
|
|
+ var tiles = Follow(start, dir).ToList();
|
|
|
|
|
+ var next = At(tiles.Last().Next())!;
|
|
|
|
|
+ map.TryAdd(next.Position, new PathNode());
|
|
|
|
|
+ var endNode = map[next.Position];
|
|
|
|
|
+
|
|
|
|
|
+ if (startNode.Outgoing.All(o => o.End != endNode))
|
|
|
{
|
|
{
|
|
|
- foreach (var outgoing in Neighbors(crossing)
|
|
|
|
|
- .Where(x => x.Position.Add(x.ArrowDirection()) != crossing.Position))
|
|
|
|
|
|
|
+ var edge = new PathEdge(startNode, endNode, tiles);
|
|
|
|
|
+ startNode.Outgoing.Add(edge);
|
|
|
|
|
+ endNode.Incoming.Add(edge);
|
|
|
|
|
+
|
|
|
|
|
+ if (next != At(_goal))
|
|
|
{
|
|
{
|
|
|
- var outgoingPath = BuildPath(outgoing, outgoing.ArrowDirection(), map);
|
|
|
|
|
- outgoingPath.Incoming.Add(pathNode);
|
|
|
|
|
- pathNode.Outgoing.Add(outgoingPath);
|
|
|
|
|
|
|
+ foreach (var outgoing in Neighbors(next)
|
|
|
|
|
+ .Where(x => x.Position.Add(x.ArrowDirection()) != next.Position))
|
|
|
|
|
+ {
|
|
|
|
|
+ BuildEdge(outgoing, outgoing.ArrowDirection(), map);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- return pathNode;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private Tile? At(Vec p)
|
|
private Tile? At(Vec p)
|