From ff8924991c1dcb049402fcccbda58d773539d187 Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Sun, 28 Apr 2024 11:12:57 +0300 Subject: [PATCH] solve day 19 part 2 --- src/day19.zig | 153 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 144 insertions(+), 9 deletions(-) diff --git a/src/day19.zig b/src/day19.zig index 31bed2c..a601eb8 100644 --- a/src/day19.zig +++ b/src/day19.zig @@ -45,7 +45,7 @@ const Rule = struct { condition: RuleCondition, property: PartProperty = .X, next: NameString, - value: u32 = 0, + value: u16 = 0, }; const Workflow = std.ArrayList(Rule); @@ -57,6 +57,43 @@ const MachinePart = struct { s: u32 = 0 }; +const PropertyRange = struct { + min: u16, + max: u16, + + fn is_empty(self: @This()) bool { + return self.min > self.max; + } + + fn size(self: @This()) u64 { + return (self.max - self.min) + 1; + } +}; + +const MachinePartRange = struct { + x: PropertyRange, + m: PropertyRange, + a: PropertyRange, + s: PropertyRange, + + fn get(self: *MachinePartRange, prop: PartProperty) *PropertyRange { + return switch (prop) { + .X => &self.x, + .M => &self.m, + .A => &self.a, + .S => &self.s, + }; + } + + fn is_empty(self: MachinePartRange) bool { + return self.x.is_empty() or self.m.is_empty() or self.a.is_empty() or self.s.is_empty(); + } + + fn size(self: MachinePartRange) u64 { + return self.x.size() * self.m.size() * self.a.size() * self.s.size(); + } +}; + const Input = struct { workflows: std.AutoHashMap(NameString, Workflow), machine_parts: std.ArrayList(MachinePart), @@ -78,17 +115,21 @@ const Input = struct { } }; +fn print_rule(rule: Rule) void { + switch (rule.condition) { + .Always => std.debug.print("{s}", .{rule.next.slice()}), + .Less => std.debug.print("{s}<{}:{s}", .{rule.property.to_str(), rule.value, rule.next.slice()}), + .More => std.debug.print("{s}>{}:{s}", .{rule.property.to_str(), rule.value, rule.next.slice()}) + } +} + fn print_workflow(workflow: Workflow) void { std.debug.print("{{", .{}); for (0.., workflow.items) |i, rule| { if (i > 0) { std.debug.print(",", .{}); } - switch (rule.condition) { - .Always => std.debug.print("{s}", .{rule.next.slice()}), - .Less => std.debug.print("{s}<{}:{s}", .{rule.property.to_str(), rule.value, rule.next.slice()}), - .More => std.debug.print("{s}>{}:{s}", .{rule.property.to_str(), rule.value, rule.next.slice()}) - } + print_rule(rule); } std.debug.print("}}\n", .{}); } @@ -119,7 +160,7 @@ fn parse_workflow(allocator: Allocator, line: []const u8) !std.meta.Tuple(&.{ Na try parsed.append(Rule{ .condition = condition, .property = PartProperty.from_str(rule_str[0..cmp_idx]) orelse return error.Invalid, - .value = try std.fmt.parseUnsigned(u32, rule_str[(cmp_idx+1)..colon], 10), + .value = try std.fmt.parseUnsigned(u16, rule_str[(cmp_idx+1)..colon], 10), .next = try NameString.fromSlice(rule_str[(colon+1)..]) }); } else { @@ -264,7 +305,101 @@ pub fn part2(input: *aoc.Input) !aoc.Result { var parsed = try parse_input(allocator, input.lines); defer parsed.deinit(); - return .{ .uint = 0 }; + const State = struct { + name: NameString, + machine_part: MachinePartRange, + }; + + var stack = std.ArrayList(State).init(allocator); + defer stack.deinit(); + + try stack.append(State{ + .name = try NameString.fromSlice("in"), + .machine_part = MachinePartRange{ + .x = PropertyRange{ .min = 1, .max = 4000 }, + .m = PropertyRange{ .min = 1, .max = 4000 }, + .a = PropertyRange{ .min = 1, .max = 4000 }, + .s = PropertyRange{ .min = 1, .max = 4000 }, + } + }); + + var accepted_count: u64 = 0; + + while (stack.popOrNull()) |state| { + var current: MachinePartRange = state.machine_part; + + const name_str = state.name.slice(); + if (std.mem.eql(u8, name_str, "A")) { + accepted_count += current.size(); + continue; + } else if (std.mem.eql(u8, name_str, "R")) { + continue; + } + + const workflow: Workflow = parsed.workflows.get(state.name).?; + for (workflow.items) |rule| { + if (current.is_empty()) break; + + if (rule.condition == .Always) { + try stack.append(State{ + .name = rule.next, + .machine_part = current, + }); + break; + } + + var current_property_range = current.get(rule.property); + + var is_min_captured = false; + var is_max_captured = false; + if (rule.condition == .Less) { + is_min_captured = current_property_range.min < rule.value; + is_max_captured = current_property_range.max < rule.value; + } else if (rule.condition == .More) { + is_min_captured = current_property_range.min > rule.value; + is_max_captured = current_property_range.max > rule.value; + } else { + unreachable; + } + + if (!is_min_captured and !is_max_captured) { + // Nothing gets captured by this rule, go to the next rule + } else if (is_min_captured and is_max_captured) { + // Everything gets captured by this rule, can skip checking other rules + try stack.append(State{ + .name = rule.next, + .machine_part = current + }); + break; + } else if (!is_min_captured and is_max_captured) { + // Only the upper part is captured + var upper_machine_parts = current; + var upper_property_range = upper_machine_parts.get(rule.property); + upper_property_range.min = rule.value + 1; + current_property_range.max = rule.value; + + try stack.append(State{ + .name = rule.next, + .machine_part = upper_machine_parts + }); + } else if (is_min_captured and !is_max_captured) { + // Only the lower part is captured + var lower_machine_parts = current; + var lower_property_range = lower_machine_parts.get(rule.property); + lower_property_range.max = rule.value - 1; + current_property_range.min = rule.value; + + try stack.append(State{ + .name = rule.next, + .machine_part = lower_machine_parts + }); + } else { + unreachable; + } + } + } + + return .{ .uint = accepted_count }; } const example_input = [_][]const u8{ @@ -292,5 +427,5 @@ test "part 1 example" { } test "part 2 example" { - try aoc.expectAnswerUInt(part1, 167409079868000, &example_input); + try aoc.expectAnswerUInt(part2, 167409079868000, &example_input); }