grammar Packet {
regex TOP { <packet> <padding> }
regex packet { <version> <body> }
token version { <[01]> ** 3 }
token data { <[01]> ** 4 }
regex padding { '0'* }
token sub-packets-length { <[01]> ** 15 }
token sub-packets-number { <[01]> ** 11 }
token length-header { '0' <sub-packets-length> || '1' <sub-packets-number> }
proto regex body { * }
token body:sym<literal> { '100' ['1' <data>]* '0' <data> }
regex body:sym<sum> { '000' <length-header> <packet>+? <?{ body-match-header($<length-header>.made, $<packet>) }> }
regex body:sym<prod> { '001' <length-header> <packet>+? <?{ body-match-header($<length-header>.made, $<packet>) }> }
regex body:sym<min> { '010' <length-header> <packet>+? <?{ body-match-header($<length-header>.made, $<packet>) }> }
regex body:sym<max> { '011' <length-header> <packet>+? <?{ body-match-header($<length-header>.made, $<packet>) }> }
regex body:sym<gt> { '101' <length-header> <packet> ** 2 <?{ body-match-header($<length-header>.made, $<packet>) }> }
regex body:sym<lt> { '110' <length-header> <packet> ** 2 <?{ body-match-header($<length-header>.made, $<packet>) }> }
regex body:sym<eq> { '111' <length-header> <packet> ** 2 <?{ body-match-header($<length-header>.made, $<packet>) }> }
sub body-match-header(Hash:D \header, Array:D \packets --> Bool:D) {
header<length>.defined ??
header<length> == packets.map({ .Str.chars }).sum !!
header<number> == packets.elems
}
}
class PacketActions {
method TOP($/) { make $<packet>.made }
method packet($/) { make %(versum => $<version>.made + $<body>.made<versum>, value => $<body>.made<value>) }
method version($/) { make $/.Str.parse-base(2) }
method data($/) { make $/.Str.parse-base(2) }
method sub-packets-length($/) { make $/.Str.parse-base(2) }
method sub-packets-number($/) { make $/.Str.parse-base(2) }
method length-header($/) {
make $<sub-packets-length>.defined ??
%(length => $<sub-packets-length>.made) !!
%(number => $<sub-packets-number>.made)
}
method body:sym<literal>($/) { make %(versum => 0, value => $<data>».made.reduce({ $^a * 16 + $^b })) }
method body:sym<sum>($/) { make %(versum => $<packet>.map({ .made<versum> }).sum, value => [+] $<packet>.map({ .made<value> })) }
method body:sym<prod>($/) { make %(versum => $<packet>.map({ .made<versum> }).sum, value => [*] $<packet>.map({ .made<value> })) }
method body:sym<min>($/) { make %(versum => $<packet>.map({ .made<versum> }).sum, value => $<packet>.map({ .made<value> }).min) }
method body:sym<max>($/) { make %(versum => $<packet>.map({ .made<versum> }).sum, value => $<packet>.map({ .made<value> }).max) }
method body:sym<gt>($/) { make %(versum => $<packet>.map({ .made<versum> }).sum, value => +[>] $<packet>.map({ .made<value> })) }
method body:sym<lt>($/) { make %(versum => $<packet>.map({ .made<versum> }).sum, value => +[<] $<packet>.map({ .made<value> })) }
method body:sym<eq>($/) { make %(versum => $<packet>.map({ .made<versum> }).sum, value => +[==] $<packet>.map({ .made<value> })) }
}
sub solve(Str:D \hs) {
my \ss = hs.comb».map({ $_.parse-base(16).fmt('%04b') }).join;
Packet.parse(ss, :actions(PacketActions)).made
}
my \answer = solve($*IN.words[0]);
put 'part 1: ', answer<versum>;
put 'part 2: ', answer<value>;