grammar Parts {
token TOP { <line>+ %% "\n" }
token line { [<number> || <symbol> || '.']+ }
token number { \d+ }
token symbol { <[-&@+*/%$=#]> }
}
my $width;
my %symbols is SetHash;
my %stars is SetHash;
class PartsActions {
my $line-num = 0;
method TOP($/){ make $<line>.map(*.made).flat.Array }
method line($/) {
my @elm;
@elm.push(|$<number>.map(*.made)) if $<number>;
++$line-num;
make @elm
}
method number($/) { make (:number($/.Int), :row($line-num), :col($/.from % $width)).Hash }
method symbol($/) {
my $coord = "{$line-num}-{$/.from % $width}";
%symbols.set($coord);
%stars.set($coord) if $/.Str eq '*';
make $/.Str
}
}
my $s = $*IN.slurp;
$width = $s.index("\n") + 1;
my ($sum01, %gears);
for Parts.parse($s, :actions(PartsActions)).made -> $n {
my @vici = ($n<row>-1.. $n<row>+1 X $n<col>-1 .. $n<col>+$n<number>.chars).map({ "{.[0]}-{.[1]}" });
$sum01 += $n<number> if %symbols{@vici}.any;
@vici.grep({ %stars{$_} }).map({ %gears{$_}.push($n<number>) });
}
put 'part 1: ', $sum01;
put 'part 2: ', %gears.keys.map({ with %gears{$_} -> $g { $g[0] * $g[1] if +$g == 2 } }).sum;