Advent of Code 2024 Day 11

This time, I decided to try out Raku.

Link to problem statement.

Link to full code.

Parsing

Parsing is simple, with the input being a list of space-separated numbers that we parse by splitting on space characters and converting each string to an Int type.

sub parse_input($input) {
$input.split(' ').map( { .Int } ).Array;
}

Part One

We need to transform our list of numbers into a new list based on some rules and repeat this tranformation 25 times and finally output the length of our list after the transformations.

sub part1($parsed) {
my @list = $parsed.Array;
for ^25 -> $i {
@list = blink(@list);
}
@list.elems
}

The rules are:

  • A 0 turns into a 1
  • If the number has an even number of digits, it splits into two numbers, that is, 3677 becomes 36 and 77
  • otherwise the number is multiplied by 2024

To find the number of digits, we need to find the log10 of that number, floor that and then add 1. For example, any 3 digit number n will be ≥ 100 and < 1000 which means that log10(n) will be ≥ 2 and < 3. This can be extended to any number of digits.

sub blink(@list) {
my @newlist = ();
for @list -> $item {
if $item == 0 {
@newlist.push(1)
} else {
my $num_digits = $item.Num.log10.floor.succ;
if $num_digits % 2 == 0 {
my $splitter = (10 ** ($num_digits/2));
my @pair = (($item / $splitter).floor, $item % $splitter );
@newlist.append(@pair);
} else {
@newlist.push($item * 2024);
}
}
}
@newlist
}

Part Two

This time, we need to do this expansion process 75 times instead of 25. Since one of the rules splits a number into two numbers, our list would roughly have around 275 items, making it impractical to represent the list directly in memory.

Since we only care about the number of elements in the list and order does not matter and because there will be a lot of repeated elements in the list, we can represent our list as a hashmap storing the count of each number. This greatly compacts the list and lets us solve part 2.

sub part2($parsed) {
my @list = $parsed.Array;
my %counts;
for @list -> $item {
if %counts{$item}:exists.not {
%counts{$item} = 0;
}
%counts{$item} += 1;
}
for ^75 -> $i {
my %newcounts;
for %counts.kv -> $num, $count {
my @newnums = ();
if $num == 0 {
@newnums.push(1);
} else {
my $num_digits = $num.Num.log10.floor.succ;
if $num_digits % 2 == 0 {
my $splitter = (10 ** ($num_digits/2));
my @pair = ($num div $splitter, $num % $splitter );
@newnums.append(@pair);
} else {
@newnums.push($num * 2024);
}
}
for @newnums -> $newnum {
if %newcounts{$newnum}:exists.not {
%newcounts{$newnum} = 0;
}
%newcounts{$newnum} += $count;
}
}
%counts = %newcounts;
}
%counts.values.sum
}