TL;DR

Where I sacrifice some efficiency for maintenability.

In post Multidimensional Conwayâ€™s Game of Life I discussed my approach to address a puzzle in Advent of Code that deals with Conwayâ€™s Game of Life.

As usual, that dayâ€™s challenge was split into two halves, basically requiring to solve the same problem in different dimensions. As it is, I just did some copy-and-paste with adaptations - it got the job done, but of course Iâ€™ll have to go in hell for this too.

So I thoughtâ€¦ why not generalize the solution for a generic dimension? Possibly using one old friendâ€¦ Algorithm::Loops? Here is what I came up with:

``````#!/usr/bin/env perl
use 5.024;
use warnings;
use autodie;
use experimental qw< postderef signatures >;
no warnings qw< experimental::postderef experimental::signatures >;
use Algorithm::Loops 'NestedLoops';
\$|++;

my @active;
my \$x = 0;
while (<DATA>) {
my @line = split m{\s*}mxs;
for my \$y (0 .. \$#line) {
push @active, "\$x \$y" if \$line[\$y] eq '#';
}
++\$x;
}

for my \$dimension (3, 4) {
\$_ .= ' 0' for @active;
my \$ticker = conway_ticker(@active);
\$ticker->() for 1 .. 5;
say \$dimension, ' ', scalar \$ticker->()->@*;
}

sub conway_ticker (@active) {
return sub { return [] } unless scalar @active;
my \$state = \@active;
my \$N = scalar split m{\s+}mxs, \$state->[0]; # dimension
my @ranges = map { [-1 .. 1] } 1 .. \$N;
return sub {
my %previously_active;
my %count_for;
for my \$cell (\$state->@*) {
my @pos = split m{\s+}mxs, \$cell;
\$previously_active{\$cell}++;
NestedLoops(
\@ranges,
sub (@ds) {
my \$key = join ' ', map { \$pos[\$_] + \$ds[\$_] } 0 .. \$#ds;
\$count_for{\$key}++ if \$key ne \$cell;
}
);
}
my @active;
while (my (\$key, \$count) = each %count_for) {
if (\$previously_active{\$key}) {
push @active, \$key if \$count == 2 || \$count == 3;
}
else {
push @active, \$key if \$count == 3;
}
}
return \$state = \@active;
};
}

__DATA__
##.#...#
#..##...
....#..#
....####
#.#....#
###.#.#.
.#.#.#..
.#.....#
``````

One interesting thing is that this code is about 5 to 6 times slower than the copy-and-paste solution. Without having done any kind of benchmark (which is the only way to go when you have these kind of doubts!), my gut feeling is that this generic solution makes heavy use of sub calls, which is not the case in the â€śless maintainableâ€ť version.

Take it like thisâ€¦ a gut feeling.

Anywayâ€¦ it works, so enjoy!

Comments? Octodon, , GitHub, Reddit, or drop me a line!