91 lines
2.9 KiB
Python

# SPDX-License-Identifier: MIT
# Copyright (c) 2023 Akumatic
#
# https://adventofcode.com/2023/day/3
def read_file(filename: str = "input.txt") -> list:
with open(f"{__file__.rstrip('code.py')}{filename}", "r") as f:
return f.read().strip().split("\n")
def check_surrounding_fields(vals: list, x: int, y: int) -> bool:
return any(
vals[y + j][x + i] != "." and not vals[y + j][x + i].isdigit()
for i in range(-1, 2) for j in range(-1, 2)
if 0 <= x + i < len(vals[0]) and 0 <= y + j < len(vals)
)
def sum_part_numbers(vals: list) -> int:
num_sum = 0
for y in range(len(vals)):
valid = False
num = 0
for x in range(len(vals[0])):
if not vals[y][x].isdigit():
if valid:
num_sum += num
valid = False
num = 0
continue
num = num * 10 + int(vals[y][x])
if not valid:
valid = check_surrounding_fields(vals, x, y)
if valid:
num_sum += num
return num_sum
def find_possible_gears(vals: list) -> list:
positions = list()
for y in range(len(vals)):
positions.extend([(x, y) for x, char in enumerate(vals[y]) if vals[y][x] == "*"])
return positions
def check_left_of_symbol(vals: list, x, y) -> int:
if not 0 <= y < len(vals):
return -1
i, num = 1, 0
check = False
while (x - i) >= 0 and vals[y][x - i].isdigit():
check = True
num += int(vals[y][x - i]) * 10**(i - 1)
i += 1
return num if check else -1
def check_right_of_symbol(vals:list, x, y) -> int:
if not 0 <= y < len(vals):
return -1
i, num = 1, 0
check = False
while (x + i) < len(vals[y]) and vals[y][x + i].isdigit():
check = True
num = num * 10 + int(vals[y][x + i])
i += 1
return num if check else -1
def determine_gear_ratios(vals: list, possible_gears: list) -> list:
result = list()
for x, y in possible_gears:
part_numbers = list()
for j in (-1, 0, 1):
tmp_l = check_left_of_symbol(vals, x, y + j)
tmp_r = check_right_of_symbol(vals, x, y + j)
tmp_m = vals[y + j][x] if 0 <= y + j < len(vals) and vals[y + j][x].isdigit() else None
if tmp_m is not None:
nums = [int("".join([str(x) for x in (tmp_l, tmp_m, tmp_r) if x != -1]))]
else:
nums = [x for x in (tmp_l, tmp_r) if x != -1]
part_numbers.extend(nums)
if len(part_numbers) == 2:
result.append(part_numbers[0] * part_numbers[1])
return result
def part1(vals: list) -> int:
return sum_part_numbers(vals)
def part2(vals: list) -> int:
possible_gears = find_possible_gears(vals)
return sum(determine_gear_ratios(vals, possible_gears))
if __name__ == "__main__":
vals = read_file()
print(f"Part 1: {part1(vals)}")
print(f"Part 2: {part2(vals)}")