TL;DR

As I see it, I started toying with Advent of Code 2018 about in mid-May this year, and stopped at puzzle 4 at the beginning of June.

Itâ€™s not particularly difficult, which is probablyâ€¦ the problem. It gave me the subtle feeling that there had to be a very quick and dirty solution to it, but I didnâ€™t actually know what that looked like.

So I avoided this puzzle for a few months, thinking (hoping?) that an elegant solution would come out. Until a few days ago, when I thought that enough is enough and it was time to do the hard coding job. Which made me feel old.

Then I thought: well, letâ€™s do this in Raku! And I felt old even more, because I coded my solution with a strong Perl accent, and my thoughts constantly went to all people that know how to bend Raku to their will and make wonders with a few lines. Heck, I even struggled with parsing the inputs!

Anyway, hereâ€™s my solution:

``````#!/usr/bin/env raku
use v6;

sub MAIN (\$filename = Nil) {
my \$inputs = get-inputs(\$filename // \$?FILE.subst(/\.raku\$/, '.tmp'));
my (\$part1, \$part2) = solve(\$inputs);

my \$highlight = "\e[1;97;45m";
my \$reset     = "\e[0m";
put "part1 \$highlight\$part1\$reset";
put "part2 \$highlight\$part2\$reset";
}

sub get-inputs (\$filename) {
my @inputs = \$filename.IO.basename.IO.lines.sort({\$^a leg \$^b})
.map: {
when /\d+ \: (\d+) \] \s+ falls/ { ('sleep', \$0)  }
when /\d+ \: (\d+) \] \s+ wakes/ { ('wake', \$0) }
when /Guard \s+ '#' (\d+) / { ('start', \$0) }
default { die \$_ }
};
return @inputs;
} ## end sub get_inputs (\$filename = undef)

sub solve (\$inputs) {
return (part1(\$inputs), part2(\$inputs));
}

sub part1 (\$inputs) {
my %minutes-for;
my %slots-for;
my (\$guard, \$start);
my (\$max-guard, \$max-sleep) = (0, 0);
for @\$inputs -> \$input {
my (\$action, \$param) = @\$input;
with \$action {
when 'sleep' {
\$start = \$param;
}
when 'wake' {
(%slots-for{\$guard} //= []).push(\$start ..^ \$param);
my \$mins = %minutes-for{\$guard} += \$param - \$start;
(\$max-guard, \$max-sleep) = (\$guard, \$mins)
if \$mins > \$max-sleep;
}
when 'start' {
\$guard = \$param;
}
default { die \$action }
}
}
my %count-for;
my (\$max-minute, \$max-count) = (0, 0);
for %slots-for{\$max-guard}.List -> \$slot {
for @\$slot -> \$minute {
my \$count = ++%count-for{\$minute};
(\$max-minute, \$max-count) = (\$minute, \$count)
if \$count > \$max-count;
}
}
return \$max-guard * \$max-minute;
}

sub part2 (\$inputs) {
my (%count-for);
my (\$max-guard, \$max-count, \$max-minute) = (0, 0, 0);
my (\$guard, \$start);
for @\$inputs -> \$input {
my (\$action, \$param) = @\$input;
with \$action {
when 'start' { \$guard = \$param }
when 'sleep' { \$start = \$param }
when 'wake'  {
for \$start ..^ \$param -> \$minute {
my \$count = ++%count-for{\$guard}{\$minute};
(\$max-guard, \$max-count, \$max-minute) =
(\$guard, \$count, \$minute) if \$count > \$max-count;
}
}
default { die 'wtf?!?' }
}
}
return \$max-guard * \$max-minute;
}
``````

• I used `with/when` for the first time, and it felt natural.