Refactor code for API #3

Merged
mikael.capelle merged 13 commits from dev/refactor-for-ui into master 2024-12-08 13:06:42 +00:00
30 changed files with 384 additions and 403 deletions
Showing only changes of commit a9bcf9ef8f - Show all commits

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,43 +1,43 @@
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()
games: dict[int, list[dict[CubeType, int]]] = {}
for line in filter(bool, lines):
id_part, sets_part = line.split(":")
games[int(id_part.split(" ")[-1])] = [ class Solver(BaseSolver):
{ def solve(self, input: str) -> Iterator[Any]:
cast(CubeType, s[1]): int(s[0]) lines = input.splitlines()
for cube_draw in cube_set_s.strip().split(", ") games: dict[int, list[dict[CubeType, int]]] = {}
if (s := cube_draw.split(" ")) for line in filter(bool, lines):
} id_part, sets_part = line.split(":")
for cube_set_s in sets_part.strip().split(";")
]
# part 1 games[int(id_part.split(" ")[-1])] = [
answer_1 = sum( {
id cast(CubeType, s[1]): int(s[0])
for id, set_of_cubes in games.items() for cube_draw in cube_set_s.strip().split(", ")
if all( if (s := cube_draw.split(" "))
n_cubes <= MAX_CUBES[cube] }
for cube_set in set_of_cubes for cube_set_s in sets_part.strip().split(";")
for cube, n_cubes in cube_set.items() ]
)
)
print(f"answer 1 is {answer_1}")
# part 2 yield sum(
answer_2 = sum( id
math.prod( for id, set_of_cubes in games.items()
max(cube_set.get(cube, 0) for cube_set in set_of_cubes) for cube in MAX_CUBES if all(
) n_cubes <= MAX_CUBES[cube]
for set_of_cubes in games.values() for cube_set in set_of_cubes
) for cube, n_cubes in cube_set.items()
print(f"answer 2 is {answer_2}") )
)
yield sum(
math.prod(
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()
)

View File

@ -1,53 +1,53 @@
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] = []
j = 0 gears: dict[tuple[int, int], list[int]] = defaultdict(list)
while j < len(line):
# skip everything until a digit is found (start of a number)
if line[j] not in string.digits:
j += 1
continue
# extract the range of the number and its value for i, line in enumerate(lines):
k = j + 1 j = 0
while k < len(line) and line[k] in string.digits: while j < len(line):
k += 1 # skip everything until a digit is found (start of a number)
if line[j] not in string.digits:
j += 1
continue
value = int(line[j:k]) # extract the range of the number and its value
k = j + 1
while k < len(line) and line[k] in string.digits:
k += 1
# lookup around the number if there is a symbol - we go through the number value = int(line[j:k])
# itself but that should not matter since it only contains digits
found = False
for i2 in range(max(0, i - 1), min(i + 1, len(lines) - 1) + 1):
for j2 in range(max(0, j - 1), min(k, len(line) - 1) + 1):
assert i2 >= 0 and i2 < len(lines)
assert j2 >= 0 and j2 < len(line)
if lines[i2][j2] not in NOT_A_SYMBOL: # lookup around the number if there is a symbol - we go through the number
found = True # itself but that should not matter since it only contains digits
found = False
for i2 in range(max(0, i - 1), min(i + 1, len(lines) - 1) + 1):
for j2 in range(max(0, j - 1), min(k, len(line) - 1) + 1):
assert i2 >= 0 and i2 < len(lines)
assert j2 >= 0 and j2 < len(line)
if lines[i2][j2] == "*": if lines[i2][j2] not in NOT_A_SYMBOL:
gears[i2, j2].append(value) found = True
if found: if lines[i2][j2] == "*":
values.append(value) gears[i2, j2].append(value)
# continue starting from the end of the number if found:
j = k values.append(value)
# part 1 # continue starting from the end of the number
answer_1 = sum(values) j = k
print(f"answer 1 is {answer_1}")
# part 2 yield sum(values)
answer_2 = sum(v1 * v2 for v1, v2 in filter(lambda vs: len(vs) == 2, gears.values())) yield 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,33 +11,34 @@ 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(
Card( Card(
id=int(id_part.split()[1]), id=int(id_part.split()[1]),
numbers=[int(v.strip()) for v in numbers_s.strip().split()], numbers=[int(v.strip()) for v in numbers_s.strip().split()],
values=[int(v.strip()) for v in values_s.strip().split()], values=[int(v.strip()) for v in values_s.strip().split()],
) )
) )
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):
for map1, map2 in zip(MAP_ORDER[:-1], MAP_ORDER[1:]): def solve(self, input: str) -> Iterator[Any]:
seeds = [s2 for s1 in seeds for s2 in find_range(s1, maps[map1, map2])] lines = input.splitlines()
return seeds
# 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]]] = {}
# part 1 - use find_range() with range of length 1 def find_location_ranges(
seeds_p1 = [(int(s), 1) for s in lines[0].split(":")[1].strip().split()] seeds: Sequence[tuple[int, int]],
answer_1 = min(start for start, _ in find_location_ranges(seeds_p1)) ) -> Sequence[tuple[int, int]]:
print(f"answer 1 is {answer_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])]
return seeds
# # part 2 # parsing
parts = lines[0].split(":")[1].strip().split() index = 2
seeds_p2 = [(int(s), int(e)) for s, e in zip(parts[::2], parts[1::2])] while index < len(lines):
answer_2 = min(start for start, _ in find_location_ranges(seeds_p2)) p1, _, p2 = lines[index].split()[0].split("-")
print(f"answer 2 is {answer_2}")
# 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
# 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):
diff = [a - b for a, b in zip(level[:-1], level[1:], strict=True)] 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)]
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,34 +1,30 @@
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):
for m in re.finditer(r"mul\(([0-9]{1,3}),\s*([0-9]{1,3})\)", line): def solve(self, input: str) -> Iterator[Any]:
yield int(m.group(1)) * int(m.group(2)) def extract_multiply(line: str) -> Iterator[int]:
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))
def valid_memory_blocks(line: str) -> Iterator[str]:
accumulate = True
while line:
if accumulate:
if (dont_i := line.find("don't()")) != -1:
yield line[:dont_i]
line, accumulate = line[dont_i:], False
else:
yield line
line = ""
else:
if (do_i := line.find("do()")) != -1:
line, accumulate = line[do_i:], True
else:
line = ""
def valid_memory_blocks(line: str) -> Iterator[str]: yield sum(extract_multiply(input))
accumulate = True yield sum(sum(extract_multiply(block)) for block in valid_memory_blocks(input))
while line:
if accumulate:
if (dont_i := line.find("don't()")) != -1:
yield line[:dont_i]
line, accumulate = line[dont_i:], False
else:
yield line
line = ""
else:
if (do_i := line.find("do()")) != -1:
line, accumulate = line[do_i:], True
else:
line = ""
line = sys.stdin.read().strip()
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,31 +1,37 @@
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(
line.count("XMAS") + line.count("SAMX")
for i in range(n)
for ri, rk, ro, ci, ck, cm in (
(1, 0, 0, 0, 1, n),
(0, 1, 0, 1, 0, n),
(0, 1, 0, 1, 1, n - i),
(0, -1, -1, 1, 1, n - i),
(1, 1, 0, 0, 1, n - i if i != 0 else 0),
(-1, -1, -1, 0, 1, n - i if i != 0 else 0),
)
if (
line := "".join(lines[ri * i + rk * k + ro][ci * i + ck * k] for k in range(cm))
)
)
answer_2 = sum( class Solver(BaseSolver):
lines[i][j] == "A" def solve(self, input: str) -> Iterator[Any]:
and "".join(lines[i + di][j + dj] for di, dj in it.product((-1, 1), (-1, 1))) lines = input.splitlines()
in {"MSMS", "SSMM", "MMSS", "SMSM"} n = len(lines)
for i, j in it.product(range(1, n - 1), range(1, n - 1))
)
print(f"answer 1 is {answer_1}") yield sum(
print(f"answer 2 is {answer_2}") line.count("XMAS") + line.count("SAMX")
for i in range(n)
for ri, rk, ro, ci, ck, cm in (
(1, 0, 0, 0, 1, n),
(0, 1, 0, 1, 0, n),
(0, 1, 0, 1, 1, n - i),
(0, -1, -1, 1, 1, n - i),
(1, 1, 0, 0, 1, n - i if i != 0 else 0),
(-1, -1, -1, 0, 1, n - i if i != 0 else 0),
)
if (
line := "".join(
lines[ri * i + rk * k + ro][ci * i + ck * k] for k in range(cm)
)
)
)
yield sum(
lines[i][j] == "A"
and "".join(
lines[i + di][j + dj] for di, dj in it.product((-1, 1), (-1, 1))
)
in {"MSMS", "SSMM", "MMSS", "SMSM"}
for i, j in it.product(range(1, n - 1), range(1, n - 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,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]: ...