mirror of
https://github.com/MorizzG/aoc-2024.git
synced 2025-12-06 04:22:43 +00:00
day 9 finished
This commit is contained in:
parent
f080dd64dd
commit
8b34776508
7 changed files with 722 additions and 2 deletions
329
day9.zig
Normal file
329
day9.zig
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
const std = @import("std");
|
||||
|
||||
const utils = @import("lib/utils.zig");
|
||||
|
||||
const spice = @import("lib/spice.zig");
|
||||
|
||||
const filename = "inputs/day9.txt";
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const alloc = gpa.allocator();
|
||||
|
||||
{
|
||||
const file_reader = try utils.FileReader.init(alloc, filename);
|
||||
defer file_reader.deinit();
|
||||
|
||||
const result = try part1(alloc, file_reader.reader());
|
||||
|
||||
try std.io.getStdOut().writer().print("Day 8, part 1: {}\n", .{result});
|
||||
}
|
||||
|
||||
{
|
||||
const file_reader = try utils.FileReader.init(alloc, filename);
|
||||
defer file_reader.deinit();
|
||||
|
||||
const result = try part2(alloc, file_reader.reader());
|
||||
|
||||
try std.io.getStdOut().writer().print("Day 8, part 2: {}\n", .{result});
|
||||
}
|
||||
}
|
||||
|
||||
fn part1(alloc: std.mem.Allocator, reader: anytype) !u64 {
|
||||
const FsEntry = enum(u32) {
|
||||
empty = std.math.maxInt(u32),
|
||||
_,
|
||||
};
|
||||
|
||||
const input = try reader.readAllAlloc(alloc, std.math.maxInt(usize));
|
||||
defer alloc.free(input);
|
||||
|
||||
var filesystem_list = std.ArrayList(FsEntry).init(alloc);
|
||||
defer filesystem_list.deinit();
|
||||
|
||||
var next_id: u16 = 0;
|
||||
var is_gap = false;
|
||||
|
||||
for (input) |byte| {
|
||||
if (byte == '\n') {
|
||||
break;
|
||||
}
|
||||
|
||||
std.debug.assert('0' <= byte and byte <= '9');
|
||||
|
||||
const len: usize = @as(usize, byte - '0');
|
||||
|
||||
if (is_gap) {
|
||||
try filesystem_list.appendNTimes(.empty, len);
|
||||
} else {
|
||||
try filesystem_list.appendNTimes(@enumFromInt(next_id), len);
|
||||
|
||||
next_id += 1;
|
||||
|
||||
std.debug.assert(next_id != @intFromEnum(FsEntry.empty));
|
||||
}
|
||||
|
||||
is_gap = !is_gap;
|
||||
}
|
||||
|
||||
const filesystem = filesystem_list.items;
|
||||
|
||||
// for (filesystem) |x| {
|
||||
// switch (x) {
|
||||
// .empty => std.debug.print(".", .{}),
|
||||
// else => std.debug.print("{}", .{@intFromEnum(x)}),
|
||||
// }
|
||||
// }
|
||||
// std.debug.print("\n", .{});
|
||||
|
||||
var start_idx: usize = 0;
|
||||
var end_idx: usize = filesystem.len - 1;
|
||||
|
||||
while (start_idx < end_idx) {
|
||||
|
||||
// for start_idx: seek next empty position
|
||||
if (filesystem[start_idx] != .empty) {
|
||||
start_idx += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// for end_idx: seek next full position
|
||||
if (filesystem[end_idx] == .empty) {
|
||||
end_idx -= 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
std.debug.assert(filesystem[start_idx] == .empty and filesystem[end_idx] != .empty);
|
||||
|
||||
filesystem[start_idx] = filesystem[end_idx];
|
||||
filesystem[end_idx] = .empty;
|
||||
}
|
||||
|
||||
const first_empty_idx = std.mem.indexOfScalar(FsEntry, filesystem, .empty).?;
|
||||
|
||||
for (filesystem[first_empty_idx..]) |id| {
|
||||
std.debug.assert(id == .empty);
|
||||
}
|
||||
|
||||
var checksum: u64 = 0;
|
||||
|
||||
for (0.., filesystem) |idx, id| {
|
||||
if (id == .empty) {
|
||||
break;
|
||||
}
|
||||
|
||||
checksum += @as(u64, idx) * @intFromEnum(id);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
fn part2(alloc: std.mem.Allocator, reader: anytype) !u64 {
|
||||
const FsId = enum(u16) {
|
||||
empty = std.math.maxInt(u16),
|
||||
_,
|
||||
};
|
||||
|
||||
const FsEntry = struct {
|
||||
len: u16,
|
||||
id: FsId,
|
||||
};
|
||||
|
||||
const input = try reader.readAllAlloc(alloc, std.math.maxInt(usize));
|
||||
defer alloc.free(input);
|
||||
|
||||
var filesystem = std.ArrayList(FsEntry).init(alloc);
|
||||
defer filesystem.deinit();
|
||||
|
||||
var max_id: u16 = undefined;
|
||||
|
||||
{
|
||||
var next_id: u16 = 0;
|
||||
var is_gap = false;
|
||||
|
||||
for (input) |byte| {
|
||||
if (byte == '\n') {
|
||||
break;
|
||||
}
|
||||
|
||||
std.debug.assert('0' <= byte and byte <= '9');
|
||||
|
||||
const len: u16 = @as(u16, byte - '0');
|
||||
|
||||
if (is_gap) {
|
||||
try filesystem.append(.{ .len = len, .id = .empty });
|
||||
} else {
|
||||
try filesystem.append(.{ .len = len, .id = @enumFromInt(next_id) });
|
||||
|
||||
next_id += 1;
|
||||
|
||||
std.debug.assert(next_id != @intFromEnum(FsId.empty));
|
||||
}
|
||||
|
||||
is_gap = !is_gap;
|
||||
}
|
||||
|
||||
max_id = next_id - 1;
|
||||
}
|
||||
|
||||
// for (filesystem.items) |entry| {
|
||||
// for (0..entry.len) |_| {
|
||||
// switch (entry.id) {
|
||||
// .empty => std.debug.print(".", .{}),
|
||||
// else => std.debug.print("{}", .{@intFromEnum(entry.id)}),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// std.debug.print("\n", .{});
|
||||
|
||||
{
|
||||
// next ID to check; neccessary for checking all files once
|
||||
// since file entries are sorted in increasing order, we can simply decrease this with each
|
||||
// checked file
|
||||
var next_id = max_id;
|
||||
|
||||
// index of next filesystem entry to check
|
||||
var idx: usize = filesystem.items.len - 1;
|
||||
while (idx > 0) : (idx -= 1) {
|
||||
const entry = filesystem.items[idx];
|
||||
|
||||
// we should never see and ID smaller than the one we're looking for
|
||||
// if we do that means we would skip that file
|
||||
std.debug.assert(entry.id == .empty or @intFromEnum(entry.id) >= next_id);
|
||||
|
||||
if (@intFromEnum(entry.id) != next_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// found next entry to move
|
||||
|
||||
// decrease next ID to find
|
||||
next_id -= 1;
|
||||
|
||||
const len = entry.len;
|
||||
|
||||
for (0..idx) |jdx| {
|
||||
const target_entry = filesystem.items[jdx];
|
||||
|
||||
// find the first entry with is empty and at least as large as our entry
|
||||
if (target_entry.id != .empty or target_entry.len < len) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// found target entry
|
||||
|
||||
if (target_entry.len == entry.len) {
|
||||
// exact hit! no need to split entry, just swap IDs
|
||||
|
||||
std.mem.swap(FsId, &filesystem.items[idx].id, &filesystem.items[jdx].id);
|
||||
} else {
|
||||
// empty space is larger than our entry; need to split
|
||||
|
||||
// warning! this (potentially) invalidates all pointers into filesystem.items
|
||||
// and we need to adjust indices (i.e. increase idx by one) since everything after
|
||||
// jdx shift back by one index
|
||||
|
||||
_ = try filesystem.addManyAt(jdx + 1, 1);
|
||||
|
||||
idx += 1;
|
||||
|
||||
// jdx + 1 becomes empty entry of len target_entry.len - entry.len
|
||||
filesystem.items[jdx + 1].id = .empty;
|
||||
filesystem.items[jdx + 1].len = target_entry.len - entry.len;
|
||||
|
||||
// jdx becomes entry
|
||||
filesystem.items[jdx].len = entry.len;
|
||||
filesystem.items[jdx].id = entry.id;
|
||||
|
||||
// idx becomes empty
|
||||
filesystem.items[idx].id = .empty;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for (filesystem.items) |entry| {
|
||||
// for (0..entry.len) |_| {
|
||||
// switch (entry.id) {
|
||||
// .empty => std.debug.print(".", .{}),
|
||||
// else => std.debug.print("{}", .{@intFromEnum(entry.id)}),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// std.debug.print("\n", .{});
|
||||
|
||||
var checksum: u64 = 0;
|
||||
|
||||
{
|
||||
var idx: usize = 0;
|
||||
|
||||
for (filesystem.items) |entry| {
|
||||
const len = entry.len;
|
||||
|
||||
// skip over empty entry
|
||||
if (entry.id == .empty) {
|
||||
idx += len;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const id = @intFromEnum(entry.id);
|
||||
|
||||
for (0..len) |_| {
|
||||
checksum += id * idx;
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
test "part1 example" {
|
||||
const alloc = std.testing.allocator;
|
||||
|
||||
const example = "2333133121414131402";
|
||||
|
||||
var stream = std.io.fixedBufferStream(example);
|
||||
|
||||
const result = try part1(alloc, stream.reader());
|
||||
|
||||
try std.testing.expectEqual(1928, result);
|
||||
}
|
||||
|
||||
test "part1 input" {
|
||||
const alloc = std.testing.allocator;
|
||||
|
||||
const file_reader = try utils.FileReader.init(alloc, filename);
|
||||
defer file_reader.deinit();
|
||||
|
||||
const result = try part1(alloc, file_reader.reader());
|
||||
|
||||
try std.testing.expectEqual(6307275788409, result);
|
||||
}
|
||||
|
||||
test "part2 example" {
|
||||
const alloc = std.testing.allocator;
|
||||
|
||||
const example = "2333133121414131402";
|
||||
|
||||
var stream = std.io.fixedBufferStream(example);
|
||||
|
||||
const result = try part2(alloc, stream.reader());
|
||||
|
||||
try std.testing.expectEqual(2858, result);
|
||||
}
|
||||
|
||||
test "part2 input" {
|
||||
const alloc = std.testing.allocator;
|
||||
|
||||
const file_reader = try utils.FileReader.init(alloc, filename);
|
||||
defer file_reader.deinit();
|
||||
|
||||
const result = try part2(alloc, file_reader.reader());
|
||||
|
||||
try std.testing.expectEqual(6327174563252, result);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue