193 lines
5.1 KiB
C#
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);
|
|
}
|
|
} |