From 533c7437dc70fbbf507cb600be925b8293f92b0e Mon Sep 17 00:00:00 2001 From: Eveldee Date: Wed, 3 Dec 2025 20:20:23 +0100 Subject: [PATCH] [2k25] Add day 2 --- DayBenchmarker.cs | 2 +- Days/Day2.cs | 229 ++++++++++++++++++++++++++++++++++++++++++++++ Inputs/Day2.txt | 1 + 3 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 Days/Day2.cs create mode 100644 Inputs/Day2.txt diff --git a/DayBenchmarker.cs b/DayBenchmarker.cs index fd3cdc7..dc95d94 100644 --- a/DayBenchmarker.cs +++ b/DayBenchmarker.cs @@ -7,7 +7,7 @@ namespace AdventOfCode; [MemoryDiagnoser(false)] public class DayBenchmark { - private Day Day { get; } = new Day1(); + private Day Day { get; } = new Day2(); [GlobalSetup] public void Setup() diff --git a/Days/Day2.cs b/Days/Day2.cs new file mode 100644 index 0000000..9f37429 --- /dev/null +++ b/Days/Day2.cs @@ -0,0 +1,229 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Spectre.Console; + +namespace AdventOfCode.Days; + +public class Day2 : Day +{ + public override int Number => 2; + public override string Name => "Gift Shop"; + + public override void RunPart1(bool display = true) + { + var inputSpan = Input.AsSpan(); + var sum = 0L; + + // Parse the ranges + var idsCount = inputSpan.Count(',') + 1; + + Span idRanges = stackalloc IdRange[idsCount]; + + var index = 0; + var maxDigits = 0; + foreach (var spanRange in inputSpan.Split(',')) + { + var span = inputSpan[spanRange]; + + var separatorIndex = span.IndexOf('-'); + var lowerBound = long.Parse(span[..separatorIndex]); + var upperBound = long.Parse(span[(separatorIndex + 1)..]); + + idRanges[index] = new IdRange(lowerBound, upperBound); + index++; + + // Also find the biggest number + var digitsCount = CountDigits((ulong) upperBound); + if (digitsCount > maxDigits) + { + maxDigits = digitsCount; + } + } + + // For each possible invalid ID, check if it's in a range + var maxSingleId = 1L * (long) Math.Pow(10, Math.Ceiling(maxDigits / 2D)) - 1L; + for (var invalidIdSingle = 1L; invalidIdSingle <= maxSingleId; invalidIdSingle++) + { + // Build invalid ID + var invalidId = (invalidIdSingle * (long) Math.Pow(10, (int) Math.Log10(invalidIdSingle) + 1)) + invalidIdSingle; + + // Check if invalid ID is in a range + // This could be optimized by building a sorted list of ranges and using binary search + foreach (var idRange in idRanges) + { + if (invalidId >= idRange.Lower && invalidId <= idRange.Upper) + { + sum += invalidId; + + break; + } + } + } + + if (display) + { + AnsiConsole.MarkupLine($"[green]Sum of invalid IDs: [yellow]{sum}[/][/]"); + } + } + + public override void RunPart2(bool display = true) + { + var inputSpan = Input.AsSpan(); + var sum = 0L; + + // Parse the ranges + var idsCount = inputSpan.Count(',') + 1; + + Span idRanges = stackalloc IdRange[idsCount]; + + var index = 0; + var maxDigits = 0; + foreach (var spanRange in inputSpan.Split(',')) + { + var span = inputSpan[spanRange]; + + var separatorIndex = span.IndexOf('-'); + var lowerBound = long.Parse(span[..separatorIndex]); + var upperBound = long.Parse(span[(separatorIndex + 1)..]); + + idRanges[index] = new IdRange(lowerBound, upperBound); + index++; + + // Also find the biggest number + var digitsCount = CountDigits((ulong) upperBound); + if (digitsCount > maxDigits) + { + maxDigits = digitsCount; + } + } + + // For each possible invalid ID, check if it's in a range + Span invalidIds = stackalloc long[maxDigits]; + var visitedIds = new HashSet(); + + var maxSingleId = 1L * (long) Math.Pow(10, Math.Ceiling(maxDigits / 2D)) - 1L; + for (var invalidIdSingle = 1L; invalidIdSingle <= maxSingleId; invalidIdSingle++) + { + // Build invalid IDs + var filledCount = FillPossibleInvalidIds(invalidIds, invalidIdSingle, maxDigits); + + // Check if invalid ID is in a range + // This could be optimized by building a sorted list of ranges and using binary search + for (var i = 0; i < filledCount; i++) + { + var invalidId = invalidIds[i]; + + if (visitedIds.Add(invalidId)) + { + foreach (var idRange in idRanges) + { + if (invalidId >= idRange.Lower && invalidId <= idRange.Upper) + { + sum += invalidId; + + break; + } + } + } + } + } + + if (display) + { + AnsiConsole.MarkupLine($"[green]Sum of invalid IDs: [yellow]{sum}[/][/]"); + } + + return; + + static int FillPossibleInvalidIds(Span invalidIds, long invalidIdSingle, int maxDigits) + { + Span idSpanBuffer = stackalloc char[20]; + Span target = stackalloc char[maxDigits]; + + // Write number to char span + invalidIdSingle.TryFormat(idSpanBuffer, out var idLength); + var idSpan = idSpanBuffer[..idLength]; + + // First part will always be the single id + idSpan.CopyTo(target); + + var times = 2; + var index = 0; + while (true) + { + var targetSize = idSpan.Length * times; + + // Check if result number would be too big + if (targetSize > target.Length) + { + break; + } + + // Build concatenated string + for (var i = 1; i < times; i++) + { + idSpan.CopyTo(target[(idSpan.Length * i)..]); + } + + invalidIds[index] = long.Parse(target[..targetSize]); + + times++; + index++; + } + + return index; + } + } + + private record struct IdRange(long Lower, long Upper); + + // Source: https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/FormattingHelpers.CountDigits.cs,b2bb39bb9360e1d1,references + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CountDigits(ulong value) + { + // Map the log2(value) to a power of 10. + ReadOnlySpan log2ToPow10 = + [ + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20 + ]; + + nint elementOffset = log2ToPow10[(int)ulong.Log2(value)]; + + // Read the associated power of 10. + ReadOnlySpan powersOf10 = + [ + 0, // unused entry to avoid needing to subtract + 0, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000, + ]; + + ulong powerOf10 = Unsafe.Add(ref MemoryMarshal.GetReference(powersOf10), elementOffset); + + // Return the number of digits based on the power of 10, shifted by 1 + // if it falls below the threshold. + int index = (int)elementOffset; + + return index - (value < powerOf10 ? 1 : 0); + } +} \ No newline at end of file diff --git a/Inputs/Day2.txt b/Inputs/Day2.txt new file mode 100644 index 0000000..86f8ecd --- /dev/null +++ b/Inputs/Day2.txt @@ -0,0 +1 @@ +6161588270-6161664791,128091420-128157776,306-494,510-1079,10977-20613,64552-123011,33-46,28076-52796,371150-418737,691122-766624,115-221,7426210-7504719,819350-954677,7713444-7877541,63622006-63661895,1370-1981,538116-596342,5371-8580,8850407-8965070,156363-325896,47-86,452615-473272,2012-4265,73181182-73335464,1102265-1119187,3343315615-3343342551,8388258268-8388317065,632952-689504,3-22,988344-1007943 \ No newline at end of file