--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+
+my @map = map { chomp; [ split // ] } <>;
+my $maxx = @{ $map[0] };
+my $maxy = @map;
+
+$; = ',';
+
+my %portals;
+
+sub add_portal {
+ my ($name, $x, $y) = @_;
+ say "add_portal: $name, $x, $y";
+ if (defined $portals{$name}) {
+ my ($x1, $y1) = @{ $portals{$name} };
+ $map[$y][$x] = [$x1, $y1];
+ $map[$y1][$x1] = [$x, $y];
+ say "portal $name: $x,$y <==> $x1,$y1";
+ } else {
+ $portals{$name} = [$x, $y];
+ }
+}
+
+sub find_portals {
+ my @d = @_;
+ for my $x (2 .. $maxx-3) {
+ for my $y (2 .. $maxy-3) {
+ next if $map[$y][$x] ne '.';
+ my $l1 = $map[$y+$d[1]][$x+$d[0]];
+ my $l2 = $map[$y+$d[3]][$x+$d[2]];
+ next if "$l1$l2" !~ /^[A-Z][A-Z]$/;
+ add_portal("$l1$l2", $x, $y);
+ }
+ }
+}
+
+find_portals(0, -2, 0, -1);
+find_portals(0, 1, 0, 2);
+find_portals(-2, 0, -1, 0);
+find_portals(1, 0, 2, 0);
+
+
+my %seen;
+my @q = [ @{ $portals{'AA'} }, 0, 0 ];
+my ($ex, $ey) = @{ $portals{'ZZ'} };
+say "walking towards $ex, $ey, 0";
+
+while (@q) {
+ my ($x, $y, $z, $steps) = @{ shift @q };
+ next if $seen{$x,$y,$z}++;
+ say "at $x,$y,$z $steps";
+
+ if ($x == $ex && $y == $ey && $z == 0) {
+ say "Found after $steps steps";
+ last;
+ }
+
+ $steps++;
+ if (ref $map[$y][$x]) {
+ my ($nx, $ny) = @{ $map[$y][$x] };
+ if ($x == 2 || $y == 2 || $x == $maxx - 3 || $y == $maxy - 3) {
+ if ($z > 0) {
+ push @q, [ $nx, $ny, $z-1, $steps ];
+ }
+ } else {
+ push @q, [ $nx, $ny, $z+1, $steps ];
+ }
+ }
+ for my ($dx, $dy) (0, 1, 1, 0, -1, 0, 0, -1) {
+ my $nx = $x + $dx;
+ my $ny = $y + $dy;
+ next if $map[$ny][$nx] ne '.' && !ref $map[$ny][$nx];
+ push @q, [$nx, $ny, $z, $steps];
+ }
+}
+