feat: solve part 1 and part 2

This commit is contained in:
Stevan Freeborn
2023-12-20 00:21:58 -06:00
parent 92e3e32d0b
commit c20b490454
2 changed files with 509 additions and 82 deletions
+124 -3
View File
@@ -19,6 +19,15 @@ public class PatternTests
result.Should().BeEquivalentTo(expected); 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] [Fact]
public void SummarizePatternNotes_GivenExampleInput_ItShouldReturnExpectedValue() public void SummarizePatternNotes_GivenExampleInput_ItShouldReturnExpectedValue()
{ {
@@ -49,14 +58,35 @@ public class PatternTests
.Split(Environment.NewLine + Environment.NewLine) .Split(Environment.NewLine + Environment.NewLine)
.Select(p => p.Split(Environment.NewLine)); .Select(p => p.Split(Environment.NewLine));
var result = patterns var summaries = patterns
.Select(Pattern.Parse) .Select(Pattern.Parse)
.Select(p => p.SummarizePatternNotes()) .Select(p => p.SummarizePatternNotes());
.Sum();
var result = summaries.Sum();
result.Should().Be(33728); 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 class TestData
{ {
public static IEnumerable<object[]> ParseTestData => public static IEnumerable<object[]> ParseTestData =>
@@ -89,6 +119,97 @@ public class PatternTests
} }
}; };
public static IEnumerable<object[]> FindPointOfReflectionWithoutSmudgeTestData =>
new List<object[]>
{
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<object[]> FindPointOfReflectionTestData => public static IEnumerable<object[]> FindPointOfReflectionTestData =>
new List<object[]> new List<object[]>
{ {
+385 -79
View File
@@ -18,6 +18,7 @@ public class Program
return -2; return -2;
} }
var isPart2 = args.Length > 1 && args[1] == "part2";
var input = await File.ReadAllTextAsync(args[0]); var input = await File.ReadAllTextAsync(args[0]);
var patterns = input var patterns = input
@@ -30,7 +31,7 @@ public class Program
var result = patterns var result = patterns
.Select(Pattern.Parse) .Select(Pattern.Parse)
.Sum(p => p.SummarizePatternNotes()); .Sum(p => p.SummarizePatternNotes(isPart2));
stopwatch.Stop(); stopwatch.Stop();
@@ -40,105 +41,279 @@ public class Program
} }
} }
/// <summary>
/// Represents a pattern.
/// </summary>
/// <param name="rows">The rows of the pattern.</param>
/// <param name="columns">The columns of the pattern.</param>
/// <returns>A <see cref="Pattern"/> instance.</returns>
public class Pattern( public class Pattern(
List<List<char>> rows, List<List<char>> rows,
List<List<char>> columns List<List<char>> columns
) )
{ {
/// <summary>
/// Gets the rows of the pattern.
/// </summary>
public List<List<char>> Rows { get; init; } = rows; public List<List<char>> Rows { get; init; } = rows;
/// <summary>
/// Gets the columns of the pattern.
/// </summary>
public List<List<char>> Columns { get; init; } = columns; public List<List<char>> Columns { get; init; } = columns;
public PointOfReflection FindPointOfReflection() private bool HasSmudge(List<char> row, List<char> otherRow)
{ {
var pointOfReflection = new PointOfReflection(); var numOfDiffs = 0;
var startRowIndex = 0;
var endRowIndex = 1;
while (endRowIndex < Rows.Count) for (var i = 0; i < row.Count; i++)
{ {
var startRow = Rows[startRowIndex]; if (row[i] != otherRow[i])
var endRow = Rows[endRowIndex];
if (startRow.SequenceEqual(endRow))
{ {
var isHorizontalReflection = true; numOfDiffs++;
var possibleReflectionPoint = new PointOfReflection }
{ }
StartIndex = startRowIndex,
EndIndex = endRowIndex,
Type = ReflectionType.Horizontal,
};
while (startRowIndex > 0 && endRowIndex < Rows.Count - 1) return numOfDiffs is 1;
{ }
var previousRow = Rows[startRowIndex - 1];
var nextRow = Rows[endRowIndex + 1];
if (previousRow.SequenceEqual(nextRow) is false) private bool CheckForReflection(
int startIndex,
int endIndex,
ReflectionType reflectionType,
out PointOfReflection pointOfReflection
)
{
List<char> start;
List<char> end;
List<List<char>> 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<char> start;
List<char> end;
List<List<char>> 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; // if we can clean the smudge and
startRowIndex = possibleReflectionPoint.StartIndex; // keep reflection intact continue
endRowIndex = possibleReflectionPoint.EndIndex; // checking for reflection at this point
if (HasSmudge(previous, next) && smudgeCleaned is false)
{
smudgeCleaned = true;
startIndex--;
endIndex++;
continue;
}
isReflection = false;
break; break;
} }
startRowIndex--; startIndex--;
endRowIndex++; 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; smudgeCleaned = true;
break; 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;
}
/// <summary>
/// Finds the point of reflection.
/// </summary>
/// <param name="cleanSmudge">Indicates whether to clean smudges.</param>
/// <returns>A <see cref="PointOfReflection"/> instance at which the reflection occurs.</returns>
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++; startRowIndex++;
endRowIndex++; endRowIndex++;
} }
if (pointOfReflection.Type == ReflectionType.None) // if no horizontal reflection was found
if (orgPointOfReflection.Type == ReflectionType.None)
{ {
var startColumnIndex = 0; var startColumnIndex = 0;
var endColumnIndex = 1; var endColumnIndex = 1;
// check for vertical reflection
while (endColumnIndex < Columns.Count) while (endColumnIndex < Columns.Count)
{ {
var startColumn = Columns[startColumnIndex]; var isReflection = CheckForReflection(
var endColumn = Columns[endColumnIndex]; startColumnIndex,
endColumnIndex,
ReflectionType.Vertical,
out var reflectionPoint
);
if (startColumn.SequenceEqual(endColumn)) if (isReflection)
{ {
var isVerticalReflection = true; orgPointOfReflection = reflectionPoint;
var possibleReflectionPoint = new PointOfReflection break;
{
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++; 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<PointOfReflection>();
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() /// <summary>
/// Summarizes the pattern notes.
/// </summary>
/// <param name="cleanSmudge">Indicates whether to clean smudges.</param>
/// <returns>A number that represents the summary of the pattern notes.</returns>
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; ReflectionType.Horizontal => (pointOfReflection.StartIndex + 1) * 100,
} ReflectionType.Vertical => pointOfReflection.StartIndex + 1,
_ => 0,
if (pointOfReflection.Type is ReflectionType.Vertical) };
{
return pointOfReflection.StartIndex + 1;
}
return (pointOfReflection.StartIndex + 1) * 100;
} }
/// <summary>
/// Parses the input into a <see cref="Pattern"/> instance.
/// </summary>
/// <param name="input">The input to parse.</param>
/// <returns>A <see cref="Pattern"/> instance.</returns>
public static Pattern Parse(string[] input) public static Pattern Parse(string[] input)
{ {
var columns = new List<List<char>>(); var columns = new List<List<char>>();
@@ -197,16 +445,74 @@ public class Pattern(
} }
} }
public class PointOfReflection /// <summary>
/// Represents a point of reflection
/// </summary>
public class PointOfReflection : IEquatable<PointOfReflection>
{ {
public ReflectionType Type = ReflectionType.None; /// <summary>
/// Gets or sets the type of the reflection.
/// </summary>
public ReflectionType Type { get; set; } = ReflectionType.None;
/// <summary>
/// Gets or sets the start index of the reflection.
/// </summary>
public int StartIndex { get; set; } = -1; public int StartIndex { get; set; } = -1;
/// <summary>
/// Gets or sets the end index of the reflection.
/// </summary>
public int EndIndex { get; set; } = -1; public int EndIndex { get; set; } = -1;
/// <summary>
/// Indicates whether the current instance is equal to another instance.
/// </summary>
/// <param name="other">An instance of <see cref="PointOfReflection"/> to compare with this instance.</param>
/// <returns>true if the current instance is equal to the other parameter; otherwise, false.</returns>
public bool Equals(PointOfReflection? other) =>
other is not null &&
Type == other.Type &&
StartIndex == other.StartIndex &&
EndIndex == other.EndIndex;
/// <summary>
/// Determines whether the specified object is equal to the current object.
/// </summary>
/// <param name="obj">The object to compare with the current object.</param>
/// <returns>true if the specified object is equal to the current object; otherwise, false.</returns>
public override bool Equals(object? obj)
{
return Equals(obj as PointOfReflection);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>An integer that is the hash code for this instance.</returns>
public override int GetHashCode()
{
return HashCode.Combine(Type, StartIndex, EndIndex);
}
} }
/// <summary>
/// Represents the type of reflection
/// </summary>
public enum ReflectionType public enum ReflectionType
{ {
/// <summary>
/// Reflection that occurs on the horizontal axis
/// </summary>
Horizontal, Horizontal,
/// <summary>
/// Reflection that occurs on the vertical axis
/// </summary>
Vertical, Vertical,
/// <summary>
/// No reflection
/// </summary>
None, None,
} }