Advent of Code 2024 - Day 3, in Kotlin - Mull It Over
Kotlin solutions to parts 1 and 2 of Advent of Code 2024, Day 3: 'Mull It Over'
Today we’ll solve the puzzle with the help of a pair of regular expressions. I don’t use regular expressions very often in my day-to-day work, but very early in my career I wrote a lot of regular expressions in PERL and retained enough of it to solve today with a minimum of aggravation.
If you’d rather just view the code, my GitHub Repository is here.
Puzzle Input
It should be noted that today’s input file is spread over multiple lines that must be concatenated. I do this from my test case via a helper function that I have used for the past several Advents of Code.
class Day03(private val input: String) {
}
We will take this String
in as input
and keep it as a private val in our daily class. Parsing will take place later.
⭐ Day 3, Part 1
The puzzle text can be found here.
For part 1, we’ll use a Regular Expression to pick through our input
and find all of the mul
instructions. One of the features of regular expressions is capture groups. Meaning “match this string, and capture the values inside it”. So we’ll create a regular expression to find mul
instructions, and capture the two values being multiplied.
// In Day03
private fun executeMuls(instructions: String): Int =
"""mul\((\d{1,3}),(\d{1,3})\)"""
.toRegex()
.findAll(instructions)
.sumOf { match ->
match.groupValues
.drop(1)
.map { it.toInt() }
.reduce(Int::times)
}
I know that’s a lot, so let’s go through it.
First, we create a regular expression that finds mul
and groups the arguments. Next we turn this String
into a Regex
via toRegex()
. We then findAll
of the places the regex matches in the instructions
. We can then get the sumOf
the mul values.
To do this, we look a the match groupValues
for each match (each “mul(a,b)"). Since groupValues
has the entire match and not just the group values as the first element, we drop
it. Then we can map
each of the resulting group String
to an Int
, and then reduce
to multiply them all together.
At the end of this function call, we will have found every mul
instruction, multiplied its arguments together, and added them up.
Calling this function with our input
solves part 1.
// In Day03
fun solvePart1(): Int =
executeMuls(input)
Star earned! Onward!
⭐ Day 3, Part 2
The puzzle text can be found here.
Part 2 is also solved via a regular expression!
Instead of thinking of don't()..<bad stuff here>..do()
, let’s think about it backwards: do()..<good stuff here>..don't()
. Anything between do()
and don't()
is something we care about. Anything else is not. We have to take care and watch out for a couple of edge cases. For example, there is an implicit do()
a the start of the instructions
(represented by ^
in our regex), and there is an implicit don't()
at the end of the instructions
(represented by $
in the regex).
// In Day03
private fun executeDisabled(instructions: String): Int =
"""(^|do\(\)).*?($|don't\(\))"""
.toRegex()
.findAll(instructions)
.sumOf { executeMuls(it.value) }
One thing to notice about this regular expression is that the .*?
in the middle is reluctant. In regular expressions there are two kinds of matches: Greedy (“give me the largest possible match”) or reluctant (“give me the shortest possible match”). Since we want to pick out anything between do()
and next occurrance of don't()
, we will make this a reluctant match. This is so we don’t overrun the first don't()
we run into and match a later one (a greedy qualifier would do this if we wanted it).
The regular expression itself can be read as: Find either the start of a string or “'“do()"'”, any number of characters, and either the end of a string or “don’t”. Anything else is ignored.
We take all of these matches, and get the sumOf
subsequent calls to our previously written executeMuls
function.
Calling the executeDisabled
function with our input
solves part 2!
// In Day03
fun solvePart2(): Int =
executeDisabled(input)
Star earned! See you tomorrow!
Further Reading
- Index of All Solutions - All posts and solutions for 2024, in Kotlin.
- My Github repo - Solutions and tests for each day.
- Solution - Full code for day 3
- Advent of Code - Come join in and do these challenges yourself!