solve day 19 part 1
This commit is contained in:
parent
14c4b266c1
commit
722811225c
296
src/day19.zig
Normal file
296
src/day19.zig
Normal file
@ -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);
|
||||||
|
}
|
@ -39,6 +39,7 @@ const Days = [_]aoc.Day{
|
|||||||
create_day(@import("./day16.zig")),
|
create_day(@import("./day16.zig")),
|
||||||
create_day(@import("./day17.zig")),
|
create_day(@import("./day17.zig")),
|
||||||
create_day(@import("./day18.zig")),
|
create_day(@import("./day18.zig")),
|
||||||
|
create_day(@import("./day19.zig")),
|
||||||
};
|
};
|
||||||
|
|
||||||
fn kilobytes(count: u32) u32 {
|
fn kilobytes(count: u32) u32 {
|
||||||
@ -200,4 +201,5 @@ test {
|
|||||||
_ = @import("./day16.zig");
|
_ = @import("./day16.zig");
|
||||||
_ = @import("./day17.zig");
|
_ = @import("./day17.zig");
|
||||||
_ = @import("./day18.zig");
|
_ = @import("./day18.zig");
|
||||||
|
_ = @import("./day19.zig");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user