Advent of Code 2024 Day 7

For day 7, I used Python.

Link to problem statement.

Link to full code.

Setup

inp = open("input.txt", "r").read()
parsed = parse_input(inp)
print(f"Part 1: {part1(parsed)}")
print(f"Part 2: {part2(parsed)}")

I read the input file, parse it, and then print out the part 1 and part 2 answers.

Parsing

def parse_input(inp):
    out = list()
    for line in inp.strip().splitlines():
        test_value_s, equation_s = line.split(":")
        test_value = int(test_value_s)
        equation = [int(num) for num in equation_s.split()]
        out.append((test_value, equation))
    return out

Each line of the input is in the format of 1234: 55 666 7 where 1234 is the “test value” and the list [55, 666, 7] is the “equation”. I split the string accordingly and convert the values into integers.

Part One

Elephants have stolen the operators from our equations and we need to figure out what operators go between the numbers in our equations. Thankfully, there are only two operators that can be there and the equation is evaluated left-to-right with no precedence rules to worry about. The two operators are + and *, for addition and multiplication.

We need to test out each possible combination of operators that can go in the gaps in our equations. Testing means that we check if computing the equation with that combination matches the equation’s test value. The final answers is the sum of all the test values whose equations have a valid combination of operators that compute to the test value.

def part1(parsed):
    ans = 0
    for line in parsed:
        if check_equation(line):
            ans += line[0]
    return ans

def check_equation(line):
    test_value, equation = line
    for function_list in itertools.product(["+", "*"], repeat=(len(equation)-1)):
        ans = equation[0]
        for i, func in enumerate(function_list):
            if func == "+":
                ans = ans + equation[i+1]
            elif func == "*":
                ans = ans * equation[i+1]
        if ans == test_value:
            return True
    return False

The itertools.product function was very handy for this problem for generating all possible combinations of operators.

Part Two

We now have a third operator || that concatenates two numbers together. I implemented this operator by converting the numbers to a string and then concatenating and then converting back to an integer. There is a more efficient way to do this using logs and multiplication but it was more convenient to do it like this.

def part2(parsed):
    ans = 0
    for line in parsed:
        print(line[0])
        if check_equation_p2(line):
            ans += line[0]
    return ans

def check_equation_p2(line):
    test_value, equation = line
    for function_list in itertools.product(["+", "*", "||"], repeat=(len(equation)-1)):
        ans = equation[0]
        for i, func in enumerate(function_list):
            if func == "+":
                ans = ans + equation[i+1]
            elif func == "*":
                ans = ans * equation[i+1]
            elif func == "||":
                ans = int(str(ans)+str(equation[i+1]))
        if ans == test_value:
            return True
    return False

Since this now takes much longer to complete (tens of seconds on my computer), I print out the test number of the equation being checked as a rough progress indicator.