diff --git a/13/PointOfIncidence.Tests/PatternTests.cs b/13/PointOfIncidence.Tests/PatternTests.cs new file mode 100644 index 0000000..5022129 --- /dev/null +++ b/13/PointOfIncidence.Tests/PatternTests.cs @@ -0,0 +1,160 @@ +namespace PointOfIncidence.Tests; + +public class PatternTests +{ + [Theory] + [MemberData(nameof(TestData.ParseTestData), MemberType = typeof(TestData))] + public void Parse_GivenAPattern_ItShouldReturnPatternInstance(string[] input, Pattern expected) + { + var result = Pattern.Parse(input); + result.Should().BeEquivalentTo(expected); + } + + [Theory] + [MemberData(nameof(TestData.FindPointOfReflectionTestData), MemberType = typeof(TestData))] + public void FindPointOfReflection_GivenAPattern_ItShouldReturnPointOfReflection(string[] input, PointOfReflection expected) + { + var pattern = Pattern.Parse(input); + var result = pattern.FindPointOfReflection(); + result.Should().BeEquivalentTo(expected); + } + + [Fact] + public void SummarizePatternNotes_GivenExampleInput_ItShouldReturnExpectedValue() + { + var input = File + .ReadAllText("EXAMPLE.txt") + .ReplaceLineEndings(); + + var patterns = input + .Split(Environment.NewLine + Environment.NewLine) + .Select(p => p.Split(Environment.NewLine)); + + var result = patterns + .Select(Pattern.Parse) + .Sum(p => p.SummarizePatternNotes()); + + result.Should().Be(405); + } + + + [Fact] + public void SummarizePatternNotes_GivenInput_ItShouldReturnExpectedValue() + { + var input = File + .ReadAllText("INPUT.txt") + .ReplaceLineEndings(); + + var patterns = input + .Split(Environment.NewLine + Environment.NewLine) + .Select(p => p.Split(Environment.NewLine)); + + var result = patterns + .Select(Pattern.Parse) + .Select(p => p.SummarizePatternNotes()) + .Sum(); + + result.Should().Be(33728); + } + + public static class TestData + { + public static IEnumerable ParseTestData => + new List + { + new object[] + { + new string[] + { + "#.##..##.", + "..#.##.#.", + }, + new Pattern( + [ + ['#', '.', '#', '#', '.', '.', '#', '#', '.'], + ['.', '.', '#', '.', '#', '#', '.', '#', '.'], + ], + [ + ['#', '.'], + ['.', '.'], + ['#', '#'], + ['#', '.'], + ['.', '#'], + ['.', '#'], + ['#', '.'], + ['#', '#'], + ['.', '.'], + ] + ), + } + }; + + public static IEnumerable FindPointOfReflectionTestData => + new List + { + new object[] + { + new string[] + { + "#...##..#", + "#....#..#", + "..##..###", + "#####.##.", + "#####.##.", + "..##..###", + "#....#..#", + }, + new PointOfReflection + { + Type = ReflectionType.Horizontal, + StartIndex = 3, + EndIndex = 4, + }, + }, + new object[] + { + new string[] + { + "#.##..##.", + "..#.##.#.", + "##......#", + "##......#", + "..#.##.#.", + "..##..##.", + "#.#.##.#.", + }, + new PointOfReflection + { + Type = ReflectionType.Vertical, + StartIndex = 4, + EndIndex = 5, + } + }, + new object[] + { + new string[] + { + "...##..##.#.#", + ".##.#####.###", + "##.#....####.", + "#.#....#.#..#", + ".#..#.#...#.#", + ".#..#.#...#.#", + "#.#....#.#..#", + "##.#....####.", + ".########.###", + "...##..##.#.#", + "#.#.###.....#", + "#.#.###.....#", + "...##..##.#.#", + }, + new PointOfReflection + { + Type = ReflectionType.Horizontal, + StartIndex = 10, + EndIndex = 11, + }, + }, + }; + } +} \ No newline at end of file diff --git a/13/PointOfIncidence.Tests/PointOfIncidence.Tests.csproj b/13/PointOfIncidence.Tests/PointOfIncidence.Tests.csproj index 8c7a13b..21b4499 100644 --- a/13/PointOfIncidence.Tests/PointOfIncidence.Tests.csproj +++ b/13/PointOfIncidence.Tests/PointOfIncidence.Tests.csproj @@ -27,4 +27,16 @@ + + + PreserveNewest + + + + + + PreserveNewest + + + diff --git a/13/PointOfIncidence.Tests/UnitTest1.cs b/13/PointOfIncidence.Tests/UnitTest1.cs deleted file mode 100644 index df19976..0000000 --- a/13/PointOfIncidence.Tests/UnitTest1.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace PointOfIncidence.Tests; - -public class UnitTest1 -{ - [Fact] - public void Test1() - { - - } -} \ No newline at end of file diff --git a/13/PointOfIncidence/Program.cs b/13/PointOfIncidence/Program.cs index 3751555..0c67213 100644 --- a/13/PointOfIncidence/Program.cs +++ b/13/PointOfIncidence/Program.cs @@ -1,2 +1,212 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); +using System.Diagnostics; + +namespace PointOfIncidence; + +public class Program +{ + public static async Task Main(string[] args) + { + if (args.Length is 0) + { + Console.WriteLine("Please provide a path to the input file."); + return -1; + } + + if (File.Exists(args[0]) is false) + { + Console.WriteLine("The provided file does not exist."); + return -2; + } + + var input = await File.ReadAllTextAsync(args[0]); + + var patterns = input + .ReplaceLineEndings() + .Split(Environment.NewLine + Environment.NewLine) + .Select(p => p.Split(Environment.NewLine)); + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + var result = patterns + .Select(Pattern.Parse) + .Sum(p => p.SummarizePatternNotes()); + + stopwatch.Stop(); + + Console.WriteLine($"The total of all pattern note summaries is {result}. ({stopwatch.ElapsedMilliseconds}ms)"); + + return (int)result; + } +} + +public class Pattern( + List> rows, + List> columns +) +{ + public List> Rows { get; init; } = rows; + public List> Columns { get; init; } = columns; + + public PointOfReflection FindPointOfReflection() + { + var pointOfReflection = new PointOfReflection(); + var startRowIndex = 0; + var endRowIndex = 1; + + while (endRowIndex < Rows.Count) + { + var startRow = Rows[startRowIndex]; + var endRow = Rows[endRowIndex]; + + if (startRow.SequenceEqual(endRow)) + { + var isHorizontalReflection = true; + var possibleReflectionPoint = new PointOfReflection + { + StartIndex = startRowIndex, + EndIndex = endRowIndex, + Type = ReflectionType.Horizontal, + }; + + while (startRowIndex > 0 && endRowIndex < Rows.Count - 1) + { + var previousRow = Rows[startRowIndex - 1]; + var nextRow = Rows[endRowIndex + 1]; + + if (previousRow.SequenceEqual(nextRow) is false) + { + isHorizontalReflection = false; + startRowIndex = possibleReflectionPoint.StartIndex; + endRowIndex = possibleReflectionPoint.EndIndex; + break; + } + + startRowIndex--; + endRowIndex++; + } + + if (isHorizontalReflection) + { + pointOfReflection = possibleReflectionPoint; + break; + } + } + + startRowIndex++; + endRowIndex++; + } + + if (pointOfReflection.Type == ReflectionType.None) + { + var startColumnIndex = 0; + var endColumnIndex = 1; + + while (endColumnIndex < Columns.Count) + { + var startColumn = Columns[startColumnIndex]; + var endColumn = Columns[endColumnIndex]; + + if (startColumn.SequenceEqual(endColumn)) + { + var isVerticalReflection = true; + var possibleReflectionPoint = new PointOfReflection + { + StartIndex = startColumnIndex, + EndIndex = endColumnIndex, + Type = ReflectionType.Vertical, + }; + + while (startColumnIndex > 0 && endColumnIndex < Columns.Count - 1) + { + var previousColumn = Columns[startColumnIndex - 1]; + var nextColumn = Columns[endColumnIndex + 1]; + + if (previousColumn.SequenceEqual(nextColumn) is false) + { + isVerticalReflection = false; + startColumnIndex = possibleReflectionPoint.StartIndex; + endColumnIndex = possibleReflectionPoint.EndIndex; + break; + } + + startColumnIndex--; + endColumnIndex++; + } + + if (isVerticalReflection) + { + pointOfReflection = possibleReflectionPoint; + break; + } + } + + startColumnIndex++; + endColumnIndex++; + } + } + + return pointOfReflection; + } + + public long SummarizePatternNotes() + { + var pointOfReflection = FindPointOfReflection(); + + if (pointOfReflection.Type is ReflectionType.None) + { + return 0; + } + + if (pointOfReflection.Type is ReflectionType.Vertical) + { + return pointOfReflection.StartIndex + 1; + } + + return (pointOfReflection.StartIndex + 1) * 100; + } + + public static Pattern Parse(string[] input) + { + var columns = new List>(); + + var height = input.Length; + var width = input[0].Length; + + for (var i = 0; i < width; i++) + { + var column = new List(); + + for (var j = 0; j < height; j++) + { + column.Add(input[j][i]); + } + + columns.Add(column); + } + + var rows = input + .Select( + row => row + .ToCharArray() + .ToList() + ) + .ToList(); + + return new Pattern(rows, columns); + } +} + +public class PointOfReflection +{ + public ReflectionType Type = ReflectionType.None; + public int StartIndex { get; set; } = -1; + public int EndIndex { get; set; } = -1; +} + +public enum ReflectionType +{ + Horizontal, + Vertical, + None, +} \ No newline at end of file