diff --git a/src/holt59/aoc/2021/day1.py b/src/holt59/aoc/2021/day1.py index 3fa54b5..4f405e7 100644 --- a/src/holt59/aoc/2021/day1.py +++ b/src/holt59/aoc/2021/day1.py @@ -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 -answer_1 = sum(v2 > v1 for v1, v2 in zip(values[:-1], values[1:])) -print(f"answer 1 is {answer_1}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: + lines = input.splitlines() -# part 2 -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:])) -print(f"answer 2 is {answer_2}") + values = [int(line) for line in lines] + + # part 1 + 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:])) diff --git a/src/holt59/aoc/2021/day10.py b/src/holt59/aoc/2021/day10.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day10.py +++ b/src/holt59/aoc/2021/day10.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day11.py b/src/holt59/aoc/2021/day11.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day11.py +++ b/src/holt59/aoc/2021/day11.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day12.py b/src/holt59/aoc/2021/day12.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day12.py +++ b/src/holt59/aoc/2021/day12.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day13.py b/src/holt59/aoc/2021/day13.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day13.py +++ b/src/holt59/aoc/2021/day13.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day14.py b/src/holt59/aoc/2021/day14.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day14.py +++ b/src/holt59/aoc/2021/day14.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day15.py b/src/holt59/aoc/2021/day15.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day15.py +++ b/src/holt59/aoc/2021/day15.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day16.py b/src/holt59/aoc/2021/day16.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day16.py +++ b/src/holt59/aoc/2021/day16.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day17.py b/src/holt59/aoc/2021/day17.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day17.py +++ b/src/holt59/aoc/2021/day17.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day18.py b/src/holt59/aoc/2021/day18.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day18.py +++ b/src/holt59/aoc/2021/day18.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day19.py b/src/holt59/aoc/2021/day19.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day19.py +++ b/src/holt59/aoc/2021/day19.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day2.py b/src/holt59/aoc/2021/day2.py index 6a8db61..bc1a5c8 100644 --- a/src/holt59/aoc/2021/day2.py +++ b/src/holt59/aoc/2021/day2.py @@ -1,41 +1,38 @@ -import sys 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"] -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): - aim, pos, depth = 0, 0, 0 - 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 + commands: list[tuple[Command, int]] = [ + (cast(Command, (p := line.split())[0]), int(p[1])) for line in lines + ] - if use_aim: - aim += d_depth - else: - depth += value + def depth_and_position(use_aim: bool): + aim, pos, depth = 0, 0, 0 + 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 - return depth, pos + if use_aim: + aim += d_depth + else: + depth += value + return depth, pos -# part 1 -answer_1 = prod(depth_and_position(False)) -print(f"answer 1 is {answer_1}") - -# part 2 -answer_2 = prod(depth_and_position(True)) -print(f"answer 2 is {answer_2}") + yield prod(depth_and_position(False)) + yield prod(depth_and_position(True)) diff --git a/src/holt59/aoc/2021/day20.py b/src/holt59/aoc/2021/day20.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day20.py +++ b/src/holt59/aoc/2021/day20.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day21.py b/src/holt59/aoc/2021/day21.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day21.py +++ b/src/holt59/aoc/2021/day21.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day22.py b/src/holt59/aoc/2021/day22.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day22.py +++ b/src/holt59/aoc/2021/day22.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day23.py b/src/holt59/aoc/2021/day23.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day23.py +++ b/src/holt59/aoc/2021/day23.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day24.py b/src/holt59/aoc/2021/day24.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day24.py +++ b/src/holt59/aoc/2021/day24.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day25.py b/src/holt59/aoc/2021/day25.py index dbaa94d..07e201e 100644 --- a/src/holt59/aoc/2021/day25.py +++ b/src/holt59/aoc/2021/day25.py @@ -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 -answer_2 = ... -print(f"answer 2 is {answer_2}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: ... diff --git a/src/holt59/aoc/2021/day3.py b/src/holt59/aoc/2021/day3.py index 9b21ba4..c19232a 100644 --- a/src/holt59/aoc/2021/day3.py +++ b/src/holt59/aoc/2021/day3.py @@ -1,6 +1,7 @@ -import sys from collections import Counter -from typing import Literal +from typing import Any, Iterator, Literal + +from ..base import BaseSolver def generator_rating( @@ -20,20 +21,23 @@ def generator_rating( 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 -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) -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}") + # part 2 + oxygen_generator_rating = int(generator_rating(lines, True, "1"), base=2) + co2_scrubber_rating = int(generator_rating(lines, False, "0"), base=2) + yield oxygen_generator_rating * co2_scrubber_rating diff --git a/src/holt59/aoc/2021/day4.py b/src/holt59/aoc/2021/day4.py index 2e86096..df97649 100644 --- a/src/holt59/aoc/2021/day4.py +++ b/src/holt59/aoc/2021/day4.py @@ -1,45 +1,52 @@ -import sys +from typing import Any, Iterator 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( - [ - [[int(c) for c in line.split()] for line in lines[start : start + 5]] - for start in range(2, len(lines), 6) - ] -) +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: + lines = input.splitlines() -# (round, score) for each board (-1 when not found) -winning_rounds: list[tuple[int, int]] = [(-1, -1) for _ in range(len(boards))] -marked = np.zeros_like(boards, dtype=bool) + numbers = [int(c) for c in lines[0].split(",")] -for round, number in enumerate(numbers): - # mark boards - marked[boards == number] = True + boards = np.asarray( + [ + [[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 - for index in range(len(boards)): - if winning_rounds[index][0] > 0: - continue + # (round, score) for each board (-1 when not found) + winning_rounds: list[tuple[int, int]] = [(-1, -1) for _ in range(len(boards))] + marked = np.zeros_like(boards, dtype=bool) - if np.any(np.all(marked[index], axis=0) | np.all(marked[index], axis=1)): - winning_rounds[index] = ( - round, - number * int(np.sum(boards[index][~marked[index]])), - ) + for round, number in enumerate(numbers): + # mark boards + marked[boards == number] = True - # all boards are winning - break - if np.all(marked.all(axis=1) | marked.all(axis=2)): - break + # check each board for winning + for index in range(len(boards)): + if winning_rounds[index][0] > 0: + continue -# part 1 -(_, score) = min(winning_rounds, key=lambda w: w[0]) -print(f"answer 1 is {score}") + if np.any( + np.all(marked[index], axis=0) | np.all(marked[index], axis=1) + ): + winning_rounds[index] = ( + round, + number * int(np.sum(boards[index][~marked[index]])), + ) -# part 2 -(_, score) = max(winning_rounds, key=lambda w: w[0]) -print(f"answer 2 is {score}") + # all boards are winning - break + if np.all(marked.all(axis=1) | marked.all(axis=2)): + 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 diff --git a/src/holt59/aoc/2021/day5.py b/src/holt59/aoc/2021/day5.py index 5e75518..5c99204 100644 --- a/src/holt59/aoc/2021/day5.py +++ b/src/holt59/aoc/2021/day5.py @@ -1,48 +1,48 @@ -import sys +from typing import Any, Iterator 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 = ( - 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()), - max(np_sections[:, 1].max(), np_sections[:, 3].max()), -) + 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 + ] -counts_1 = np.zeros((y_max + 1, x_max + 1), dtype=int) -counts_2 = counts_1.copy() + np_sections = np.array(sections).reshape(-1, 4) -for (x1, y1), (x2, y2) in sections: - 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) + x_max, y_max = ( + max(np_sections[:, 0].max(), np_sections[:, 2].max()), + max(np_sections[:, 1].max(), np_sections[:, 3].max()), + ) - if x1 == x2 or y1 == y2: - 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 + counts_1 = np.zeros((y_max + 1, x_max + 1), dtype=int) + counts_2 = counts_1.copy() -answer_1 = (counts_1 >= 2).sum() -print(f"answer 1 is {answer_1}") + for (x1, y1), (x2, y2) in sections: + 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() -print(f"answer 2 is {answer_2}") + if x1 == x2 or y1 == y2: + 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() diff --git a/src/holt59/aoc/2021/day6.py b/src/holt59/aoc/2021/day6.py index 5847186..573042b 100644 --- a/src/holt59/aoc/2021/day6.py +++ b/src/holt59/aoc/2021/day6.py @@ -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): - for day2 in range(day + 9, days, 7): - lanterns[day2] += lanterns[day] +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: + values = [int(c) for c in input.split(",")] -# part 1 -answer_1 = sum(v for k, v in lanterns.items() if k < 80) + len(values) -print(f"answer 1 is {answer_1}") + days = 256 + lanterns = {day: 0 for day in range(days)} + for value in values: + for day in range(value, days, 7): + lanterns[day] += 1 -# part 2 -answer_2 = sum(lanterns.values()) + len(values) -print(f"answer 2 is {answer_2}") + for day in range(days): + for day2 in range(day + 9, days, 7): + lanterns[day2] += lanterns[day] + + yield sum(v for k, v in lanterns.items() if k < 80) + len(values) + yield sum(lanterns.values()) + len(values) diff --git a/src/holt59/aoc/2021/day7.py b/src/holt59/aoc/2021/day7.py index 2532edb..1092d2d 100644 --- a/src/holt59/aoc/2021/day7.py +++ b/src/holt59/aoc/2021/day7.py @@ -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 -answer_1 = min( - sum(abs(p - position) for p in positions) - for position in range(min_position, max_position + 1) -) -print(f"answer 1 is {answer_1}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: + positions = [int(c) for c in input.split(",")] -# part 2 -answer_2 = min( - sum(abs(p - position) * (abs(p - position) + 1) // 2 for p in positions) - for position in range(min_position, max_position + 1) -) -print(f"answer 2 is {answer_2}") + min_position, max_position = min(positions), max(positions) + + # part 1 + yield min( + sum(abs(p - position) for p in positions) + 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) + ) diff --git a/src/holt59/aoc/2021/day8.py b/src/holt59/aoc/2021/day8.py index 1560410..16d502d 100644 --- a/src/holt59/aoc/2021/day8.py +++ b/src/holt59/aoc/2021/day8.py @@ -1,8 +1,7 @@ import itertools -import os -import sys +from typing import Any, Iterator -VERBOSE = os.getenv("AOC_VERBOSE") == "True" +from ..base import BaseSolver digits = { "abcefg": 0, @@ -17,71 +16,74 @@ digits = { "abcdfg": 9, } -lines = sys.stdin.read().splitlines() -# part 1 -lengths = {len(k) for k, v in digits.items() if v in (1, 4, 7, 8)} -answer_1 = sum( - len(p) in lengths for line in lines for p in line.split("|")[1].strip().split() -) -print(f"answer 1 is {answer_1}") +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: + lines = input.splitlines() -# part 2 -values: list[int] = [] + # part 1 + 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: - parts = line.split("|") - broken_digits = sorted(parts[0].strip().split(), key=len) + # part 2 + values: list[int] = [] - per_length = { - k: list(v) - for k, v in itertools.groupby(sorted(broken_digits, key=len), key=len) - } + for line in lines: + parts = line.split("|") + broken_digits = sorted(parts[0].strip().split(), key=len) - # a can be found immediately - a = next(u for u in per_length[3][0] if u not in per_length[2][0]) + per_length = { + 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 - # length 2 - cf = list(per_length[2][0]) + # a can be found immediately + a = next(u for u in per_length[3][0] if u not in per_length[2][0]) - # the only digit of length 4 contains bcdf, so we can deduce bd by removing cf - bd = [u for u in per_length[4][0] if u not in cf] + # c and f have only two possible values corresponding to the single entry of + # length 2 + cf = list(per_length[2][0]) - # the 3 digits of length 5 have a, d and g in common - adg = [u for u in per_length[5][0] if all(u in pe for pe in per_length[5][1:])] + # the only digit of length 4 contains bcdf, so we can deduce bd by removing cf + bd = [u for u in per_length[4][0] if u not in cf] - # we can remove a - dg = [u for u in adg if u != a] + # the 3 digits of length 5 have a, d and g in common + 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 - d = next(u for u in dg if u in bd) - g = next(u for u in dg if u != d) + # we can remove a + dg = [u for u in adg if u != a] - # then b - b = next(u for u in bd if u != d) + # we can deduce d and g + 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 - f = next(u for u in cf if all(u in p for p in per_length[6])) + # then b + b = next(u for u in bd if u != d) - # c is not f - c = next(u for u in cf if u != f) + # f is in the three 6-length digits, while c is only in 2 + f = next(u for u in cf if all(u in p for p in per_length[6])) - # e is the last one - e = next(u for u in "abcdefg" if u not in {a, b, c, d, f, g}) + # c is not f + 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 - for number in parts[1].strip().split(): - digit = "".join(sorted(mapping[c] for c in number)) - value = 10 * value + digits[digit] + mapping = dict(zip((a, b, c, d, e, f, g), "abcdefg")) - if VERBOSE: - print(value) + value = 0 + 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) -print(f"answer 2 is {answer_2}") + yield sum(values) diff --git a/src/holt59/aoc/2021/day9.py b/src/holt59/aoc/2021/day9.py index 850131d..fb1f064 100644 --- a/src/holt59/aoc/2021/day9.py +++ b/src/holt59/aoc/2021/day9.py @@ -1,18 +1,18 @@ -import sys from math import prod +from typing import Any, Iterator -values = [[int(c) for c in row] for row in sys.stdin.read().splitlines()] -n_rows, n_cols = len(values), len(values[0]) +from ..base import BaseSolver -def neighbors(point: tuple[int, int]): +def neighbors(point: tuple[int, int], n_rows: int, n_cols: int): i, j = point for di, dj in ((-1, 0), (+1, 0), (0, -1), (0, +1)): if 0 <= i + di < n_rows and 0 <= j + dj < n_cols: 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() queue = [start] @@ -23,22 +23,25 @@ def basin(start: tuple[int, int]) -> set[tuple[int, int]]: continue visited.add((i, j)) - queue.extend(neighbors((i, j))) + queue.extend(neighbors((i, j), n_rows, n_cols)) return visited -low_points = [ - (i, j) - 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))) -] +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: + values = [[int(c) for c in row] for row in input.splitlines()] + n_rows, n_cols = len(values), len(values[0]) -# part 1 -answer_1 = sum(values[i][j] + 1 for i, j in low_points) -print(f"answer 1 is {answer_1}") + low_points = [ + (i, j) + 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 -answer_2 = prod(sorted(len(basin(point)) for point in low_points)[-3:]) -print(f"answer 2 is {answer_2}") + yield sum(values[i][j] + 1 for i, j in low_points) + yield prod(sorted(len(basin(values, point)) for point in low_points)[-3:])