ETOOBUSY 🚀 minimal blogging for the impatient
AoC 2022/2 - Rock Paper Scissors cheat guide
TL;DR
On with Advent of Code puzzle 2 from 2022: elves are so predictable!
The second day seems to follow the unwritten rules of getting slightly more difficult than yesterday, but not too much.
Which, of course, already got me providing the wrong solution for both part 1 and part 2 in the first place.
Inputs are pairs of letters, each representing a single draw in the game. It seems that elves are quite predictable at playing the game, so much so that they collected an extensive cheat guide that is able to predict exactly what the opponent is going to draw. Every. Single. Game.
I wonder how the game stuck with them, honestly. If all elves play like that, they must have endured endless draws, right?
Anyway.
Reading the inputs this time was much easier: each line contains two
characters, so I decided to comb
on sequences of non-space characters:
sub get-inputs ($filename) {
$filename.IO.lines».comb(/\S+/).Array
}
It seems that .comb
gives me back tuples that can be reused for both
parts, so I don’t have to do anything fancy for turning each of them
into something more durable. Go figure.
My initial solution was… messy. It was something like this:
sub part1 ($inputs) {
my %score-for = <X 1 Y 2 Z 3 A 1 B 2 C 3>;
my %dscore-for = <0 3 1 0 2 6>;
my $sum = 0;
for @$inputs -> $tuple {
my ($elf, $me) = $tuple.map({ %score-for{$_} });
my $outcome = (($elf - $me) % 3);
$sum += $me + %dscore-for{$outcome};
}
return $sum;
}
I’m not sure that works, to be honest. My initial solution had the
.comb
directly inside part 1, anyway this gives the gist of the
solution: using hashes to get workable values.
We can get rid of the mapping by just doing maths on the .ord
value
for each input character, then we can observe that we don’t need no
stinkin’ for
loop when we have .map
. This leads us to this:
sub part1 ($inputs) {
return $inputs
.map({[$_[0].ord - 'A'.ord, $_[1].ord - 'X'.ord]})
.map({
1 + $_[1]
+ 3 * ((1 + $_[1] - $_[0]) % 3)
})
.sum;
}
It’s possible to coalesce the two .map
into one, but I think it’s more
readable in this way. The first one turns characters into values (this
time in range 0..2
), while the second one does the actual score
calculation according to the rules in part 1.
The first .map
might be even moved in the get-inputs
function, but
this would mean anticipating what’s needed for part2 and I feel that
it’s sort of cheating. I know, we’re talking ex-post here.
The second part just asks to apply a different algorithm. It’s possible
to reuse the whole of part 1 scaffolding and just change the .map
where
the calculation is done:
sub part2 ($inputs) {
return $inputs
.map({[$_[0].ord - 'A'.ord, $_[1].ord - 'X'.ord]})
.map({
3 * $_[1]
+ 1 + ($_[0] + $_[1] - 1) % 3;
})
.sum;
}
In hindsight, I think that nothing beats a lookup table in this case, as there are only 9 different cases. This would allow us to work directly on each line, without even parsing it:
my %score-for = 'A X' => 4, 'A Y' => 8, 'A Z' => 3 ...
But we all know… hindsight is 20/20!
I guess this is it for today, stay safe!