#!/usr/bin/perl
use strict;
use warnings;
use feature qw(say);
use List::AllUtils qw(sum indexes);
use Math::Vector::Real;
my ($y,$x) = Math::Vector::Real->canonical_base(2);
my @Dirs = ($y, $x, -$y, -$x);
# Read in grid, adding sentinel 99s to right and bottom
my @Grid = map { chomp; [map {int} split(//), 99] } <>;
push( @Grid, [(99) x $Grid[0]->@*] );
sub grid_at ($) { my $p = shift; return ($Grid[$p->[0]][$p->[1]]) }
# Get trailheads
my @trailheads;
foreach my $y (0 .. @Grid - 2) {
push( @trailheads, map {V($y,$_)} indexes {$_ == 0} $Grid[$y]->@* );
}
# Recurse on pos, return is hash of trail ends => number of routes
sub recurse {
my ($pos) = @_;
my $next = grid_at($pos) + 1;
return ($pos => 1) if ($next == 10); # found 1 route to pos
my %ret;
foreach my $dir (@Dirs) {
my $npos = $pos + $dir;
if (grid_at($npos) == $next) {
my %routes = &recurse( $npos );
$ret{$_} += $routes{$_} foreach (keys %routes); # accumulate routes
}
}
return (%ret);
}
my $part1 = 0;
my $part2 = 0;
foreach my $head (@trailheads) {
my %res = &recurse( $head );
$part1 += scalar keys %res;
$part2 += sum values %res;
}
say "Part 1: $part1";
say "Part 2: $part2";