Add day 9

This commit is contained in:
2025-01-24 10:19:11 +01:00
parent ef9892502e
commit 8b46a65d56
2 changed files with 182 additions and 0 deletions

181
Days/Day9.cs Normal file
View File

@@ -0,0 +1,181 @@
using System.Collections.Frozen;
using System.Numerics;
using Spectre.Console;
namespace AdventOfCode.Days;
public class Day9 : Day
{
public override int Number => 9;
public override string Name => "Disk Fragmenter";
public override void RunPart1(bool display = true)
{
var (blocks, freeBlocksStarts) = ParseDiskBlocks();
var nextFreeIndex = freeBlocksStarts.Dequeue();
for (var i = blocks.Count - 1; i >= 0; i--)
{
// There is no more available space to move blocks into on the left
if (nextFreeIndex >= i)
{
break;
}
if (blocks[i] is -1)
{
continue;
}
blocks[nextFreeIndex] = blocks[i];
blocks[i] = -1;
nextFreeIndex++;
// Go to next free blocks if current free block is full
if (blocks[nextFreeIndex] is not -1)
{
nextFreeIndex = freeBlocksStarts.Dequeue();
}
}
var checksum = blocks
.Select((block, index) => block is -1 ? 0 : (long)block * index)
.Sum();
if (display)
{
AnsiConsole.MarkupLine($"[green]Filesystem checksum: [yellow]{checksum}[/][/]");
}
}
public override void RunPart2(bool display = true)
{
var (blocks, freeBlocks) = ParseDiskBlocks2();
var mapStart = string.Join(" ", blocks);
for (var i = blocks.Count - 1; i >= 0; i--)
{
if (blocks[i] is -1)
{
continue;
}
// Get file length
var fileId = blocks[i];
var endPosition = i;
var startPosition = i;
while (startPosition > 0 && blocks[startPosition - 1] == fileId)
{
startPosition--;
}
var length = (endPosition - startPosition) + 1;
// Find first available free block on the left
var freeBlockIndex = freeBlocks.FindIndex(b => b.StartIndex < startPosition && b.Length >= length);
if (freeBlockIndex is not -1)
{
var freeBlock = freeBlocks[freeBlockIndex];
for (var offset = 0; offset < length; offset++)
{
blocks[freeBlock.StartIndex + offset] = blocks[startPosition + offset];
blocks[startPosition + offset] = -1;
}
// Update free block start and length
freeBlocks[freeBlockIndex] = (freeBlock.StartIndex + length, freeBlock.Length - length);
}
// Move to next file or free block
i = startPosition;
}
var mapEnd = string.Join(" ", blocks).Replace("-1", ".");
var checksum = blocks
.Select((block, index) => block is -1 ? 0 : (long)block * index)
.Sum();
if (display)
{
AnsiConsole.MarkupLine($"[green]Filesystem checksum: [yellow]{checksum}[/][/]");
}
}
private (List<int> Blocks, Queue<int> FreeBlocksStarts) ParseDiskBlocks()
{
var blocks = new List<int>(20_000 * 10);
var freeBlocksStarts = new Queue<int>();
var readingBlock = true;
var blockId = 0;
foreach (var length in Input.Select(encoded => encoded - '0'))
{
if (readingBlock)
{
for (var i = 0; i < length; i++)
{
blocks.Add(blockId);
}
blockId++;
}
else if (length > 0)
{
freeBlocksStarts.Enqueue(blocks.Count);
for (var i = 0; i < length; i++)
{
blocks.Add(-1);
}
}
readingBlock = !readingBlock;
}
return (blocks, freeBlocksStarts);
}
private (List<int> Blocks, List<(int StartIndex, int Length)> FreeBlocks) ParseDiskBlocks2()
{
var blocks = new List<int>(20_000 * 10);
var freeBlocksStarts = new List<(int StartIndex, int Length)>();
var readingBlock = true;
var blockId = 0;
foreach (var length in Input.Select(encoded => encoded - '0'))
{
if (readingBlock)
{
for (var i = 0; i < length; i++)
{
blocks.Add(blockId);
}
blockId++;
}
else if (length > 0)
{
freeBlocksStarts.Add((blocks.Count, length));
for (var i = 0; i < length; i++)
{
blocks.Add(-1);
}
}
readingBlock = !readingBlock;
}
return (blocks, freeBlocksStarts);
}
}

1
Inputs/Day9.txt Normal file

File diff suppressed because one or more lines are too long