169 lines
5.2 KiB
C#
169 lines
5.2 KiB
C#
using System.Collections.Frozen;
|
|
using System.Numerics;
|
|
using Spectre.Console;
|
|
|
|
namespace AdventOfCode.Days;
|
|
|
|
public class Day8 : Day
|
|
{
|
|
public override int Number => 8;
|
|
public override string Name => "Resonant Collinearity";
|
|
|
|
private const int GridSize = 50;
|
|
|
|
public override void RunPart1(bool display = true)
|
|
{
|
|
HashSet<Point> antinodePositions = [];
|
|
|
|
var frequencyAntennas = ParseAntennas();
|
|
|
|
// Find antinodes for each frequency
|
|
foreach (var (_, antennas) in frequencyAntennas)
|
|
{
|
|
// Take antennas by pair to find antinodes
|
|
foreach (var firstAntenna in antennas)
|
|
{
|
|
foreach (var secondAntenna in antennas.Where(a => a != firstAntenna))
|
|
{
|
|
var firstAntinode = firstAntenna + (firstAntenna - secondAntenna);
|
|
var secondAntinode = secondAntenna + (secondAntenna - firstAntenna);
|
|
|
|
if (firstAntinode is { X: >= 0 and < GridSize, Y: >= 0 and < GridSize })
|
|
{
|
|
antinodePositions.Add(firstAntinode);
|
|
}
|
|
|
|
if (secondAntinode is { X: >= 0 and < GridSize, Y: >= 0 and < GridSize })
|
|
{
|
|
antinodePositions.Add(secondAntinode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (display)
|
|
{
|
|
AnsiConsole.MarkupLine($"[green]Unique antinode positions count: [yellow]{antinodePositions.Count}[/][/]");
|
|
}
|
|
}
|
|
|
|
public override void RunPart2(bool display = true)
|
|
{
|
|
HashSet<Point> antinodePositions = [];
|
|
|
|
var frequencyAntennas = ParseAntennas();
|
|
|
|
// Find antinodes for each frequency
|
|
foreach (var (_, antennas) in frequencyAntennas)
|
|
{
|
|
// Take antennas by pair to find antinodes
|
|
foreach (var firstAntenna in antennas)
|
|
{
|
|
foreach (var secondAntenna in antennas.Where(a => a != firstAntenna))
|
|
{
|
|
// Always add antinodes on antennas if a pair is formed
|
|
antinodePositions.Add(firstAntenna);
|
|
antinodePositions.Add(secondAntenna);
|
|
|
|
// Compute potential antinode positions
|
|
var firstAntinodeOffset = firstAntenna - secondAntenna;
|
|
var secondAntinodeOffset = secondAntenna - firstAntenna;
|
|
|
|
for (var i = 1; ; i++)
|
|
{
|
|
var firstAntinode = firstAntenna + (i * firstAntinodeOffset);
|
|
|
|
if (firstAntinode is { X: >= 0 and < GridSize, Y: >= 0 and < GridSize })
|
|
{
|
|
antinodePositions.Add(firstAntinode);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (var i = 1; ; i++)
|
|
{
|
|
var secondAntinode = secondAntenna + (i * secondAntinodeOffset);
|
|
|
|
if (secondAntinode is { X: >= 0 and < GridSize, Y: >= 0 and < GridSize })
|
|
{
|
|
antinodePositions.Add(secondAntinode);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (display)
|
|
{
|
|
AnsiConsole.MarkupLine($"[green]Unique antinode positions count: [yellow]{antinodePositions.Count}[/][/]");
|
|
}
|
|
}
|
|
|
|
private FrozenDictionary<char, List<Point>> ParseAntennas()
|
|
{
|
|
var antennas = new Dictionary<char, List<Point>>();
|
|
|
|
var y = 0;
|
|
foreach (var line in Input.AsSpan().EnumerateLines())
|
|
{
|
|
var x = 0;
|
|
|
|
foreach (var frequency in line)
|
|
{
|
|
if (frequency is '.')
|
|
{
|
|
x++;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (!antennas.TryGetValue(frequency, out var list))
|
|
{
|
|
list = [];
|
|
antennas[frequency] = list;
|
|
}
|
|
|
|
list.Add(new Point(x, y));
|
|
|
|
x++;
|
|
}
|
|
|
|
y++;
|
|
}
|
|
|
|
return antennas.ToFrozenDictionary();
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
} |