AdventOfCode/AoC_2025/Day02.cs

131 lines
4.5 KiB
C#

using AdvenOfCode.Contracts;
namespace AoC_2025;
public class Day02 : IPuzzleSolver<long>
{
private string[] ParsePuzzleInput(string path)
{
var puzzleInput = File.ReadAllText(path)
.Trim()
.Split(',', StringSplitOptions.RemoveEmptyEntries);
return puzzleInput;
}
public long SolvePart1(string pathToPuzzleInput)
{
var idRanges = ParsePuzzleInput(pathToPuzzleInput);
var invalidIds = idRanges.SelectMany(range => FindInvalidIds(range));
var sum = invalidIds.Sum();
return sum;
}
public long SolvePart2(string pathToPuzzleInput)
{
var idRanges = ParsePuzzleInput(pathToPuzzleInput);
var invalidIds = idRanges.SelectMany(range => FindInvalidSegmentedIds(range));
var sum = invalidIds.Sum();
return sum;
}
private IEnumerable<long> FindInvalidIds(string range)
{
var (start, end) = Parse(range);
var halvableIds = FindHalvableIds(start, end);
var invalidIds = GetInvalidIds(halvableIds);
return invalidIds;
}
private IEnumerable<long> FindInvalidSegmentedIds(string range)
{
var (start, end) = Parse(range);
var idsAsAllSegments = GetIdAsAllSegments(start, end);
var invalidIdSegments = FindIdsWhereAllSegmentsAreEqual(idsAsAllSegments);
var invalidIds = MergeSegments(invalidIdSegments);
var invalidIdNumbers = invalidIds.Select(id => long.Parse(id));
return invalidIdNumbers;
}
private (long start, long end) Parse(string range)
{
var parts = range.Split('-', StringSplitOptions.RemoveEmptyEntries);
return (long.Parse(parts[0]), long.Parse(parts[1]));
}
private IEnumerable<string> FindHalvableIds(long start, long end)
{
var ids = Range(start, end);
var idStrings = ids.Select(id => id.ToString());
var halvableIds = idStrings.Where(id => (id.Length % 2) == 0);
return halvableIds;
}
private IEnumerable<long> GetInvalidIds(IEnumerable<string> halvableIds)
{
var idParts = halvableIds.Select(id => (id[..(id.Length / 2)], id[(id.Length / 2)..]));
var invalidIds = idParts.Where(parts=> parts.Item1 == parts.Item2);
var invalidNumberedIds = invalidIds.Select(id => long.Parse(id.Item1 + id.Item2));
return invalidNumberedIds;
}
private IEnumerable<long> Range(long start, long end)
{
for (var i = start; i <= end; i++)
{
yield return i;
}
}
private IEnumerable<IEnumerable<IEnumerable<string>>> GetIdAsAllSegments(long start, long end)
{
var ids = Range(start, end);
var idStrings = ids.Select(id => id.ToString());
var idSegments = idStrings.Select(id => GetSegments(id));
return idSegments;
}
private IEnumerable<IEnumerable<string>> GetSegments(string id)
{
for (int i = id.Length / 2; i > 0; i--)
{
if (id.Length % i == 0)
{
yield return id.Chunk(i).Select(chunks => new string(chunks));
}
}
}
private IEnumerable<IEnumerable<string>> FindIdsWhereAllSegmentsAreEqual(IEnumerable<IEnumerable<IEnumerable<string>>> idAllSegments)
{
var segmentsWithOneAllMatching = FilterForAnyAllSegmentsEqual(idAllSegments);
var idSegments = GetFirstForEachSegments(segmentsWithOneAllMatching);
return idSegments;
}
private static IEnumerable<IEnumerable<string>> GetFirstForEachSegments(IEnumerable<IEnumerable<IEnumerable<string>>> segmentsWithOneAllMatching)
{
var idSegments = segmentsWithOneAllMatching
.Select(segments => segments.First());
return idSegments;
}
private IEnumerable<string> MergeSegments(IEnumerable<IEnumerable<string>> segments)
{
return segments.Select(segment => string.Concat(segment));
}
private IEnumerable<IEnumerable<IEnumerable<string>>> FilterForAnyAllSegmentsEqual(IEnumerable<IEnumerable<IEnumerable<string>>> idAllSegments)
{
var segmentsWithOneAllMatching = idAllSegments
.Where(idSegments => idSegments.Any(segments => CheckAllSegmentsEqual(segments)));
return segmentsWithOneAllMatching;
}
private bool CheckAllSegmentsEqual(IEnumerable<string> segments)
{
var (allEqual, _) = segments.Aggregate(
(allEqual: true, prev: ""),
(tuple, segment) => (tuple.allEqual && (tuple.prev == "" || tuple.prev == segment), segment));
return allEqual;
}
}