ETOOBUSY 🚀 minimal blogging for the impatient
AoC 2022/23 - Unstable diffusion
TL;DR
On with Advent of Code puzzle XX from 2022: one challenge that needed patience.
This was one of those challenges in which the solution - in particular, the solution for part 2 - was in that bitter spot between you need to do better and optimize this thing and you can get a solution in some minutes, just hold on.
I opted for the waiting part, of course.
The full solution is available; let’s just comment a few passages.
Each “planter elf” is represented as an object of class Planter
:
class Planter {
has $.P is built;
method pos { $!P.join(',') }
method make-proposal ($team, $direction is copy) {
state @deltas = [-1, -1], [-1, 0], [-1, 1],
[ 0, -1], [ 0, 1],
[ 1, -1], [ 1, 0], [ 1, 1];
state @tests-for =
[2, 4, 7], # north
[0, 3, 5], # south
[0, 1, 2], # west
[5, 6, 7]; # east
state @move-for = [0, 1], [0, -1], [-1, 0], [1, 0];
my @is-empty = @deltas.map: { $team{($!P «+» $_).join(',')}:!exists };
return if @is-empty.all;
for ^4 {
return ($!P «+» @move-for[$direction])
if @is-empty[|@tests-for[$direction]].all;
$direction = ($direction + 1) % 4;
}
return;
}
method move-to ($Q) { $!P = $Q }
}
The two phases are represented as methods: one to figure out a
proposal (make-proposal
), another one for actually doing the move.
The planter is “controlled” externally, so there is no autonomous, local
decision for doing the move.
Moving in part 1 is done according to the rules, including tracking the direction of movements as time goes by:
my $direction = -1;
for ^10 {
$direction = ($direction + 1) % 4;
# collect proposals, keep moving one separated
my (%moving, %freezing);
for %team.values -> $elf {
my $proposal = $elf.make-proposal(%team, $direction) or next;
my $key = $proposal.join(',');
next if %freezing{$key};
if %moving{$key} {
%moving{$key}:delete;
%freezing{$key} = 1;
}
else {
%moving{$key} = [$elf, $proposal];
}
}
# move
for %moving.values -> ($elf, $target) {
%team{$elf.pos}:delete;
$elf.move-to($target);
%team{$elf.pos} = $elf;
}
}
Depending on the propostals, elves/planters are tracked inside either
%moving
or %freezing
. At the end of the proposals resolution phase,
the actual movement phase happens, applied only to the elves/planters
remained in %moving
.
At this point, calculating the result is simple, keeping in mind that we have to remove the elves themselves because we need the empty locations:
my @Ps = %team.values».P;
my $area = [*] (0, 1).map({ my @Vs = @Ps»[$_]; 1 + @Vs.max - @Vs.min });
return $area - %team.elems;
Part 2 is considerably less predictable, at least for me. I ended up extending the loop in part 1 and waiting until no movement happened, with the hope it would not take too much time. I was lucky, it only takes about 15 minutes to calculate the solution, so it’s good for me.
Still, I’ll go looking into the solutions megathread because I can’t imagine that there’s no better way of doing this.
Cheers!