Przeglądaj źródła

Day 23 part 2 - slow but working

Lukas Angerer 2 lat temu
rodzic
commit
9d95bc5afa
5 zmienionych plików z 110 dodań i 28 usunięć
  1. 70 24
      Day23/Map.cs
  2. 20 0
      Day23/PathEdge.cs
  3. 6 4
      Day23/PathNode.cs
  4. 4 0
      Day23/Program.cs
  5. 10 0
      Day23/Tile.cs

+ 70 - 24
Day23/Map.cs

@@ -60,7 +60,9 @@ public class Map
     public void BuildGraph()
     {
         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()
@@ -68,54 +70,98 @@ public class Map
         Assert(_root != null);
 
         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));
         while (queue.Count > 0)
         {
             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)
             {
-                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) 
         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)

+ 20 - 0
Day23/PathEdge.cs

@@ -0,0 +1,20 @@
+namespace Day23;
+
+public record PathEdge(PathNode Start, PathNode End, List<Tile> Tiles)
+{
+    private static int _count = 0;
+
+    private PathEdge? _inverted;
+    
+    public int Id { get; private set; } = ++_count;
+
+    public PathEdge Invert()
+    {
+        if (_inverted == null)
+        {
+            _inverted = new PathEdge(End, Start, Enumerable.Reverse(Tiles).ToList()) { _inverted = this };
+        }
+
+        return _inverted;
+    }
+}

+ 6 - 4
Day23/PathNode.cs

@@ -2,7 +2,9 @@
 
 public class PathNode
 {
-    public List<Tile> Tiles { get; } = new List<Tile>();
-    public List<PathNode> Outgoing { get; } = new List<PathNode>();
-    public List<PathNode> Incoming { get; } = new List<PathNode>();
-}
+    private static int _count = 0;
+    
+    public int Id { get; private set; } = ++_count;
+    public List<PathEdge> Outgoing { get; } = new List<PathEdge>();
+    public List<PathEdge> Incoming { get; } = new List<PathEdge>();
+}

+ 4 - 0
Day23/Program.cs

@@ -25,4 +25,8 @@ var steps = map.LongestPath();
 Console.WriteLine();
 Console.WriteLine($"Longest Path: {steps}");
 
+steps = map.LongestPathIgnoreDirection();
+Console.WriteLine();
+Console.WriteLine($"Longest Path Ignoring Direction: {steps}");
+
 return 0;

+ 10 - 0
Day23/Tile.cs

@@ -22,6 +22,16 @@ public class Tile
         yield return Position.Add(Vec.Left);
     }
 
+    public Vec Previous()
+    {
+        return Position.Subtract(ArrowDirection());
+    }
+
+    public Vec Next()
+    {
+        return Position.Add(ArrowDirection());
+    }
+
     public Vec ArrowDirection()
     {
         return Value switch