194 lines
4.9 KiB
C#
194 lines
4.9 KiB
C#
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<Point> 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<Point, Point, Point>
|
|
{
|
|
public static Point operator +(Point left, Point right)
|
|
{
|
|
return new Point(left.X + right.X, left.Y + right.Y);
|
|
}
|
|
}
|
|
} |