Refactor 2021 for new UI.
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing

This commit is contained in:
Mikaël Capelle 2024-12-08 14:03:34 +01:00
parent cd96140378
commit 1b4dd32898
25 changed files with 310 additions and 355 deletions

View File

@ -1,14 +1,17 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
values = [int(line) for line in lines]
# part 1 class Solver(BaseSolver):
answer_1 = sum(v2 > v1 for v1, v2 in zip(values[:-1], values[1:])) def solve(self, input: str) -> Iterator[Any]:
print(f"answer 1 is {answer_1}") lines = input.splitlines()
# part 2 values = [int(line) for line in lines]
runnings = [sum(values[i : i + 3]) for i in range(len(values) - 2)]
answer_2 = sum(v2 > v1 for v1, v2 in zip(runnings[:-1], runnings[1:])) # part 1
print(f"answer 2 is {answer_2}") yield sum(v2 > v1 for v1, v2 in zip(values[:-1], values[1:]))
# part 2
runnings = [sum(values[i : i + 3]) for i in range(len(values) - 2)]
yield sum(v2 > v1 for v1, v2 in zip(runnings[:-1], runnings[1:]))

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,41 +1,38 @@
import sys
from math import prod from math import prod
from typing import Literal, TypeAlias, cast from typing import Any, Iterator, Literal, TypeAlias, cast
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
Command: TypeAlias = Literal["forward", "up", "down"] Command: TypeAlias = Literal["forward", "up", "down"]
commands: list[tuple[Command, int]] = [
(cast(Command, (p := line.split())[0]), int(p[1])) for line in lines
]
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
def depth_and_position(use_aim: bool): commands: list[tuple[Command, int]] = [
aim, pos, depth = 0, 0, 0 (cast(Command, (p := line.split())[0]), int(p[1])) for line in lines
for command, value in commands: ]
d_depth = 0
match command:
case "forward":
pos += value
depth += value * aim
case "up":
d_depth = -value
case "down":
d_depth = value
if use_aim: def depth_and_position(use_aim: bool):
aim += d_depth aim, pos, depth = 0, 0, 0
else: for command, value in commands:
depth += value d_depth = 0
match command:
case "forward":
pos += value
depth += value * aim
case "up":
d_depth = -value
case "down":
d_depth = value
return depth, pos if use_aim:
aim += d_depth
else:
depth += value
return depth, pos
# part 1 yield prod(depth_and_position(False))
answer_1 = prod(depth_and_position(False)) yield prod(depth_and_position(True))
print(f"answer 1 is {answer_1}")
# part 2
answer_2 = prod(depth_and_position(True))
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,11 +1,7 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
# part 1
answer_1 = ...
print(f"answer 1 is {answer_1}")
# part 2 class Solver(BaseSolver):
answer_2 = ... def solve(self, input: str) -> Iterator[Any]: ...
print(f"answer 2 is {answer_2}")

View File

@ -1,6 +1,7 @@
import sys
from collections import Counter from collections import Counter
from typing import Literal from typing import Any, Iterator, Literal
from ..base import BaseSolver
def generator_rating( def generator_rating(
@ -20,20 +21,23 @@ def generator_rating(
return values[0] return values[0]
lines = sys.stdin.read().splitlines() class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
# part 1
most_and_least_common = [
tuple(
Counter(line[col] for line in lines).most_common(2)[m][0]
for m in range(2)
)
for col in range(len(lines[0]))
]
gamma_rate = int("".join(most for most, _ in most_and_least_common), base=2)
epsilon_rate = int("".join(least for _, least in most_and_least_common), base=2)
yield gamma_rate * epsilon_rate
# part 1 # part 2
most_and_least_common = [ oxygen_generator_rating = int(generator_rating(lines, True, "1"), base=2)
tuple(Counter(line[col] for line in lines).most_common(2)[m][0] for m in range(2)) co2_scrubber_rating = int(generator_rating(lines, False, "0"), base=2)
for col in range(len(lines[0])) yield oxygen_generator_rating * co2_scrubber_rating
]
gamma_rate = int("".join(most for most, _ in most_and_least_common), base=2)
epsilon_rate = int("".join(least for _, least in most_and_least_common), base=2)
print(f"answer 1 is {gamma_rate * epsilon_rate}")
# part 2
oxygen_generator_rating = int(generator_rating(lines, True, "1"), base=2)
co2_scrubber_rating = int(generator_rating(lines, False, "0"), base=2)
answer_2 = oxygen_generator_rating * co2_scrubber_rating
print(f"answer 2 is {answer_2}")

View File

@ -1,45 +1,52 @@
import sys from typing import Any, Iterator
import numpy as np import numpy as np
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
numbers = [int(c) for c in lines[0].split(",")]
boards = np.asarray( class Solver(BaseSolver):
[ def solve(self, input: str) -> Iterator[Any]:
[[int(c) for c in line.split()] for line in lines[start : start + 5]] lines = input.splitlines()
for start in range(2, len(lines), 6)
]
)
# (round, score) for each board (-1 when not found) numbers = [int(c) for c in lines[0].split(",")]
winning_rounds: list[tuple[int, int]] = [(-1, -1) for _ in range(len(boards))]
marked = np.zeros_like(boards, dtype=bool)
for round, number in enumerate(numbers): boards = np.asarray(
# mark boards [
marked[boards == number] = True [[int(c) for c in line.split()] for line in lines[start : start + 5]]
for start in range(2, len(lines), 6)
]
)
# check each board for winning # (round, score) for each board (-1 when not found)
for index in range(len(boards)): winning_rounds: list[tuple[int, int]] = [(-1, -1) for _ in range(len(boards))]
if winning_rounds[index][0] > 0: marked = np.zeros_like(boards, dtype=bool)
continue
if np.any(np.all(marked[index], axis=0) | np.all(marked[index], axis=1)): for round, number in enumerate(numbers):
winning_rounds[index] = ( # mark boards
round, marked[boards == number] = True
number * int(np.sum(boards[index][~marked[index]])),
)
# all boards are winning - break # check each board for winning
if np.all(marked.all(axis=1) | marked.all(axis=2)): for index in range(len(boards)):
break if winning_rounds[index][0] > 0:
continue
# part 1 if np.any(
(_, score) = min(winning_rounds, key=lambda w: w[0]) np.all(marked[index], axis=0) | np.all(marked[index], axis=1)
print(f"answer 1 is {score}") ):
winning_rounds[index] = (
round,
number * int(np.sum(boards[index][~marked[index]])),
)
# part 2 # all boards are winning - break
(_, score) = max(winning_rounds, key=lambda w: w[0]) if np.all(marked.all(axis=1) | marked.all(axis=2)):
print(f"answer 2 is {score}") break
# part 1
(_, score) = min(winning_rounds, key=lambda w: w[0])
yield score
# part 2
(_, score) = max(winning_rounds, key=lambda w: w[0])
yield score

View File

@ -1,48 +1,48 @@
import sys from typing import Any, Iterator
import numpy as np import numpy as np
lines: list[str] = sys.stdin.read().splitlines() from ..base import BaseSolver
sections: list[tuple[tuple[int, int], tuple[int, int]]] = [
(
(
int(line.split(" -> ")[0].split(",")[0]),
int(line.split(" -> ")[0].split(",")[1]),
),
(
int(line.split(" -> ")[1].split(",")[0]),
int(line.split(" -> ")[1].split(",")[1]),
),
)
for line in lines
]
np_sections = np.array(sections).reshape(-1, 4) class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
x_min, x_max, y_min, y_max = ( sections: list[tuple[tuple[int, int], tuple[int, int]]] = [
min(np_sections[:, 0].min(), np_sections[:, 2].min()), (
max(np_sections[:, 0].max(), np_sections[:, 2].max()), (
min(np_sections[:, 1].min(), np_sections[:, 3].min()), int(line.split(" -> ")[0].split(",")[0]),
max(np_sections[:, 1].max(), np_sections[:, 3].max()), int(line.split(" -> ")[0].split(",")[1]),
) ),
(
int(line.split(" -> ")[1].split(",")[0]),
int(line.split(" -> ")[1].split(",")[1]),
),
)
for line in lines
]
counts_1 = np.zeros((y_max + 1, x_max + 1), dtype=int) np_sections = np.array(sections).reshape(-1, 4)
counts_2 = counts_1.copy()
for (x1, y1), (x2, y2) in sections: x_max, y_max = (
x_rng = range(x1, x2 + 1, 1) if x2 >= x1 else range(x1, x2 - 1, -1) max(np_sections[:, 0].max(), np_sections[:, 2].max()),
y_rng = range(y1, y2 + 1, 1) if y2 >= y1 else range(y1, y2 - 1, -1) max(np_sections[:, 1].max(), np_sections[:, 3].max()),
)
if x1 == x2 or y1 == y2: counts_1 = np.zeros((y_max + 1, x_max + 1), dtype=int)
counts_1[list(y_rng), list(x_rng)] += 1 counts_2 = counts_1.copy()
counts_2[list(y_rng), list(x_rng)] += 1
elif abs(x2 - x1) == abs(y2 - y1):
for i, j in zip(y_rng, x_rng):
counts_2[i, j] += 1
answer_1 = (counts_1 >= 2).sum() for (x1, y1), (x2, y2) in sections:
print(f"answer 1 is {answer_1}") x_rng = range(x1, x2 + 1, 1) if x2 >= x1 else range(x1, x2 - 1, -1)
y_rng = range(y1, y2 + 1, 1) if y2 >= y1 else range(y1, y2 - 1, -1)
answer_2 = (counts_2 >= 2).sum() if x1 == x2 or y1 == y2:
print(f"answer 2 is {answer_2}") counts_1[list(y_rng), list(x_rng)] += 1
counts_2[list(y_rng), list(x_rng)] += 1
elif abs(x2 - x1) == abs(y2 - y1):
for i, j in zip(y_rng, x_rng):
counts_2[i, j] += 1
yield (counts_1 >= 2).sum()
yield (counts_2 >= 2).sum()

View File

@ -1,21 +1,21 @@
import sys from typing import Any, Iterator
values = [int(c) for c in sys.stdin.read().strip().split(",")] from ..base import BaseSolver
days = 256
lanterns = {day: 0 for day in range(days)}
for value in values:
for day in range(value, days, 7):
lanterns[day] += 1
for day in range(days): class Solver(BaseSolver):
for day2 in range(day + 9, days, 7): def solve(self, input: str) -> Iterator[Any]:
lanterns[day2] += lanterns[day] values = [int(c) for c in input.split(",")]
# part 1 days = 256
answer_1 = sum(v for k, v in lanterns.items() if k < 80) + len(values) lanterns = {day: 0 for day in range(days)}
print(f"answer 1 is {answer_1}") for value in values:
for day in range(value, days, 7):
lanterns[day] += 1
# part 2 for day in range(days):
answer_2 = sum(lanterns.values()) + len(values) for day2 in range(day + 9, days, 7):
print(f"answer 2 is {answer_2}") lanterns[day2] += lanterns[day]
yield sum(v for k, v in lanterns.items() if k < 80) + len(values)
yield sum(lanterns.values()) + len(values)

View File

@ -1,19 +1,22 @@
import sys from typing import Any, Iterator
positions = [int(c) for c in sys.stdin.read().strip().split(",")] from ..base import BaseSolver
min_position, max_position = min(positions), max(positions)
# part 1 class Solver(BaseSolver):
answer_1 = min( def solve(self, input: str) -> Iterator[Any]:
sum(abs(p - position) for p in positions) positions = [int(c) for c in input.split(",")]
for position in range(min_position, max_position + 1)
)
print(f"answer 1 is {answer_1}")
# part 2 min_position, max_position = min(positions), max(positions)
answer_2 = min(
sum(abs(p - position) * (abs(p - position) + 1) // 2 for p in positions) # part 1
for position in range(min_position, max_position + 1) yield min(
) sum(abs(p - position) for p in positions)
print(f"answer 2 is {answer_2}") for position in range(min_position, max_position + 1)
)
# part 2
yield min(
sum(abs(p - position) * (abs(p - position) + 1) // 2 for p in positions)
for position in range(min_position, max_position + 1)
)

View File

@ -1,8 +1,7 @@
import itertools import itertools
import os from typing import Any, Iterator
import sys
VERBOSE = os.getenv("AOC_VERBOSE") == "True" from ..base import BaseSolver
digits = { digits = {
"abcefg": 0, "abcefg": 0,
@ -17,71 +16,74 @@ digits = {
"abcdfg": 9, "abcdfg": 9,
} }
lines = sys.stdin.read().splitlines()
# part 1 class Solver(BaseSolver):
lengths = {len(k) for k, v in digits.items() if v in (1, 4, 7, 8)} def solve(self, input: str) -> Iterator[Any]:
answer_1 = sum( lines = input.splitlines()
len(p) in lengths for line in lines for p in line.split("|")[1].strip().split()
)
print(f"answer 1 is {answer_1}")
# part 2 # part 1
values: list[int] = [] lengths = {len(k) for k, v in digits.items() if v in (1, 4, 7, 8)}
yield sum(
len(p) in lengths
for line in lines
for p in line.split("|")[1].strip().split()
)
for line in lines: # part 2
parts = line.split("|") values: list[int] = []
broken_digits = sorted(parts[0].strip().split(), key=len)
per_length = { for line in lines:
k: list(v) parts = line.split("|")
for k, v in itertools.groupby(sorted(broken_digits, key=len), key=len) broken_digits = sorted(parts[0].strip().split(), key=len)
}
# a can be found immediately per_length = {
a = next(u for u in per_length[3][0] if u not in per_length[2][0]) k: list(v)
for k, v in itertools.groupby(sorted(broken_digits, key=len), key=len)
}
# c and f have only two possible values corresponding to the single entry of # a can be found immediately
# length 2 a = next(u for u in per_length[3][0] if u not in per_length[2][0])
cf = list(per_length[2][0])
# the only digit of length 4 contains bcdf, so we can deduce bd by removing cf # c and f have only two possible values corresponding to the single entry of
bd = [u for u in per_length[4][0] if u not in cf] # length 2
cf = list(per_length[2][0])
# the 3 digits of length 5 have a, d and g in common # the only digit of length 4 contains bcdf, so we can deduce bd by removing cf
adg = [u for u in per_length[5][0] if all(u in pe for pe in per_length[5][1:])] bd = [u for u in per_length[4][0] if u not in cf]
# we can remove a # the 3 digits of length 5 have a, d and g in common
dg = [u for u in adg if u != a] adg = [
u for u in per_length[5][0] if all(u in pe for pe in per_length[5][1:])
]
# we can deduce d and g # we can remove a
d = next(u for u in dg if u in bd) dg = [u for u in adg if u != a]
g = next(u for u in dg if u != d)
# then b # we can deduce d and g
b = next(u for u in bd if u != d) d = next(u for u in dg if u in bd)
g = next(u for u in dg if u != d)
# f is in the three 6-length digits, while c is only in 2 # then b
f = next(u for u in cf if all(u in p for p in per_length[6])) b = next(u for u in bd if u != d)
# c is not f # f is in the three 6-length digits, while c is only in 2
c = next(u for u in cf if u != f) f = next(u for u in cf if all(u in p for p in per_length[6]))
# e is the last one # c is not f
e = next(u for u in "abcdefg" if u not in {a, b, c, d, f, g}) c = next(u for u in cf if u != f)
mapping = dict(zip((a, b, c, d, e, f, g), "abcdefg")) # e is the last one
e = next(u for u in "abcdefg" if u not in {a, b, c, d, f, g})
value = 0 mapping = dict(zip((a, b, c, d, e, f, g), "abcdefg"))
for number in parts[1].strip().split():
digit = "".join(sorted(mapping[c] for c in number))
value = 10 * value + digits[digit]
if VERBOSE: value = 0
print(value) for number in parts[1].strip().split():
digit = "".join(sorted(mapping[c] for c in number))
value = 10 * value + digits[digit]
values.append(value) self.logger.info(f"value for '{line}' is {value}")
values.append(value)
answer_2 = sum(values) yield sum(values)
print(f"answer 2 is {answer_2}")

View File

@ -1,18 +1,18 @@
import sys
from math import prod from math import prod
from typing import Any, Iterator
values = [[int(c) for c in row] for row in sys.stdin.read().splitlines()] from ..base import BaseSolver
n_rows, n_cols = len(values), len(values[0])
def neighbors(point: tuple[int, int]): def neighbors(point: tuple[int, int], n_rows: int, n_cols: int):
i, j = point i, j = point
for di, dj in ((-1, 0), (+1, 0), (0, -1), (0, +1)): for di, dj in ((-1, 0), (+1, 0), (0, -1), (0, +1)):
if 0 <= i + di < n_rows and 0 <= j + dj < n_cols: if 0 <= i + di < n_rows and 0 <= j + dj < n_cols:
yield (i + di, j + dj) yield (i + di, j + dj)
def basin(start: tuple[int, int]) -> set[tuple[int, int]]: def basin(values: list[list[int]], start: tuple[int, int]) -> set[tuple[int, int]]:
n_rows, n_cols = len(values), len(values[0])
visited: set[tuple[int, int]] = set() visited: set[tuple[int, int]] = set()
queue = [start] queue = [start]
@ -23,22 +23,25 @@ def basin(start: tuple[int, int]) -> set[tuple[int, int]]:
continue continue
visited.add((i, j)) visited.add((i, j))
queue.extend(neighbors((i, j))) queue.extend(neighbors((i, j), n_rows, n_cols))
return visited return visited
low_points = [ class Solver(BaseSolver):
(i, j) def solve(self, input: str) -> Iterator[Any]:
for i in range(n_rows) values = [[int(c) for c in row] for row in input.splitlines()]
for j in range(n_cols) n_rows, n_cols = len(values), len(values[0])
if all(values[ti][tj] > values[i][j] for ti, tj in neighbors((i, j)))
]
# part 1 low_points = [
answer_1 = sum(values[i][j] + 1 for i, j in low_points) (i, j)
print(f"answer 1 is {answer_1}") for i in range(n_rows)
for j in range(n_cols)
if all(
values[ti][tj] > values[i][j]
for ti, tj in neighbors((i, j), n_rows, n_cols)
)
]
# part 2 yield sum(values[i][j] + 1 for i, j in low_points)
answer_2 = prod(sorted(len(basin(point)) for point in low_points)[-3:]) yield prod(sorted(len(basin(values, point)) for point in low_points)[-3:])
print(f"answer 2 is {answer_2}")