# --- Day 18: Operation Order --- # As you look out the window and notice a heavily-forested continent slowly # appear over the horizon, you are interrupted by the child sitting next to # you. They're curious if you could help them with their math homework. # Unfortunately, it seems like this "math" follows different rules than you # remember. # The homework (your puzzle input) consists of a series of expressions that # consist of addition (+), multiplication (*), and parentheses ((...)). Just # like normal math, parentheses indicate that the expression inside must be # evaluated before it can be used by the surrounding expression. Addition still # finds the sum of the numbers on both sides of the operator, and # multiplication still finds the product. # However, the rules of operator precedence have changed. Rather than # evaluating multiplication before addition, the operators have the same # precedence, and are evaluated left-to-right regardless of the order in which # they appear. # For example, the steps to evaluate the expression 1 + 2 * 3 + 4 * 5 + 6 are # as follows: # 1 + 2 * 3 + 4 * 5 + 6 # 3 * 3 + 4 * 5 + 6 # 9 + 4 * 5 + 6 # 13 * 5 + 6 # 65 + 6 # 71 # Parentheses can override this order; for example, here is what happens if # parentheses are added to form 1 + (2 * 3) + (4 * (5 + 6)): # 1 + (2 * 3) + (4 * (5 + 6)) # 1 + 6 + (4 * (5 + 6)) # 7 + (4 * (5 + 6)) # 7 + (4 * 11 ) # 7 + 44 # 51 # Here are a few more examples: # 2 * 3 + (4 * 5) becomes 26. # 5 + (8 * 3 + 9 + 3 * 4 * 3) becomes 437. # 5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4)) becomes 12240. # ((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2 becomes 13632. # Before you can help with the homework, you need to understand it yourself. # Evaluate the expression on each line of the homework; what is the sum of the # resulting values? with open("files/P18.txt", "r") as f: homework = [line for line in f.read().strip().split("\n")] def eval(n1, n2, op) -> int: if op == "+": return n1 + n2 elif op == "*": return n1 * n2 def simplify(expr) -> str: while len(expr) > 1: n1 = int(expr.pop()) op = expr.pop() n2 = int(expr.pop()) result = eval(n1, n2, op) expr.append(str(result)) return expr[0] def part_1() -> None: total = 0 for line in homework: line = line.replace("(", "( ") line = line.replace(")", " )") expr = line.split() stack = [] for char in expr: if char.isdigit() or char in ["+", "*", "("]: stack.append(char) elif char == ")": ordered_stack = [] while stack[len(stack) - 1] != "(": ordered_stack.append(stack.pop()) stack.pop() result = simplify(ordered_stack) stack.append(result) stack.reverse() result = simplify(stack) total += int(result) print(f"The result of evaluating my homework is {total}") # --- Part Two --- # You manage to answer the child's questions and they finish part 1 of their # homework, but get stuck when they reach the next section: advanced math. # Now, addition and multiplication have different precedence levels, but # they're not the ones you're familiar with. Instead, addition is evaluated # before multiplication. # For example, the steps to evaluate the expression 1 + 2 * 3 + 4 * 5 + 6 are # now as follows: # 1 + 2 * 3 + 4 * 5 + 6 # 3 * 3 + 4 * 5 + 6 # 3 * 7 * 5 + 6 # 3 * 7 * 11 # 21 * 11 # 231 # Here are the other examples from above: # 1 + (2 * 3) + (4 * (5 + 6)) still becomes 51. # 2 * 3 + (4 * 5) becomes 46. # 5 + (8 * 3 + 9 + 3 * 4 * 3) becomes 1445. # 5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4)) becomes 669060. # ((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2 becomes 23340. # What do you get if you add up the results of evaluating the homework problems # using these new rules? def simplify_rules(expr) -> str: while len(expr) > 1: ordered_stack, hold = [], [] done = False while not done and len(expr) > 0: char = expr.pop() if char == "+": done = True hold.append(char) if not done and len(expr) == 0: result = simplify(hold) expr.append(result) else: ordered_stack.append(expr.pop()) for _ in range(2): ordered_stack.append(hold.pop()) result = simplify(ordered_stack) hold.append(result) while len(hold) > 0: expr.append(hold.pop()) return expr[0] def part_2() -> None: total = 0 for line in homework: line = line.replace("(", "( ") line = line.replace(")", " )") expr = line.split() stack = [] for char in expr: if char.isdigit() or char in ["+", "*", "("]: stack.append(char) elif char == ")": ordered_stack = [] while stack[len(stack) - 1] != "(": ordered_stack.append(stack.pop()) stack.pop() result = simplify_rules(ordered_stack) stack.append(result) stack.reverse() result = simplify_rules(stack) total += int(result) print(f"The result of evaluating my homework is {total}") if __name__ == "__main__": part_1() part_2()