[2k25] Add day 2

This commit is contained in:
2025-12-03 20:20:23 +01:00
parent 0c3a8478d4
commit 533c7437dc
3 changed files with 231 additions and 1 deletions

View File

@@ -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()

229
Days/Day2.cs Normal file
View File

@@ -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<IdRange> 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<IdRange> 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<long> invalidIds = stackalloc long[maxDigits];
var visitedIds = new HashSet<long>();
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<long> invalidIds, long invalidIdSingle, int maxDigits)
{
Span<char> idSpanBuffer = stackalloc char[20];
Span<char> 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<byte> 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<ulong> 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);
}
}

1
Inputs/Day2.txt Normal file
View File

@@ -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