From e37fbc977cbdb847b2248f56979d1e4e731f7a86 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Mon, 18 Dec 2023 11:01:37 -0600 Subject: [PATCH 1/5] chore: stub out puzzle problem, input, and solution --- 13/PointOfIncidence.Tests/GlobalUsings.cs | 2 ++ .../PointOfIncidence.Tests.csproj | 30 +++++++++++++++++++ 13/PointOfIncidence.Tests/UnitTest1.cs | 10 +++++++ 13/PointOfIncidence/PointOfIncidence.csproj | 10 +++++++ 13/PointOfIncidence/Program.cs | 2 ++ AdventOfCode2023.sln | 16 ++++++++++ 6 files changed, 70 insertions(+) create mode 100644 13/PointOfIncidence.Tests/GlobalUsings.cs create mode 100644 13/PointOfIncidence.Tests/PointOfIncidence.Tests.csproj create mode 100644 13/PointOfIncidence.Tests/UnitTest1.cs create mode 100644 13/PointOfIncidence/PointOfIncidence.csproj create mode 100644 13/PointOfIncidence/Program.cs diff --git a/13/PointOfIncidence.Tests/GlobalUsings.cs b/13/PointOfIncidence.Tests/GlobalUsings.cs new file mode 100644 index 0000000..7fef4b0 --- /dev/null +++ b/13/PointOfIncidence.Tests/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Xunit; +global using FluentAssertions; \ No newline at end of file diff --git a/13/PointOfIncidence.Tests/PointOfIncidence.Tests.csproj b/13/PointOfIncidence.Tests/PointOfIncidence.Tests.csproj new file mode 100644 index 0000000..8c7a13b --- /dev/null +++ b/13/PointOfIncidence.Tests/PointOfIncidence.Tests.csproj @@ -0,0 +1,30 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/13/PointOfIncidence.Tests/UnitTest1.cs b/13/PointOfIncidence.Tests/UnitTest1.cs new file mode 100644 index 0000000..df19976 --- /dev/null +++ b/13/PointOfIncidence.Tests/UnitTest1.cs @@ -0,0 +1,10 @@ +namespace PointOfIncidence.Tests; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + + } +} \ No newline at end of file diff --git a/13/PointOfIncidence/PointOfIncidence.csproj b/13/PointOfIncidence/PointOfIncidence.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/13/PointOfIncidence/PointOfIncidence.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/13/PointOfIncidence/Program.cs b/13/PointOfIncidence/Program.cs new file mode 100644 index 0000000..3751555 --- /dev/null +++ b/13/PointOfIncidence/Program.cs @@ -0,0 +1,2 @@ +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/AdventOfCode2023.sln b/AdventOfCode2023.sln index 741151e..90f8bdb 100644 --- a/AdventOfCode2023.sln +++ b/AdventOfCode2023.sln @@ -75,6 +75,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotSprings", "12\HotSprings EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotSprings.Tests", "12\HotSprings.Tests\HotSprings.Tests.csproj", "{2731DE56-AF4B-4009-8259-2EA5A9A102FE}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "13", "13", "{DC4E691A-0061-4937-8732-E8D8073C868D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PointOfIncidence", "13\PointOfIncidence\PointOfIncidence.csproj", "{334D6DFF-6131-49C1-B54C-B9E8E00F1E3E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PointOfIncidence.Tests", "13\PointOfIncidence.Tests\PointOfIncidence.Tests.csproj", "{0E74E842-A876-4E8C-9D10-BA09F40039EC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -180,6 +186,14 @@ Global {2731DE56-AF4B-4009-8259-2EA5A9A102FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {2731DE56-AF4B-4009-8259-2EA5A9A102FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {2731DE56-AF4B-4009-8259-2EA5A9A102FE}.Release|Any CPU.Build.0 = Release|Any CPU + {334D6DFF-6131-49C1-B54C-B9E8E00F1E3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {334D6DFF-6131-49C1-B54C-B9E8E00F1E3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {334D6DFF-6131-49C1-B54C-B9E8E00F1E3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {334D6DFF-6131-49C1-B54C-B9E8E00F1E3E}.Release|Any CPU.Build.0 = Release|Any CPU + {0E74E842-A876-4E8C-9D10-BA09F40039EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E74E842-A876-4E8C-9D10-BA09F40039EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E74E842-A876-4E8C-9D10-BA09F40039EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E74E842-A876-4E8C-9D10-BA09F40039EC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {3F8EAC09-4BC7-43AA-B72B-48DDD2710F6D} = {8C29858C-623A-461A-BF0B-254E151CD9C2} @@ -206,5 +220,7 @@ Global {0D6CD658-65BB-47DC-A865-E3428EBE3075} = {01A6D73F-996E-48B3-B111-2725016A49C2} {DB4A8A1F-54A8-4534-AE0A-027B365AA73B} = {ABF03698-B81C-45DE-B76A-C7FBC0C72DB6} {2731DE56-AF4B-4009-8259-2EA5A9A102FE} = {ABF03698-B81C-45DE-B76A-C7FBC0C72DB6} + {334D6DFF-6131-49C1-B54C-B9E8E00F1E3E} = {DC4E691A-0061-4937-8732-E8D8073C868D} + {0E74E842-A876-4E8C-9D10-BA09F40039EC} = {DC4E691A-0061-4937-8732-E8D8073C868D} EndGlobalSection EndGlobal From 92e3e32d0b5484b4062f99cf850ccfbcf1c2da03 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:51:12 -0600 Subject: [PATCH 2/5] feat: solve part 1 --- 13/PointOfIncidence.Tests/PatternTests.cs | 160 +++++++++++++ .../PointOfIncidence.Tests.csproj | 12 + 13/PointOfIncidence.Tests/UnitTest1.cs | 10 - 13/PointOfIncidence/Program.cs | 214 +++++++++++++++++- 4 files changed, 384 insertions(+), 12 deletions(-) create mode 100644 13/PointOfIncidence.Tests/PatternTests.cs delete mode 100644 13/PointOfIncidence.Tests/UnitTest1.cs 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 From c20b490454fe373f4cbef82980b962dd054bd4c0 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Wed, 20 Dec 2023 00:21:58 -0600 Subject: [PATCH 3/5] feat: solve part 1 and part 2 --- 13/PointOfIncidence.Tests/PatternTests.cs | 127 +++++- 13/PointOfIncidence/Program.cs | 464 ++++++++++++++++++---- 2 files changed, 509 insertions(+), 82 deletions(-) diff --git a/13/PointOfIncidence.Tests/PatternTests.cs b/13/PointOfIncidence.Tests/PatternTests.cs index 5022129..2000d85 100644 --- a/13/PointOfIncidence.Tests/PatternTests.cs +++ b/13/PointOfIncidence.Tests/PatternTests.cs @@ -19,6 +19,15 @@ public class PatternTests result.Should().BeEquivalentTo(expected); } + [Theory] + [MemberData(nameof(TestData.FindPointOfReflectionWithoutSmudgeTestData), MemberType = typeof(TestData))] + public void FindPointOfReflection_GivenAPatternAndCleanSmudgeIsTrue_ItShouldReturnPointOfReflection(string[] input, PointOfReflection expected) + { + var pattern = Pattern.Parse(input); + var result = pattern.FindPointOfReflection(true); + result.Should().BeEquivalentTo(expected); + } + [Fact] public void SummarizePatternNotes_GivenExampleInput_ItShouldReturnExpectedValue() { @@ -49,14 +58,35 @@ public class PatternTests .Split(Environment.NewLine + Environment.NewLine) .Select(p => p.Split(Environment.NewLine)); - var result = patterns + var summaries = patterns .Select(Pattern.Parse) - .Select(p => p.SummarizePatternNotes()) - .Sum(); + .Select(p => p.SummarizePatternNotes()); + + var result = summaries.Sum(); result.Should().Be(33728); } + [Fact] + public void SummarizePatternNotes_GivenInputAndIsPartTwo_ItShouldReturnExpectedValue() + { + var input = File + .ReadAllText("INPUT.txt") + .ReplaceLineEndings(); + + var patterns = input + .Split(Environment.NewLine + Environment.NewLine) + .Select(p => p.Split(Environment.NewLine)); + + var summaries = patterns + .Select(Pattern.Parse) + .Select(p => p.SummarizePatternNotes(true)); + + var result = summaries.Sum(); + + result.Should().Be(28235); + } + public static class TestData { public static IEnumerable ParseTestData => @@ -89,6 +119,97 @@ public class PatternTests } }; + public static IEnumerable FindPointOfReflectionWithoutSmudgeTestData => + new List + { + new object[] + { + new string[] + { + "#...##..#", + "#....#..#", + "..##..###", + "#####.##.", + "#####.##.", + "..##..###", + "#....#..#", + }, + new PointOfReflection + { + Type = ReflectionType.Horizontal, + StartIndex = 0, + EndIndex = 1, + }, + }, + new object[] + { + new string[] + { + "#.##..##.", + "..#.##.#.", + "##......#", + "##......#", + "..#.##.#.", + "..##..##.", + "#.#.##.#.", + }, + new PointOfReflection + { + Type = ReflectionType.Horizontal, + StartIndex = 2, + EndIndex = 3, + } + }, + new object[] + { + new string[] + { + "#..#.#........#", + "#..######..####", + ".##..#.#.##.#.#", + "#..##..........", + "######........#", + "#..####......##", + ".##.##.#...##.#", + }, + new PointOfReflection + { + Type = ReflectionType.Vertical, + StartIndex = 9, + EndIndex = 10, + } + }, + new object[] + { + new string[] + { + "####.##.....###.#", + "#####.......#..#.", + "#####.......#..#.", + "####..#.....###.#", + "#..####..#.#.##.#", + ".##..#..........#", + "######..#.##....#", + "#..####...###...#", + "####..#..###.....", + ".......#.##.##...", + "....##.###.##..#.", + "#..##..###...#.#.", + "....#...#.##...##", + ".##........##....", + ".....#.####..##.#", + "#..#..#..#.#.#..#", + "#######.#.#.#....", + }, + new PointOfReflection + { + Type = ReflectionType.Horizontal, + StartIndex = 1, + EndIndex = 2, + }, + } + }; + public static IEnumerable FindPointOfReflectionTestData => new List { diff --git a/13/PointOfIncidence/Program.cs b/13/PointOfIncidence/Program.cs index 0c67213..9b26656 100644 --- a/13/PointOfIncidence/Program.cs +++ b/13/PointOfIncidence/Program.cs @@ -18,6 +18,7 @@ public class Program return -2; } + var isPart2 = args.Length > 1 && args[1] == "part2"; var input = await File.ReadAllTextAsync(args[0]); var patterns = input @@ -30,7 +31,7 @@ public class Program var result = patterns .Select(Pattern.Parse) - .Sum(p => p.SummarizePatternNotes()); + .Sum(p => p.SummarizePatternNotes(isPart2)); stopwatch.Stop(); @@ -40,105 +41,279 @@ public class Program } } +/// +/// Represents a pattern. +/// +/// The rows of the pattern. +/// The columns of the pattern. +/// A instance. public class Pattern( List> rows, List> columns ) { + /// + /// Gets the rows of the pattern. + /// public List> Rows { get; init; } = rows; + + /// + /// Gets the columns of the pattern. + /// public List> Columns { get; init; } = columns; - public PointOfReflection FindPointOfReflection() + private bool HasSmudge(List row, List otherRow) { - var pointOfReflection = new PointOfReflection(); - var startRowIndex = 0; - var endRowIndex = 1; + var numOfDiffs = 0; - while (endRowIndex < Rows.Count) + for (var i = 0; i < row.Count; i++) { - var startRow = Rows[startRowIndex]; - var endRow = Rows[endRowIndex]; - - if (startRow.SequenceEqual(endRow)) + if (row[i] != otherRow[i]) { - var isHorizontalReflection = true; - var possibleReflectionPoint = new PointOfReflection - { - StartIndex = startRowIndex, - EndIndex = endRowIndex, - Type = ReflectionType.Horizontal, - }; + numOfDiffs++; + } + } - while (startRowIndex > 0 && endRowIndex < Rows.Count - 1) - { - var previousRow = Rows[startRowIndex - 1]; - var nextRow = Rows[endRowIndex + 1]; + return numOfDiffs is 1; + } - if (previousRow.SequenceEqual(nextRow) is false) + private bool CheckForReflection( + int startIndex, + int endIndex, + ReflectionType reflectionType, + out PointOfReflection pointOfReflection + ) + { + List start; + List end; + List> collection; + var isReflection = false; + pointOfReflection = new PointOfReflection(); + + // maintain the original indices for the point of reflection + var originalStartIndex = startIndex; + var originalEndIndex = endIndex; + + // set the start and end points and the collection to check + // based on the reflection type + if (reflectionType == ReflectionType.Horizontal) + { + start = Rows[startIndex]; + end = Rows[endIndex]; + collection = Rows; + } + else + { + start = Columns[startIndex]; + end = Columns[endIndex]; + collection = Columns; + } + + // check if the start and end collections are equal + if (start.SequenceEqual(end)) + { + isReflection = true; + + // move the start and end indices outwards + // until the start index reaches start of the collection + // or the end index reaches the end of the collection + while (startIndex > 0 && endIndex < collection.Count - 1) + { + var previous = collection[startIndex - 1]; + var next = collection[endIndex + 1]; + + // if the previous and next collections + // are not equal then the reflection is + // not complete + if (previous.SequenceEqual(next) is false) + { + isReflection = false; + break; + } + + startIndex--; + endIndex++; + } + } + + // if the reflection is complete + // set the point of reflection + if (isReflection) + { + pointOfReflection = new PointOfReflection + { + StartIndex = originalStartIndex, + EndIndex = originalEndIndex, + Type = reflectionType, + }; + } + + return isReflection; + } + + private bool CheckForReflectionWithoutSmudges( + PointOfReflection originalPointOfReflection, + int startIndex, + int endIndex, + ReflectionType reflectionType, + out PointOfReflection pointOfReflection + ) + { + List start; + List end; + List> collection; + var isReflection = false; + var smudgeCleaned = false; + pointOfReflection = new PointOfReflection(); + + // maintain the original indices for the point of reflection + var originalStartIndex = startIndex; + var originalEndIndex = endIndex; + + // set the start and end points and the collection to check + // based on the reflection type + if (reflectionType == ReflectionType.Horizontal) + { + start = Rows[startIndex]; + end = Rows[endIndex]; + collection = Rows; + } + else + { + start = Columns[startIndex]; + end = Columns[endIndex]; + collection = Columns; + } + + var isNotOriginalPointOfReflection = + ( + startIndex != originalPointOfReflection.StartIndex && + endIndex != originalPointOfReflection.EndIndex + ) || originalPointOfReflection.Type != reflectionType; + + do + { + // check if the start and end collections are equal + // and that they are not the original point of reflection + // or that the smudge has been cleaned + if ((start.SequenceEqual(end) && isNotOriginalPointOfReflection) || smudgeCleaned) + { + isReflection = true; + + // move the start and end indices outwards + // until the start index reaches start of the collection + // or the end index reaches the end of the collection + while (startIndex > 0 && endIndex < collection.Count - 1) + { + var previous = collection[startIndex - 1]; + var next = collection[endIndex + 1]; + + // if the previous and next collections + // are not equal then the reflection is + // not complete + if (previous.SequenceEqual(next) is false) { - isHorizontalReflection = false; - startRowIndex = possibleReflectionPoint.StartIndex; - endRowIndex = possibleReflectionPoint.EndIndex; + // if we can clean the smudge and + // keep reflection intact continue + // checking for reflection at this point + if (HasSmudge(previous, next) && smudgeCleaned is false) + { + smudgeCleaned = true; + startIndex--; + endIndex++; + continue; + } + + isReflection = false; break; } - startRowIndex--; - endRowIndex++; + startIndex--; + endIndex++; } - if (isHorizontalReflection) + smudgeCleaned = false; + } + else + { + // if we can clean the smudge and + // then we need to check for reflection + // at this point + if (HasSmudge(start, end)) { - pointOfReflection = possibleReflectionPoint; - break; + smudgeCleaned = true; + continue; } } + } while (smudgeCleaned); + + // if the reflection is complete + // set the point of reflection + if (isReflection) + { + pointOfReflection = new PointOfReflection + { + StartIndex = originalStartIndex, + EndIndex = originalEndIndex, + Type = reflectionType, + }; + } + + return isReflection; + } + + /// + /// Finds the point of reflection. + /// + /// Indicates whether to clean smudges. + /// A instance at which the reflection occurs. + public PointOfReflection FindPointOfReflection(bool cleanSmudge = false) + { + var orgPointOfReflection = new PointOfReflection(); + + var startRowIndex = 0; + var endRowIndex = 1; + + // check for horizontal reflection + while (endRowIndex < Rows.Count) + { + var isReflection = CheckForReflection( + startRowIndex, + endRowIndex, + ReflectionType.Horizontal, + out var reflectionPoint + ); + + if (isReflection) + { + orgPointOfReflection = reflectionPoint; + break; + } startRowIndex++; endRowIndex++; } - if (pointOfReflection.Type == ReflectionType.None) + // if no horizontal reflection was found + if (orgPointOfReflection.Type == ReflectionType.None) { var startColumnIndex = 0; var endColumnIndex = 1; + // check for vertical reflection while (endColumnIndex < Columns.Count) { - var startColumn = Columns[startColumnIndex]; - var endColumn = Columns[endColumnIndex]; + var isReflection = CheckForReflection( + startColumnIndex, + endColumnIndex, + ReflectionType.Vertical, + out var reflectionPoint + ); - if (startColumn.SequenceEqual(endColumn)) + if (isReflection) { - 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; - } + orgPointOfReflection = reflectionPoint; + break; } startColumnIndex++; @@ -146,26 +321,99 @@ public class Pattern( } } - return pointOfReflection; + // if we are not factoring in smudges + // return the original point of reflection + if (cleanSmudge is false) + { + return orgPointOfReflection; + } + + var newPointsOfReflection = new List(); + startRowIndex = 0; + endRowIndex = 1; + + // check for horizontal reflection + // without smudges + while (endRowIndex < Rows.Count) + { + var isReflection = CheckForReflectionWithoutSmudges( + orgPointOfReflection, + startRowIndex, + endRowIndex, + ReflectionType.Horizontal, + out var reflectionPoint + ); + + if (isReflection) + { + newPointsOfReflection.Add(reflectionPoint); + break; + } + + startRowIndex++; + endRowIndex++; + } + + // if no horizontal reflection was found + // without smudges or the original point of reflection + // was found again + if (newPointsOfReflection.Count is 0 || newPointsOfReflection[0].Equals(orgPointOfReflection)) + { + var startColumnIndex = 0; + var endColumnIndex = 1; + + // check for vertical reflection + // without smudges + while (endColumnIndex < Columns.Count) + { + var isReflection = CheckForReflectionWithoutSmudges( + orgPointOfReflection, + startColumnIndex, + endColumnIndex, + ReflectionType.Vertical, + out var reflectionPoint + ); + + if (isReflection) + { + newPointsOfReflection.Add(reflectionPoint); + break; + } + + startColumnIndex++; + endColumnIndex++; + } + } + + // return the first point of reflection + // that is not the original point of reflection + return newPointsOfReflection.First( + p => p.Equals(orgPointOfReflection) is false + ); } - public long SummarizePatternNotes() + /// + /// Summarizes the pattern notes. + /// + /// Indicates whether to clean smudges. + /// A number that represents the summary of the pattern notes. + public long SummarizePatternNotes(bool cleanSmudge = false) { - var pointOfReflection = FindPointOfReflection(); + var pointOfReflection = FindPointOfReflection(cleanSmudge); - if (pointOfReflection.Type is ReflectionType.None) + return pointOfReflection.Type switch { - return 0; - } - - if (pointOfReflection.Type is ReflectionType.Vertical) - { - return pointOfReflection.StartIndex + 1; - } - - return (pointOfReflection.StartIndex + 1) * 100; + ReflectionType.Horizontal => (pointOfReflection.StartIndex + 1) * 100, + ReflectionType.Vertical => pointOfReflection.StartIndex + 1, + _ => 0, + }; } + /// + /// Parses the input into a instance. + /// + /// The input to parse. + /// A instance. public static Pattern Parse(string[] input) { var columns = new List>(); @@ -197,16 +445,74 @@ public class Pattern( } } -public class PointOfReflection +/// +/// Represents a point of reflection +/// +public class PointOfReflection : IEquatable { - public ReflectionType Type = ReflectionType.None; + /// + /// Gets or sets the type of the reflection. + /// + public ReflectionType Type { get; set; } = ReflectionType.None; + + /// + /// Gets or sets the start index of the reflection. + /// public int StartIndex { get; set; } = -1; + + /// + /// Gets or sets the end index of the reflection. + /// public int EndIndex { get; set; } = -1; + + /// + /// Indicates whether the current instance is equal to another instance. + /// + /// An instance of to compare with this instance. + /// true if the current instance is equal to the other parameter; otherwise, false. + public bool Equals(PointOfReflection? other) => + other is not null && + Type == other.Type && + StartIndex == other.StartIndex && + EndIndex == other.EndIndex; + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? obj) + { + return Equals(obj as PointOfReflection); + } + + /// + /// Returns the hash code for this instance. + /// + /// An integer that is the hash code for this instance. + public override int GetHashCode() + { + return HashCode.Combine(Type, StartIndex, EndIndex); + } } +/// +/// Represents the type of reflection +/// public enum ReflectionType { + /// + /// Reflection that occurs on the horizontal axis + /// Horizontal, + + /// + /// Reflection that occurs on the vertical axis + /// Vertical, + + /// + /// No reflection + /// None, } \ No newline at end of file From 5160ccab1d9239d59d846d51496916ff71f4c3f0 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Wed, 20 Dec 2023 00:22:59 -0600 Subject: [PATCH 4/5] chore: dotnet format --- 11/CosmicExpansion.Tests/GlobalUsings.cs | 3 ++- 12/HotSprings.Tests/GlobalUsings.cs | 3 ++- 12/HotSprings/Program.cs | 20 ++++++++++---------- 13/PointOfIncidence.Tests/GlobalUsings.cs | 3 ++- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/11/CosmicExpansion.Tests/GlobalUsings.cs b/11/CosmicExpansion.Tests/GlobalUsings.cs index 7fef4b0..2d5b473 100644 --- a/11/CosmicExpansion.Tests/GlobalUsings.cs +++ b/11/CosmicExpansion.Tests/GlobalUsings.cs @@ -1,2 +1,3 @@ +global using FluentAssertions; + global using Xunit; -global using FluentAssertions; \ No newline at end of file diff --git a/12/HotSprings.Tests/GlobalUsings.cs b/12/HotSprings.Tests/GlobalUsings.cs index 7fef4b0..2d5b473 100644 --- a/12/HotSprings.Tests/GlobalUsings.cs +++ b/12/HotSprings.Tests/GlobalUsings.cs @@ -1,2 +1,3 @@ +global using FluentAssertions; + global using Xunit; -global using FluentAssertions; \ No newline at end of file diff --git a/12/HotSprings/Program.cs b/12/HotSprings/Program.cs index c41a588..83bf898 100644 --- a/12/HotSprings/Program.cs +++ b/12/HotSprings/Program.cs @@ -177,23 +177,23 @@ public class Condition( if (unfold) { springs = [ - ..springs, + .. springs, SpringCondition.Unknown, - ..springs, + .. springs, SpringCondition.Unknown, - ..springs, + .. springs, SpringCondition.Unknown, - ..springs, + .. springs, SpringCondition.Unknown, - ..springs, + .. springs, ]; contiguousGroupSizes = [ - ..contiguousGroupSizes, - ..contiguousGroupSizes, - ..contiguousGroupSizes, - ..contiguousGroupSizes, - ..contiguousGroupSizes, + .. contiguousGroupSizes, + .. contiguousGroupSizes, + .. contiguousGroupSizes, + .. contiguousGroupSizes, + .. contiguousGroupSizes, ]; } diff --git a/13/PointOfIncidence.Tests/GlobalUsings.cs b/13/PointOfIncidence.Tests/GlobalUsings.cs index 7fef4b0..2d5b473 100644 --- a/13/PointOfIncidence.Tests/GlobalUsings.cs +++ b/13/PointOfIncidence.Tests/GlobalUsings.cs @@ -1,2 +1,3 @@ +global using FluentAssertions; + global using Xunit; -global using FluentAssertions; \ No newline at end of file From 2b7de8a9296fa54badd95fd604db172e6c2abadd Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Wed, 20 Dec 2023 00:23:57 -0600 Subject: [PATCH 5/5] docs: update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 35d0522..eb8f2d6 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ dotnet build | 10 | [Problem](./10/PROBLEM.md) | [Solution](./10/PipeMaze/) | ✅ | So...my solutions are rather slow, but they work. Part 2 appears to have a mathematical solution, but scanning is what I came up with on my own. | | 11 | [Problem](./11/PROBLEM.md) | [Solution](./11/CosmicExpansion/) | ✅ | Expanding the universe was fine in part 1, but part 2 showed I actually needed to calculate the expansion instead of expanding the input. | | 12 | [Problem](./12/PROBLEM.md) | [Solution](./12/HotSprings/) | ✅ | This one stretched my skills. I needed lots of help from the interwebz. | -| 13 | [Problem](./13/PROBLEM.md) | [Solution](./13/) | ⌛ | +| 13 | [Problem](./13/PROBLEM.md) | [Solution](./13/PointOfIncidence/) | ✅ | This solution is the one I think I'm most proud of so far. | | 14 | [Problem](./14/PROBLEM.md) | [Solution](./14/) | ⌛ | | 15 | [Problem](./15/PROBLEM.md) | [Solution](./15/) | ⌛ | | 16 | [Problem](./16/PROBLEM.md) | [Solution](./16/) | ⌛ |