Add day 9
This commit is contained in:
181
Days/Day9.cs
Normal file
181
Days/Day9.cs
Normal 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
1
Inputs/Day9.txt
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user