Advent of Code 2024 Day 3

This time, I am using Python for my solution.

Link to problem statement.

Link to full code.

Setup

input = open("input.txt", "r").read()
sample = \
"""xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))
"""
print(f"Part 1: {part1(input)}")
print(f"Part 2: {part2(input)}")

Very minimal setup here: I read the input file, and then run the part 1 and part 2 functions on it. I don’t need to parse the input for this day as the problem asks us to do some string processing. Also, since python doesn’t have a main function, this is at the end of the file, after all the function definitions.

Part One

The program is composed of mul instructions, which are in the exact form of mul(X,Y) where X and Y are numbers. As the name implies, the mul instruction is used to multiply two numbers. We then need to find the sum of all these products. This is a textbook case for using regexes and that is what I’ve done.

def part1(input):
    regex_mul = re.compile(r"mul\((\d+),(\d+)\)")
    sum = 0
    for tup in regex_mul.findall(input):
        a, b = tup
        sum += int(a)*int(b)
    return sum

I used a raw string for the regex to avoid escaping backslashes.when matching the round brackets.

Explaining the regular expression:

  • The mul part of the regex matches the literal characters m, u, and l.
  • The \( and the \) part matches the round brackets in the pattern.
  • The second ( starts a capturing group which matches everything inside the round brackets.
  • The \d matches any digit character and the + modifier means one or more than one of. So the combined \d+ matches one or more digits.
  • The , matches the comma between two numbers.
  • Then we have another capturing group for the second number before matching the closing bracket.

Part Two

Now, it turns out that we missed a few conditional statements. We need to stop processing the mul statements when we see a don’t() statement and start processing them again when we see a do() statement.

def part2(input):
    sum = 0
    nxt = input
    while True:
        try:
            valid, nxt = nxt.split("don't()", maxsplit=1)
            sum += part1(valid)
        except ValueError:
            sum += part1(nxt)
            break
        try:
            skip, nxt = nxt.split("do()", maxsplit=1)
        except ValueError:
            break
    return sum

I do this by continously splitting the string into two parts, one before the don’t statement and one after. Then I call the part one function on the before part. Then I split on the do statement and skip the part that comes before the do(). I continue doing this in a loop until there are no do or don’t statements remaining. That happens when the split method throws a ValueError.