using System.Collections.Frozen; using System.Numerics; using System.Text; using Spectre.Console; namespace AdventOfCode.Days; public class Day15 : Day { public override int Number => 15; public override string Name => "Warehouse Woes"; private const int Size = 50; private const char Wall = '#'; private const char Box = 'O'; private const char Empty = '.'; private const char InstructionLeft = '<'; private const char InstructionRight = '>'; private const char InstructionUp = '^'; private const char InstructionDown = 'v'; public override void RunPart1(bool display = true) { var (map, position, instructions) = ParseMap(); foreach (var instruction in instructions) { var direction = instruction switch { InstructionLeft => new Point(-1, 0), InstructionRight => new Point(1, 0), InstructionUp => new Point(0, -1), InstructionDown => new Point(0, 1), _ => throw new ArgumentException($"Invalid instruction: {instruction}") }; var destination = position + direction; // Destination is free, just move if (map[destination.X, destination.Y] is not (Wall or Box)) { position = destination; } // Destination is a box, check if there is a free space further up and move accordingly else if (map[destination.X, destination.Y] is Box) { var pushTarget = destination + direction; while (map[pushTarget.X, pushTarget.Y] is Box) { pushTarget += direction; } // Cannot move anything since there is a wall if (map[pushTarget.X, pushTarget.Y] is Wall) { continue; } // Move robot and boxes (only need to move first and last box of a chain) position = destination; map[destination.X, destination.Y] = Empty; map[pushTarget.X, pushTarget.Y] = Box; } // Else destination is a wall, do nothing } var coordinatesSum = 0; // Count gps coordinates for (var x = 0; x < Size; x++) { for (var y = 0; y < Size; y++) { if (map[x, y] is Box) { coordinatesSum += 100 * y + x; } } } if (display) { AnsiConsole.MarkupLine($"[green]Sum of boxes GPS coordinates: [yellow]{coordinatesSum}[/][/]"); } } private static void DisplayGrid(char[,] map) { for (var y = 0; y < Size; y++) { for (var x = 0; x < Size; x++) { AnsiConsole.Write(map[x, y]); } AnsiConsole.WriteLine(); } AnsiConsole.WriteLine(); } public override void RunPart2(bool display = true) { } private (char[,] Map, Point Start, List Instructions) ParseMap() { var readingMap = true; var map = new char[Size, Size]; Point start = default; var instructions = new List(); var y = 0; foreach (var line in Input.AsSpan().EnumerateLines()) { if (line.IsWhiteSpace()) { readingMap = false; continue; } if (readingMap) { var x = 0; foreach (var symbol in line) { map[x, y] = Empty; if (symbol is Wall or Box) { map[x, y] = symbol; } else if (symbol is '@') { start = new Point(x, y); } x++; } y++; } else { foreach (var instruction in line) { instructions.Add(instruction); } } } return (map, start, instructions); } 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); } }