Files
2024-12-19 00:27:38 -06:00

148 lines
3.6 KiB
C#

using System.Diagnostics;
if (args.Length is 0)
{
Console.WriteLine("Please provide a path to the input file.");
return;
}
if (File.Exists(args[0]) is false)
{
Console.WriteLine("The provided file does not exist.");
return;
}
var isPart2 = args.Length is 2 && args[1] is "part2";
var input = await File.ReadAllLinesAsync(args[0]);
var stopwatch = new Stopwatch();
stopwatch.Start();
var result = new PuzzleParser().Parse(input).GetAntinodes(input, isPart2).Count;
stopwatch.Stop();
Console.WriteLine($"The number of antinode locations is {result}. ({stopwatch.ElapsedMilliseconds}ms)");
// TODO: Make this better...it is ugly
// TODO: Add tests for actual input and corresponding solution
static class AntennaGroupingsExtensions
{
public static List<Point> GetAntinodes(this List<IGrouping<char,Antenna>> groupings, string[] input, bool isPart2 = false)
{
var antinodeLocations = new HashSet<Point>();
foreach (var group in groupings)
{
var antennas = group.ToList();
foreach (var a1 in antennas)
{
foreach (var a2 in antennas)
{
if (a1 == a2)
{
continue;
}
var pointOne = a1.ToPoint();
var pointTwo = a2.ToPoint();
var line = new Line(pointOne, pointTwo, input);
var antinodes = isPart2
? line.GetPart2Antinodes()
: line.GetAntinodes();
antinodes.ForEach(a => antinodeLocations.Add(a));
}
}
}
return antinodeLocations.ToList();
}
}
class PuzzleParser
{
public List<IGrouping<char,Antenna>> Parse(string[] input)
{
var antennas = new List<Antenna>();
foreach (var (line, rowIndex) in input.Select((line, index) => (line, index)))
{
foreach (var (character, colIndex) in line.Select((character, index) => (character, index)))
{
if (char.IsDigit(character) || char.IsLetter(character))
{
antennas.Add(new(rowIndex, colIndex, character));
}
}
}
var antennaGroupedByFrequency = antennas.GroupBy(a => a.Frequency).ToList();
return antennaGroupedByFrequency;
}
}
record Antenna(int RowIndex, int ColIndex, char Frequency)
{
public Point ToPoint()
{
return new(RowIndex, ColIndex);
}
}
record Point(int RowIndex, int ColIndex)
{
public bool IsOnMap(string[] map)
{
return RowIndex > -1 &&
RowIndex < map.Length &&
ColIndex > -1 &&
ColIndex < map[0].Length;
}
}
record Slope(int Rise, int Run);
class Line(Point start, Point end, string[] map)
{
private readonly Slope _slope = new(end.RowIndex - start.RowIndex, end.ColIndex - start.ColIndex);
public List<Point> GetAntinodes()
{
var doubledRun = _slope.Run * 2;
var doubledRise = _slope.Rise * 2;
return new List<Point>()
{
new(end.RowIndex - doubledRise, end.ColIndex - doubledRun),
new(start.RowIndex + doubledRise, start.ColIndex + doubledRun),
}.Where(p => p.IsOnMap(map)).ToList();
}
public List<Point> GetPart2Antinodes()
{
var antinodes = new List<Point>();
// we are finding antinodes from
// start antenna back
var currentPoint = start;
while (currentPoint.IsOnMap(map))
{
antinodes.Add(currentPoint);
currentPoint = new(currentPoint.RowIndex - _slope.Rise, currentPoint.ColIndex - _slope.Run);
}
// we are finding antinodes from
// end antenna out
currentPoint = end;
while (currentPoint.IsOnMap(map))
{
antinodes.Add(currentPoint);
currentPoint = new(currentPoint.RowIndex + _slope.Rise, currentPoint.ColIndex + _slope.Run);
}
return antinodes;
}
}