class Gear: def __init__(self, line_idx, column_idx): self.line_idx = line_idx self.column_idx = column_idx self.connecting_parts = [] def add_part_to_gear(line_idx, column_idx, part_number_line_idx, part_number_column_idx, gears): for gear in gears: if gear.line_idx == line_idx and gear.column_idx == column_idx: gear.connecting_parts.append((part_number_line_idx, part_number_column_idx)) break else: new_gear = Gear(line_idx, column_idx) new_gear.connecting_parts.append((part_number_line_idx, part_number_column_idx)) gears.append(new_gear) def check_if_part_number(line_index, column_index, lines, gears): ret_val = False current_line = lines[line_index] if column_index > 0 and current_line[column_index - 1] != ".": if current_line[column_index - 1] == "*": add_part_to_gear(line_index, column_index - 1, line_index, column_index, gears) ret_val = True max_column_index = column_index for index in range(column_index + 1, len(current_line)): if not current_line[index].isdigit(): break max_column_index = index if max_column_index + 1 < len(current_line) and current_line[max_column_index + 1] != ".": if current_line[max_column_index + 1] == "*": add_part_to_gear(line_index, column_index + 1, line_index, column_index, gears) ret_val = True min_other_column_index = max(column_index - 1, 0) max_other_column_index = min(max_column_index + 1, len(current_line) - 1) if line_index > 0: other_line = lines[line_index - 1] for current_column_index in range(min_other_column_index, max_other_column_index + 1): other_char = other_line[current_column_index] if not other_char.isdigit() and other_char != ".": if other_char == "*": add_part_to_gear(line_index - 1, current_column_index, line_index, column_index, gears) ret_val = True if line_index + 1 < len(lines): other_line = lines[line_index + 1] for current_column_index in range(min_other_column_index, max_other_column_index + 1): other_char = other_line[current_column_index] if not other_char.isdigit() and other_char != ".": if other_char == "*": add_part_to_gear(line_index + 1, current_column_index, line_index, column_index, gears) ret_val = True return ret_val def main(): lines = [] positions = [] gears = [] with open("input", "r") as f: for line in f: lines.append(line.strip()) part_numbers = [] parsed_number = None is_part_number_parsed = False for line_index, line in enumerate(lines): for column_index, char in enumerate(line): if not char.isdigit(): if is_part_number_parsed: part_numbers.append(int(parsed_number)) parsed_number = None is_part_number_parsed = False elif parsed_number is not None: parsed_number += char else: parsed_number = char is_part_number_parsed = check_if_part_number(line_index, column_index, lines, gears) if is_part_number_parsed: positions.append((line_index, column_index)) if is_part_number_parsed: part_numbers.append(int(parsed_number)) parsed_number = None is_part_number_parsed = False gears = [gear for gear in gears if len(gear.connecting_parts) == 2] ratios = [] for gear in gears: ratios.append(part_numbers[positions.index(gear.connecting_parts[0])] * part_numbers[positions.index(gear.connecting_parts[1])]) print(sum(ratios)) if __name__ == '__main__': main()