From 4279067eae77e868c4f2ce75e83d0d25b7e941ae Mon Sep 17 00:00:00 2001 From: Jeremy Kaplan Date: Wed, 4 Dec 2024 22:20:28 -0800 Subject: [PATCH] 2024 Day 5 Zig backport --- 2024/src/aoc.zig | 33 ++++++++ 2024/src/day05.zig | 205 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 2024/src/day05.zig diff --git a/2024/src/aoc.zig b/2024/src/aoc.zig index 902ef86..4845736 100644 --- a/2024/src/aoc.zig +++ b/2024/src/aoc.zig @@ -28,3 +28,36 @@ pub const Lines = struct { } } }; + +pub fn AutoHashSet(comptime T: type) type { + const Empty = struct {}; + const Map = std.AutoHashMap(T, Empty); + + return struct { + map: Map, + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator) Self { + return .{ + .map = Map.init(allocator), + }; + } + + pub fn deinit(self: *Self) void { + self.map.deinit(); + } + + pub fn put(self: *Self, v: T) !void { + return self.map.put(v, .{}); + } + + pub fn contains(self: Self, v: T) bool { + return self.map.contains(v); + } + + pub fn valueIterator(self: Self) Map.KeyIterator { + return self.map.keyIterator(); + } + }; +} diff --git a/2024/src/day05.zig b/2024/src/day05.zig new file mode 100644 index 0000000..43a43c7 --- /dev/null +++ b/2024/src/day05.zig @@ -0,0 +1,205 @@ +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/day05.txt", .{}); + defer file.close(); + + const text = try file.readToEndAlloc(allocator, comptime 1 << 30); + defer allocator.free(text); + + var input = try Input.parse(allocator, text); + defer input.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(input)}); + try bw.flush(); + + try stdout.print("{d}\n", .{try part2(input)}); + try bw.flush(); +} + +const Input = struct { + rules: ArrayList(Rule), + updates: ArrayList(Update), + constraints: std.AutoHashMap(u32, Constraints), + + fn deinit(self: *Input) void { + self.rules.deinit(); + + for (self.updates.items) |update| { + update.deinit(); + } + + self.updates.deinit(); + + var it = self.constraints.valueIterator(); + while (it.next()) |value| { + value.before.deinit(); + value.after.deinit(); + } + self.constraints.deinit(); + } + + fn parse(alloc: Allocator, text: []const u8) !Input { + var blocks = std.mem.tokenizeSequence(u8, text, "\n\n"); + + var rule_lines = std.mem.tokenizeScalar(u8, blocks.next().?, '\n'); + var rules = ArrayList(Rule).init(alloc); + while (rule_lines.next()) |line| { + try rules.append(try Rule.parse(line)); + } + + var update_lines = std.mem.tokenizeScalar(u8, blocks.next().?, '\n'); + var updates = ArrayList(Update).init(alloc); + while (update_lines.next()) |line| { + try updates.append(try Update.parse(alloc, line)); + } + + var constraints = std.AutoHashMap(u32, Constraints).init(alloc); + + for (rules.items) |rule| { + { + var c = try constraints.getOrPut(rule.x); + if (!c.found_existing) { + c.value_ptr.* = Constraints.init(alloc); + } + try c.value_ptr.after.put(rule.y); + } + + { + var c = try constraints.getOrPut(rule.y); + if (!c.found_existing) { + c.value_ptr.* = Constraints.init(alloc); + } + try c.value_ptr.before.put(rule.x); + } + } + + return .{ + .rules = rules, + .updates = updates, + .constraints = constraints, + }; + } +}; + +const Rule = struct { + x: u32, + y: u32, + + fn parse(line: []const u8) !Rule { + var it = std.mem.tokenizeScalar(u8, line, '|'); + + const x = try std.fmt.parseInt(u32, it.next().?, 10); + const y = try std.fmt.parseInt(u32, it.next().?, 10); + + return .{ + .x = x, + .y = y, + }; + } +}; + +const Update = struct { + pages: ArrayList(u32), + + fn deinit(self: Update) void { + self.pages.deinit(); + } + + fn parse(alloc: Allocator, line: []const u8) !Update { + var pages = ArrayList(u32).init(alloc); + + var it = std.mem.tokenizeScalar(u8, line, ','); + while (it.next()) |p| { + const page = try std.fmt.parseInt(u32, p, 10); + try pages.append(page); + } + + return .{ + .pages = pages, + }; + } + + fn valid(self: Update, constraints: std.AutoHashMap(u32, Constraints)) bool { + const items = self.pages.items; + for (items[0..(items.len - 1)], items[1..]) |x, y| { + if (constraints.get(x)) |c| { + if (!c.after.contains(y)) { + return false; + } + } + } + return true; + } +}; + +const Constraints = struct { + before: aoc.AutoHashSet(u32), + after: aoc.AutoHashSet(u32), + + fn init(alloc: Allocator) Constraints { + return .{ + .before = aoc.AutoHashSet(u32).init(alloc), + .after = aoc.AutoHashSet(u32).init(alloc), + }; + } + + fn deinit(self: *Constraints) void { + self.before.deinit(); + self.before = undefined; + self.after.deinit(); + self.after = undefined; + } +}; + +fn part1(input: Input) !u32 { + var sum: u32 = 0; + for (input.updates.items) |u| { + if (u.valid(input.constraints)) { + sum += u.pages.items[u.pages.items.len / 2]; + } + } + return sum; +} + +const FixUpdate = struct { + constraints: std.AutoHashMap(u32, Constraints), + + fn lessThan(self: FixUpdate, x: u32, y: u32) bool { + if (self.constraints.get(x)) |c| { + return c.after.contains(y); + } + return false; + } +}; + +fn part2(input: Input) !u32 { + var sum: u32 = 0; + for (input.updates.items) |u| { + if (u.valid(input.constraints)) { + continue; + } + + std.mem.sort( + u32, + u.pages.items, + FixUpdate{ .constraints = input.constraints }, + FixUpdate.lessThan, + ); + + sum += u.pages.items[u.pages.items.len / 2]; + } + return sum; +}