grammar Xmas {
rule TOP { <name> '{' <work>+ % ',' '}' }
token name { <[a..z]>+ || 'A' || 'R' }
rule work { <rate> <op> <value> ':' <name> || <name> }
token rate { <[xmas]> }
token op { '<' | '>' }
token value { \d+ }
rule input { '{' <entry>+ % ',' '}' }
rule entry { <rate> '=' <value> }
}
class XmasActions {
method TOP($/) { make $<name>.made => ($<work>».made).Array }
method name($/) { make ~$/ }
method work($/) {
my %h = 'next' => $<name>.made, ;
with $<rate>.made {
%h<rate> = $<rate>.made;
%h<op> = $<op>.made;
%h<value> = $<value>.made;
}
make %h
}
method rate($/) { make ~$/ }
method op($/) { make ~$/ }
method value($/) { make +$/ }
method input($/) { make ($<entry>».made).Hash }
method entry($/) { make $<rate>.made => $<value>.made }
}
my (%flows, $sum2);
put 'part 1: ', do with $*IN.split("\n\n") {
%flows = .[0].lines.map({ Xmas.parse($_, :actions(XmasActions)).made });
[+] (process-p1($_, %flows) for .[1].lines.map({ Xmas.parse($_, :rule<input>, :actions(XmasActions)).made }))
};
process-p2((:x(1..4000), :m(1..4000), :a(1..4000), :s(1..4000)).Hash, 'in', %flows);
put 'part 2: ', $sum2;
sub process-p1(%input, %flows) {
my $fn = 'in';
loop {
for %flows{$fn}.Array -> $work {
given $fn {
when 'A' { return %input.map(*.value).sum; }
when 'R' { return 0; }
}
with $work<op> {
when '<' { { $fn = $work<next>; last; } if %input{$work<rate>} < $work<value>; }
when '>' { { $fn = $work<next>; last; } if %input{$work<rate>} > $work<value>; }
} else {
$fn = $work<next>;
}
}
}
}
sub process-p2(%ranges is copy, $fn_, %flows) {
my $fn = $fn_;
for %flows{$fn}.Array -> $work {
given $fn {
when 'A' { $sum2 += [*] %ranges.values.map(+*); return; }
when 'R' { return; }
}
with $work<op> {
my %ranges_ = %ranges;
when '<' {
with %ranges_{$work<rate>} -> $rr {
if $rr.min < $work<value> {
%ranges_{$work<rate>} = $rr.min .. $work<value> - 1;
process-p2(%ranges_, $work<next>, %flows);
}
}
with %ranges{$work<rate>} -> $rr {
$rr.max ≥ $work<value> ?? (%ranges{$work<rate>} = $work<value> .. $rr.max) !! return;
}
}
when '>' {
with %ranges_{$work<rate>} -> $rr {
if $rr.max > $work<value> {
%ranges_{$work<rate>} = max($rr.min, $work<value> + 1) .. $rr.max;
process-p2(%ranges_, $work<next>, %flows);
}
}
with %ranges{$work<rate>} -> $rr {
$rr.min ≤ $work<value> ?? (%ranges{$work<rate>} = $rr.min .. $work<value>) !! return;
}
}
} else {
process-p2(%ranges, $work<next>, %flows);
}
}
}