solve day 19 part 1

This commit is contained in:
Rokas Puzonas 2024-04-25 00:24:59 +03:00
parent 14c4b266c1
commit 722811225c
2 changed files with 298 additions and 0 deletions

296
src/day19.zig Normal file
View 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);
}

View File

@ -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");
}