Created
December 7, 2024 11:32
-
-
Save jongyllen/b6891542ec8175df3761fe859749be57 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| def evaluate_expression_slow(nums, operators): | |
| """Evaluate expression with given numbers and operators.""" | |
| result = nums[0] | |
| for i in range(len(operators)): | |
| if operators[i] == '+': | |
| result += nums[i + 1] | |
| elif operators[i] == '*': | |
| result *= nums[i + 1] | |
| else: # '||' | |
| # Convert both numbers to strings, concatenate, then back to int | |
| result = int(str(result) + str(nums[i + 1])) | |
| return result | |
| def evaluate_expression_fast(nums, operators): | |
| """Evaluate expression with given numbers and operators.""" | |
| result = nums[0] | |
| for i in range(len(operators)): | |
| if operators[i] == '+': | |
| result += nums[i + 1] | |
| elif operators[i] == '*': | |
| result *= nums[i + 1] | |
| else: # '||' | |
| # Optimize concatenation by using powers of 10 instead of string operations | |
| num_digits = len(str(nums[i + 1])) | |
| result = result * (10 ** num_digits) + nums[i + 1] | |
| return result | |
| def can_make_equation_part2_slow(test_value, numbers): | |
| """Try combinations with +, *, and || operators - slower version.""" | |
| def try_combinations(pos, operators): | |
| if pos == len(numbers) - 1: | |
| result = evaluate_expression_slow(numbers, operators) | |
| return result == test_value | |
| # Try all three operators | |
| for op in ['+', '*', '||']: | |
| operators.append(op) | |
| if try_combinations(pos + 1, operators): | |
| return True | |
| operators.pop() | |
| return False | |
| return try_combinations(0, []) | |
| def can_make_equation_part2_fast(test_value, numbers): | |
| """Try combinations with +, *, and || operators - faster version.""" | |
| def try_combinations(pos, operators, current_result): | |
| if pos == len(numbers) - 1: | |
| return current_result == test_value | |
| next_num = numbers[pos + 1] | |
| # Try addition | |
| new_result = current_result + next_num | |
| operators.append('+') | |
| if new_result <= test_value and try_combinations(pos + 1, operators, new_result): | |
| return True | |
| operators.pop() | |
| # Try multiplication | |
| new_result = current_result * next_num | |
| operators.append('*') | |
| if new_result <= test_value and try_combinations(pos + 1, operators, new_result): | |
| return True | |
| operators.pop() | |
| # Try concatenation | |
| num_digits = len(str(next_num)) | |
| new_result = current_result * (10 ** num_digits) + next_num | |
| operators.append('||') | |
| if new_result <= test_value and try_combinations(pos + 1, operators, new_result): | |
| return True | |
| operators.pop() | |
| return False | |
| return try_combinations(0, [], numbers[0]) | |
| def can_make_equation_part1(test_value, numbers): | |
| """Try combinations with only + and * operators.""" | |
| def try_combinations(pos, operators): | |
| if pos == len(numbers) - 1: | |
| result = evaluate_expression_fast(numbers, operators) | |
| return result == test_value | |
| # Try both + and * | |
| for op in ['+', '*']: | |
| operators.append(op) | |
| if try_combinations(pos + 1, operators): | |
| return True | |
| operators.pop() | |
| return False | |
| return try_combinations(0, []) | |
| def parse_line(line): | |
| """Parse a line into test_value and numbers.""" | |
| test_value, numbers = line.split(': ') | |
| return int(test_value), [int(x) for x in numbers.split()] | |
| def solve_calibration_part1(input_data): | |
| """Solve the calibration puzzle - Part 1.""" | |
| total = 0 | |
| for line in input_data.strip().split('\n'): | |
| test_value, numbers = parse_line(line) | |
| if can_make_equation_part1(test_value, numbers): | |
| total += test_value | |
| return total | |
| def solve_calibration_part2(input_data): | |
| """Solve the calibration puzzle - Part 2.""" | |
| total = 0 | |
| for line in input_data.strip().split('\n'): | |
| test_value, numbers = parse_line(line) | |
| if can_make_equation_part2_fast(test_value, numbers): | |
| total += test_value | |
| return total | |
| # Test case | |
| test_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""" | |
| def test_solution(): | |
| # Test Part 1 | |
| result1 = solve_calibration_part1(test_input) | |
| expected1 = 3749 # 190 + 3267 + 292 | |
| assert result1 == expected1, f"Part 1: Expected {expected1}, but got {result1}" | |
| print(f"Part 1 test passed! Result: {result1}") | |
| # Test Part 2 | |
| result2 = solve_calibration_part2(test_input) | |
| expected2 = 11387 # 3749 + 156 + 7290 + 192 | |
| assert result2 == expected2, f"Part 2: Expected {expected2}, but got {result2}" | |
| print(f"Part 2 test passed! Result: {result2}") | |
| def solve_file(): | |
| try: | |
| with open('day7.data', 'r') as file: | |
| data = file.read() | |
| # Solve Part 1 | |
| result1 = solve_calibration_part1(data) | |
| print(f"Solution for input file (Part 1): {result1}") | |
| # Solve Part 2 | |
| result2 = solve_calibration_part2(data) | |
| print(f"Solution for input file (Part 2): {result2}") | |
| except FileNotFoundError: | |
| print("Error: Could not find day7.data file") | |
| if __name__ == "__main__": | |
| # Run the test cases first | |
| test_solution() | |
| # Then solve the actual puzzle | |
| solve_file() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment