using System.Collections.Generic; using System.IO; using System.Linq; using AdvenOfCode.Contracts; using Range = (long from, long to); namespace AoC_2025; public class Day05 : IPuzzleSolver { private (List freshIdRanges, List ids) ParsePuzzleInput(string path) { var puzzleInput = File.ReadAllLines(path) .Select(line => line.Trim()) .ToList(); var middle = puzzleInput.IndexOf(""); var freshIdRanges = puzzleInput[..middle]; var ids = puzzleInput[(middle + 1)..]; return (freshIdRanges, ids); } public long SolvePart1(string pathToPuzzleInput) { var database = ParsePuzzleInput(pathToPuzzleInput); var freshIdRanges = ParseRanges(database.freshIdRanges); var ids = ParseIds(database.ids); var freshIds = FilterFreshIds(ids, freshIdRanges); return freshIds.Count(); } public long SolvePart2(string pathToPuzzleInput) { var database = ParsePuzzleInput(pathToPuzzleInput); var freshIdRanges = ParseRanges(database.freshIdRanges); var sumIds = SumIdsInRanges(freshIdRanges); return sumIds; } public long SolvePart2Again(string pathToPuzzleInput) { var database = ParsePuzzleInput(pathToPuzzleInput); var freshIdRanges = ParseRanges(database.freshIdRanges); var combinedRanges = CombineOverlappingRanges(freshIdRanges); var countIdsInRanges = CountIdsInRanges(combinedRanges); var sumIds = countIdsInRanges.Sum(); return sumIds; } private IEnumerable ParseIds(List ids) { var parsedIds = ids.Select(id => long.Parse(id)); return parsedIds; } private Range[] ParseRanges(IEnumerable ranges) { return ranges.Select(range => { var split = range.Split('-', StringSplitOptions.RemoveEmptyEntries); return (long.Parse(split[0]), long.Parse(split[1])); }) .OrderBy(range => range.Item1) .ThenBy(range => range.Item2) .ToArray(); } private IEnumerable FilterFreshIds(IEnumerable ids, Range[] freshIdRanges) { var freshIds = ids.Where(id => IsIdInRanges(id, freshIdRanges)); return freshIds; } private bool IsIdInRanges(long id, IEnumerable ranges) { return ranges.Any(range => IsIdInRange(id, range)); } private bool IsIdInRange(long id, Range range) { return range.from <= id && range.to >= id; } private long SumIdsInRanges(IEnumerable ranges) { var sumFreshIds = 0L; var highestSeenId = -1L; foreach (var range in ranges) { var lowestNewId = Math.Max(highestSeenId + 1, range.from); highestSeenId = Math.Max(highestSeenId, range.to); sumFreshIds += Math.Max(0, highestSeenId - lowestNewId + 1); } return sumFreshIds; } private List CombineOverlappingRanges(Range[] ranges) { var optimizedRanges = new List(ranges.Length) { ranges[0] }; foreach (var range in ranges) { if (TryCombineRanges(optimizedRanges[^1], range, out var combined)) { optimizedRanges[^1] = combined; } else { optimizedRanges.Add(range); } } return optimizedRanges; } private bool TryCombineRanges(Range first, Range second, out Range combined) { if (IsIdInRange(second.from, first)) { var isEndInRange = IsIdInRange(second.to, first); combined = isEndInRange ? first : (first.from, second.to); return true; } // if (IsIdInRange(first.from, second)) // { // var isEndInRange = IsIdInRange(first.to, second); // combined = isEndInRange ? second : (second.from, first.to); // return true; // } combined = first; return false; } private static IEnumerable CountIdsInRanges(IEnumerable combinedRanges) { var countIdsInRanges = combinedRanges.Select(range => range.to - range.from + 1); return countIdsInRanges; } }