using System.Numerics; using System.Runtime.Intrinsics.X86; using Spectre.Console; namespace AdventOfCode.Days; public class Day18 : Day { public override int Number => 18; public override string Name => "RAM Run"; private const int Size = 71; private const char Corrupted = '#'; private const char Empty = ' '; public override void RunPart1(bool display = true) { var grid = ParseGrid(1024); var visited = new Dictionary(); var toVisit = new Queue<(Point Position, int Score)>(); var end = new Point(Size - 1, Size - 1); var minimumScore = int.MaxValue; toVisit.Enqueue((new Point(0, 0), 0)); while (toVisit.Count > 0) { var (position, score) = toVisit.Dequeue(); // Cannot go out of bounds if (position is { X: < 0 or >= Size } or { Y: < 0 or >= Size }) { continue; } // Cannot go onto a corrupted space if (grid[position.X, position.Y] is Corrupted) { continue; } if (visited.TryGetValue(position, out var savedScore) && savedScore <= score) { continue; } visited[position] = score; // End if (position == end) { if (score < minimumScore) { minimumScore = score; } continue; } toVisit.Enqueue((position + (1, 0), score + 1)); toVisit.Enqueue((position + (-1, 0), score + 1)); toVisit.Enqueue((position + (0, 1), score + 1)); toVisit.Enqueue((position + (0, -1), score + 1)); } if (display) { AnsiConsole.MarkupLine($"[green]Minimum number of steps to reach the exit: [yellow]{minimumScore}[/][/]"); } } public override void RunPart2(bool display = true) { (int X, int Y)[] bytesToFall = Input.ReadAllLines() .Select(line => line.Split(',')) .Select(split => (int.Parse(split[0]), int.Parse(split[1]))) .ToArray(); var nextByteIndex = 1024; var grid = ParseGrid(1024); while (true) { var nextByte = bytesToFall[nextByteIndex]; grid[nextByte.X, nextByte.Y] = Corrupted; if (!IsGridCompletable()) { break; } nextByteIndex++; } if (display) { AnsiConsole.MarkupLine($"[green]Grid will be blocked at byte n°{nextByteIndex} at position: [yellow]{bytesToFall[nextByteIndex]}[/][/]"); } return; bool IsGridCompletable() { var visited = new HashSet(); var toVisit = new Queue(); var end = new Point(Size - 1, Size - 1); toVisit.Enqueue(new Point(0, 0)); while (toVisit.Count > 0) { var position = toVisit.Dequeue(); // Cannot go out of bounds if (position is { X: < 0 or >= Size } or { Y: < 0 or >= Size }) { continue; } // Cannot go onto a corrupted space if (grid[position.X, position.Y] is Corrupted) { continue; } if (!visited.Add(position)) { continue; } // End if (position == end) { return true; } toVisit.Enqueue(position + (1, 0)); toVisit.Enqueue(position + (-1, 0)); toVisit.Enqueue(position + (0, 1)); toVisit.Enqueue(position + (0, -1)); } return false; } } private char[,] ParseGrid(int limit) { var grid = new char[Size, Size]; var counter = 0; foreach (var line in Input.AsSpan().EnumerateLines()) { if (counter >= limit) { break; } var x = int.Parse(line[..line.IndexOf(',')]); var y = int.Parse(line[(line.IndexOf(',') + 1)..]); grid[x, y] = Corrupted; counter++; } return grid; } private readonly record struct Point(int X, int Y) : IAdditionOperators, ISubtractionOperators, IMultiplyOperators { public static Point operator +(Point left, Point right) { return new Point(left.X + right.X, left.Y + right.Y); } public static Point operator -(Point left, Point right) { return new Point(left.X - right.X, left.Y - right.Y); } public static Point operator *(Point left, int right) { return new Point(left.X * right, left.Y * right); } public static Point operator *(int left, Point right) { return new Point(right.X * left, right.Y * left); } public static implicit operator Point((int X, int Y) point) => new(point.X, point.Y); } }