181 lines
4.8 KiB
C#
181 lines
4.8 KiB
C#
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);
|
|
}
|
|
|
|
} |