#import "Basic";
#import "String";
#import "File";
#import "Math";
#import "Hash_Table";
solve_day19 :: (test: bool) {
contents := read_entire_file(ifx test then "inputs/day19_test.txt" else "inputs/day19.txt");
sections := split(contents, "\n\n");
debug_it := true;
part1 := 0;
part2 := 0;
workflows: Table(string, [..]Rule);
workflow_order: [..]string;
workflow_lines := split(sections[0], "\n"[0]);
for line: workflow_lines {
a := split(line, "{");
workflow_name := a[0];
rules := split(slice(a[1], 0, a[1].count - 1), ",");
workflow_rules: [..]Rule;
for rule: rules {
array_add(*workflow_rules, parse_rule(rule));
}
table_add(*workflows, workflow_name, workflow_rules);
array_add(*workflow_order, workflow_name);
}
run_section_lines := split(sections[1], "\n"[0]);
for line: run_section_lines {
part := parse_part(slice(line, 1, line.count - 2));
is_rejected := false;
is_accepted := false;
current, found := table_find(*workflows, "in");
while !is_rejected && !is_accepted {
for item: current {
a: u32 = 0;
if item.part == {
case #char "d";
if item.workflow[0] == #char "A" {
is_accepted = true;
break;
} else if item.workflow[0] == #char "R" {
is_rejected = true;
break;
} else {
current, found = table_find(*workflows, item.workflow);
break;
}
case #char "x";
a = part.x;
case #char "m";
a = part.m;
case #char "a";
a = part.a;
case #char "s";
a = part.s;
case;
assert(false, "Bad part: %", item.part);
}
if item.comparator == #char ">" {
if a > item.amount {
if item.workflow[0] == #char "A" {
is_accepted = true;
break;
} else if item.workflow[0] == #char "R" {
is_rejected = true;
break;
}
current, found = table_find(*workflows, item.workflow);
break;
}
} else if item.comparator == #char "<" {
if a < item.amount {
if item.workflow[0] == #char "A" {
is_accepted = true;
break;
} else if item.workflow[0] == #char "R" {
is_rejected = true;
break;
}
current, found = table_find(*workflows, item.workflow);
break;
}
}
}
}
if is_accepted {
part1 += part.x + part.m + part.a + part.s;
}
}
print("Part 1: %\n", part1);
stack: [..]Queue_Part;
array_add(*stack, .{
part = .{
x = Range.{ start = 1, stop = 4000 },
m = Range.{ start = 1, stop = 4000 },
a = Range.{ start = 1, stop = 4000 },
s = Range.{ start = 1, stop = 4000 },
},
workflow = "in"
});
while stack.count > 0 {
part := stack[stack.count - 1];
array_unordered_remove_by_index(*stack, stack.count - 1);
if part.workflow[0] == #char "A" {
part2 += xx length(part.part);
continue;
} else if part.workflow[0] == #char "R" {
continue;
}
workflow, found := table_find(*workflows, part.workflow);
for rule, rule_i: array_view(workflow, 0, workflow.count - 1) {
part_b := part;
part_b.workflow = rule.workflow;
compare_amount: Range = ---;
xmas_a: Range = ---;
xmas_b: Range = ---;
if rule.part == {
case #char "x";
compare_amount = part.part.x;
case #char "m";
compare_amount = part.part.m;
case #char "a";
compare_amount = part.part.a;
case #char "s";
compare_amount = part.part.s;
case;
assert(false, "Bad part in rule: %", rule.part);
}
if rule.comparator == #char ">" {
if compare_amount.stop <= rule.amount continue;
if compare_amount.start > rule.amount {
part.workflow = rule.workflow;
array_add(*stack, part);
break;
}
xmas_a = .{ start = compare_amount.start, stop = rule.amount };
xmas_b = .{ start = rule.amount + 1, stop = compare_amount.stop };
} else if rule.comparator == #char "<" {
if compare_amount.start >= rule.amount continue;
if compare_amount.stop < rule.amount {
part.workflow = rule.workflow;
array_add(*stack, part);
break;
}
xmas_a = .{ start = rule.amount, stop = compare_amount.stop };
xmas_b = .{ start = compare_amount.start, stop = rule.amount - 1 };
} else {
assert(false, "Invalid comparator: %", rule.comparator);
}
if rule.part == {
case #char "x";
part.part.x = xmas_a;
part_b.part.x = xmas_b;
case #char "m";
part.part.m = xmas_a;
part_b.part.m = xmas_b;
case #char "a";
part.part.a = xmas_a;
part_b.part.a = xmas_b;
case #char "s";
part.part.s = xmas_a;
part_b.part.s = xmas_b;
case;
assert(false);
}
array_add(*stack, part_b);
}
part.workflow = workflow[workflow.count - 1].workflow;
array_add(*stack, part);
}
print("Part 2: %\n", part2);
}
parse_part :: (p: string) -> (part: Part, success: bool) {
ps := split(p, ",");
x, success_x := string_to_int(slice(ps[0], 2, ps[0].count - 2), 10, u32);
m, success_m := string_to_int(slice(ps[1], 2, ps[1].count - 2), 10, u32);
a, success_a := string_to_int(slice(ps[2], 2, ps[2].count - 2), 10, u32);
s, success_s := string_to_int(slice(ps[3], 2, ps[3].count - 2), 10, u32);
return .{
x = x,
m = m,
a = a,
s = s,
}, success_x && success_m && success_a && success_s;
}
parse_rule :: (p: string) -> (rule: Rule, success: bool) {
number_split := split(p, ":");
part := number_split[0];
rule := Rule.{
part = part[0],
comparator = 0,
amount = 0,
workflow = "",
};
if number_split.count > 1 {
workflow := number_split[1];
rule.comparator = part[1];
amount := string_to_int(slice(part, 2, part.count - 2), 10, u32);
rule.amount = amount;
rule.workflow = workflow;
} else {
rule.part = #char "d";
rule.workflow = part;
}
return rule, true;
}
length_inclusive :: (range: Range) -> u64 {
if range.stop < range.start {
return 0;
} else {
return range.stop - range.start + 1;
}
}
length :: (pr: Part_Range) -> u64 {
return length_inclusive(pr.x) * length_inclusive(pr.m) * length_inclusive(pr.a) * length_inclusive(pr.s);
}
Rule :: struct {
part: u8;
comparator: u8;
amount: u32;
workflow: string;
}
Queue_Part :: struct {
part: Part_Range;
workflow: string;
}
Range :: struct {
start: u32;
stop: u32;
}
Part_Range :: struct {
x: Range;
m: Range;
a: Range;
s: Range;
}
Part :: struct {
x: u32;
m: u32;
a: u32;
s: u32;
}