Advent of Code 2024 - Day 3: Mull It Over

:: programming, python, puzzle

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from advent import parse, re

input = "".join(parse(3))

def solve(ignore_commands=False):
    enabled = True
    sum     = 0

    for match in re.finditer(r"mul\((\d{1,3}),(\d{1,3})\)|don't\(\)|do\(\)", input):
        if match.group() == "do()":
            enabled = True
        elif match.group() == "don't()":
            enabled = False
        elif enabled or ignore_commands:
            sum += (int(match.group(1)) * int(match.group(2)))

    return sum

# ---------------------------------------------------------------------------------------------

assert solve(True) == 174960292
assert solve()     == 56275602

Parsing

Today’s input comes in multiple lines, but for our purposes, we just want one long string. parse() will give us a tuple of strings, and we join them together with join() using an empty string as the separator:

1
input = "".join(parse(3))

Solution

Parts 1 and 2 are similar enough today that we’ll solve them both with a single function that differentiates between the parts with a parameter indicating whether we should ignore the do() and don't() commands.

The key element today is a regular expression. There are three expressions we care about: mul(m,n), do() and don't(), so we’ll create patterns to match those three alternatives, and combine them with a vertical bar |:

To match mul(m,n), where m and n are 1 to 3 numeric digits: mul\((\d{1,3}),(\d{1,3})\) We use subgroups so we can easily extract m and n later.

To match do(): do\(\)

To match don't(): don't\(\)

Once we have the proper regular expression matching in place, we simply loop over all the matches, and perform the appropriate action:

  • do() —> set enabled to True
  • don't() —> set enabled to False
  • mul(m,n) —> if either enabled or ignore_commands is True add the product of m and n to sum

New Python Concepts

  • int()
  • join()
  • Match.group()
  • re.finditer()
  • re
  • Default parameters def solve(ignore_commands=False)

Conclusion

While custom parsing code may perform better on today’s task, the convenience of regular expressions make them a win in my opinion. Python has fantastic support for regular expressions.