#!/usr/bin/perl -w

use v5.36;
use Data::Dumper;
use Array::Heap;
use feature 'multidimensional';

my @map = map { chomp; [ split // ] } <>;

my %pos_of;
for my $y (0 .. $#map) {
	for my $x (0 .. $#{ $map[0]}) {
		my $c = $map[$y][$x];
		next if $c !~ /[a-z1-4]/;
		$pos_of{$c} = [ $x, $y ];
	}
}

my $nkeys = keys %pos_of;
say "nkeys = $nkeys";

$; = ';';

my $best;

my $min_dist = 1_000_000;

my %min_cost;
my $max_opened;
my $max_cost;
my @q0;
sub walk {
	my ($cost0, $cost, @opened) = @_;

	my @robots = splice(@opened, 0, 4);
	my %opened = map { $_ => 1 } @opened;

	my $key = join('', $robots[0], sort(@robots[1..3]), '|', sort @opened);

	if (defined $min_cost{$key} && $min_cost{$key} <= $cost) {
		return;
	}

	$min_cost{$key} = $cost;

	$max_opened = @opened if (!defined $max_opened || @opened > $max_opened);
	if (!defined $max_cost || $max_cost < $cost) {
		$max_cost = $cost;
		say "walk $cost - $key ", length $key, ' ', $max_opened;
	}
	if ($nkeys == @opened) {
		say "Found!";
		exit 1;
	}
		

	my $p = $pos_of{$robots[0]};
	my @q = [ @$p, $cost ];

	my %seen;
	$seen{$p->[0],$p->[1]} = 1;

	my %dist;

	while (my $pos = shift @q) {
		my ($x, $y, $steps) = @$pos;

		my $v = $map[$y][$x];
		next if $v eq '#';
		if ($v =~ /[a-z]/ && !$opened{$v}) {
			$dist{$v} //= $steps;
			my $cost = $steps + $nkeys - @opened;
			# say "opened=". scalar @opened;
			push_heap @q0, [ $cost, $steps, $v, @robots[1..3], $v, @opened ];
			push_heap @q0, [ $cost, $steps, $robots[1], $v, @robots[2..3], $v, @opened ];
			push_heap @q0, [ $cost, $steps, $robots[2], $v, @robots[1,3], $v, @opened ];
			push_heap @q0, [ $cost, $steps, $robots[3], $v, @robots[1..2], $v, @opened ];
		}
		if ($v =~ /[A-Z]/) {
			next if !$opened{lc($v)};
		}
	
		for my ($dx, $dy) (1, 0, 0, 1, -1, 0, 0, -1) {
			my ($nx, $ny) = ($x + $dx, $y + $dy);
			next if $seen{$nx,$ny}++;
			push @q, [ $nx, $ny, $steps+1 ];
		}
	}
}

push_heap @q0, [0, 0, 1, 2, 3, 4, 1, 2, 3, 4];
while (@q0) {
	my $elem = pop_heap @q0;
	walk(@$elem);
}

