I'm a software engineer get me out of here

my $d = qq:to/END/;
         00000
      00003130000
    000321322221000
   00231222432132200
  0041433223233211100
  0232231612142618530
 003152122326114121200
 031252235216111132210
 022211246332311115210
00113232262121317213200
03152118212313211411110
03231234121132221411410
03513213411311414112320
00427534125412213211400
 013322444412122123210
 015132331312411123120
 003333612214233913300
  0219126511415312570
  0021321524341325100
   00211415413523200
    000122111322000
      00001120000
         00000
END

my $w = $d.split("\n")».chars.max;
$d = $d.split("\n")».fmt("%-{$w}s").join("\n"); # pad lines to same length
$w++;

my @directions = ( 1, -1, -$w-1, -$w, -$w+1, $w-1, $w, $w+1);
my @nodes.push: .pos - 1 for $d ~~ m:g/\d/;
my %dist = @nodes.race.map: { $_ => all-destinations([$_]) };

sub all-destinations (@queue) {
    my %to;
    my $dd = $d;
    while shift @queue -> $path {
        my $from = ($path.split(' '))[*-1];
        my $steps = $dd.substr($from, 1);
        next if $steps eq ' ';
        %to{$from} //= $path;
        next if $steps eq '0';
        $dd.substr-rw($from, 1) = '0';
        for @directions -> $dir {
            my $next = $from + $steps × $dir;
            next if $next < 0 or $next > $dd.chars;
            @queue.push: "$path $next" if $dd.substr($next, 1) ~~ /\d/;
        }
    }
    %to;
}

sub   to-xy ($nodes) { join ' ', $nodes.split(' ').map: { '(' ~ join(',', floor($_/$w), $_%$w) ~ ')' } }
sub from-xy ($x, $y) { $x × $w + $y }

my $startpos = from-xy 11, 11;

my %routes;
%routes{.split(' ').elems}.push: .&to-xy
    for grep { .so }, map { %dist{$startpos}{$_} }, grep { '0' eq $d.substr($_, 1) }, @nodes;
my $n = %routes{ my $short-route = %routes.keys.sort.first }.elems;
say "Shortest escape routes ($n) of length {$short-route - 1}:\n\t" ~
    %routes{$short-route}.join("\n\t");

say "\nShortest from (21,11) to  (1,11):\n\t" ~ %dist{from-xy 21, 11}{from-xy  1, 11}.&to-xy;
say "\nShortest from  (1,11) to (21,11):\n\t" ~ %dist{from-xy  1, 11}{from-xy 21, 11}.&to-xy;

my @long-short = reverse sort { .split(' ').elems }, gather %dist.deepmap(*.take);
my $l = @long-short[0].split(' ').elems;
say "\nLongest 'shortest' paths (length {$l-1}):";
say "\t" ~ .&to-xy for grep { .split(' ').elems == $l }, @long-short;

say "\nNot reachable from HQ:\n\t" ~ @nodes.grep({not %dist{$startpos}{$_}}).&to-xy;

my @HQ;
@HQ[.split(' ').elems].push: .&to-xy for %dist{$startpos}.values;
say "\nLongest reinforcement from HQ is {@HQ - 2} for:\n\t" ~ @HQ[*-1].join("\n\t");

Output:

Shortest escape routes (40) of length 4:
    (11,11) (11,12) (8,12) (6,12) (0,12)
    (11,11) (10,11) (7,11) (7,12) (1,6)
    (11,11) (11,12) (8,9) (2,9) (1,8)
    (11,11) (11,12) (8,9) (2,9) (1,9)
    (11,11) (10,10) (8,10) (5,13) (1,13)
    (11,11) (11,12) (8,9) (2,15) (1,14)
    (11,11) (11,12) (8,9) (2,15) (1,15)
    (11,11) (11,12) (8,9) (2,15) (1,16)
    (11,11) (10,10) (8,10) (5,7) (2,4)
    (11,11) (10,11) (7,8) (7,5) (2,5)
    (11,11) (11,12) (8,9) (2,15) (2,16)
    (11,11) (11,12) (11,9) (9,9) (3,3)
    (11,11) (10,11) (7,8) (4,5) (3,4)
    (11,11) (12,11) (12,14) (8,18) (3,18)
    (11,11) (12,11) (9,14) (6,17) (4,19)
    (11,11) (11,12) (8,9) (8,3) (6,1)
    (11,11) (10,10) (8,8) (8,4) (6,2)
    (11,11) (11,12) (11,15) (11,17) (7,21)
    (11,11) (11,12) (8,9) (8,3) (8,1)
    (11,11) (10,10) (8,8) (12,4) (9,1)
    (11,11) (11,12) (8,9) (14,3) (11,0)
    (11,11) (10,11) (7,8) (7,5) (12,0)
    (11,11) (12,10) (13,10) (13,5) (13,0)
    (11,11) (10,10) (12,8) (16,4) (13,1)
    (11,11) (12,11) (12,14) (16,18) (13,21)
    (11,11) (10,10) (8,8) (12,4) (15,1)
    (11,11) (11,12) (11,15) (11,17) (15,21)
    (11,11) (10,10) (12,8) (16,4) (16,1)
    (11,11) (10,11) (10,14) (12,16) (16,20)
    (11,11) (12,11) (12,14) (16,18) (16,21)
    (11,11) (12,11) (15,8) (15,5) (18,2)
    (11,11) (10,11) (13,8) (14,7) (18,3)
    (11,11) (11,12) (14,9) (18,5) (19,4)
    (11,11) (11,12) (14,15) (16,15) (19,18)
    (11,11) (11,12) (14,12) (16,12) (20,16)
    (11,11) (10,11) (13,11) (17,15) (20,18)
    (11,11) (12,10) (13,10) (18,15) (21,15)
    (11,11) (11,12) (14,9) (18,13) (22,9)
    (11,11) (12,11) (15,8) (18,11) (22,11)
    (11,11) (11,12) (14,9) (18,13) (22,13)

Shortest from (21,11) to  (1,11):
    (21,11) (21,12) (19,10) (14,10) (10,10) (8,8) (4,8) (1,11)

Shortest from  (1,11) to (21,11):
    (1,11) (2,10) (5,13) (9,9) (15,3) (20,8) (20,10) (21,11)

Longest 'shortest' paths (length 9):
    (7,3) (7,4) (5,4) (8,7) (8,9) (14,15) (16,17) (17,16) (18,16) (20,14)
    (10,21) (10,20) (9,19) (9,16) (9,9) (15,3) (15,8) (15,5) (15,2) (14,2)
    (11,21) (11,20) (11,16) (11,17) (11,13) (13,11) (17,7) (15,5) (15,2) (14,2)
    (12,21) (12,19) (12,17) (12,16) (12,12) (12,11) (15,8) (15,5) (15,2) (14,2)
    (1,11) (1,12) (4,9) (6,9) (8,9) (14,15) (16,17) (17,16) (18,16) (20,14)
    (2,9) (2,10) (5,7) (8,7) (8,9) (14,15) (16,17) (17,16) (18,16) (20,14)
    (2,13) (2,15) (3,15) (6,12) (12,18) (13,19) (13,20) (17,16) (18,16) (20,14)

Not reachable from HQ:
    (2,18) (4,3) (18,20)

Longest reinforcement from HQ is 6 for:
    (11,11) (11,12) (11,15) (11,17) (7,17) (7,20) (6,20)
    (11,11) (11,12) (11,15) (13,17) (13,19) (13,20) (17,20)
    (11,11) (11,12) (14,12) (16,12) (20,12) (21,11) (22,12)
    (11,11) (11,12) (14,15) (16,17) (17,16) (18,16) (20,14)
    (11,11) (12,11) (9,14) (6,17) (4,17) (4,18) (3,19)

Last updated