Files
AdventOfCode/Days/Day15.cs
2025-01-24 10:19:12 +01:00

193 lines
5.1 KiB
C#

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<char> Instructions) ParseMap()
{
var readingMap = true;
var map = new char[Size, Size];
Point start = default;
var instructions = new List<char>();
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<Point, Point, Point>,
ISubtractionOperators<Point, Point, Point>,
IMultiplyOperators<Point, int, Point>
{
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);
}
}