Day 9: Disk Fragmenter
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
You are viewing a single thread.
View all comments 2 points
C#
using System.Collections;
using System.Diagnostics;
using Common;
namespace Day09;
static class Program
{
static void Main()
{
var start = Stopwatch.GetTimestamp();
var sampleInput = Input.ParseInput("sample.txt");
var programInput = Input.ParseInput("input.txt");
Console.WriteLine($"Part 1 sample: {Part1(sampleInput)}");
Console.WriteLine($"Part 1 input: {Part1(programInput)}");
Console.WriteLine($"Part 2 sample: {Part2(sampleInput)}");
Console.WriteLine($"Part 2 input: {Part2(programInput)}");
Console.WriteLine($"That took about {Stopwatch.GetElapsedTime(start)}");
}
static object Part1(Input i)
{
var disk = i.Disk.ToList();
while (true)
{
// Find the next free space with some blocks open.
var nextFree = disk.FindIndex(d => (d is Free { Blocks: > 0 }));
var nextUsed = disk.FindLastIndex(d => (d is Used { Blocks: > 0 }));
if (nextFree > nextUsed) break;
var free = disk[nextFree] as Free ?? throw new Exception("This is not a Free");
var used = disk[nextUsed] as Used ?? throw new Exception("This is not a Used");
var canMove = Math.Min(free.Blocks, used.Blocks);
disk[nextFree] = free with { Blocks = free.Blocks - canMove };
disk[nextUsed] = used with { Blocks = used.Blocks - canMove };
var addingFree = disk[nextUsed - 1] as Free;
disk[nextUsed - 1] = addingFree! with { Blocks = addingFree.Blocks + canMove };
var addingUsed = used! with { Blocks = canMove };
disk.Insert(nextFree, addingUsed);
}
// DumpString(disk);
return CheckSum(disk);
}
static object Part2(Input i)
{
var disk = i.Disk.ToList();
var lastUsedId = int.MaxValue;
while (true)
{
// Find the next free space with some blocks open.
var nextUsed = disk.FindLastIndex(d => (d is Used { Blocks: > 0 } u) && (u.Id < lastUsedId));
if (nextUsed < 0) break;
var nextFree = disk.FindIndex(d => (d is Free f) && (f.Blocks >= disk[nextUsed].Blocks));
var used = disk[nextUsed] as Used ?? throw new Exception("This is not a Used");
lastUsedId = used.Id;
if ((nextFree < 0) || (nextFree > nextUsed)) continue;
var free = disk[nextFree] as Free ?? throw new Exception("This is not a Free");
var canMove = Math.Min(free.Blocks, used.Blocks);
disk[nextFree] = free with { Blocks = free.Blocks - canMove };
disk[nextUsed] = used with { Blocks = used.Blocks - canMove };
var addingFree = disk[nextUsed - 1] as Free;
disk[nextUsed - 1] = addingFree! with { Blocks = addingFree.Blocks + canMove };
var addingUsed = used! with { Blocks = canMove };
disk.Insert(nextFree, addingUsed);
// DumpString(disk);
}
return CheckSum(disk);
}
static long CheckSum(IEnumerable<DiskSpace> disk) => disk
.SelectMany(d => Expand(d))
.Select((d, i) => (d is Used u) ? (long)(i * u.Id) : 0)
.Sum();
static IEnumerable<DiskSpace> Expand(DiskSpace d)
{
for (int i = 0; i < d.Blocks; i++)
{
yield return d with { Blocks = 1 };
}
}
static void DumpString(IEnumerable<DiskSpace> disk)
{
foreach(var s in disk.Select(d =>
(d is Used u) ? new string((char)(u.Id + '0'), u.Blocks) :
(d is Free { Blocks: > 0 } f) ? new string('.', f.Blocks) :
""))
{
Console.Write(s);
}
Console.WriteLine();
}
}
public abstract record DiskSpace(int Blocks);
public record Free(int Blocks) : DiskSpace(Blocks);
public record Used(int Id, int Blocks) : DiskSpace(Blocks);
public class Input
{
public DiskSpace[] Disk { get; private init; } = [];
public static Input ParseInput(string file) => new Input()
{
Disk = File.ReadAllText(file)
.TakeWhile(char.IsDigit)
.Select(c => (int)(c - '0'))
.Select((c, i) => ((i % 2) == 0) ? (DiskSpace)new Used(i / 2, c) : new Free(c))
.ToArray(),
};
}