diff --git a/2023/20/part 1 solution.py b/2023/20/part 1 solution.py new file mode 100644 index 0000000..c653e8d --- /dev/null +++ b/2023/20/part 1 solution.py @@ -0,0 +1,114 @@ +import sys +from collections import defaultdict, deque +import math + +with open(r'advent_of_code\2023\20\input.txt', 'r') as file: + input = file.read() + +test_input = r'''broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a''' + +another_test_input = r'''broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output''' + +# input = test_input +# input = another_test_input + +# Read input +input_data = input +lines = input_data.split('\n') + +# Function to calculate least common multiple +def least_common_multiple(numbers): + result = 1 + for num in numbers: + result = (result * num) // math.gcd(num, result) + return result + +# Initialize dictionaries +type_dict = {} +rules_dict = {} + +# Parse input lines +for line in lines: + source, destination = line.split('->') + source = source.strip() + destination = destination.strip().split(', ') + rules_dict[source] = destination + type_dict[source[1:]] = source[0] + +# Function to adjust type +def adjust_type(type): + if type in type_dict: + return type_dict[type] + type + else: + return type + +# Initialize more dictionaries +from_dict = {} +inverse_dict = defaultdict(list) + +# Adjust types and fill dictionaries +for x, ys in rules_dict.items(): + rules_dict[x] = [adjust_type(y) for y in ys] + for y in rules_dict[x]: + if y[0] == '&': + if y not in from_dict: + from_dict[y] = {} + from_dict[y][x] = 'lo' + inverse_dict[y].append(x) + +# Initialize variables +watch = inverse_dict[inverse_dict['rx'][0]] +low_count = 0 +high_count = 0 +queue = deque() +on_set = set() +previous_dict = {} +count_dict = defaultdict(int) +to_lcm = [] + +# Main loop +for timestamp in range(1, 1001): + queue.append(('broadcaster', 'button', 'lo')) + + while queue: + x, from_, typ = queue.popleft() + + if typ == 'lo': + low_count += 1 + else: + high_count += 1 + + if x not in rules_dict: + continue + if x == 'broadcaster': + for y in rules_dict[x]: + queue.append((y, x, typ)) + elif x[0] == '%': + if typ == 'hi': + continue + else: + if x not in on_set: + on_set.add(x) + new_typ = 'hi' + else: + on_set.discard(x) + new_typ = 'lo' + for y in rules_dict[x]: + queue.append((y, x, new_typ)) + elif x[0] == '&': + from_dict[x][from_] = typ + new_typ = ('lo' if all(y == 'hi' for y in from_dict[x].values()) else 'hi') + for y in rules_dict[x]: + queue.append((y, x, new_typ)) + else: + assert False, x + if timestamp == 1000: + print(low_count * high_count) \ No newline at end of file diff --git a/2023/20/part 2 solution.py b/2023/20/part 2 solution.py new file mode 100644 index 0000000..91117c6 --- /dev/null +++ b/2023/20/part 2 solution.py @@ -0,0 +1,126 @@ +import sys +from collections import defaultdict, deque +import math + +with open(r'advent_of_code\2023\20\input.txt', 'r') as file: + input = file.read() + +test_input = r'''broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a''' + +another_test_input = r'''broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output''' + +# input = test_input +# input = another_test_input + +# Read input +input_data = input +lines = input_data.split('\n') + +# Function to calculate least common multiple +def least_common_multiple(numbers): + result = 1 + for num in numbers: + result = (result * num) // math.gcd(num, result) + return result + +# Initialize dictionaries +type_dict = {} +rules_dict = {} + +# Parse input lines +for line in lines: + source, destination = line.split('->') + source = source.strip() + destination = destination.strip().split(', ') + rules_dict[source] = destination + type_dict[source[1:]] = source[0] + +# Function to adjust type +def adjust_type(type): + if type in type_dict: + return type_dict[type] + type + else: + return type + +# Initialize more dictionaries +from_dict = {} +inverse_dict = defaultdict(list) + +# Adjust types and fill dictionaries +for x, ys in rules_dict.items(): + rules_dict[x] = [adjust_type(y) for y in ys] + for y in rules_dict[x]: + if y[0] == '&': + if y not in from_dict: + from_dict[y] = {} + from_dict[y][x] = 'lo' + inverse_dict[y].append(x) + +# Initialize variables +watch = inverse_dict[inverse_dict['rx'][0]] +low_count = 0 +high_count = 0 +queue = deque() +on_set = set() +previous_dict = {} +count_dict = defaultdict(int) +to_lcm = [] + +# Main loop +for timestamp in range(1, 10**8): + queue.append(('broadcaster', 'button', 'lo')) + + while queue: + x, from_, typ = queue.popleft() + + if typ == 'lo': + if x in previous_dict and count_dict[x] == 2 and x in watch: + to_lcm.append(timestamp - previous_dict[x]) + previous_dict[x] = timestamp + count_dict[x] += 1 + if len(to_lcm) == len(watch): + print(least_common_multiple(to_lcm)) + sys.exit(0) + + if x == 'rx' and typ == 'lo': + print(timestamp + 1) + + if typ == 'lo': + low_count += 1 + else: + high_count += 1 + + if x not in rules_dict: + continue + if x == 'broadcaster': + for y in rules_dict[x]: + queue.append((y, x, typ)) + elif x[0] == '%': + if typ == 'hi': + continue + else: + if x not in on_set: + on_set.add(x) + new_typ = 'hi' + else: + on_set.discard(x) + new_typ = 'lo' + for y in rules_dict[x]: + queue.append((y, x, new_typ)) + elif x[0] == '&': + from_dict[x][from_] = typ + new_typ = ('lo' if all(y == 'hi' for y in from_dict[x].values()) else 'hi') + for y in rules_dict[x]: + queue.append((y, x, new_typ)) + else: + assert False, x + if timestamp == 1000: + print(low_count * high_count) \ No newline at end of file