Start refactoring code better flexibility.

This commit is contained in:
Mikael CAPELLE 2024-12-04 16:03:37 +01:00
parent 1caf93b38b
commit a9bcf9ef8f
30 changed files with 384 additions and 403 deletions

View File

@ -1,27 +1,9 @@
import sys from typing import Any, Iterator
lines = sys.stdin.read().splitlines() from ..base import BaseSolver
lookups_1 = {str(d): d for d in range(1, 10)}
lookups_2 = lookups_1 | {
d: i + 1
for i, d in enumerate(
(
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
)
)
}
def find_values(lookups: dict[str, int]) -> list[int]: def find_values(lines: list[str], lookups: dict[str, int]) -> list[int]:
values: list[int] = [] values: list[int] = []
for line in filter(bool, lines): for line in filter(bool, lines):
@ -41,5 +23,27 @@ def find_values(lookups: dict[str, int]) -> list[int]:
return values return values
print(f"answer 1 is {sum(find_values(lookups_1))}") class Solver(BaseSolver):
print(f"answer 2 is {sum(find_values(lookups_2))}") def solve(self, input: str) -> Iterator[Any]:
lookups_1 = {str(d): d for d in range(1, 10)}
lookups_2 = lookups_1 | {
d: i + 1
for i, d in enumerate(
(
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
)
)
}
lines = input.splitlines()
yield sum(find_values(lines, lookups_1))
yield sum(find_values(lines, lookups_2))

View File

@ -1,15 +1,18 @@
import math import math
import sys from typing import Any, Iterator, Literal, TypeAlias, cast
from typing import Literal, TypeAlias, cast
from ..base import BaseSolver
CubeType: TypeAlias = Literal["red", "blue", "green"] CubeType: TypeAlias = Literal["red", "blue", "green"]
MAX_CUBES: dict[CubeType, int] = {"red": 12, "green": 13, "blue": 14} MAX_CUBES: dict[CubeType, int] = {"red": 12, "green": 13, "blue": 14}
# parse games
lines = sys.stdin.read().splitlines() class Solver(BaseSolver):
games: dict[int, list[dict[CubeType, int]]] = {} def solve(self, input: str) -> Iterator[Any]:
for line in filter(bool, lines): lines = input.splitlines()
games: dict[int, list[dict[CubeType, int]]] = {}
for line in filter(bool, lines):
id_part, sets_part = line.split(":") id_part, sets_part = line.split(":")
games[int(id_part.split(" ")[-1])] = [ games[int(id_part.split(" ")[-1])] = [
@ -21,8 +24,7 @@ for line in filter(bool, lines):
for cube_set_s in sets_part.strip().split(";") for cube_set_s in sets_part.strip().split(";")
] ]
# part 1 yield sum(
answer_1 = sum(
id id
for id, set_of_cubes in games.items() for id, set_of_cubes in games.items()
if all( if all(
@ -30,14 +32,12 @@ answer_1 = sum(
for cube_set in set_of_cubes for cube_set in set_of_cubes
for cube, n_cubes in cube_set.items() for cube, n_cubes in cube_set.items()
) )
) )
print(f"answer 1 is {answer_1}")
# part 2 yield sum(
answer_2 = sum(
math.prod( math.prod(
max(cube_set.get(cube, 0) for cube_set in set_of_cubes) for cube in MAX_CUBES max(cube_set.get(cube, 0) for cube_set in set_of_cubes)
for cube in MAX_CUBES
) )
for set_of_cubes in games.values() for set_of_cubes in games.values()
) )
print(f"answer 2 is {answer_2}")

View File

@ -1,15 +1,20 @@
import string import string
import sys
from collections import defaultdict from collections import defaultdict
from typing import Any, Iterator
from ..base import BaseSolver
NOT_A_SYMBOL = "." + string.digits NOT_A_SYMBOL = "." + string.digits
lines = sys.stdin.read().splitlines()
values: list[int] = [] class Solver(BaseSolver):
gears: dict[tuple[int, int], list[int]] = defaultdict(list) def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
for i, line in enumerate(lines): values: list[int] = []
gears: dict[tuple[int, int], list[int]] = defaultdict(list)
for i, line in enumerate(lines):
j = 0 j = 0
while j < len(line): while j < len(line):
# skip everything until a digit is found (start of a number) # skip everything until a digit is found (start of a number)
@ -44,10 +49,5 @@ for i, line in enumerate(lines):
# continue starting from the end of the number # continue starting from the end of the number
j = k j = k
# part 1 yield sum(values)
answer_1 = sum(values) yield sum(v1 * v2 for v1, v2 in filter(lambda vs: len(vs) == 2, gears.values()))
print(f"answer 1 is {answer_1}")
# part 2
answer_2 = sum(v1 * v2 for v1, v2 in filter(lambda vs: len(vs) == 2, gears.values()))
print(f"answer 2 is {answer_2}")

View File

@ -1,5 +1,7 @@
import sys
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Iterator
from ..base import BaseSolver
@dataclass(frozen=True) @dataclass(frozen=True)
@ -9,10 +11,12 @@ class Card:
values: list[int] values: list[int]
lines = sys.stdin.read().splitlines() class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
cards: list[Card] = [] cards: list[Card] = []
for line in lines: for line in lines:
id_part, e_part = line.split(":") id_part, e_part = line.split(":")
numbers_s, values_s = e_part.split("|") numbers_s, values_s = e_part.split("|")
cards.append( cards.append(
@ -23,19 +27,18 @@ for line in lines:
) )
) )
winnings = [sum(1 for n in card.values if n in card.numbers) for card in cards] winnings = [sum(1 for n in card.values if n in card.numbers) for card in cards]
# part 1 # part 1
answer_1 = sum(2 ** (winning - 1) for winning in winnings if winning > 0) yield sum(2 ** (winning - 1) for winning in winnings if winning > 0)
print(f"answer 1 is {answer_1}")
# part 2 # part 2
card2cards = {i: list(range(i + 1, i + w + 1)) for i, w in enumerate(winnings)} card2cards = {i: list(range(i + 1, i + w + 1)) for i, w in enumerate(winnings)}
card2values = {i: 0 for i in range(len(cards))} card2values = {i: 0 for i in range(len(cards))}
for i in range(len(cards)): for i in range(len(cards)):
card2values[i] += 1 card2values[i] += 1
for j in card2cards[i]: for j in card2cards[i]:
card2values[j] += card2values[i] card2values[j] += card2values[i]
print(f"answer 2 is {sum(card2values.values())}") yield sum(card2values.values())

View File

@ -1,5 +1,6 @@
import sys from typing import Any, Iterator, Sequence
from typing import Sequence
from ..base import BaseSolver
MAP_ORDER = [ MAP_ORDER = [
"seed", "seed",
@ -12,55 +13,6 @@ MAP_ORDER = [
"location", "location",
] ]
lines = sys.stdin.read().splitlines()
# mappings from one category to another, each list contains
# ranges stored as (source, target, length), ordered by start and
# completed to have no "hole"
maps: dict[tuple[str, str], list[tuple[int, int, int]]] = {}
# parsing
index = 2
while index < len(lines):
p1, _, p2 = lines[index].split()[0].split("-")
# extract the existing ranges from the file - we store as (source, target, length)
# whereas the file is in order (target, source, length)
index += 1
values: list[tuple[int, int, int]] = []
while index < len(lines) and lines[index]:
n1, n2, n3 = lines[index].split()
values.append((int(n2), int(n1), int(n3)))
index += 1
# sort by source value
values.sort()
# add a 'fake' interval starting at 0 if missing
if values[0][0] != 0:
values.insert(0, (0, 0, values[0][0]))
# fill gaps between intervals
for i in range(len(values) - 1):
next_start = values[i + 1][0]
end = values[i][0] + values[i][2]
if next_start != end:
values.insert(
i + 1,
(end, end, next_start - end),
)
# add an interval covering values up to at least 2**32 at the end
last_start, _, last_length = values[-1]
values.append((last_start + last_length, last_start + last_length, 2**32))
assert all(v1[0] + v1[2] == v2[0] for v1, v2 in zip(values[:-1], values[1:]))
assert values[0][0] == 0
assert values[-1][0] + values[-1][-1] >= 2**32
maps[p1, p2] = values
index += 1
def find_range( def find_range(
values: tuple[int, int], map: list[tuple[int, int, int]] values: tuple[int, int], map: list[tuple[int, int, int]]
@ -111,19 +63,71 @@ def find_range(
return ranges return ranges
def find_location_ranges(seeds: Sequence[tuple[int, int]]) -> Sequence[tuple[int, int]]: class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
# mappings from one category to another, each list contains
# ranges stored as (source, target, length), ordered by start and
# completed to have no "hole"
maps: dict[tuple[str, str], list[tuple[int, int, int]]] = {}
def find_location_ranges(
seeds: Sequence[tuple[int, int]],
) -> Sequence[tuple[int, int]]:
for map1, map2 in zip(MAP_ORDER[:-1], MAP_ORDER[1:]): for map1, map2 in zip(MAP_ORDER[:-1], MAP_ORDER[1:]):
seeds = [s2 for s1 in seeds for s2 in find_range(s1, maps[map1, map2])] seeds = [s2 for s1 in seeds for s2 in find_range(s1, maps[map1, map2])]
return seeds return seeds
# parsing
index = 2
while index < len(lines):
p1, _, p2 = lines[index].split()[0].split("-")
# part 1 - use find_range() with range of length 1 # extract the existing ranges from the file - we store as (source, target, length)
seeds_p1 = [(int(s), 1) for s in lines[0].split(":")[1].strip().split()] # whereas the file is in order (target, source, length)
answer_1 = min(start for start, _ in find_location_ranges(seeds_p1)) index += 1
print(f"answer 1 is {answer_1}") values: list[tuple[int, int, int]] = []
while index < len(lines) and lines[index]:
n1, n2, n3 = lines[index].split()
values.append((int(n2), int(n1), int(n3)))
index += 1
# # part 2 # sort by source value
parts = lines[0].split(":")[1].strip().split() values.sort()
seeds_p2 = [(int(s), int(e)) for s, e in zip(parts[::2], parts[1::2])]
answer_2 = min(start for start, _ in find_location_ranges(seeds_p2)) # add a 'fake' interval starting at 0 if missing
print(f"answer 2 is {answer_2}") if values[0][0] != 0:
values.insert(0, (0, 0, values[0][0]))
# fill gaps between intervals
for i in range(len(values) - 1):
next_start = values[i + 1][0]
end = values[i][0] + values[i][2]
if next_start != end:
values.insert(
i + 1,
(end, end, next_start - end),
)
# add an interval covering values up to at least 2**32 at the end
last_start, _, last_length = values[-1]
values.append((last_start + last_length, last_start + last_length, 2**32))
assert all(
v1[0] + v1[2] == v2[0] for v1, v2 in zip(values[:-1], values[1:])
)
assert values[0][0] == 0
assert values[-1][0] + values[-1][-1] >= 2**32
maps[p1, p2] = values
index += 1
# part 1 - use find_range() with range of length 1
seeds_p1 = [(int(s), 1) for s in lines[0].split(":")[1].strip().split()]
yield min(start for start, _ in find_location_ranges(seeds_p1))
# # part 2
parts = lines[0].split(":")[1].strip().split()
seeds_p2 = [(int(s), int(e)) for s, e in zip(parts[::2], parts[1::2])]
yield min(start for start, _ in find_location_ranges(seeds_p2))

View File

@ -1,14 +1,17 @@
import sys
from collections import Counter from collections import Counter
from typing import Any, Iterator
values = list(map(int, sys.stdin.read().strip().split())) from ..base import BaseSolver
column_1 = sorted(values[::2])
column_2 = sorted(values[1::2])
counter_2 = Counter(column_2)
answer_1 = sum(abs(v1 - v2) for v1, v2 in zip(column_1, column_2, strict=True)) class Solver(BaseSolver):
answer_2 = sum(value * counter_2.get(value, 0) for value in column_1) def solve(self, input: str) -> Iterator[Any]:
values = list(map(int, input.split()))
print(f"answer 1 is {answer_1}") column_1 = sorted(values[::2])
print(f"answer 2 is {answer_2}") column_2 = sorted(values[1::2])
yield sum(abs(v1 - v2) for v1, v2 in zip(column_1, column_2, strict=True))
counter_2 = Counter(column_2)
yield sum(value * counter_2.get(value, 0) for value in column_1)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,22 +1,23 @@
import sys from typing import Any, Iterator
from ..base import BaseSolver
def is_safe(level: list[int]) -> bool: class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
def is_safe(level: list[int]) -> bool:
diff = [a - b for a, b in zip(level[:-1], level[1:], strict=True)] diff = [a - b for a, b in zip(level[:-1], level[1:], strict=True)]
return sum(d > 0 for d in diff) in (0, len(diff)) and all( return sum(d > 0 for d in diff) in (0, len(diff)) and all(
1 <= abs(d) <= 3 for d in diff 1 <= abs(d) <= 3 for d in diff
) )
def is_any_safe(level: list[int]) -> bool:
return any(
is_safe(level[:i] + level[i + 1 :]) for i in range(0, len(level))
)
def is_any_safe(level: list[int]) -> bool: levels = [[int(c) for c in r.split()] for r in input.splitlines()]
return any(is_safe(level[:i] + level[i + 1 :]) for i in range(0, len(level)))
yield sum(is_safe(level) for level in levels)
levels = [[int(c) for c in r.split()] for r in sys.stdin.read().strip().splitlines()] yield sum(is_safe(level) or is_any_safe(level) for level in levels)
answer_1 = sum(is_safe(level) for level in levels)
answer_2 = sum(is_safe(level) or is_any_safe(level) for level in levels)
print(f"answer 1 is {answer_1}")
print(f"answer 2 is {answer_2}")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,16 @@
import re import re
import sys from typing import Any, Iterator
from typing import Iterator
from ..base import BaseSolver
def extract_multiply(line: str) -> Iterator[int]: class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
def extract_multiply(line: str) -> Iterator[int]:
for m in re.finditer(r"mul\(([0-9]{1,3}),\s*([0-9]{1,3})\)", line): for m in re.finditer(r"mul\(([0-9]{1,3}),\s*([0-9]{1,3})\)", line):
yield int(m.group(1)) * int(m.group(2)) yield int(m.group(1)) * int(m.group(2))
def valid_memory_blocks(line: str) -> Iterator[str]:
def valid_memory_blocks(line: str) -> Iterator[str]:
accumulate = True accumulate = True
while line: while line:
if accumulate: if accumulate:
@ -24,11 +26,5 @@ def valid_memory_blocks(line: str) -> Iterator[str]:
else: else:
line = "" line = ""
yield sum(extract_multiply(input))
line = sys.stdin.read().strip() yield sum(sum(extract_multiply(block)) for block in valid_memory_blocks(input))
answer_1 = sum(extract_multiply(line))
answer_2 = sum(sum(extract_multiply(block)) for block in valid_memory_blocks(line))
print(f"answer 1 is {answer_1}")
print(f"answer 2 is {answer_2}")

View File

@ -1,10 +1,15 @@
import itertools as it import itertools as it
import sys from typing import Any, Iterator
lines = sys.stdin.read().strip().splitlines() from ..base import BaseSolver
n = len(lines)
answer_1 = sum(
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
n = len(lines)
yield sum(
line.count("XMAS") + line.count("SAMX") line.count("XMAS") + line.count("SAMX")
for i in range(n) for i in range(n)
for ri, rk, ro, ci, ck, cm in ( for ri, rk, ro, ci, ck, cm in (
@ -16,16 +21,17 @@ answer_1 = sum(
(-1, -1, -1, 0, 1, n - i if i != 0 else 0), (-1, -1, -1, 0, 1, n - i if i != 0 else 0),
) )
if ( if (
line := "".join(lines[ri * i + rk * k + ro][ci * i + ck * k] for k in range(cm)) line := "".join(
lines[ri * i + rk * k + ro][ci * i + ck * k] for k in range(cm)
)
)
) )
)
answer_2 = sum( yield sum(
lines[i][j] == "A" lines[i][j] == "A"
and "".join(lines[i + di][j + dj] for di, dj in it.product((-1, 1), (-1, 1))) and "".join(
lines[i + di][j + dj] for di, dj in it.product((-1, 1), (-1, 1))
)
in {"MSMS", "SSMM", "MMSS", "SMSM"} in {"MSMS", "SSMM", "MMSS", "SMSM"}
for i, j in it.product(range(1, n - 1), range(1, n - 1)) for i, j in it.product(range(1, n - 1), range(1, n - 1))
) )
print(f"answer 1 is {answer_1}")
print(f"answer 2 is {answer_2}")

View File

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

View File

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

View File

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

View File

@ -1,9 +1,11 @@
import argparse import argparse
import importlib import importlib
import os import logging
import sys import sys
from pathlib import Path from pathlib import Path
from .base import BaseSolver
def main(): def main():
parser = argparse.ArgumentParser("Holt59 Advent-Of-Code Runner") parser = argparse.ArgumentParser("Holt59 Advent-Of-Code Runner")
@ -40,8 +42,7 @@ def main():
day: int = args.day day: int = args.day
# TODO: change this # TODO: change this
if verbose: logging.basicConfig(level=logging.INFO if verbose else logging.WARNING)
os.environ["AOC_VERBOSE"] = "True"
if input_path is None: if input_path is None:
input_path = Path(__file__).parent.joinpath( input_path = Path(__file__).parent.joinpath(
@ -49,11 +50,18 @@ def main():
) )
assert input_path.exists(), f"{input_path} missing" assert input_path.exists(), f"{input_path} missing"
solver_class: type[BaseSolver] = importlib.import_module(
f".{year}.day{day}", __package__
).Solver
solver = solver_class(logging.getLogger("AOC"), year, day)
data: str
if stdin: if stdin:
importlib.import_module(f".{year}.day{day}", __package__) data = sys.stdin.read()
else: else:
with open(input_path) as fp: with open(input_path) as fp:
sys.stdin = fp data = fp.read()
importlib.import_module(f".{year}.day{day}", __package__)
sys.stdin = sys.__stdin__ for i_answer, answer in enumerate(solver.solve(data.strip())):
print(f"answer {i_answer + 1} is {answer}")

13
src/holt59/aoc/base.py Normal file
View File

@ -0,0 +1,13 @@
from abc import abstractmethod
from logging import Logger
from typing import Any, Final, Iterator
class BaseSolver:
def __init__(self, logger: Logger, year: int, day: int):
self.logger: Final = logger
self.year: Final = year
self.day: Final = day
@abstractmethod
def solve(self, input: str) -> Iterator[Any]: ...