How to solve Advent of Code 2024 - Day 7 with Python
                            
                    The one where you just do some math and then you're done?! 😦
But before we get right into the day and my solution for this day's puzzle, here is the info on how I approach this whole event (where my code is, what editor I use, etc):

🎄❄️💻
Day 7 Puzzle
On my blog here, I will go through the puzzle and my way of solving it. If you want, you can jump directly to the code solution here:
GitHub .py script
Here is the challenge, if you want to read the full puzzle:
Advent of Code 2024 - Day 7
Every day I complain that I don't know the historical locations and today is finally my day! 💃 The rope bridge from Advent of Code 2022 Day 9 - which I actually did back in the day and even blogged about it: https://sarahglasmacher.com/how-to-solve-advent-of-code-2022-day-9-with-python/
The bridge is broken again, of course, so we need to help calculate some calibration numbers. I hope they now what they're doing - the ropes last time were kind of weird, to be honest... 🧶
Part 1 & 2
Day 7 gives us a test-value and then a list of integers that should be combined to reach the test-value:
# Example input
190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20
Solution Approach:
- I created a class for the equation (didn't do any classes yet this year, so I mainly did it for the variety)
 - the main idea is to calculate all possible combination of "add" and "multiply"
- if you are also using python, the only "trick" here is knowing that the 
itertoolsmodule exists in the Python Standard Library 
 - if you are also using python, the only "trick" here is knowing that the 
 - Then calculate all the combinations and check if the answer is correct. No other tricks needed. 🤷♀️
 
Since Part 2 only adds another operator, I'll just show everything in one go. 😊
Key Code Snippets:
Help-functions for getting the operator combinations (spoiler for part 2):
def get_all_operator_combinations(
    nr_of_operators: int, part2: bool = False
) -> list[tuple[str, ...]]:
    if part2:
        return list(product(["add", "multiply", "concatenate"], repeat=nr_of_operators))
    else:
        return list(product(["add", "multiply"], repeat=nr_of_operators - 1))
The equation class:
class Equation:
    def __init__(self, line: str):
        integer_list = read_int_list_from_string(line)
        self.target_number = integer_list[0]
        self.input_numbers = integer_list[1:]
    def __repr__(self):
        return f"Equation(target_number={self.target_number}, input_numbers={self.input_numbers})"
    def check_if_solvable(self, part2: bool = False):
        nr_of_operators = len(self.input_numbers) - 1
        operator_combinations = get_all_operator_combinations(
            nr_of_operators, part2=part2
        )
        for operator_combination in operator_combinations:
            if self.check_operator_combination(operator_combination):
                return True
        return False
    def check_operator_combination(self, operator_combination: tuple[str, ...]):
        total = self.input_numbers[0]
        for i, operator in enumerate(operator_combination):
            if operator == "add":
                total += self.input_numbers[i + 1]
            elif operator == "multiply":
                total *= self.input_numbers[i + 1]
            elif operator == "concatenate":
                total = int(str(total) + str(self.input_numbers[i + 1]))
        return total == self.target_number](<def find_start_position(grid: np.ndarray) -%3E tuple[tuple[int, int], str]:
    for i in range(grid.shape[0]):
        for j in range(grid.shape[1]):
            if grid[i, j] in ["^", "v", "%3C", ">"]:
                return (i, j), grid[i, j]
Full Solution for Part 1:
def solve_part_1(mode: str = "example"):
    file_lines = read_input_file(day=7, mode=mode)
    total_calibration_result = 0
    for line in file_lines:
        equation = Equation(line)
        if equation.check_if_solvable():
            total_calibration_result += equation.target_number
    return total_calibration_result
Full Solution for Part 2 (literally the same, except add "concatenate" as an option):
def solve_part_2(mode: str = "example"):
    file_lines = read_input_file(day=7, mode=mode)
    total_calibration_result = 0
    for line in file_lines:
        equation = Equation(line)
        if equation.check_if_solvable(part2=True):
            total_calibration_result += equation.target_number
    return total_calibration_result
Conclusion
I'm pretty sure that after the difficult puzzle yesterday, the creators wanted to give us a break because I couldn't believe that they let us simply try out all combinations. 🤯
I guess the difficult part is knowing about itertools, which I thankfully did, because I had to calculate a bunch of probabilities in my math degree. If you've never seen this module before, take a look around the documentation, it has some incredibly useful functions. 🪄
No spam, no sharing to third party. Only you and me.
                                            
                
                
                
                
                
                
Member discussion