diff --git a/08/ResonantCollinearity.Tests/LineTests.cs b/08/ResonantCollinearity.Tests/LineTests.cs new file mode 100644 index 0000000..809735f --- /dev/null +++ b/08/ResonantCollinearity.Tests/LineTests.cs @@ -0,0 +1,33 @@ +namespace ResonantCollinearity.Tests; + +public class LineTests +{ + private readonly string[] _exampleInput = [ + "............", + "........0...", + ".....0......", + ".......0....", + "....0.......", + "......A.....", + "............", + "............", + "........A...", + ".........A..", + "............", + "............", + ]; + + [Test] + public async Task Slope_WhenCalled_ItShouldReturnExpectedValue() + { + var line = new Line(new(3, 4), new(5, 5), _exampleInput); + + var result = line.GetAntinodes(); + + await Assert.That(result).IsEquivalentTo(new List() + { + new(1, 3), + new(7, 6), + }); + } +} \ No newline at end of file diff --git a/08/ResonantCollinearity.Tests/PuzzleParserTests.cs b/08/ResonantCollinearity.Tests/PuzzleParserTests.cs new file mode 100644 index 0000000..2da31d4 --- /dev/null +++ b/08/ResonantCollinearity.Tests/PuzzleParserTests.cs @@ -0,0 +1,93 @@ +namespace ResonantCollinearity.Tests; + +public class PuzzleParserTests +{ + private readonly PuzzleParser _parser = new(); + private readonly string[] _exampleInput = [ + "............", + "........0...", + ".....0......", + ".......0....", + "....0.......", + "......A.....", + "............", + "............", + "........A...", + ".........A..", + "............", + "............", + ]; + + [Test] + public async Task Parse_WhenGivenExampleInput_ItShouldReturnExpectedListOfFrequencies() + { + var antenna = new List() + { + new(1, 8, '0'), + new(2, 5, '0'), + new(3, 7, '0'), + new(4, 4, '0'), + new(5, 6, 'A'), + new(8, 8, 'A'), + new(9, 9, 'A'), + }; + + var expectedGroupings = antenna.GroupBy(c => c.Frequency).ToList(); + + var result = _parser.Parse(_exampleInput); + + await Assert.That(result).IsEquivalentTo(expectedGroupings); + } + + [Test] + public async Task SolutionPartOne_WhenGivenExampleInput_ItShouldReturnExpectedAntinodes() + { + var expectedAntinodes = new List() + { + new(0, 11), + new(3, 2), + new(5, 6), + new(7, 0), + new(1, 3), + new(4, 9), + new(0, 6), + new(6, 3), + new(2, 10), + new(5, 1), + new(2, 4), + new(11, 10), + new(7, 7), + new(10, 10), + }; + + var result = _parser.Parse(_exampleInput).GetAntinodes(_exampleInput); + + await Assert.That(result).IsEquivalentTo(expectedAntinodes); + } + + [Test] + public async Task SolutionPartTwo_WhenGivenExampleInput_ItShouldReturnExpectedAntinodes() + { + // var expectedAntinodes = new List() + // { + // new(0, 11), + // new(3, 2), + // new(5, 6), + // new(7, 0), + // new(1, 3), + // new(4, 9), + // new(0, 6), + // new(6, 3), + // new(2, 10), + // new(5, 1), + // new(2, 4), + // new(11, 10), + // new(7, 7), + // new(10, 10), + // }; + + var result = _parser.Parse(_exampleInput).GetAntinodes(_exampleInput, isPart2: true); + + await Assert.That(result.Count).IsEquivalentTo(34); + } +} \ No newline at end of file diff --git a/08/ResonantCollinearity/Program.cs b/08/ResonantCollinearity/Program.cs index 5f94bf8..22c97a2 100644 --- a/08/ResonantCollinearity/Program.cs +++ b/08/ResonantCollinearity/Program.cs @@ -18,7 +18,130 @@ var input = await File.ReadAllLinesAsync(args[0]); var stopwatch = new Stopwatch(); stopwatch.Start(); -// TODO: Implement solution +var result = new PuzzleParser().Parse(input).GetAntinodes(input, isPart2).Count; stopwatch.Stop(); -Console.WriteLine($". ({stopwatch.ElapsedMilliseconds}ms)"); \ No newline at end of file +Console.WriteLine($"The number of antinode locations is {result}. ({stopwatch.ElapsedMilliseconds}ms)"); + +// TODO: Make this better...it is ugly +static class AntennaGroupingsExtensions +{ + public static List GetAntinodes(this List> groupings, string[] input, bool isPart2 = false) + { + var antinodeLocations = new HashSet(); + + 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> Parse(string[] input) + { + var antennas = new List(); + + 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 GetAntinodes() + { + var doubledRun = _slope.Run * 2; + var doubledRise = _slope.Rise * 2; + + return new List() + { + new(end.RowIndex - doubledRise, end.ColIndex - doubledRun), + new(start.RowIndex + doubledRise, start.ColIndex + doubledRun), + }.Where(p => p.IsOnMap(Map)).ToList(); + } + + public List GetPart2Antinodes() + { + var antinodes = new List(); + + // 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; + } +} \ No newline at end of file diff --git a/README.md b/README.md index 51ad911..c6df107 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,13 @@ dotnet build ## Challenges -| Day | Problem | Solution | Notes | -|-----|----------------------------|-------------------------------------|----------------------------------------------------------------------------------------------| -| 01 | [Problem](./01/PROBLEM.md) | [Solution](./01/HistorianHysteria/) | A great way to start! | -| 02 | [Problem](./02/PROBLEM.md) | [Solution](./02/RedNosedReports/) | Damn their was an edge case that really bit me...direction change after first pair of levels | -| 03 | [Problem](./03/PROBLEM.md) | [Solution](./03/MullItOver/) | Thank goodness this wasn't a repeat of day 2. | -| 04 | [Problem](./04/PROBLEM.md) | [Solution](./04/CeresSearch/) | This was fun! Definitely was able to see the growth in my abilities here. | -| 05 | [Problem](./05/PROBLEM.md) | [Solution](./05/PrintQueue/) | A graph to the rescue! | -| 06 | [Problem](./06/PROBLEM.md) | [Solution](./06/GuardGallivant/) | I kind of brute forced part two... | -| 07 | [Problem](./07/PROBLEM.md) | [Solution](./07/BridgeRepair/) | Not perfect or pretty, but good enough | +| Day | Problem | Solution | Notes | +|-----|----------------------------|----------------------------------------|--------------------------------------------------------------------------------------------------------| +| 01 | [Problem](./01/PROBLEM.md) | [Solution](./01/HistorianHysteria/) | A great way to start! | +| 02 | [Problem](./02/PROBLEM.md) | [Solution](./02/RedNosedReports/) | Damn their was an edge case that really bit me...direction change after first pair of levels | +| 03 | [Problem](./03/PROBLEM.md) | [Solution](./03/MullItOver/) | Thank goodness this wasn't a repeat of day 2. | +| 04 | [Problem](./04/PROBLEM.md) | [Solution](./04/CeresSearch/) | This was fun! Definitely was able to see the growth in my abilities here. | +| 05 | [Problem](./05/PROBLEM.md) | [Solution](./05/PrintQueue/) | A graph to the rescue! | +| 06 | [Problem](./06/PROBLEM.md) | [Solution](./06/GuardGallivant/) | I kind of brute forced part two... | +| 07 | [Problem](./07/PROBLEM.md) | [Solution](./07/BridgeRepair/) | Not perfect or pretty, but good enough | +| 08 | [Problem](./08/PROBLEM.md) | [Solution](./08/ResonantCollinearity/) | Without some help from chat I don't know where I'd be...to be fair the description kind of mislead me. |