109 lines
3.2 KiB
Python
109 lines
3.2 KiB
Python
from collections import deque
|
|
|
|
|
|
def process_modules(filename, debug=False):
|
|
"""
|
|
Reads the module configuration from a file, initializes the modules, and simulates the pulse propagation.
|
|
Returns the product of total high and low pulses after 1000 button presses.
|
|
"""
|
|
with open(filename) as f:
|
|
lines = f.read().splitlines()
|
|
|
|
modules = {}
|
|
broadcaster = []
|
|
for line in lines:
|
|
left, right = line.split(" -> ")
|
|
destinations = right.split(", ")
|
|
if left == "broadcaster":
|
|
broadcaster = destinations
|
|
continue
|
|
|
|
module_type = left[0]
|
|
module_name = left[1:]
|
|
modules[module_name] = [
|
|
module_type,
|
|
False if module_type == "%" else {},
|
|
destinations,
|
|
]
|
|
|
|
# Initialize conjunction modules with False for each input
|
|
for name, (_, _, destinations) in modules.items():
|
|
for destination in destinations:
|
|
if destination in modules and modules[destination][0] == "&":
|
|
modules[destination][1][name] = False
|
|
|
|
low, high = 0, 0
|
|
|
|
for iteration in range(1000):
|
|
queue = deque([("broadcaster", target, False) for target in broadcaster])
|
|
|
|
while queue:
|
|
name, target, pulse = queue.popleft()
|
|
|
|
if debug:
|
|
print(
|
|
f"Iteration {iteration}: Processing {target}, Pulse: {'High' if pulse else 'Low'}"
|
|
)
|
|
|
|
if target not in modules:
|
|
continue
|
|
|
|
module = modules[target]
|
|
|
|
if module[0] == "%":
|
|
if pulse:
|
|
continue
|
|
module[1] = not module[1]
|
|
new_pulse = module[1]
|
|
else:
|
|
module[1][name] = pulse
|
|
new_pulse = not all(module[1].values())
|
|
|
|
for destination in module[2]:
|
|
queue.append((target, destination, new_pulse))
|
|
# Count pulses when they are sent
|
|
if new_pulse:
|
|
high += 1
|
|
else:
|
|
low += 1
|
|
|
|
if debug:
|
|
print(
|
|
f"Iteration {iteration}: {target} -> {'High' if new_pulse else 'Low'} to {module[2]}"
|
|
)
|
|
|
|
return low * high
|
|
|
|
|
|
def test():
|
|
"""
|
|
Runs the process_modules function with the test input and checks if it meets the expected outcome.
|
|
"""
|
|
expected_result = 11687500 # Replace with the expected result of the test input
|
|
test_result = process_modules("../test.txt", debug=True)
|
|
assert (
|
|
test_result == expected_result
|
|
), f"Test failed: Expected {expected_result}, got {test_result}"
|
|
print(f"Test passed with result: {test_result}")
|
|
|
|
|
|
def main():
|
|
"""
|
|
Main function to run the test and then process the actual puzzle input.
|
|
"""
|
|
try:
|
|
print("Running test...")
|
|
test()
|
|
print("Test passed. Running main input...")
|
|
result = process_modules("../input.txt")
|
|
print(f"Puzzle result: {result}")
|
|
except AssertionError as e:
|
|
print(str(e))
|
|
print("Stopping execution due to test failure.")
|
|
except Exception as e:
|
|
print(f"An error occurred: {e}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|