Advertisement
mgla

Advent of Code - 2024 - Day 12

Dec 12th, 2024 (edited)
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 4.95 KB | None | 0 0
  1. var input = await File.ReadAllLinesAsync("input.txt");
  2.  
  3. var gridSize = input.Length;
  4. var grid = new Node[gridSize, gridSize];
  5. var regions = new List<(char Value, HashSet<Node> Nodes)>();
  6.  
  7. for (var row = 0; row < gridSize; row++)
  8. {
  9.     for (var col = 0; col < gridSize; col++)
  10.     {
  11.         grid[row, col] = new Node(input[row][col], new Position(row, col), []);
  12.     }
  13. }
  14.  
  15. for (var row = 0; row < gridSize; row++)
  16. {
  17.     for (var col = 0; col < gridSize; col++)
  18.     {
  19.         //Populate all neighbors, including those that are out of bounds.
  20.         AddNeighbor(row - 1, col); //Up
  21.         AddNeighbor(row + 1, col); //Down
  22.         AddNeighbor(row, col - 1); //Left
  23.         AddNeighbor(row, col + 1); //Right
  24.  
  25.         continue;
  26.  
  27.         void AddNeighbor(int r, int c)
  28.         {
  29.             var position = new Position(r, c);
  30.             var neighbor = position.OutOfBounds(gridSize) ? new Node('#', position, []) : grid[r, c];
  31.            
  32.             grid[row, col].Neighbors.Add(neighbor);
  33.             neighbor.Neighbors.Add(grid[row, col]);
  34.         }
  35.     }
  36. }
  37.  
  38. for (var row = 0; row < gridSize; row++)
  39. {
  40.     for (var col = 0; col < gridSize; col++)
  41.     {
  42.         var node = grid[row, col];
  43.         var region = regions.FirstOrDefault(r => r.Nodes.Contains(node));
  44.  
  45.         if (region == default)
  46.         {
  47.             //Node does not belong to any region, so we create a new one and do a simple BFS flood fill.
  48.             region = (node.Value, [node]);
  49.  
  50.             var queue = new Queue<Node>();
  51.             queue.Enqueue(node);
  52.  
  53.             while (queue.Count > 0)
  54.             {
  55.                 var current = queue.Dequeue();
  56.                 foreach (var neighbor in current.Neighbors.Where(n => n.Value == region.Value))
  57.                 {
  58.                     if (region.Nodes.Add(neighbor))
  59.                     {
  60.                         queue.Enqueue(neighbor);
  61.                     }
  62.                 }
  63.             }
  64.  
  65.             regions.Add(region);
  66.         }
  67.     }
  68. }
  69.  
  70. var part1 = 0;
  71. var part2 = 0;
  72.  
  73. foreach (var region in regions)
  74. {
  75.     //We will store the collection of nodes outside the region and the direction from the region to the node.
  76.     //Every node in this list can represent a fence, so the perimeter (number of fences) is the number of nodes.
  77.     //We will use the direction in order to walk alongside the perimeter and count the sides.
  78.     var outerPerimeter = new List<(Node Node, Direction Direction)>();
  79.  
  80.     foreach (var node in region.Nodes)
  81.     {
  82.         outerPerimeter.AddRange(node.Neighbors.Where(n => n.Value != region.Value)
  83.             .Select(neighbor => (node, node.Position.DirectionTo(neighbor.Position))));
  84.     }
  85.  
  86.     var area = region.Nodes.Count;
  87.     part1 += area * outerPerimeter.Count;
  88.  
  89.     var sides = 0;
  90.  
  91.     while (outerPerimeter.Count > 0)
  92.     {
  93.         //Walk the current section of the outer perimeter in both directions until we reach a corner.
  94.         //The visited nodes represent a single side of the polygon.
  95.         //We remove the visited nodes from the outer perimeter list and increase the sides counter.
  96.  
  97.         var (node, direction) = outerPerimeter[0];
  98.         outerPerimeter.RemoveAt(0);
  99.  
  100.         WalkSide(direction.TurnLeft());
  101.         WalkSide(direction.TurnRight());
  102.  
  103.         sides++;
  104.  
  105.         continue;
  106.  
  107.         void WalkSide(Direction dir)
  108.         {
  109.             var next = node.Position.Move(dir);
  110.             var nextNode = outerPerimeter.FirstOrDefault(n => n.Node.Position == next && n.Direction == direction);
  111.             while (nextNode != default)
  112.             {
  113.                 outerPerimeter.Remove(nextNode);
  114.                 next = next.Move(dir);
  115.                 nextNode = outerPerimeter.FirstOrDefault(n => n.Node.Position == next && n.Direction == direction);
  116.             }
  117.         }
  118.     }
  119.  
  120.     part2 += area * sides;
  121. }
  122.  
  123.  
  124. Console.WriteLine($"Part 1: {part1}");
  125. Console.WriteLine($"Part 2: {part2}");
  126.  
  127. internal record Node(char Value, Position Position, HashSet<Node> Neighbors);
  128.  
  129. internal record Direction(int Row, int Col)
  130. {
  131.     public Direction TurnLeft()
  132.     {
  133.         return new Direction(-Col, Row);
  134.     }
  135.  
  136.     public Direction TurnRight()
  137.     {
  138.         return new Direction(Col, -Row);
  139.     }
  140.  
  141.     public static readonly Direction Up = new(-1, 0);
  142.     public static readonly Direction Down = new(1, 0);
  143.     public static readonly Direction Left = new(0, -1);
  144.     public static readonly Direction Right = new(0, 1);
  145. }
  146.  
  147. internal record Position(int Row, int Col)
  148. {
  149.     public Position Move(Direction dir)
  150.     {
  151.         return new Position(Row + dir.Row, Col + dir.Col);
  152.     }
  153.  
  154.     public Direction DirectionTo(Position other)
  155.     {
  156.         return new Direction(other.Row - Row, other.Col - Col);
  157.     }
  158.  
  159.     public bool OutOfBounds(int size)
  160.     {
  161.         return OutOfBounds(0, size);
  162.     }
  163.  
  164.     public bool OutOfBounds(int start, int end)
  165.     {
  166.         return Row < start || Row >= end || Col < start || Col >= end;
  167.     }
  168. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement