Watch
1
0
Fork
You've already forked advent-of-code
0
advent-of-code/2024/src/day09.zig
2024-12-08 22:59:35 -08:00

232 lines
5.9 KiB
Zig

const std = @import("std");
const aoc = @import("aoc.zig");
const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList;
pub fn main() !void {
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .{};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const file = try std.fs.cwd().openFile("input/day09.txt", .{});
defer file.close();
const text = try file.readToEndAlloc(allocator, comptime 1 << 30);
defer allocator.free(text);
var disk = try Disk.parse(allocator, text);
defer disk.deinit();
const stdout_file = std.io.getStdOut().writer();
var bw = std.io.bufferedWriter(stdout_file);
const stdout = bw.writer();
try stdout.print("{d}\n", .{try part1(disk)});
try bw.flush();
try stdout.print("{d}\n", .{try part2(disk)});
try bw.flush();
}
fn part1(start: Disk) !u64 {
var disk = try start.clone();
defer disk.deinit();
while (try disk.defrag_step_split()) {}
return disk.checksum();
}
fn part2(start: Disk) !u64 {
var disk = try start.clone();
defer disk.deinit();
var id = disk.max_id;
// File 0 can't move anyway, so no need to deal with the integer overflow!
while (id > 0) : (id -= 1) {
_ = try disk.defrag_step_move(id);
}
return disk.checksum();
}
const File = struct {
id: usize,
len: u64,
};
const Run = union(enum) {
file: File,
empty: struct {
len: u64,
},
fn empty(len: u64) Run {
return Run{ .empty = .{ .len = len } };
}
fn file(id: usize, len: u64) Run {
return Run{ .file = .{ .id = id, .len = len } };
}
};
const Disk = struct {
runs: ArrayList(Run),
max_id: usize,
fn deinit(self: *Disk) void {
self.runs.deinit();
}
fn clone(self: Disk) !Disk {
const runs = try self.runs.clone();
return .{
.runs = runs,
.max_id = self.max_id,
};
}
fn parse(allocator: Allocator, text: []const u8) !Disk {
var runs = ArrayList(Run).init(allocator);
var id: usize = 0;
var isFile = true;
for (text) |char| {
if (char == '\n') {
break;
}
const len = char - '0';
if (isFile) {
try runs.append(Run{ .file = .{ .id = id, .len = len } });
id += 1;
} else {
try runs.append(Run{ .empty = .{ .len = len } });
}
isFile = !isFile;
}
return .{
.runs = runs,
.max_id = id - 1,
};
}
fn defrag_step_split(self: *Disk) !bool {
var last: File = undefined;
while (true) {
switch (self.runs.pop()) {
.file => |file| {
last = file;
break;
},
.empty => continue,
}
}
var i: usize = 0;
while (i < self.runs.items.len) : (i += 1) {
const want = switch (self.runs.items[i]) {
.file => continue,
.empty => |empty| empty.len,
};
if (last.len == want) {
self.runs.items[i] = Run.file(last.id, want);
} else if (last.len > want) {
self.runs.items[i] = Run.file(last.id, want);
try self.runs.append(Run.file(last.id, last.len - want));
} else if (last.len < want) {
self.runs.items[i] = Run.file(last.id, last.len);
try self.runs.insert(i + 1, Run.empty(want - last.len));
}
return true;
}
try self.runs.append(Run{ .file = last });
return false;
}
fn defrag_step_move(self: *Disk, id: usize) !bool {
var file: File = undefined;
var pos: usize = undefined;
for (self.runs.items, 0..) |run, i| {
switch (run) {
.file => |f| if (f.id == id) {
file = f;
pos = i;
break;
},
.empty => continue,
}
}
for (self.runs.items, 0..) |run, i| {
const want = switch (run) {
.file => |f| if (f.id == file.id) {
return false;
} else {
continue;
},
.empty => |empty| empty.len,
};
if (file.len == want) {
self.runs.items[pos] = Run.empty(file.len);
self.runs.items[i] = Run.file(file.id, file.len);
return true;
}
if (file.len < want) {
self.runs.items[pos] = Run.empty(file.len);
self.runs.items[i] = Run.file(file.id, file.len);
try self.runs.insert(i + 1, Run.empty(want - file.len));
return true;
}
}
return false;
}
fn checksum(self: Disk) u64 {
var sum: u64 = 0;
var i: usize = 0;
for (self.runs.items) |run| {
switch (run) {
.file => |file| {
for (0..file.len) |j| {
sum += file.id * (i + j);
}
i += file.len;
},
.empty => |empty| {
i += empty.len;
},
}
}
return sum;
}
pub fn format(
self: Disk,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
for (self.runs.items) |run| {
switch (run) {
.file => |file| {
for (0..file.len) |_| {
try writer.print("{}", .{file.id});
}
},
.empty => |empty| for (0..empty.len) |_| {
try writer.print(".", .{});
},
}
}
}
};