Skip to main content

AoC 2024 Day 03: Mull It Over

·3 mins
Table of Contents

Corrupt memory computer crisis in the North Pole toboggan rental shop! Before diving in, have a read of the Day 03 story.

Input Data #

The input is a string of characters representing a program to multiple 1-3 digit integers. Unfortunately the program is corrupted and contains characters that should be ignored. Here’s an example: xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))

Today’s input is a single line, but we’ll use the readInput function from Day 01 and grab the first line.

val testInput = readInput("Day03").first()

Part 1 #

Our goal is to find all valid occurrences of the mul instruction in the input string, calculate the product of each, and sum them up.

1
2
3
4
5
6
7
fun part1(input: String) =
    """mul\((\d+),(\d+)\)""".toRegex().findAll(input)
        .map { match ->
            val (first, second) = match.destructured
            first.toInt() * second.toInt()
        }
        .sum()

Line 2 uses a regular expression to search the entire input string for all occurrences that matches the pattern mul(x, y). Here, \d+ denotes one or more digit characters (0-9). findAll(input) returns all the matches as Sequence<MatchResult>.

We then apply the map function to each MatchResult (line 3) in the sequence and then use destructured to extract the two numbers (first, second) from each match line 4). Next, we convert the strings to integers and multiply them (line 5). Finally, we use the sum function to add up all the products (line 6).

Part 2 #

It turns out that programs can contain two additional instructions that dictate whether or not to process otherwise valid mul instructions:

  • The do() instruction enables subsequent mul instructions
  • The don't() instruction disables subsequent mul instructions
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    fun part2(input: String) =
        """(do\(\)|don't\(\)|mul\((\d+),(\d+)\))""".toRegex().findAll(input)
            .fold(true to 0) { (enabled, sum), match ->
                when {
                    match.value == "do()" -> true to sum
                    match.value == "don't()" -> false to sum
                    enabled -> enabled to sum + match.groupValues[2].toInt() * match.groupValues[3].toInt()
                    else -> enabled to sum
                }
            }.second

As before, we’re using a regular expression to search the entire input string, but this time we’re looking for occurrences that matches the pattern do() or don't() or mul(x, y) (where x and y are \d+).

Next, we use the fold function to process each match in the sequence. The fold function takes an initial value true to 01 where true is the initial value for enabled, and 0 is the initial value of sum, and a lambda function that takes the current value (enabled, sum) and the next match and returns the next value based on the criteria in the when block.

  • If the match is do(), the enabled flag is set to true and the sum remains unchanged.
  • If the match is don't(), the enabled flag is set to false and the sum remains unchanged.
  • If the enabled flag is true, the sum is updated with the product of the two numbers in the mul instruction.
  • If the enabled flag is false, the sum remains unchanged.

Finally, on line 10, we return the final value of sum - the second element of the Pair (enabled, sum).

That’s it! The full source code is up on GitHub.


  1. true to 0 is Kotlin infix function shorthand for Pair(true, 0) ↩︎