From 722811225c5870953e1c334bf401de8f0b71666e Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Thu, 25 Apr 2024 00:24:59 +0300 Subject: [PATCH] solve day 19 part 1 --- src/day19.zig | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 2 + 2 files changed, 298 insertions(+) create mode 100644 src/day19.zig diff --git a/src/day19.zig b/src/day19.zig new file mode 100644 index 0000000..31bed2c --- /dev/null +++ b/src/day19.zig @@ -0,0 +1,296 @@ +const std = @import("std"); +const aoc = @import("./aoc.zig"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; + +const NameString = std.BoundedArray(u8, 3); + +const RuleCondition = enum { + Less, + More, + Always, +}; + +const PartProperty = enum { + X, + M, + A, + S, + + fn from_str(text: []const u8) ?PartProperty { + if (std.mem.eql(u8, text, "x")) { + return .X; + } else if (std.mem.eql(u8, text, "m")) { + return .M; + } else if (std.mem.eql(u8, text, "a")) { + return .A; + } else if (std.mem.eql(u8, text, "s")) { + return .S; + } else { + return null; + } + } + + fn to_str(self: PartProperty) []const u8 { + return switch (self) { + .X => "x", + .A => "a", + .M => "m", + .S => "s", + }; + } +}; + +const Rule = struct { + condition: RuleCondition, + property: PartProperty = .X, + next: NameString, + value: u32 = 0, +}; + +const Workflow = std.ArrayList(Rule); + +const MachinePart = struct { + x: u32 = 0, + m: u32 = 0, + a: u32 = 0, + s: u32 = 0 +}; + +const Input = struct { + workflows: std.AutoHashMap(NameString, Workflow), + machine_parts: std.ArrayList(MachinePart), + + fn init(allocator: Allocator) Input { + return Input{ + .workflows = std.AutoHashMap(NameString, Workflow).init(allocator), + .machine_parts = std.ArrayList(MachinePart).init(allocator), + }; + } + + fn deinit(self: *Input) void { + var workflows = self.workflows.valueIterator(); + while (workflows.next()) |workflow| { + workflow.deinit(); + } + self.machine_parts.deinit(); + self.workflows.deinit(); + } +}; + +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()}) + } + } + std.debug.print("}}\n", .{}); +} + +fn parse_workflow(allocator: Allocator, line: []const u8) !std.meta.Tuple(&.{ NameString, Workflow }) { + var parsed = Workflow.init(allocator); + errdefer parsed.deinit(); + + const rules_start = std.mem.indexOfScalar(u8, line, '{') orelse return error.Invalid; + const rules_stop = std.mem.lastIndexOfScalar(u8, line, '}') orelse return error.Invalid; + + const name = try NameString.fromSlice(line[0..rules_start]); + const rules_str = line[(rules_start+1)..rules_stop]; + var rules_iter = std.mem.splitScalar(u8, rules_str, ','); + while (rules_iter.next()) |rule_str| { + if (std.mem.indexOfScalar(u8, rule_str, ':')) |colon| { + const cmp_idx = std.mem.indexOfAny(u8, rule_str, "><") orelse return error.Invalid; + + var condition: RuleCondition = undefined; + if (rule_str[cmp_idx] == '>') { + condition = .More; + } else if (rule_str[cmp_idx] == '<') { + condition = .Less; + } else { + unreachable; + } + + 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), + .next = try NameString.fromSlice(rule_str[(colon+1)..]) + }); + } else { + try parsed.append(Rule{ + .condition = .Always, + .next = try NameString.fromSlice(rule_str) + }); + } + } + + return .{ name, parsed }; +} + +fn parse_machine_part(line: []const u8) !MachinePart { + if (line[0] != '{') return error.Invalid; + if (line[line.len-1] != '}') return error.Invalid; + + var machine_part = MachinePart{}; + + var parts = std.mem.splitScalar(u8, line[1..(line.len-1)], ','); + while (parts.next()) |part| { + const equal_idx = std.mem.indexOfScalar(u8, part, '=') orelse return error.InvalidKeyValue; + const name = part[0..equal_idx]; + const value_str = part[(equal_idx+1)..]; + const value = try std.fmt.parseUnsigned(u32, value_str, 10); + + if (std.mem.eql(u8, name, "a")) { + machine_part.a = value; + } else if (std.mem.eql(u8, name, "x")) { + machine_part.x = value; + } else if (std.mem.eql(u8, name, "m")) { + machine_part.m = value; + } else if (std.mem.eql(u8, name, "s")) { + machine_part.s = value; + } else { + return error.InvalidRatingName; + } + } + + return machine_part; +} + +fn parse_input(allocator: Allocator, lines: []const []const u8) !Input { + var parsed = Input.init(allocator); + errdefer parsed.deinit(); + + var section_split: usize = 0; + for (0.., lines) |i, line| { + if (line.len == 0) { + section_split = i; + break; + } + } + assert(lines[section_split].len == 0); + + for (lines[0..section_split]) |line| { + const row = try parse_workflow(allocator, line); + try parsed.workflows.put(row[0], row[1]); + } + + for (lines[(section_split+1)..]) |line| { + try parsed.machine_parts.append(try parse_machine_part(line)); + } + + return parsed; +} + +fn eval_rule(part: MachinePart, rule: Rule) ?NameString { + if (rule.condition == .Always) { + return rule.next; + } + + const property_value = switch (rule.property) { + .X => part.x, + .M => part.m, + .A => part.a, + .S => part.s, + }; + + var condition_result: bool = undefined; + if (rule.condition == .Less) { + condition_result = property_value < rule.value; + } else if (rule.condition == .More) { + condition_result = property_value > rule.value; + } else { + unreachable; + } + + if (condition_result) { + return rule.next; + } else { + return null; + } +} + +fn eval_workflow(part: MachinePart, workflow: Workflow) NameString { + for (workflow.items) |rule| { + if (eval_rule(part, rule)) |name| { + return name; + } + } + + unreachable; +} + +fn is_part_accepted(part: MachinePart, workflows: std.AutoHashMap(NameString, Workflow)) !bool { + var current = workflows.get(try NameString.fromSlice("in")) orelse return error.MissingInWorkflow; + while (true) { + const next_name = eval_workflow(part, current); + const next_name_str = next_name.slice(); + + if (std.mem.eql(u8, next_name_str, "R")) { + return false; + } else if (std.mem.eql(u8, next_name_str, "A")) { + return true; + } else { + current = workflows.get(next_name) orelse return error.MissingWorkflow; + } + } +} + +pub fn part1(input: *aoc.Input) !aoc.Result { + const allocator = input.allocator; + var parsed = try parse_input(allocator, input.lines); + defer parsed.deinit(); + + var result: u64 = 0; + for (parsed.machine_parts.items) |part| { + if (try is_part_accepted(part, parsed.workflows)) { + result += part.s; + result += part.x; + result += part.m; + result += part.a; + } + } + + return .{ .uint = result }; +} + +pub fn part2(input: *aoc.Input) !aoc.Result { + const allocator = input.allocator; + var parsed = try parse_input(allocator, input.lines); + defer parsed.deinit(); + + return .{ .uint = 0 }; +} + +const example_input = [_][]const u8{ + "px{a<2006:qkq,m>2090:A,rfg}", + "pv{a>1716:R,A}", + "lnx{m>1548:A,A}", + "rfg{s<537:gd,x>2440:R,A}", + "qs{s>3448:A,lnx}", + "qkq{x<1416:A,crn}", + "crn{x>2662:A,R}", + "in{s<1351:px,qqz}", + "qqz{s>2770:qs,m<1801:hdj,R}", + "gd{a>3333:R,R}", + "hdj{m>838:A,pv}", + "", + "{x=787,m=2655,a=1222,s=2876}", + "{x=1679,m=44,a=2067,s=496}", + "{x=2036,m=264,a=79,s=2244}", + "{x=2461,m=1339,a=466,s=291}", + "{x=2127,m=1623,a=2188,s=1013}", +}; + +test "part 1 example" { + try aoc.expectAnswerUInt(part1, 19114, &example_input); +} + +test "part 2 example" { + try aoc.expectAnswerUInt(part1, 167409079868000, &example_input); +} diff --git a/src/main.zig b/src/main.zig index 36083ed..1b7d176 100644 --- a/src/main.zig +++ b/src/main.zig @@ -39,6 +39,7 @@ const Days = [_]aoc.Day{ create_day(@import("./day16.zig")), create_day(@import("./day17.zig")), create_day(@import("./day18.zig")), + create_day(@import("./day19.zig")), }; fn kilobytes(count: u32) u32 { @@ -200,4 +201,5 @@ test { _ = @import("./day16.zig"); _ = @import("./day17.zig"); _ = @import("./day18.zig"); + _ = @import("./day19.zig"); }