2023-12-03 13:00:58 +01:00
|
|
|
from functools import reduce
|
|
|
|
|
|
|
|
|
2023-12-03 11:40:26 +01:00
|
|
|
def part1(file: str):
|
|
|
|
acc = []
|
|
|
|
with open(file) as f:
|
|
|
|
for line in f:
|
|
|
|
acc.append([(c, False) for c in line if c != "\n"])
|
|
|
|
|
|
|
|
for i1, r in enumerate(acc):
|
|
|
|
for i2, c in enumerate(r):
|
|
|
|
if not c[0].isdigit():
|
|
|
|
continue
|
|
|
|
|
|
|
|
check = False
|
|
|
|
|
|
|
|
def checker(a: int, b: int, check: bool):
|
|
|
|
# If already true
|
|
|
|
if check:
|
|
|
|
return True
|
|
|
|
|
|
|
|
# Sanity check
|
|
|
|
if a < 0 or len(acc) <= a:
|
|
|
|
return check
|
|
|
|
if b < 0 or len(acc[a]) <= b:
|
|
|
|
return check
|
|
|
|
|
|
|
|
# Adjacency check
|
|
|
|
it = acc[a][b][0]
|
|
|
|
if not it.isdigit() and not it == ".":
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
# Coordinates
|
|
|
|
for i in range(-1, 2):
|
|
|
|
for j in range(-1, 2):
|
|
|
|
if i == 0 and j == 0:
|
|
|
|
continue
|
|
|
|
check = checker(i1 + i, i2 + j, check)
|
|
|
|
|
|
|
|
if check:
|
|
|
|
acc[i1][i2] = (c[0], True)
|
|
|
|
|
|
|
|
# Find problems
|
|
|
|
buffer = ""
|
|
|
|
res = 0
|
|
|
|
for lines in acc:
|
|
|
|
test = False
|
|
|
|
for i, (c, t) in enumerate(lines):
|
|
|
|
if c.isdigit():
|
|
|
|
buffer += c
|
|
|
|
if t:
|
|
|
|
test = True
|
|
|
|
|
|
|
|
# End of number
|
|
|
|
if not c.isdigit() or i + 1 >= len(lines):
|
|
|
|
if len(buffer) > 0:
|
|
|
|
if test:
|
|
|
|
res += int(buffer)
|
|
|
|
test = False
|
|
|
|
buffer = ""
|
|
|
|
|
|
|
|
return res
|
|
|
|
|
|
|
|
|
2023-12-03 13:00:58 +01:00
|
|
|
def part2(file: str):
|
|
|
|
acc = []
|
|
|
|
default = (-1, 0)
|
|
|
|
bad = (-2, 0)
|
|
|
|
with open(file) as f:
|
|
|
|
for line in f:
|
|
|
|
acc.append([(c, default) for c in line if c != "\n"])
|
|
|
|
|
|
|
|
for i1, r in enumerate(acc):
|
|
|
|
for i2, c in enumerate(r):
|
|
|
|
if not c[0].isdigit():
|
|
|
|
continue
|
|
|
|
|
|
|
|
def checker(a: int, b: int, check: tuple[int, int]):
|
|
|
|
# Sanity check
|
|
|
|
if a < 0 or len(acc) <= a:
|
|
|
|
return check
|
|
|
|
if b < 0 or len(acc[a]) <= b:
|
|
|
|
return check
|
|
|
|
|
|
|
|
# Adjacency check
|
|
|
|
if acc[a][b][0] == "*":
|
|
|
|
# If already set
|
|
|
|
if check[0] >= 0 or check[0] == -2:
|
|
|
|
# Shouldn't be multiplied more than 2, if already encountered:
|
|
|
|
# Ignore the number
|
|
|
|
return bad
|
|
|
|
return a, b
|
|
|
|
|
|
|
|
return check
|
|
|
|
|
|
|
|
# Coordinates
|
|
|
|
check = default
|
|
|
|
for i in range(-1, 2):
|
|
|
|
for j in range(-1, 2):
|
|
|
|
if i == 0 and j == 0:
|
|
|
|
continue
|
|
|
|
check = checker(i1 + i, i2 + j, check)
|
|
|
|
|
|
|
|
acc[i1][i2] = (c[0], check)
|
|
|
|
|
|
|
|
# Find problems
|
|
|
|
buffer = ""
|
|
|
|
res = []
|
|
|
|
for lines in acc:
|
|
|
|
test = default
|
|
|
|
for i, (c, star_pos) in enumerate(lines):
|
|
|
|
if c.isdigit():
|
|
|
|
buffer += c
|
|
|
|
# test déjà défini + différente star
|
|
|
|
if test != default and star_pos != test and star_pos != default:
|
|
|
|
test = bad
|
|
|
|
# test pas défini + star ok
|
|
|
|
if test == default and star_pos != bad:
|
|
|
|
test = star_pos
|
|
|
|
|
|
|
|
# End of number
|
|
|
|
if not c.isdigit() or i + 1 >= len(lines):
|
|
|
|
if len(buffer) > 0:
|
|
|
|
if test != default and test != bad:
|
|
|
|
res.append((int(buffer), test))
|
|
|
|
test = default
|
|
|
|
buffer = ""
|
|
|
|
|
|
|
|
ratios = {}
|
|
|
|
for number, values in res:
|
|
|
|
try:
|
|
|
|
ratios[f"{values}"].append(number)
|
|
|
|
except KeyError:
|
|
|
|
ratios[f"{values}"] = [number]
|
|
|
|
return sum(
|
|
|
|
reduce(lambda x, y: x * y, gear) for gear in ratios.values() if len(gear) > 1
|
|
|
|
)
|
2023-12-03 11:40:26 +01:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
assert part1("example.txt") == 4361
|
|
|
|
print(part1("input.txt"))
|
|
|
|
|
2023-12-03 13:00:58 +01:00
|
|
|
assert part2("example.txt") == 467835
|
|
|
|
print(part2("input.txt"))
|