using System.Numerics; using Spectre.Console; namespace AdventOfCode.Days; public class Day6 : Day { public override int Number => 6; public override string Name => "Guard Gallivant"; private const int GridSize = 130; private static readonly Point DirectionUp = new(0, -1); private static readonly Point DirectionDown = new(0, 1); private static readonly Point DirectionLeft = new(-1, 0); private static readonly Point DirectionRight = new(1, 0); public override void RunPart1(bool display = true) { HashSet visited = []; var direction = DirectionUp; var (obstaclesGrid, position) = ReadInputGrid(); visited.Add(position); // Move the guard till it leaves the grid while (true) { var destination = position + direction; if (destination.X is < 0 or >= GridSize || destination.Y is < 0 or >= GridSize) { break; } // Check if it's possible to move or if there is an obstacle if (obstaclesGrid[destination.X, destination.Y]) { direction = NextDirection(direction); continue; } position = destination; visited.Add(position); } if (display) { AnsiConsole.MarkupLine($"[green]Amount of distinct positions: [yellow]{visited.Count}[/][/]"); } } public override void RunPart2(bool display = true) { var possibleLoops = 0; var (initialGrid, startPosition) = ReadInputGrid(); for (var y = 0; y < GridSize; y++) { for (var x = 0; x < GridSize; x++) { // Store obstacle state var initialState = initialGrid[x, y]; // Check if grid is a loop after adding an obstacle there initialGrid[x, y] = true; if (IsLoop(initialGrid, startPosition)) { possibleLoops++; } // Restore obstacle state initialGrid[x, y] = initialState; } } if (display) { AnsiConsole.MarkupLine($"[green]Amount of possible loop positions: [yellow]{possibleLoops}[/][/]"); } return; bool IsLoop(bool[,] obstaclesGrid, Point position) { HashSet<(Point position, Point direction)> visited = []; var direction = DirectionUp; visited.Add((position, direction)); // Move the guard till it loops or leaves the grid while (true) { var destination = position + direction; if (visited.Contains((destination, direction))) { return true; } if (destination.X is < 0 or >= GridSize || destination.Y is < 0 or >= GridSize) { return false; } // Check if it's possible to move or if there is an obstacle if (obstaclesGrid[destination.X, destination.Y]) { direction = NextDirection(direction); continue; } position = destination; visited.Add((position, direction)); } } } private (bool[,] ObstaclesGrid, Point Start) ReadInputGrid() { var grid = new bool[GridSize, GridSize]; Point start = default; var y = 0; foreach (var line in Input.AsSpan().EnumerateLines()) { var x = 0; foreach (var symbol in line) { if (symbol == '^') { start = new Point(x, y); grid[x, y] = false; } else { grid[x, y] = symbol switch { '#' => true, _ => false }; } x++; } y++; } return (grid, start); } private static Point NextDirection(Point direction) { if (direction == DirectionUp) { return DirectionRight; } if (direction == DirectionRight) { return DirectionDown; } if (direction == DirectionDown) { return DirectionLeft; } if (direction == DirectionLeft) { return DirectionUp; } throw new ArgumentException("Invalid direction", nameof(direction)); } private readonly record struct Point(int X, int Y) : IAdditionOperators { public static Point operator +(Point left, Point right) { return new Point(left.X + right.X, left.Y + right.Y); } } }