grammar Monkey {
rule TOP { <monkey-id> <items> <operation> <test> <action> }
rule monkey-id { 'Monkey' <number> ':' }
rule items { 'Starting' 'items:' <number>+ % ',' }
rule operation { 'Operation:' 'new' '=' 'old' <op> <operand> }
rule test { 'Test:' 'divisible' 'by' <number> }
rule action {
'If' 'true:' 'throw' 'to' 'monkey' <number>
'If' 'false:' 'throw' 'to' 'monkey' <number>
}
rule operand { <number> | 'old' }
token op { '+' | '*' }
token number { \d+ }
}
class MonkeyActions {
method TOP($/) {
make (|$<monkey-id>.made, |$<items>.made,
|$<operation>.made, |$<test>.made,
|$<action>.made).Hash
}
method monkey-id($/) { make (:id($<number>.made)).Hash }
method items($/) { make (:items($<number>».made)).Hash }
method operation($/) { make (:op($<op>.made), :oper($<operand>.made)).Hash }
method test($/) { make (:divisor($<number>.made)).Hash }
method action($/) { make (:targets($<number>».made.reverse.Array)).Hash }
method operand($/) { make $<number> ?? $<number>.made !! -1 }
method op($/) { make $/.Str }
method number($/) { make $/.Int }
}
sub inspections-p1(@monkeys, UInt:D \rounds) {
my @monkeys_ = @monkeys.deepmap(*.clone);
for ^rounds {
for @monkeys_ -> \mk {
for |mk<items> -> \itm {
my $level = do given mk<op> {
when '*' { itm * (mk<oper> == -1 ?? itm !! mk<oper>) }
when '+' { itm + (mk<oper> == -1 ?? itm !! mk<oper>) }
} div 3;
@monkeys_[mk<targets>[+($level %% mk<divisor>)]]<items>.push($level);
++mk<inspection>;
}
mk<items> = [ ];
}
}
@monkeys_.map(*<inspection>).Array
}
sub inspections-p2(@monkeys, UInt:D \rounds) {
my @monkeys_ = @monkeys.deepmap(*.clone);
my $modulo = [*] @monkeys.map(*<divisor>);
for ^rounds {
for @monkeys_ -> \mk {
for |mk<items> -> \itm {
my $level = do given mk<op> {
when '*' { itm * (mk<oper> == -1 ?? itm !! mk<oper>) }
when '+' { itm + (mk<oper> == -1 ?? itm !! mk<oper>) }
} mod $modulo;
@monkeys_[mk<targets>[+($level %% mk<divisor>)]]<items>.push($level);
++mk<inspection>;
}
mk<items> = [ ];
}
}
@monkeys_.map(*<inspection>).Array
}
my @monkeys = $*IN.slurp.split("\n\n").map({ Monkey.parse($_, :actions(MonkeyActions)).made }).Array;
put 'part 1: ', [*] inspections-p1(@monkeys, 20).sort.tail(2);
put 'part 2: ', [*] inspections-p2(@monkeys, 10000).sort.tail(2);