[2k25] Add day 2
This commit is contained in:
@@ -7,7 +7,7 @@ namespace AdventOfCode;
|
|||||||
[MemoryDiagnoser(false)]
|
[MemoryDiagnoser(false)]
|
||||||
public class DayBenchmark
|
public class DayBenchmark
|
||||||
{
|
{
|
||||||
private Day Day { get; } = new Day1();
|
private Day Day { get; } = new Day2();
|
||||||
|
|
||||||
[GlobalSetup]
|
[GlobalSetup]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
|
|||||||
229
Days/Day2.cs
Normal file
229
Days/Day2.cs
Normal 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
1
Inputs/Day2.txt
Normal 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
|
||||||
Reference in New Issue
Block a user