Compare commits
2 Commits
6fd569aeba
...
ci/add-ci
Author | SHA1 | Date | |
---|---|---|---|
|
b21c50562f | ||
|
211483f679 |
16
poetry.lock
generated
16
poetry.lock
generated
@@ -1245,20 +1245,6 @@ files = [
|
||||
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
|
||||
test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
|
||||
|
||||
[[package]]
|
||||
name = "types-networkx"
|
||||
version = "3.4.2.20241115"
|
||||
description = "Typing stubs for networkx"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "types-networkx-3.4.2.20241115.tar.gz", hash = "sha256:d669b650cf6c6c9ec879a825449eb04a5c10742f3109177e1683f57ee49e0f59"},
|
||||
{file = "types_networkx-3.4.2.20241115-py3-none-any.whl", hash = "sha256:f0c382924d6614e06bf0b1ca0b837b8f33faa58982bc086ea762efaf39aa98dd"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
numpy = ">=1.20"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.12.2"
|
||||
@@ -1295,4 +1281,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "c91bc307ff4a5b3e8cd1976ebea211c9749fe09d563dd80861f70ce26826cda9"
|
||||
content-hash = "b643261f91a781d77735e05f6d2ac1002867600c2df6393a9d1a15f5e1189109"
|
||||
|
@@ -23,7 +23,6 @@ ruff = "^0.8.1"
|
||||
poethepoet = "^0.31.1"
|
||||
ipykernel = "^6.29.5"
|
||||
networkx-stubs = "^0.0.1"
|
||||
types-networkx = "^3.4.2.20241115"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
holt59-aoc = "holt59.aoc.__main__:main"
|
||||
|
@@ -1,12 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
line = sys.stdin.read().strip()
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
floor = 0
|
||||
floors = [(floor := floor + (1 if c == "(" else -1)) for c in input]
|
||||
floors = [(floor := floor + (1 if c == "(" else -1)) for c in line]
|
||||
|
||||
yield floors[-1]
|
||||
yield floors.index(-1)
|
||||
|
||||
print(f"answer 1 is {floors[-1]}")
|
||||
print(f"answer 2 is {floors.index(-1)}")
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import itertools
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
line = sys.stdin.read().strip()
|
||||
|
||||
# see http://www.se16.info/js/lands2.htm for the explanation of 'atoms' (or elements)
|
||||
#
|
||||
@@ -9,7 +9,7 @@ from ..base import BaseSolver
|
||||
# CodeGolf answer https://codegolf.stackexchange.com/a/8479/42148
|
||||
|
||||
# fmt: off
|
||||
ATOMS: list[tuple[str, tuple[int, ...]]] = [
|
||||
atoms = [
|
||||
("22", (0, )), # 0
|
||||
("13112221133211322112211213322112", (71, 90, 0, 19, 2, )), # 1
|
||||
("312211322212221121123222112", (1, )), # 2
|
||||
@@ -105,7 +105,7 @@ ATOMS: list[tuple[str, tuple[int, ...]]] = [
|
||||
]
|
||||
# fmt: on
|
||||
|
||||
STARTERS = [
|
||||
starters = [
|
||||
"1",
|
||||
"11",
|
||||
"21",
|
||||
@@ -122,26 +122,27 @@ def look_and_say_length(s: str, n: int) -> int:
|
||||
if n == 0:
|
||||
return len(s)
|
||||
|
||||
if s in STARTERS:
|
||||
if s in starters:
|
||||
return look_and_say_length(
|
||||
"".join(f"{len(list(g))}{k}" for k, g in itertools.groupby(s)), n - 1
|
||||
)
|
||||
|
||||
counts = {i: 0 for i in range(len(ATOMS))}
|
||||
idx = next(i for i, (a, _) in enumerate(ATOMS) if s == a)
|
||||
counts = {i: 0 for i in range(len(atoms))}
|
||||
idx = next(i for i, (a, _) in enumerate(atoms) if s == a)
|
||||
counts[idx] = 1
|
||||
|
||||
for _ in range(n):
|
||||
c2 = {i: 0 for i in range(len(ATOMS))}
|
||||
c2 = {i: 0 for i in range(len(atoms))}
|
||||
for i in counts:
|
||||
for j in ATOMS[i][1]:
|
||||
for j in atoms[i][1]:
|
||||
c2[j] += counts[i]
|
||||
counts = c2
|
||||
|
||||
return sum(counts[i] * len(a[0]) for i, a in enumerate(ATOMS))
|
||||
return sum(counts[i] * len(a[0]) for i, a in enumerate(atoms))
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any] | None:
|
||||
yield look_and_say_length(input, 40)
|
||||
yield look_and_say_length(input, 50)
|
||||
answer_1 = look_and_say_length(line, 40)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = look_and_say_length(line, 50)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import itertools
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
|
||||
|
||||
def is_valid(p: str) -> bool:
|
||||
@@ -42,8 +40,10 @@ def find_next_password(p: str) -> str:
|
||||
return p
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
answer_1 = find_next_password(input)
|
||||
yield answer_1
|
||||
yield find_next_password(increment(answer_1))
|
||||
line = sys.stdin.read().strip()
|
||||
|
||||
answer_1 = find_next_password(line)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = find_next_password(increment(answer_1))
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import json
|
||||
from typing import Any, Iterator, TypeAlias
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
from typing import TypeAlias
|
||||
|
||||
JsonObject: TypeAlias = dict[str, "JsonObject"] | list["JsonObject"] | int | str
|
||||
|
||||
@@ -19,9 +18,10 @@ def json_sum(value: JsonObject, ignore: str | None = None) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
data: JsonObject = json.loads(input)
|
||||
data: JsonObject = json.load(sys.stdin)
|
||||
|
||||
yield json_sum(data)
|
||||
yield json_sum(data, "red")
|
||||
answer_1 = json_sum(data)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = json_sum(data, "red")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,11 +1,10 @@
|
||||
import itertools
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator, Literal, cast
|
||||
from typing import Literal, cast
|
||||
|
||||
import parse # type: ignore
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def max_change_in_happiness(happiness: dict[str, dict[str, int]]) -> int:
|
||||
guests = list(happiness)
|
||||
@@ -18,9 +17,7 @@ def max_change_in_happiness(happiness: dict[str, dict[str, int]]) -> int:
|
||||
)
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
happiness: dict[str, dict[str, int]] = defaultdict(dict)
|
||||
for line in lines:
|
||||
@@ -32,9 +29,13 @@ class Solver(BaseSolver):
|
||||
)
|
||||
happiness[u1][u2] = hap if gain_or_loose == "gain" else -hap
|
||||
|
||||
yield max_change_in_happiness(happiness)
|
||||
|
||||
answer_1 = max_change_in_happiness(happiness)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
for guest in list(happiness):
|
||||
happiness["me"][guest] = 0
|
||||
happiness[guest]["me"] = 0
|
||||
|
||||
yield max_change_in_happiness(happiness)
|
||||
answer_2 = max_change_in_happiness(happiness)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,10 +1,9 @@
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Iterator, Literal, cast
|
||||
from typing import Literal, cast
|
||||
|
||||
import parse # type: ignore
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Reindeer:
|
||||
@@ -14,9 +13,7 @@ class Reindeer:
|
||||
rest_time: int
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
reindeers: list[Reindeer] = []
|
||||
for line in lines:
|
||||
@@ -29,9 +26,7 @@ class Solver(BaseSolver):
|
||||
),
|
||||
)
|
||||
reindeers.append(
|
||||
Reindeer(
|
||||
name=reindeer, speed=speed, fly_time=speed_time, rest_time=rest_time
|
||||
)
|
||||
Reindeer(name=reindeer, speed=speed, fly_time=speed_time, rest_time=rest_time)
|
||||
)
|
||||
|
||||
target = 1000 if len(reindeers) <= 2 else 2503
|
||||
@@ -42,7 +37,7 @@ class Solver(BaseSolver):
|
||||
distances: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
||||
points: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
||||
|
||||
for time in self.progress.wrap(range(target)):
|
||||
for time in range(target):
|
||||
for reindeer in reindeers:
|
||||
if states[reindeer][0] == "flying":
|
||||
distances[reindeer] += reindeer.speed
|
||||
@@ -59,5 +54,9 @@ class Solver(BaseSolver):
|
||||
else:
|
||||
states[reindeer] = ("resting", time + reindeer.rest_time)
|
||||
|
||||
yield max(distances.values())
|
||||
yield max(points.values()) - 1
|
||||
|
||||
answer_1 = max(distances.values())
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = max(points.values()) - 1
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,10 +1,9 @@
|
||||
import math
|
||||
from typing import Any, Iterator, Sequence, cast
|
||||
import sys
|
||||
from typing import Sequence, cast
|
||||
|
||||
import parse # type: ignore
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def score(ingredients: list[list[int]], teaspoons: Sequence[int]) -> int:
|
||||
return math.prod(
|
||||
@@ -19,9 +18,7 @@ def score(ingredients: list[list[int]], teaspoons: Sequence[int]) -> int:
|
||||
)
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
ingredients: list[list[int]] = []
|
||||
for line in lines:
|
||||
@@ -52,5 +49,9 @@ class Solver(BaseSolver):
|
||||
)
|
||||
)
|
||||
|
||||
yield max(scores)
|
||||
yield max(score for score, calory in zip(scores, calories) if calory == 500)
|
||||
|
||||
answer_1 = max(scores)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = max(score for score, calory in zip(scores, calories) if calory == 500)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import operator as op
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Callable, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
from typing import Callable
|
||||
|
||||
MFCSAM: dict[str, int] = {
|
||||
"children": 3,
|
||||
@@ -18,10 +17,18 @@ MFCSAM: dict[str, int] = {
|
||||
"perfumes": 1,
|
||||
}
|
||||
|
||||
lines = sys.stdin.readlines()
|
||||
|
||||
def match(
|
||||
aunts: list[dict[str, int]], operators: dict[str, Callable[[int, int], bool]]
|
||||
) -> int:
|
||||
aunts: list[dict[str, int]] = [
|
||||
{
|
||||
match[1]: int(match[2])
|
||||
for match in re.findall(R"((?P<compound>[^:, ]+): (?P<quantity>\d+))", line)
|
||||
}
|
||||
for line in lines
|
||||
]
|
||||
|
||||
|
||||
def match(operators: dict[str, Callable[[int, int], bool]]) -> int:
|
||||
return next(
|
||||
i
|
||||
for i, aunt in enumerate(aunts, start=1)
|
||||
@@ -29,29 +36,16 @@ def match(
|
||||
)
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
answer_1 = match(defaultdict(lambda: op.eq))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
aunts: list[dict[str, int]] = [
|
||||
{
|
||||
match[1]: int(match[2])
|
||||
for match in re.findall(
|
||||
R"((?P<compound>[^:, ]+): (?P<quantity>\d+))", line
|
||||
)
|
||||
}
|
||||
for line in lines
|
||||
]
|
||||
|
||||
yield match(aunts, defaultdict(lambda: op.eq))
|
||||
|
||||
yield match(
|
||||
aunts,
|
||||
answer_2 = match(
|
||||
defaultdict(
|
||||
lambda: op.eq,
|
||||
trees=op.gt,
|
||||
cats=op.gt,
|
||||
pomeranians=op.lt,
|
||||
goldfish=op.lt,
|
||||
),
|
||||
)
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,6 +1,5 @@
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
from typing import Iterator
|
||||
|
||||
|
||||
def iter_combinations(value: int, containers: list[int]) -> Iterator[tuple[int, ...]]:
|
||||
@@ -17,18 +16,15 @@ def iter_combinations(value: int, containers: list[int]) -> Iterator[tuple[int,
|
||||
yield (containers[i],) + combination
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
containers = [int(c) for c in input.split()]
|
||||
containers = [int(c) for c in sys.stdin.read().split()]
|
||||
total = 25 if len(containers) <= 5 else 150
|
||||
|
||||
combinations = [
|
||||
combination for combination in iter_combinations(total, containers)
|
||||
]
|
||||
combinations = [combination for combination in iter_combinations(total, containers)]
|
||||
|
||||
yield len(combinations)
|
||||
answer_1 = len(combinations)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
min_containers = min(len(combination) for combination in combinations)
|
||||
yield sum(
|
||||
1 for combination in combinations if len(combination) == min_containers
|
||||
)
|
||||
|
||||
answer_2 = sum(1 for combination in combinations if len(combination) == min_containers)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,15 +1,10 @@
|
||||
import itertools
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
from numpy.typing import NDArray
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
grid0 = np.array([[c == "#" for c in line] for line in input.splitlines()])
|
||||
grid0 = np.array([[c == "#" for c in line] for line in sys.stdin.read().splitlines()])
|
||||
|
||||
# add an always off circle around
|
||||
grid0 = np.concatenate(
|
||||
@@ -39,6 +34,7 @@ class Solver(BaseSolver):
|
||||
ins = iis[:, None] + np.array(moves)[:, 0]
|
||||
jns = jjs[:, None] + np.array(moves)[:, 1]
|
||||
|
||||
|
||||
def game_of_life(grid: NDArray[np.bool_]) -> NDArray[np.bool_]:
|
||||
neighbors_on = grid[ins, jns].sum(axis=1)
|
||||
cells_on = grid[iis, jjs]
|
||||
@@ -48,12 +44,15 @@ class Solver(BaseSolver):
|
||||
|
||||
return grid
|
||||
|
||||
|
||||
grid = grid0
|
||||
n_steps = 4 if len(grid) < 10 else 100
|
||||
for _ in range(n_steps):
|
||||
grid = game_of_life(grid)
|
||||
|
||||
yield grid.sum()
|
||||
answer_1 = grid.sum()
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
|
||||
n_steps = 5 if len(grid) < 10 else 100
|
||||
grid = grid0
|
||||
@@ -63,4 +62,5 @@ class Solver(BaseSolver):
|
||||
|
||||
grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True
|
||||
|
||||
yield sum(cell for line in grid for cell in line)
|
||||
answer_2 = sum(cell for line in grid for cell in line)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,12 +1,7 @@
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
replacements_s, molecule = input.split("\n\n")
|
||||
replacements_s, molecule = sys.stdin.read().split("\n\n")
|
||||
|
||||
REPLACEMENTS: dict[str, list[str]] = defaultdict(list)
|
||||
for replacement_s in replacements_s.splitlines():
|
||||
@@ -22,7 +17,8 @@ class Solver(BaseSolver):
|
||||
if molecule[i:].startswith(symbol)
|
||||
]
|
||||
|
||||
yield len(set(generated))
|
||||
answer_1 = len(set(generated))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
inversion: dict[str, str] = {
|
||||
replacement: symbol
|
||||
@@ -55,4 +51,6 @@ class Solver(BaseSolver):
|
||||
# print(m2)
|
||||
molecule = m2
|
||||
|
||||
yield count
|
||||
|
||||
answer_2 = count
|
||||
print(f"answer 2 is {count}")
|
||||
|
@@ -1,24 +1,20 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
length, width, height = np.array(
|
||||
[[int(c) for c in line.split("x")] for line in input.splitlines()]
|
||||
[[int(c) for c in line.split("x")] for line in lines]
|
||||
).T
|
||||
|
||||
lw, wh, hl = (length * width, width * height, height * length)
|
||||
|
||||
yield np.sum(2 * (lw + wh + hl) + np.min(np.stack([lw, wh, hl]), axis=0))
|
||||
answer_1 = np.sum(2 * (lw + wh + hl) + np.min(np.stack([lw, wh, hl]), axis=0))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
yield np.sum(
|
||||
answer_2 = np.sum(
|
||||
length * width * height
|
||||
+ 2
|
||||
* np.min(
|
||||
np.stack([length + width, length + height, height + width]), axis=0
|
||||
)
|
||||
+ 2 * np.min(np.stack([length + width, length + height, height + width]), axis=0)
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import itertools
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
target = int(sys.stdin.read())
|
||||
|
||||
|
||||
def presents(n: int, elf: int, max: int) -> int:
|
||||
def presents(n: int, elf: int, max: int = target) -> int:
|
||||
count = 0
|
||||
k = 1
|
||||
while k * k < n:
|
||||
@@ -21,9 +21,8 @@ def presents(n: int, elf: int, max: int) -> int:
|
||||
return count
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
target = int(input)
|
||||
answer_1 = next(n for n in itertools.count(1) if presents(n, 10) >= target)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
yield next(n for n in itertools.count(1) if presents(n, 10, target) >= target)
|
||||
yield next(n for n in itertools.count(1) if presents(n, 11, 50) >= target)
|
||||
answer_2 = next(n for n in itertools.count(1) if presents(n, 11, 50) >= target)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import itertools
|
||||
import sys
|
||||
from math import ceil
|
||||
from typing import Any, Iterator, TypeAlias
|
||||
|
||||
from ..base import BaseSolver
|
||||
from typing import TypeAlias
|
||||
|
||||
Modifier: TypeAlias = tuple[str, int, int, int]
|
||||
|
||||
@@ -34,9 +33,7 @@ RINGS: list[Modifier] = [
|
||||
]
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
player_hp = 100
|
||||
|
||||
@@ -44,6 +41,7 @@ class Solver(BaseSolver):
|
||||
boss_armor = int(lines[2].split(":")[1].strip())
|
||||
boss_hp = int(lines[0].split(":")[1].strip())
|
||||
|
||||
|
||||
min_cost, max_cost = 1_000_000, 0
|
||||
for equipments in itertools.product(WEAPONS, ARMORS, RINGS, RINGS):
|
||||
if equipments[-1][0] != "" and equipments[-2] == equipments[-1]:
|
||||
@@ -60,5 +58,9 @@ class Solver(BaseSolver):
|
||||
else:
|
||||
max_cost = max(cost, max_cost)
|
||||
|
||||
yield min_cost
|
||||
yield max_cost
|
||||
|
||||
answer_1 = min_cost
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = max_cost
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,9 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import heapq
|
||||
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
from typing import Literal, TypeAlias, cast
|
||||
|
||||
PlayerType: TypeAlias = Literal["player", "boss"]
|
||||
SpellType: TypeAlias = Literal["magic missile", "drain", "shield", "poison", "recharge"]
|
||||
@@ -63,6 +62,17 @@ def play(
|
||||
continue
|
||||
|
||||
visited.add((player, player_hp, player_mana, player_armor, boss_hp, buffs))
|
||||
|
||||
if hard_mode and player == "player":
|
||||
player_hp = max(0, player_hp - 1)
|
||||
|
||||
if player_hp == 0:
|
||||
continue
|
||||
|
||||
if boss_hp == 0:
|
||||
winning_node = spells
|
||||
continue
|
||||
|
||||
new_buffs: list[tuple[BuffType, int]] = []
|
||||
for buff, length in buffs:
|
||||
length = length - 1
|
||||
@@ -78,16 +88,6 @@ def play(
|
||||
if length > 0:
|
||||
new_buffs.append((buff, length))
|
||||
|
||||
if hard_mode and player == "player":
|
||||
player_hp = player_hp - 1
|
||||
|
||||
if player_hp <= 0:
|
||||
continue
|
||||
|
||||
if boss_hp <= 0:
|
||||
winning_node = spells
|
||||
continue
|
||||
|
||||
buffs = tuple(new_buffs)
|
||||
|
||||
if player == "boss":
|
||||
@@ -155,9 +155,7 @@ def play(
|
||||
return winning_node
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
player_hp = 50
|
||||
player_mana = 500
|
||||
@@ -166,17 +164,14 @@ class Solver(BaseSolver):
|
||||
boss_hp = int(lines[0].split(":")[1].strip())
|
||||
boss_attack = int(lines[1].split(":")[1].strip())
|
||||
|
||||
yield sum(
|
||||
answer_1 = sum(
|
||||
c
|
||||
for _, c in play(
|
||||
player_hp, player_mana, player_armor, boss_hp, boss_attack, False
|
||||
)
|
||||
for _, c in play(player_hp, player_mana, player_armor, boss_hp, boss_attack, False)
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# 1242 (not working)
|
||||
yield sum(
|
||||
c
|
||||
for _, c in play(
|
||||
player_hp, player_mana, player_armor, boss_hp, boss_attack, True
|
||||
)
|
||||
answer_2 = sum(
|
||||
c for _, c in play(player_hp, player_mana, player_armor, boss_hp, boss_attack, True)
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
line = sys.stdin.read().strip()
|
||||
|
||||
|
||||
def process(directions: str) -> dict[tuple[int, int], int]:
|
||||
@@ -27,7 +27,8 @@ def process(directions: str) -> dict[tuple[int, int], int]:
|
||||
return counts
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
yield len(process(input))
|
||||
yield len(process(input[::2]) | process(input[1::2]))
|
||||
answer_1 = len(process(line))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = len(process(line[::2]) | process(line[1::2]))
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,20 +1,16 @@
|
||||
import hashlib
|
||||
import itertools
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
line = sys.stdin.read().strip()
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
it = iter(itertools.count(1))
|
||||
yield next(
|
||||
i
|
||||
for i in it
|
||||
if hashlib.md5(f"{input}{i}".encode()).hexdigest().startswith("00000")
|
||||
answer_1 = next(
|
||||
i for i in it if hashlib.md5(f"{line}{i}".encode()).hexdigest().startswith("00000")
|
||||
)
|
||||
yield next(
|
||||
i
|
||||
for i in it
|
||||
if hashlib.md5(f"{input}{i}".encode()).hexdigest().startswith("000000")
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = next(
|
||||
i for i in it if hashlib.md5(f"{line}{i}".encode()).hexdigest().startswith("000000")
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,6 +1,4 @@
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
|
||||
VOWELS = "aeiou"
|
||||
FORBIDDEN = {"ab", "cd", "pq", "xy"}
|
||||
@@ -29,8 +27,10 @@ def is_nice_2(s: str) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
yield sum(map(is_nice_1, lines))
|
||||
yield sum(map(is_nice_2, lines))
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = sum(map(is_nice_1, lines))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = sum(map(is_nice_2, lines))
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,16 +1,14 @@
|
||||
from typing import Any, Iterator, Literal, cast
|
||||
import sys
|
||||
from typing import Literal, cast
|
||||
|
||||
import numpy as np
|
||||
import parse # type: ignore
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lights_1 = np.zeros((1000, 1000), dtype=bool)
|
||||
lights_2 = np.zeros((1000, 1000), dtype=int)
|
||||
for line in input.splitlines():
|
||||
for line in lines:
|
||||
action, sx, sy, ex, ey = cast(
|
||||
tuple[Literal["turn on", "turn off", "toggle"], int, int, int, int],
|
||||
parse.parse("{} {:d},{:d} through {:d},{:d}", line), # type: ignore
|
||||
@@ -28,5 +26,8 @@ class Solver(BaseSolver):
|
||||
lights_1[sx:ex, sy:ey] = ~lights_1[sx:ex, sy:ey]
|
||||
lights_2[sx:ex, sy:ey] += 2
|
||||
|
||||
yield lights_1.sum()
|
||||
yield lights_2.sum()
|
||||
answer_1 = lights_1.sum()
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = lights_2.sum()
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,11 @@
|
||||
import logging
|
||||
import operator
|
||||
from typing import Any, Callable, Iterator
|
||||
import os
|
||||
import sys
|
||||
from typing import Callable
|
||||
|
||||
from ..base import BaseSolver
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
|
||||
OPERATORS = {
|
||||
"AND": operator.and_,
|
||||
@@ -32,22 +36,7 @@ def value_of(key: str) -> tuple[str, Callable[[dict[str, int]], int]]:
|
||||
return key, lambda values: values[key]
|
||||
|
||||
|
||||
def process(
|
||||
signals: Signals,
|
||||
values: dict[str, int],
|
||||
) -> dict[str, int]:
|
||||
while signals:
|
||||
signal = next(s for s in signals if all(p in values for p in signals[s][0]))
|
||||
_, deps, command = signals[signal]
|
||||
values[signal] = command(deps[0](values), deps[1](values)) % 65536
|
||||
del signals[signal]
|
||||
|
||||
return values
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any] | None:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
signals: Signals = {}
|
||||
values: dict[str, int] = {"": 0}
|
||||
@@ -88,9 +77,25 @@ class Solver(BaseSolver):
|
||||
|
||||
signals[signal] = ((lhs_s, rhs_s), (lhs_fn, rhs_fn), op)
|
||||
|
||||
values_1 = process(signals.copy(), values.copy())
|
||||
for k in sorted(values_1):
|
||||
self.logger.info(f"{k}: {values_1[k]}")
|
||||
yield values_1["a"]
|
||||
|
||||
yield process(signals.copy(), values | {"b": values_1["a"]})["a"]
|
||||
def process(
|
||||
signals: Signals,
|
||||
values: dict[str, int],
|
||||
) -> dict[str, int]:
|
||||
while signals:
|
||||
signal = next(s for s in signals if all(p in values for p in signals[s][0]))
|
||||
_, deps, command = signals[signal]
|
||||
values[signal] = command(deps[0](values), deps[1](values)) % 65536
|
||||
del signals[signal]
|
||||
|
||||
return values
|
||||
|
||||
|
||||
values_1 = process(signals.copy(), values.copy())
|
||||
logging.info("\n" + "\n".join(f"{k}: {values_1[k]}" for k in sorted(values_1)))
|
||||
answer_1 = values_1["a"]
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
values_2 = process(signals.copy(), values | {"b": values_1["a"]})
|
||||
answer_2 = values_2["a"]
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,13 +1,14 @@
|
||||
from typing import Any, Iterator
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
yield sum(
|
||||
answer_1 = sum(
|
||||
# left and right quotes (not in memory)
|
||||
2
|
||||
# each \\ adds one character in the literals (compared to memory)
|
||||
@@ -20,8 +21,9 @@ class Solver(BaseSolver):
|
||||
+ 3 * (line.count(R"\x") - line.count(R"\\x") + line.count(R"\\\x"))
|
||||
for line in lines
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
yield sum(
|
||||
answer_2 = sum(
|
||||
# needs to wrap in quotes (2 characters)
|
||||
2
|
||||
# needs to escape every \ with an extra \
|
||||
@@ -30,3 +32,4 @@ class Solver(BaseSolver):
|
||||
+ line.count('"')
|
||||
for line in lines
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,15 +1,11 @@
|
||||
import itertools
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator, cast
|
||||
from typing import cast
|
||||
|
||||
import parse # type: ignore
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
distances: dict[str, dict[str, int]] = defaultdict(dict)
|
||||
for line in lines:
|
||||
@@ -24,5 +20,8 @@ class Solver(BaseSolver):
|
||||
for route in map(tuple, itertools.permutations(distances))
|
||||
}
|
||||
|
||||
yield min(distance_of_routes.values())
|
||||
yield max(distance_of_routes.values())
|
||||
answer_1 = min(distance_of_routes.values())
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = max(distance_of_routes.values())
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,12 +1,7 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
blocks = input.split("\n\n")
|
||||
blocks = sys.stdin.read().split("\n\n")
|
||||
values = sorted(sum(map(int, block.split())) for block in blocks)
|
||||
|
||||
yield values[-1]
|
||||
yield sum(values[-3:])
|
||||
print(f"answer 1 is {values[-1]}")
|
||||
print(f"answer 2 is {sum(values[-3:])}")
|
||||
|
@@ -1,13 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
cycle = 1
|
||||
x = 1
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
|
||||
cycle, x = 1, 1
|
||||
values = {cycle: x}
|
||||
|
||||
for line in lines:
|
||||
@@ -26,18 +23,16 @@ class Solver(BaseSolver):
|
||||
values[cycle] = x
|
||||
|
||||
answer_1 = sum(c * values[c] for c in range(20, max(values.keys()) + 1, 40))
|
||||
yield answer_1
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
yield (
|
||||
"\n"
|
||||
+ "\n".join(
|
||||
"".join(
|
||||
"#"
|
||||
if j >= (v := values[1 + i * 40 + j]) - 1 and j <= v + 1
|
||||
else "."
|
||||
for j in range(40)
|
||||
)
|
||||
for i in range(6)
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
for i in range(6):
|
||||
for j in range(40):
|
||||
v = values[1 + i * 40 + j]
|
||||
|
||||
if j >= v - 1 and j <= v + 1:
|
||||
print("#", end="")
|
||||
else:
|
||||
print(".", end="")
|
||||
|
||||
print()
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import copy
|
||||
import sys
|
||||
from functools import reduce
|
||||
from typing import Any, Callable, Final, Iterator, Mapping, Sequence
|
||||
|
||||
from ..base import BaseSolver
|
||||
from typing import Callable, Final, Mapping, Sequence
|
||||
|
||||
|
||||
class Monkey:
|
||||
@@ -120,14 +119,13 @@ def monkey_business(inspects: dict[Monkey, int]) -> int:
|
||||
return sorted_levels[-2] * sorted_levels[-1]
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
monkeys = [parse_monkey(block.splitlines()) for block in input.split("\n\n")]
|
||||
monkeys = [parse_monkey(block.splitlines()) for block in sys.stdin.read().split("\n\n")]
|
||||
|
||||
# case 1: we simply divide the worry by 3 after applying the monkey worry operation
|
||||
yield monkey_business(
|
||||
answer_1 = monkey_business(
|
||||
run(copy.deepcopy(monkeys), 20, me_worry_fn=lambda w: w // 3)
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# case 2: to keep reasonable level values, we can use a modulo operation, we need to
|
||||
# use the product of all "divisible by" test so that the test remains valid
|
||||
@@ -138,10 +136,7 @@ class Solver(BaseSolver):
|
||||
# we use the product of all test value
|
||||
#
|
||||
total_test_value = reduce(lambda w, m: w * m.test_value, monkeys, 1)
|
||||
yield monkey_business(
|
||||
run(
|
||||
copy.deepcopy(monkeys),
|
||||
10_000,
|
||||
me_worry_fn=lambda w: w % total_test_value,
|
||||
)
|
||||
answer_2 = monkey_business(
|
||||
run(copy.deepcopy(monkeys), 10_000, me_worry_fn=lambda w: w % total_test_value)
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import heapq
|
||||
from typing import Any, Callable, Iterator, TypeVar
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
from typing import Callable, Iterator, TypeVar
|
||||
|
||||
Node = TypeVar("Node")
|
||||
|
||||
@@ -69,6 +68,30 @@ def make_path(parents: dict[Node, Node], start: Node, end: Node) -> list[Node] |
|
||||
return list(reversed(path))
|
||||
|
||||
|
||||
def print_path(path: list[tuple[int, int]], n_rows: int, n_cols: int) -> None:
|
||||
end = path[-1]
|
||||
|
||||
graph = [["." for _c in range(n_cols)] for _r in range(n_rows)]
|
||||
graph[end[0]][end[1]] = "E"
|
||||
|
||||
for i in range(0, len(path) - 1):
|
||||
cr, cc = path[i]
|
||||
nr, nc = path[i + 1]
|
||||
|
||||
if cr == nr and nc == cc - 1:
|
||||
graph[cr][cc] = "<"
|
||||
elif cr == nr and nc == cc + 1:
|
||||
graph[cr][cc] = ">"
|
||||
elif cr == nr - 1 and nc == cc:
|
||||
graph[cr][cc] = "v"
|
||||
elif cr == nr + 1 and nc == cc:
|
||||
graph[cr][cc] = "^"
|
||||
else:
|
||||
assert False, "{} -> {} infeasible".format(path[i], path[i + 1])
|
||||
|
||||
print("\n".join("".join(row) for row in graph))
|
||||
|
||||
|
||||
def neighbors(
|
||||
grid: list[list[int]], node: tuple[int, int], up: bool
|
||||
) -> Iterator[tuple[int, int]]:
|
||||
@@ -95,34 +118,7 @@ def neighbors(
|
||||
|
||||
# === main code ===
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def print_path(self, path: list[tuple[int, int]], n_rows: int, n_cols: int) -> None:
|
||||
end = path[-1]
|
||||
|
||||
graph = [["." for _c in range(n_cols)] for _r in range(n_rows)]
|
||||
graph[end[0]][end[1]] = "E"
|
||||
|
||||
for i in range(0, len(path) - 1):
|
||||
cr, cc = path[i]
|
||||
nr, nc = path[i + 1]
|
||||
|
||||
if cr == nr and nc == cc - 1:
|
||||
graph[cr][cc] = "<"
|
||||
elif cr == nr and nc == cc + 1:
|
||||
graph[cr][cc] = ">"
|
||||
elif cr == nr - 1 and nc == cc:
|
||||
graph[cr][cc] = "v"
|
||||
elif cr == nr + 1 and nc == cc:
|
||||
graph[cr][cc] = "^"
|
||||
else:
|
||||
assert False, "{} -> {} infeasible".format(path[i], path[i + 1])
|
||||
|
||||
for row in graph:
|
||||
self.logger.info("".join(row))
|
||||
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
grid = [[ord(cell) - ord("a") for cell in line] for line in lines]
|
||||
|
||||
@@ -149,20 +145,19 @@ class Solver(BaseSolver):
|
||||
grid[start[0]][start[1]] = 0
|
||||
grid[end[0]][end[1]] = ord("z") - ord("a")
|
||||
|
||||
|
||||
lengths_1, parents_1 = dijkstra(
|
||||
start=start,
|
||||
neighbors=lambda n: neighbors(grid, n, True),
|
||||
cost=lambda lhs, rhs: 1,
|
||||
start=start, neighbors=lambda n: neighbors(grid, n, True), cost=lambda lhs, rhs: 1
|
||||
)
|
||||
path_1 = make_path(parents_1, start, end)
|
||||
assert path_1 is not None
|
||||
|
||||
self.print_path(path_1, n_rows=len(grid), n_cols=len(grid[0]))
|
||||
yield lengths_1[end] - 1
|
||||
print_path(path_1, n_rows=len(grid), n_cols=len(grid[0]))
|
||||
|
||||
lengths_2, _ = dijkstra(
|
||||
start=end,
|
||||
neighbors=lambda n: neighbors(grid, n, False),
|
||||
cost=lambda lhs, rhs: 1,
|
||||
print(f"answer 1 is {lengths_1[end] - 1}")
|
||||
|
||||
lengths_2, parents_2 = dijkstra(
|
||||
start=end, neighbors=lambda n: neighbors(grid, n, False), cost=lambda lhs, rhs: 1
|
||||
)
|
||||
yield min(lengths_2.get(start, float("inf")) for start in start_s)
|
||||
answer_2 = min(lengths_2.get(start, float("inf")) for start in start_s)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,8 +1,11 @@
|
||||
import json
|
||||
import sys
|
||||
from functools import cmp_to_key
|
||||
from typing import Any, Iterator, TypeAlias, cast
|
||||
from typing import TypeAlias, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
blocks = sys.stdin.read().strip().split("\n\n")
|
||||
|
||||
pairs = [tuple(json.loads(p) for p in block.split("\n")) for block in blocks]
|
||||
|
||||
Packet: TypeAlias = list[int | list["Packet"]]
|
||||
|
||||
@@ -25,12 +28,8 @@ def compare(lhs: Packet, rhs: Packet) -> int:
|
||||
return len(rhs) - len(lhs)
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
blocks = input.split("\n\n")
|
||||
pairs = [tuple(json.loads(p) for p in block.split("\n")) for block in blocks]
|
||||
|
||||
yield sum(i + 1 for i, (lhs, rhs) in enumerate(pairs) if compare(lhs, rhs) > 0)
|
||||
answer_1 = sum(i + 1 for i, (lhs, rhs) in enumerate(pairs) if compare(lhs, rhs) > 0)
|
||||
print(f"answer_1 is {answer_1}")
|
||||
|
||||
dividers = [[[2]], [[6]]]
|
||||
|
||||
@@ -39,4 +38,4 @@ class Solver(BaseSolver):
|
||||
packets = list(reversed(sorted(packets, key=cmp_to_key(compare))))
|
||||
|
||||
d_index = [packets.index(d) + 1 for d in dividers]
|
||||
yield d_index[0] * d_index[1]
|
||||
print(f"answer 2 is {d_index[0] * d_index[1]}")
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import sys
|
||||
from enum import Enum, auto
|
||||
from typing import Any, Callable, Iterator, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
from typing import Callable, cast
|
||||
|
||||
|
||||
class Cell(Enum):
|
||||
@@ -13,6 +12,26 @@ class Cell(Enum):
|
||||
return {Cell.AIR: ".", Cell.ROCK: "#", Cell.SAND: "O"}[self]
|
||||
|
||||
|
||||
def print_blocks(blocks: dict[tuple[int, int], Cell]):
|
||||
"""
|
||||
Print the given set of blocks on a grid.
|
||||
|
||||
Args:
|
||||
blocks: Set of blocks to print.
|
||||
"""
|
||||
x_min, y_min, x_max, y_max = (
|
||||
min(x for x, _ in blocks),
|
||||
0,
|
||||
max(x for x, _ in blocks),
|
||||
max(y for _, y in blocks),
|
||||
)
|
||||
|
||||
for y in range(y_min, y_max + 1):
|
||||
print(
|
||||
"".join(str(blocks.get((x, y), Cell.AIR)) for x in range(x_min, x_max + 1))
|
||||
)
|
||||
|
||||
|
||||
def flow(
|
||||
blocks: dict[tuple[int, int], Cell],
|
||||
stop_fn: Callable[[int, int], bool],
|
||||
@@ -65,44 +84,19 @@ def flow(
|
||||
|
||||
# === inputs ===
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def print_blocks(self, blocks: dict[tuple[int, int], Cell]):
|
||||
"""
|
||||
Print the given set of blocks on a grid.
|
||||
|
||||
Args:
|
||||
blocks: Set of blocks to print.
|
||||
"""
|
||||
x_min, y_min, x_max, y_max = (
|
||||
min(x for x, _ in blocks),
|
||||
0,
|
||||
max(x for x, _ in blocks),
|
||||
max(y for _, y in blocks),
|
||||
)
|
||||
|
||||
for y in range(y_min, y_max + 1):
|
||||
self.logger.info(
|
||||
"".join(
|
||||
str(blocks.get((x, y), Cell.AIR)) for x in range(x_min, x_max + 1)
|
||||
)
|
||||
)
|
||||
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
paths: list[list[tuple[int, int]]] = []
|
||||
for line in lines:
|
||||
parts = line.split(" -> ")
|
||||
paths.append(
|
||||
[
|
||||
cast(
|
||||
tuple[int, int], tuple(int(c.strip()) for c in part.split(","))
|
||||
)
|
||||
cast(tuple[int, int], tuple(int(c.strip()) for c in part.split(",")))
|
||||
for part in parts
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
blocks: dict[tuple[int, int], Cell] = {}
|
||||
for path in paths:
|
||||
for start, end in zip(path[:-1], path[1:]):
|
||||
@@ -115,17 +109,24 @@ class Solver(BaseSolver):
|
||||
for y in range(y_start, y_end):
|
||||
blocks[x, y] = Cell.ROCK
|
||||
|
||||
self.print_blocks(blocks)
|
||||
print_blocks(blocks)
|
||||
print()
|
||||
|
||||
y_max = max(y for _, y in blocks)
|
||||
x_min, y_min, x_max, y_max = (
|
||||
min(x for x, _ in blocks),
|
||||
0,
|
||||
max(x for x, _ in blocks),
|
||||
max(y for _, y in blocks),
|
||||
)
|
||||
|
||||
# === part 1 ===
|
||||
|
||||
blocks_1 = flow(
|
||||
blocks.copy(), stop_fn=lambda x, y: y > y_max, fill_fn=lambda x, y: Cell.AIR
|
||||
)
|
||||
self.print_blocks(blocks_1)
|
||||
yield sum(v == Cell.SAND for v in blocks_1.values())
|
||||
print_blocks(blocks_1)
|
||||
print(f"answer 1 is {sum(v == Cell.SAND for v in blocks_1.values())}")
|
||||
print()
|
||||
|
||||
# === part 2 ===
|
||||
|
||||
@@ -135,5 +136,5 @@ class Solver(BaseSolver):
|
||||
fill_fn=lambda x, y: Cell.AIR if y < y_max + 2 else Cell.ROCK,
|
||||
)
|
||||
blocks_2[500, 0] = Cell.SAND
|
||||
self.print_blocks(blocks_2)
|
||||
yield sum(v == Cell.SAND for v in blocks_2.values())
|
||||
print_blocks(blocks_2)
|
||||
print(f"answer 2 is {sum(v == Cell.SAND for v in blocks_2.values())}")
|
||||
|
@@ -1,16 +1,12 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
import parse # type: ignore
|
||||
from numpy.typing import NDArray
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def part1(
|
||||
self, sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], row: int
|
||||
) -> int:
|
||||
def part1(sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], row: int) -> int:
|
||||
no_beacons_row_l: list[NDArray[np.floating[Any]]] = []
|
||||
|
||||
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
||||
@@ -20,16 +16,17 @@ class Solver(BaseSolver):
|
||||
no_beacons_row_l.append(sx + np.arange(0, d - abs(sy - row) + 1)) # type: ignore
|
||||
|
||||
beacons_at_row = set(bx for (bx, by) in sensor_to_beacon.values() if by == row)
|
||||
no_beacons_row = set(np.concatenate(no_beacons_row_l)).difference(
|
||||
beacons_at_row
|
||||
) # type: ignore
|
||||
no_beacons_row = set(np.concatenate(no_beacons_row_l)).difference(beacons_at_row) # type: ignore
|
||||
|
||||
return len(no_beacons_row)
|
||||
|
||||
|
||||
def part2_intervals(
|
||||
self, sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], xy_max: int
|
||||
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], xy_max: int
|
||||
) -> tuple[int, int, int]:
|
||||
for y in self.progress.wrap(range(xy_max + 1)):
|
||||
from tqdm import trange
|
||||
|
||||
for y in trange(xy_max + 1):
|
||||
its: list[tuple[int, int]] = []
|
||||
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
||||
d = abs(sx - bx) + abs(sy - by)
|
||||
@@ -49,8 +46,9 @@ class Solver(BaseSolver):
|
||||
|
||||
return (0, 0, 0)
|
||||
|
||||
|
||||
def part2_cplex(
|
||||
self, sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], xy_max: int
|
||||
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], xy_max: int
|
||||
) -> tuple[int, int, int]:
|
||||
from docplex.mp.model import Model
|
||||
|
||||
@@ -60,10 +58,7 @@ class Solver(BaseSolver):
|
||||
|
||||
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
||||
d = abs(sx - bx) + abs(sy - by)
|
||||
m.add_constraint(
|
||||
m.abs(x - sx) + m.abs(y - sy) >= d + 1, # type: ignore
|
||||
ctname=f"ct_{sx}_{sy}",
|
||||
)
|
||||
m.add_constraint(m.abs(x - sx) + m.abs(y - sy) >= d + 1, ctname=f"ct_{sx}_{sy}") # type: ignore
|
||||
|
||||
m.set_objective("min", x + y)
|
||||
|
||||
@@ -74,8 +69,8 @@ class Solver(BaseSolver):
|
||||
vy = int(s.get_value(y))
|
||||
return vx, vy, 4_000_000 * vx + vy
|
||||
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]] = {}
|
||||
|
||||
@@ -88,9 +83,8 @@ class Solver(BaseSolver):
|
||||
xy_max = 4_000_000 if max(sensor_to_beacon) > (1_000, 0) else 20
|
||||
row = 2_000_000 if max(sensor_to_beacon) > (1_000, 0) else 10
|
||||
|
||||
yield self.part1(sensor_to_beacon, row)
|
||||
print(f"answer 1 is {part1(sensor_to_beacon, row)}")
|
||||
|
||||
# x, y, a2 = part2_cplex(sensor_to_beacon, xy_max)
|
||||
x, y, a2 = self.part2_intervals(sensor_to_beacon, xy_max)
|
||||
self.logger.info("answer 2 is {at} (x={x}, y={y})")
|
||||
yield a2
|
||||
x, y, a2 = part2_intervals(sensor_to_beacon, xy_max)
|
||||
print(f"answer 2 is {a2} (x={x}, y={y})")
|
||||
|
@@ -5,12 +5,10 @@ import itertools
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, FrozenSet, Iterator, NamedTuple
|
||||
from typing import FrozenSet, NamedTuple
|
||||
|
||||
from tqdm import tqdm
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Pipe(NamedTuple):
|
||||
name: str
|
||||
|
@@ -1,10 +1,8 @@
|
||||
import sys
|
||||
from typing import Any, Iterator, Sequence, TypeVar
|
||||
from typing import Sequence, TypeVar
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
xyz = np.asarray(
|
||||
[
|
||||
tuple(int(x) for x in row.split(",")) # type: ignore
|
||||
|
@@ -1,12 +1,10 @@
|
||||
import sys
|
||||
from typing import Any, Iterator, Literal
|
||||
from typing import Any, Literal
|
||||
|
||||
import numpy as np
|
||||
import parse # pyright: ignore[reportMissingTypeStubs]
|
||||
from numpy.typing import NDArray
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
Reagent = Literal["ore", "clay", "obsidian", "geode"]
|
||||
REAGENTS: tuple[Reagent, ...] = (
|
||||
"ore",
|
||||
|
@@ -1,6 +1,4 @@
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
|
||||
|
||||
def score_1(ux: int, vx: int) -> int:
|
||||
@@ -35,9 +33,7 @@ def score_2(ux: int, vx: int) -> int:
|
||||
return (ux + vx - 1) % 3 + 1 + vx * 3
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.readlines()
|
||||
|
||||
# the solution relies on replacing rock / paper / scissor by values 0 / 1 / 2 and using
|
||||
# modulo-3 arithmetic
|
||||
@@ -51,7 +47,7 @@ class Solver(BaseSolver):
|
||||
values = [(ord(row[0]) - ord("A"), ord(row[2]) - ord("X")) for row in lines]
|
||||
|
||||
# part 1 - 13526
|
||||
yield sum(score_1(*v) for v in values)
|
||||
print(f"answer 1 is {sum(score_1(*v) for v in values)}")
|
||||
|
||||
# part 2 - 14204
|
||||
yield sum(score_2(*v) for v in values)
|
||||
print(f"answer 2 is {sum(score_2(*v) for v in values)}")
|
||||
|
@@ -1,9 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Number:
|
||||
|
@@ -1,8 +1,6 @@
|
||||
import operator
|
||||
import sys
|
||||
from typing import Any, Callable, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
from typing import Callable
|
||||
|
||||
|
||||
def compute(monkeys: dict[str, int | tuple[str, str, str]], monkey: str) -> int:
|
||||
|
@@ -1,11 +1,9 @@
|
||||
import re
|
||||
import sys
|
||||
from typing import Any, Callable, Iterator
|
||||
from typing import Callable
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
VOID, EMPTY, WALL = 0, 1, 2
|
||||
TILE_FROM_CHAR = {" ": VOID, ".": EMPTY, "#": WALL}
|
||||
|
||||
|
@@ -1,9 +1,6 @@
|
||||
import itertools
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
Directions = list[
|
||||
tuple[
|
||||
|
@@ -2,9 +2,6 @@ import heapq
|
||||
import math
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
|
@@ -1,7 +1,4 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
|
@@ -1,28 +1,23 @@
|
||||
import string
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
lines = [line.strip() for line in sys.stdin.readlines()]
|
||||
|
||||
# extract content of each part
|
||||
parts = [
|
||||
(set(line[: len(line) // 2]), set(line[len(line) // 2 :])) for line in lines
|
||||
]
|
||||
parts = [(set(line[: len(line) // 2]), set(line[len(line) // 2 :])) for line in lines]
|
||||
|
||||
# priorities
|
||||
priorities = {c: i + 1 for i, c in enumerate(string.ascii_letters)}
|
||||
|
||||
# part 1
|
||||
yield sum(priorities[c] for p1, p2 in parts for c in p1.intersection(p2))
|
||||
part1 = sum(priorities[c] for p1, p2 in parts for c in p1.intersection(p2))
|
||||
print(f"answer 1 is {part1}")
|
||||
|
||||
# part 2
|
||||
n_per_group = 3
|
||||
yield sum(
|
||||
part2 = sum(
|
||||
priorities[c]
|
||||
for i in range(0, len(lines), n_per_group)
|
||||
for c in set(lines[i]).intersection(*lines[i + 1 : i + n_per_group])
|
||||
)
|
||||
print(f"answer 2 is {part2}")
|
||||
|
@@ -1,6 +1,6 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = [line.strip() for line in sys.stdin.readlines()]
|
||||
|
||||
|
||||
def make_range(value: str) -> set[int]:
|
||||
@@ -8,13 +8,10 @@ def make_range(value: str) -> set[int]:
|
||||
return set(range(int(parts[0]), int(parts[1]) + 1))
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
sections = [tuple(make_range(part) for part in line.split(",")) for line in lines]
|
||||
|
||||
sections = [
|
||||
tuple(make_range(part) for part in line.split(",")) for line in lines
|
||||
]
|
||||
answer_1 = sum(s1.issubset(s2) or s2.issubset(s1) for s1, s2 in sections)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
yield sum(s1.issubset(s2) or s2.issubset(s1) for s1, s2 in sections)
|
||||
yield sum(bool(s1.intersection(s2)) for s1, s2 in sections)
|
||||
answer_2 = sum(bool(s1.intersection(s2)) for s1, s2 in sections)
|
||||
print(f"answer 1 is {answer_2}")
|
||||
|
@@ -1,12 +1,7 @@
|
||||
import copy
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
blocks_s, moves_s = (part.splitlines() for part in input.split("\n\n"))
|
||||
blocks_s, moves_s = (part.splitlines() for part in sys.stdin.read().split("\n\n"))
|
||||
|
||||
blocks: dict[str, list[str]] = {stack: [] for stack in blocks_s[-1].split()}
|
||||
|
||||
@@ -39,5 +34,8 @@ class Solver(BaseSolver):
|
||||
blocks_2[to_].extend(blocks_2[from_][-count:])
|
||||
del blocks_2[from_][-count:]
|
||||
|
||||
yield "".join(s[-1] for s in blocks_1.values())
|
||||
yield "".join(s[-1] for s in blocks_2.values())
|
||||
answer_1 = "".join(s[-1] for s in blocks_1.values())
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = "".join(s[-1] for s in blocks_2.values())
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,6 +1,4 @@
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
|
||||
|
||||
def index_of_first_n_differents(data: str, n: int) -> int:
|
||||
@@ -10,7 +8,8 @@ def index_of_first_n_differents(data: str, n: int) -> int:
|
||||
return -1
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
yield index_of_first_n_differents(input, 4)
|
||||
yield index_of_first_n_differents(input, 14)
|
||||
data = sys.stdin.read().strip()
|
||||
|
||||
|
||||
print(f"answer 1 is {index_of_first_n_differents(data, 4)}")
|
||||
print(f"answer 2 is {index_of_first_n_differents(data, 14)}")
|
||||
|
@@ -1,12 +1,7 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
# we are going to use Path to create path and go up/down in the file tree since it
|
||||
# implements everything we need
|
||||
@@ -58,6 +53,7 @@ class Solver(BaseSolver):
|
||||
trees[cur_path].append(path)
|
||||
sizes[path] = size
|
||||
|
||||
|
||||
def compute_size(path: Path) -> int:
|
||||
size = sizes[path]
|
||||
|
||||
@@ -66,10 +62,12 @@ class Solver(BaseSolver):
|
||||
|
||||
return sum(compute_size(sub) for sub in trees[path])
|
||||
|
||||
|
||||
acc_sizes = {path: compute_size(path) for path in trees}
|
||||
|
||||
# part 1
|
||||
yield sum(size for size in acc_sizes.values() if size <= 100_000)
|
||||
answer_1 = sum(size for size in acc_sizes.values() if size <= 100_000)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
total_space = 70_000_000
|
||||
@@ -78,4 +76,5 @@ class Solver(BaseSolver):
|
||||
|
||||
to_free_space = update_space - free_space
|
||||
|
||||
yield min(size for size in acc_sizes.values() if size >= to_free_space)
|
||||
answer_2 = min(size for size in acc_sizes.values() if size >= to_free_space)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,14 +1,9 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
from numpy.typing import NDArray
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
trees = np.array([[int(x) for x in row] for row in lines])
|
||||
|
||||
@@ -27,7 +22,9 @@ class Solver(BaseSolver):
|
||||
for i in range(1, trees.shape[0] - 1)
|
||||
]
|
||||
|
||||
yield (highest_trees.min(axis=2) < trees).sum()
|
||||
answer_1 = (highest_trees.min(axis=2) < trees).sum()
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
|
||||
def viewing_distance(row_of_trees: NDArray[np.int_], value: int) -> int:
|
||||
w = np.where(row_of_trees >= value)[0]
|
||||
@@ -37,6 +34,7 @@ class Solver(BaseSolver):
|
||||
|
||||
return w[0] + 1
|
||||
|
||||
|
||||
# answer 2
|
||||
v_distances = np.zeros(trees.shape + (4,), dtype=int)
|
||||
v_distances[1:-1, 1:-1, :] = [
|
||||
@@ -51,4 +49,5 @@ class Solver(BaseSolver):
|
||||
]
|
||||
for i in range(1, trees.shape[0] - 1)
|
||||
]
|
||||
yield np.prod(v_distances, axis=2).max()
|
||||
answer_2 = np.prod(v_distances, axis=2).max()
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import itertools as it
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def move(head: tuple[int, int], command: str) -> tuple[int, int]:
|
||||
h_col, h_row = head
|
||||
@@ -46,14 +43,17 @@ def run(commands: list[str], n_blocks: int) -> list[tuple[int, int]]:
|
||||
return visited
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
# flatten the commands
|
||||
commands = list(
|
||||
it.chain(*(p[0] * int(p[1]) for line in lines if (p := line.split())))
|
||||
)
|
||||
commands: list[str] = []
|
||||
for line in lines:
|
||||
d, c = line.split()
|
||||
commands.extend(d * int(c))
|
||||
|
||||
yield len(set(run(commands, n_blocks=2)))
|
||||
yield len(set(run(commands, n_blocks=10)))
|
||||
|
||||
visited_1 = run(commands, n_blocks=2)
|
||||
print(f"answer 1 is {len(set(visited_1))}")
|
||||
|
||||
visited_2 = run(commands, n_blocks=10)
|
||||
print(f"answer 2 is {len(set(visited_2))}")
|
||||
|
@@ -1,9 +1,27 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
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(lines: list[str], lookups: dict[str, int]) -> list[int]:
|
||||
def find_values(lookups: dict[str, int]) -> list[int]:
|
||||
values: list[int] = []
|
||||
|
||||
for line in filter(bool, lines):
|
||||
@@ -23,27 +41,5 @@ def find_values(lines: list[str], lookups: dict[str, int]) -> list[int]:
|
||||
return values
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
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))
|
||||
print(f"answer 1 is {sum(find_values(lookups_1))}")
|
||||
print(f"answer 2 is {sum(find_values(lookups_2))}")
|
||||
|
@@ -1,14 +1,13 @@
|
||||
from typing import Any, Iterator, Literal, cast
|
||||
import os
|
||||
import sys
|
||||
from typing import Literal, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
|
||||
Symbol = Literal["|", "-", "L", "J", "7", "F", ".", "S"]
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines: list[list[Symbol]] = [
|
||||
[cast(Symbol, symbol) for symbol in line] for line in input.splitlines()
|
||||
[cast(Symbol, symbol) for symbol in line] for line in sys.stdin.read().splitlines()
|
||||
]
|
||||
|
||||
# find starting point
|
||||
@@ -52,7 +51,8 @@ class Solver(BaseSolver):
|
||||
|
||||
loop.append((i, j))
|
||||
|
||||
yield len(loop) // 2
|
||||
answer_1 = len(loop) // 2
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
|
||||
@@ -83,18 +83,18 @@ class Solver(BaseSolver):
|
||||
if (i, j) in loop_s and lines[i][j] in "|LJ":
|
||||
cnt += 1
|
||||
|
||||
if self.verbose:
|
||||
if VERBOSE:
|
||||
for i in range(len(lines)):
|
||||
s = ""
|
||||
for j in range(len(lines[0])):
|
||||
if (i, j) == (si, sj):
|
||||
s += "\033[91mS\033[0m"
|
||||
print("\033[91mS\033[0m", end="")
|
||||
elif (i, j) in loop:
|
||||
s += lines[i][j]
|
||||
print(lines[i][j], end="")
|
||||
elif (i, j) in inside:
|
||||
s += "\033[92mI\033[0m"
|
||||
print("\033[92mI\033[0m", end="")
|
||||
else:
|
||||
s += "."
|
||||
self.logger.info(s)
|
||||
print(".", end="")
|
||||
print()
|
||||
|
||||
yield len(inside)
|
||||
answer_2 = len(inside)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,13 +1,8 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
data = np.array([[c == "#" for c in line] for line in lines])
|
||||
|
||||
@@ -16,6 +11,7 @@ class Solver(BaseSolver):
|
||||
|
||||
galaxies_y, galaxies_x = np.where(data) # type: ignore
|
||||
|
||||
|
||||
def compute_total_distance(expansion: int) -> int:
|
||||
distances: list[int] = []
|
||||
for g1 in range(len(galaxies_y)):
|
||||
@@ -35,8 +31,11 @@ class Solver(BaseSolver):
|
||||
distances.append(dx + dy)
|
||||
return sum(distances)
|
||||
|
||||
|
||||
# part 1
|
||||
yield compute_total_distance(2)
|
||||
answer_1 = compute_total_distance(2)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
yield compute_total_distance(1000000)
|
||||
answer_2 = compute_total_distance(1000000)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import os
|
||||
import sys
|
||||
from functools import lru_cache
|
||||
from typing import Any, Iterable, Iterator
|
||||
from typing import Iterable
|
||||
|
||||
from ..base import BaseSolver
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
|
||||
|
||||
@lru_cache
|
||||
@@ -75,29 +77,31 @@ def compute_possible_arrangements(
|
||||
)
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def compute_all_possible_arrangements(
|
||||
self, lines: Iterable[str], repeat: int
|
||||
) -> int:
|
||||
def compute_all_possible_arrangements(lines: Iterable[str], repeat: int) -> int:
|
||||
count = 0
|
||||
|
||||
for i_line, line in enumerate(lines):
|
||||
self.logger.info(f"processing line {i_line}: {line}...")
|
||||
if VERBOSE:
|
||||
from tqdm import tqdm
|
||||
|
||||
lines = tqdm(lines)
|
||||
|
||||
for line in lines:
|
||||
parts = line.split(" ")
|
||||
count += compute_possible_arrangements(
|
||||
tuple(
|
||||
filter(len, "?".join(parts[0] for _ in range(repeat)).split("."))
|
||||
),
|
||||
tuple(filter(len, "?".join(parts[0] for _ in range(repeat)).split("."))),
|
||||
tuple(int(c) for c in parts[1].split(",")) * repeat,
|
||||
)
|
||||
|
||||
return count
|
||||
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
|
||||
# part 1
|
||||
yield self.compute_all_possible_arrangements(lines, 1)
|
||||
answer_1 = compute_all_possible_arrangements(lines, 1)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
yield self.compute_all_possible_arrangements(lines, 5)
|
||||
answer_2 = compute_all_possible_arrangements(lines, 5)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,6 +1,5 @@
|
||||
from typing import Any, Callable, Iterator, Literal
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
from typing import Callable, Literal
|
||||
|
||||
|
||||
def split(block: list[str], axis: Literal[0, 1], count: int) -> int:
|
||||
@@ -26,18 +25,19 @@ def split(block: list[str], axis: Literal[0, 1], count: int) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
blocks = [block.splitlines() for block in input.split("\n\n")]
|
||||
blocks = [block.splitlines() for block in sys.stdin.read().split("\n\n")]
|
||||
|
||||
|
||||
# part 1
|
||||
yield sum(
|
||||
answer_1 = sum(
|
||||
split(block, axis=1, count=0) + 100 * split(block, axis=0, count=0)
|
||||
for block in blocks
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
yield sum(
|
||||
answer_2 = sum(
|
||||
split(block, axis=1, count=1) + 100 * split(block, axis=0, count=1)
|
||||
for block in blocks
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,9 +1,10 @@
|
||||
from typing import Any, Iterator, TypeAlias
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
from typing import TypeAlias
|
||||
|
||||
RockGrid: TypeAlias = list[list[str]]
|
||||
|
||||
rocks0 = [list(line) for line in sys.stdin.read().splitlines()]
|
||||
|
||||
|
||||
def slide_rocks_top(rocks: RockGrid) -> RockGrid:
|
||||
top = [0 if c == "." else 1 for c in rocks[0]]
|
||||
@@ -33,17 +34,13 @@ def cycle(rocks: RockGrid) -> RockGrid:
|
||||
return rocks
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
rocks0 = [list(line) for line in input.splitlines()]
|
||||
|
||||
rocks = slide_rocks_top([[c for c in r] for r in rocks0])
|
||||
|
||||
# part 1
|
||||
yield sum(
|
||||
(len(rocks) - i) * sum(1 for c in row if c == "O")
|
||||
for i, row in enumerate(rocks)
|
||||
answer_1 = sum(
|
||||
(len(rocks) - i) * sum(1 for c in row if c == "O") for i, row in enumerate(rocks)
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
rocks = rocks0
|
||||
@@ -64,7 +61,8 @@ class Solver(BaseSolver):
|
||||
|
||||
ci = cycle_start + (N - cycle_start) % cycle_length - 1
|
||||
|
||||
yield sum(
|
||||
answer_2 = sum(
|
||||
(len(rocks) - i) * sum(1 for c in row if c == "O")
|
||||
for i, row in enumerate(cycles[ci])
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,19 +1,16 @@
|
||||
import sys
|
||||
from functools import reduce
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
steps = sys.stdin.read().strip().split(",")
|
||||
|
||||
|
||||
def _hash(s: str) -> int:
|
||||
return reduce(lambda v, u: ((v + ord(u)) * 17) % 256, s, 0)
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
steps = input.split(",")
|
||||
|
||||
# part 1
|
||||
yield sum(map(_hash, steps))
|
||||
answer_1 = sum(map(_hash, steps))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
boxes: list[dict[str, int]] = [{} for _ in range(256)]
|
||||
@@ -26,8 +23,9 @@ class Solver(BaseSolver):
|
||||
label = step[:-1]
|
||||
boxes[_hash(label)].pop(label, None)
|
||||
|
||||
yield sum(
|
||||
answer_2 = sum(
|
||||
i_box * i_lens * length
|
||||
for i_box, box in enumerate(boxes, start=1)
|
||||
for i_lens, length in enumerate(box.values(), start=1)
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,6 +1,8 @@
|
||||
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||
import os
|
||||
import sys
|
||||
from typing import Literal, TypeAlias, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
|
||||
CellType: TypeAlias = Literal[".", "|", "-", "\\", "/"]
|
||||
Direction: TypeAlias = Literal["R", "L", "U", "D"]
|
||||
@@ -76,20 +78,19 @@ def propagate(
|
||||
return beams
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
layout: list[list[CellType]] = [
|
||||
[cast(CellType, col) for col in row] for row in input.splitlines()
|
||||
[cast(CellType, col) for col in row] for row in sys.stdin.read().splitlines()
|
||||
]
|
||||
|
||||
|
||||
beams = propagate(layout, (0, 0), "R")
|
||||
|
||||
if self.verbose:
|
||||
for row in beams:
|
||||
self.logger.info("".join("#" if col else "." for col in row))
|
||||
if VERBOSE:
|
||||
print("\n".join(["".join("#" if col else "." for col in row) for row in beams]))
|
||||
|
||||
# part 1
|
||||
yield sum(sum(map(bool, row)) for row in beams)
|
||||
answer_1 = sum(sum(map(bool, row)) for row in beams)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
n_rows, n_cols = len(layout), len(layout[0])
|
||||
@@ -102,7 +103,8 @@ class Solver(BaseSolver):
|
||||
cases.append(((0, col), "D"))
|
||||
cases.append(((n_rows - 1, col), "U"))
|
||||
|
||||
yield max(
|
||||
answer_2 = max(
|
||||
sum(sum(map(bool, row)) for row in propagate(layout, start, direction))
|
||||
for start, direction in cases
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,11 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import heapq
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Iterator, Literal, TypeAlias
|
||||
from typing import Literal, TypeAlias
|
||||
|
||||
from ..base import BaseSolver
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
|
||||
Direction: TypeAlias = Literal[">", "<", "^", "v"]
|
||||
|
||||
@@ -30,9 +32,7 @@ MAPPINGS: dict[Direction, tuple[int, int, Direction]] = {
|
||||
}
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def print_shortest_path(
|
||||
self,
|
||||
grid: list[list[int]],
|
||||
target: tuple[int, int],
|
||||
per_cell: dict[tuple[int, int], list[tuple[Label, int]]],
|
||||
@@ -66,18 +66,16 @@ class Solver(BaseSolver):
|
||||
if (r, c) != (prev_label.row, prev_label.col):
|
||||
p_grid[r][c] = f"\033[93m{grid[r][c]}\033[0m"
|
||||
|
||||
p_grid[label.row][label.col] = (
|
||||
f"\033[91m{grid[label.row][label.col]}\033[0m"
|
||||
)
|
||||
p_grid[label.row][label.col] = f"\033[91m{grid[label.row][label.col]}\033[0m"
|
||||
|
||||
prev_label = label
|
||||
|
||||
p_grid[0][0] = f"\033[92m{grid[0][0]}\033[0m"
|
||||
|
||||
for row in p_grid:
|
||||
self.logger.info("".join(row))
|
||||
print("\n".join("".join(row) for row in p_grid))
|
||||
|
||||
def shortest_many_paths(self, grid: list[list[int]]) -> dict[tuple[int, int], int]:
|
||||
|
||||
def shortest_many_paths(grid: list[list[int]]) -> dict[tuple[int, int], int]:
|
||||
n_rows, n_cols = len(grid), len(grid[0])
|
||||
|
||||
visited: dict[tuple[int, int], tuple[Label, int]] = {}
|
||||
@@ -127,8 +125,8 @@ class Solver(BaseSolver):
|
||||
|
||||
return {(r, c): visited[r, c][1] for r in range(n_rows) for c in range(n_cols)}
|
||||
|
||||
|
||||
def shortest_path(
|
||||
self,
|
||||
grid: list[list[int]],
|
||||
min_straight: int,
|
||||
max_straight: int,
|
||||
@@ -217,17 +215,19 @@ class Solver(BaseSolver):
|
||||
),
|
||||
)
|
||||
|
||||
if self.verbose:
|
||||
self.print_shortest_path(grid, target, per_cell)
|
||||
if VERBOSE:
|
||||
print_shortest_path(grid, target, per_cell)
|
||||
|
||||
return per_cell[target][0][1]
|
||||
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
data = [[int(c) for c in r] for r in input.splitlines()]
|
||||
estimates = self.shortest_many_paths(data)
|
||||
|
||||
data = [[int(c) for c in r] for r in sys.stdin.read().splitlines()]
|
||||
estimates = shortest_many_paths(data)
|
||||
|
||||
# part 1
|
||||
yield self.shortest_path(data, 1, 3, lower_bounds=estimates)
|
||||
answer_1 = shortest_path(data, 1, 3, lower_bounds=estimates)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
yield self.shortest_path(data, 4, 10, lower_bounds=estimates)
|
||||
answer_2 = shortest_path(data, 4, 10, lower_bounds=estimates)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,6 +1,5 @@
|
||||
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
from typing import Literal, TypeAlias, cast
|
||||
|
||||
Direction: TypeAlias = Literal["R", "L", "U", "D"]
|
||||
|
||||
@@ -34,19 +33,17 @@ def polygon(values: list[tuple[Direction, int]]) -> tuple[list[tuple[int, int]],
|
||||
return corners, perimeter
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
|
||||
# part 1
|
||||
yield area(
|
||||
*polygon(
|
||||
[(cast(Direction, (p := line.split())[0]), int(p[1])) for line in lines]
|
||||
)
|
||||
answer_1 = area(
|
||||
*polygon([(cast(Direction, (p := line.split())[0]), int(p[1])) for line in lines])
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
yield area(
|
||||
answer_2 = area(
|
||||
*polygon(
|
||||
[
|
||||
(DIRECTIONS[int((h := line.split()[-1])[-2])], int(h[2:-2], 16))
|
||||
@@ -54,3 +51,4 @@ class Solver(BaseSolver):
|
||||
]
|
||||
)
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,8 +1,13 @@
|
||||
import logging
|
||||
import operator
|
||||
import os
|
||||
import sys
|
||||
from math import prod
|
||||
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||
from typing import Literal, TypeAlias, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
|
||||
Category: TypeAlias = Literal["x", "m", "a", "s"]
|
||||
Part: TypeAlias = dict[Category, int]
|
||||
@@ -17,8 +22,7 @@ Check: TypeAlias = tuple[Category, Literal["<", ">"], int] | None
|
||||
Workflow: TypeAlias = list[tuple[Check, str]]
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def accept(self, workflows: dict[str, Workflow], part: Part) -> bool:
|
||||
def accept(workflows: dict[str, Workflow], part: Part) -> bool:
|
||||
workflow = "in"
|
||||
decision: bool | None = None
|
||||
|
||||
@@ -38,7 +42,8 @@ class Solver(BaseSolver):
|
||||
|
||||
return decision
|
||||
|
||||
def propagate(self, workflows: dict[str, Workflow], start: PartWithBounds) -> int:
|
||||
|
||||
def propagate(workflows: dict[str, Workflow], start: PartWithBounds) -> int:
|
||||
def _fmt(meta: PartWithBounds) -> str:
|
||||
return "{" + ", ".join(f"{k}={v}" for k, v in meta.items()) + "}"
|
||||
|
||||
@@ -47,13 +52,13 @@ class Solver(BaseSolver):
|
||||
) -> int:
|
||||
count = 0
|
||||
if target in workflows:
|
||||
self.logger.info(f" transfer to {target}")
|
||||
logging.info(f" transfer to {target}")
|
||||
queue.append((meta, target))
|
||||
elif target == "A":
|
||||
count = prod((high - low + 1) for low, high in meta.values())
|
||||
self.logger.info(f" accepted ({count})")
|
||||
logging.info(f" accepted ({count})")
|
||||
else:
|
||||
self.logger.info(" rejected")
|
||||
logging.info(" rejected")
|
||||
return count
|
||||
|
||||
accepted = 0
|
||||
@@ -64,26 +69,24 @@ class Solver(BaseSolver):
|
||||
while queue:
|
||||
n_iterations += 1
|
||||
meta, workflow = queue.pop()
|
||||
self.logger.info(f"{workflow}: {_fmt(meta)}")
|
||||
logging.info(f"{workflow}: {_fmt(meta)}")
|
||||
for check, target in workflows[workflow]:
|
||||
if check is None:
|
||||
self.logger.info(" end-of-workflow")
|
||||
logging.info(" end-of-workflow")
|
||||
accepted += transfer_or_accept(target, meta, queue)
|
||||
continue
|
||||
|
||||
category, sense, value = check
|
||||
bounds, op = meta[category], OPERATORS[sense]
|
||||
|
||||
self.logger.info(
|
||||
f" checking {_fmt(meta)} against {category} {sense} {value}"
|
||||
)
|
||||
logging.info(f" checking {_fmt(meta)} against {category} {sense} {value}")
|
||||
|
||||
if not op(bounds[0], value) and not op(bounds[1], value):
|
||||
self.logger.info(" reject, always false")
|
||||
logging.info(" reject, always false")
|
||||
continue
|
||||
|
||||
if op(meta[category][0], value) and op(meta[category][1], value):
|
||||
self.logger.info(" accept, always true")
|
||||
logging.info(" accept, always true")
|
||||
accepted += transfer_or_accept(target, meta, queue)
|
||||
break
|
||||
|
||||
@@ -93,15 +96,15 @@ class Solver(BaseSolver):
|
||||
meta[category], meta2[category] = (value, high), (low, value - 1)
|
||||
else:
|
||||
meta[category], meta2[category] = (low, value), (value + 1, high)
|
||||
self.logger.info(f" split {_fmt(meta2)} ({target}), {_fmt(meta)}")
|
||||
logging.info(f" split {_fmt(meta2)} ({target}), {_fmt(meta)}")
|
||||
|
||||
accepted += transfer_or_accept(target, meta2, queue)
|
||||
|
||||
self.logger.info(f"run took {n_iterations} iterations")
|
||||
logging.info(f"run took {n_iterations} iterations")
|
||||
return accepted
|
||||
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
workflows_s, parts_s = input.split("\n\n")
|
||||
|
||||
workflows_s, parts_s = sys.stdin.read().strip().split("\n\n")
|
||||
|
||||
workflows: dict[str, Workflow] = {}
|
||||
for workflow_s in workflows_s.split("\n"):
|
||||
@@ -126,9 +129,12 @@ class Solver(BaseSolver):
|
||||
{cast(Category, s[0]): int(s[2:]) for s in part_s[1:-1].split(",")}
|
||||
for part_s in parts_s.split("\n")
|
||||
]
|
||||
yield sum(sum(part.values()) for part in parts if self.accept(workflows, part))
|
||||
answer_1 = sum(sum(part.values()) for part in parts if accept(workflows, part))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
|
||||
# part 2
|
||||
yield self.propagate(
|
||||
answer_2 = propagate(
|
||||
workflows, {cast(Category, c): (1, 4000) for c in ["x", "m", "a", "s"]}
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,16 +1,13 @@
|
||||
import math
|
||||
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
from typing import Literal, TypeAlias, cast
|
||||
|
||||
CubeType: TypeAlias = Literal["red", "blue", "green"]
|
||||
|
||||
MAX_CUBES: dict[CubeType, int] = {"red": 12, "green": 13, "blue": 14}
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
# 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(":")
|
||||
@@ -24,7 +21,8 @@ class Solver(BaseSolver):
|
||||
for cube_set_s in sets_part.strip().split(";")
|
||||
]
|
||||
|
||||
yield sum(
|
||||
# part 1
|
||||
answer_1 = sum(
|
||||
id
|
||||
for id, set_of_cubes in games.items()
|
||||
if all(
|
||||
@@ -33,11 +31,13 @@ class Solver(BaseSolver):
|
||||
for cube, n_cubes in cube_set.items()
|
||||
)
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
yield sum(
|
||||
# part 2
|
||||
answer_2 = sum(
|
||||
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()
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,42 +1,55 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from math import lcm
|
||||
from typing import Any, Iterator, Literal, TypeAlias
|
||||
from typing import Literal, TypeAlias
|
||||
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
ModuleType: TypeAlias = Literal["broadcaster", "conjunction", "flip-flop"]
|
||||
PulseType: TypeAlias = Literal["high", "low"]
|
||||
|
||||
modules: dict[str, tuple[ModuleType, list[str]]] = {}
|
||||
|
||||
class Solver(BaseSolver):
|
||||
_modules: dict[str, tuple[ModuleType, list[str]]]
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
def _process(
|
||||
self,
|
||||
for line in lines:
|
||||
name, outputs_s = line.split(" -> ")
|
||||
outputs = outputs_s.split(", ")
|
||||
if name == "broadcaster":
|
||||
modules["broadcaster"] = ("broadcaster", outputs)
|
||||
else:
|
||||
modules[name[1:]] = (
|
||||
"conjunction" if name.startswith("&") else "flip-flop",
|
||||
outputs,
|
||||
)
|
||||
|
||||
|
||||
def process(
|
||||
start: tuple[str, str, PulseType],
|
||||
flip_flop_states: dict[str, Literal["on", "off"]],
|
||||
conjunction_states: dict[str, dict[str, PulseType]],
|
||||
) -> tuple[dict[PulseType, int], dict[str, dict[PulseType, int]]]:
|
||||
pulses: list[tuple[str, str, PulseType]] = [start]
|
||||
counts: dict[PulseType, int] = {"low": 0, "high": 0}
|
||||
inputs: dict[str, dict[PulseType, int]] = defaultdict(
|
||||
lambda: {"low": 0, "high": 0}
|
||||
)
|
||||
inputs: dict[str, dict[PulseType, int]] = defaultdict(lambda: {"low": 0, "high": 0})
|
||||
|
||||
self.logger.info("starting process... ")
|
||||
logging.info("starting process... ")
|
||||
|
||||
while pulses:
|
||||
input, name, pulse = pulses.pop(0)
|
||||
self.logger.info(f"{input} -{pulse}-> {name}")
|
||||
logging.info(f"{input} -{pulse}-> {name}")
|
||||
counts[pulse] += 1
|
||||
|
||||
inputs[name][pulse] += 1
|
||||
|
||||
if name not in self._modules:
|
||||
if name not in modules:
|
||||
continue
|
||||
|
||||
type, outputs = self._modules[name]
|
||||
type, outputs = modules[name]
|
||||
|
||||
if type == "broadcaster":
|
||||
...
|
||||
@@ -64,27 +77,11 @@ class Solver(BaseSolver):
|
||||
|
||||
return counts, inputs
|
||||
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
self._modules = {}
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
for line in lines:
|
||||
name, outputs_s = line.split(" -> ")
|
||||
outputs = outputs_s.split(", ")
|
||||
if name == "broadcaster":
|
||||
self._modules["broadcaster"] = ("broadcaster", outputs)
|
||||
else:
|
||||
self._modules[name[1:]] = (
|
||||
"conjunction" if name.startswith("&") else "flip-flop",
|
||||
outputs,
|
||||
)
|
||||
|
||||
if self.outputs:
|
||||
with open("./day20.dot", "w") as fp:
|
||||
fp.write("digraph G {\n")
|
||||
fp.write("rx [shape=circle, color=red, style=filled];\n")
|
||||
for name, (type, outputs) in self._modules.items():
|
||||
for name, (type, outputs) in modules.items():
|
||||
if type == "conjunction":
|
||||
shape = "diamond"
|
||||
elif type == "flip-flop":
|
||||
@@ -92,34 +89,29 @@ class Solver(BaseSolver):
|
||||
else:
|
||||
shape = "circle"
|
||||
fp.write(f"{name} [shape={shape}];\n")
|
||||
for name, (type, outputs) in self._modules.items():
|
||||
for name, (type, outputs) in modules.items():
|
||||
for output in outputs:
|
||||
fp.write(f"{name} -> {output};\n")
|
||||
fp.write("}\n")
|
||||
|
||||
# part 1
|
||||
flip_flop_states: dict[str, Literal["on", "off"]] = {
|
||||
name: "off"
|
||||
for name, (type, _) in self._modules.items()
|
||||
if type == "flip-flop"
|
||||
name: "off" for name, (type, _) in modules.items() if type == "flip-flop"
|
||||
}
|
||||
conjunction_states: dict[str, dict[str, PulseType]] = {
|
||||
name: {
|
||||
input: "low"
|
||||
for input, (_, outputs) in self._modules.items()
|
||||
if name in outputs
|
||||
}
|
||||
for name, (type, _) in self._modules.items()
|
||||
name: {input: "low" for input, (_, outputs) in modules.items() if name in outputs}
|
||||
for name, (type, _) in modules.items()
|
||||
if type == "conjunction"
|
||||
}
|
||||
counts: dict[PulseType, int] = {"low": 0, "high": 0}
|
||||
for _ in range(1000):
|
||||
result, _ = self._process(
|
||||
result, _ = process(
|
||||
("button", "broadcaster", "low"), flip_flop_states, conjunction_states
|
||||
)
|
||||
for pulse in ("low", "high"):
|
||||
counts[pulse] += result[pulse]
|
||||
yield counts["low"] * counts["high"]
|
||||
answer_1 = counts["low"] * counts["high"]
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
|
||||
@@ -132,27 +124,23 @@ class Solver(BaseSolver):
|
||||
conjunction_states[name][input] = "low"
|
||||
|
||||
# find the conjunction connected to rx
|
||||
to_rx = [
|
||||
name for name, (_, outputs) in self._modules.items() if "rx" in outputs
|
||||
]
|
||||
to_rx = [name for name, (_, outputs) in modules.items() if "rx" in outputs]
|
||||
assert len(to_rx) == 1, "cannot handle multiple module inputs for rx"
|
||||
assert (
|
||||
self._modules[to_rx[0]][0] == "conjunction"
|
||||
modules[to_rx[0]][0] == "conjunction"
|
||||
), "can only handle conjunction as input to rx"
|
||||
|
||||
to_rx_inputs = [
|
||||
name for name, (_, outputs) in self._modules.items() if to_rx[0] in outputs
|
||||
]
|
||||
to_rx_inputs = [name for name, (_, outputs) in modules.items() if to_rx[0] in outputs]
|
||||
assert all(
|
||||
self._modules[i][0] == "conjunction" and len(self._modules[i][1]) == 1
|
||||
for i in to_rx_inputs
|
||||
modules[i][0] == "conjunction" and len(modules[i][1]) == 1 for i in to_rx_inputs
|
||||
), "can only handle inversion as second-order inputs to rx"
|
||||
|
||||
|
||||
count = 1
|
||||
cycles: dict[str, int] = {}
|
||||
second: dict[str, int] = {}
|
||||
while len(second) != len(to_rx_inputs):
|
||||
_, inputs = self._process(
|
||||
_, inputs = process(
|
||||
("button", "broadcaster", "low"), flip_flop_states, conjunction_states
|
||||
)
|
||||
|
||||
@@ -169,4 +157,5 @@ class Solver(BaseSolver):
|
||||
second[k] == cycles[k] * 2 for k in to_rx_inputs
|
||||
), "cannot only handle cycles starting at the beginning"
|
||||
|
||||
yield lcm(*cycles.values())
|
||||
answer_2 = lcm(*cycles.values())
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,6 +1,9 @@
|
||||
from typing import Any, Iterator
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
|
||||
|
||||
def reachable(
|
||||
@@ -18,29 +21,25 @@ def reachable(
|
||||
return tiles
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
map = input.splitlines()
|
||||
map = sys.stdin.read().splitlines()
|
||||
start = next(
|
||||
(i, j)
|
||||
for i in range(len(map))
|
||||
for j in range(len(map[i]))
|
||||
if map[i][j] == "S"
|
||||
(i, j) for i in range(len(map)) for j in range(len(map[i])) if map[i][j] == "S"
|
||||
)
|
||||
|
||||
# part 1
|
||||
yield len(reachable(map, {start}, 6 if len(map) < 20 else 64))
|
||||
answer_1 = len(reachable(map, {start}, 6 if len(map) < 20 else 64))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
|
||||
# the initial map is a square and contains an empty rhombus whose diameter is
|
||||
# the size of the map, and has only empty cells around the middle row and column
|
||||
# the initial map is a square and contains an empty rhombus whose diameter is the size
|
||||
# of the map, and has only empty cells around the middle row and column
|
||||
#
|
||||
# after ~n/2 steps, the first map is filled with a rhombus, after that we get a
|
||||
# bigger rhombus every n steps
|
||||
# after ~n/2 steps, the first map is filled with a rhombus, after that we get a bigger
|
||||
# rhombus every n steps
|
||||
#
|
||||
# we are going to find the number of cells reached for the initial rhombus, n
|
||||
# steps after and n * 2 steps after
|
||||
# we are going to find the number of cells reached for the initial rhombus, n steps
|
||||
# after and n * 2 steps after
|
||||
#
|
||||
cycle = len(map)
|
||||
rhombus = (len(map) - 3) // 2 + 1
|
||||
@@ -50,7 +49,7 @@ class Solver(BaseSolver):
|
||||
values.append(len(tiles := reachable(map, tiles, cycle)))
|
||||
values.append(len(tiles := reachable(map, tiles, cycle)))
|
||||
|
||||
if self.verbose:
|
||||
if logging.root.getEffectiveLevel() == logging.INFO:
|
||||
n_rows, n_cols = len(map), len(map[0])
|
||||
|
||||
rows = [
|
||||
@@ -66,10 +65,10 @@ class Solver(BaseSolver):
|
||||
if (i // cycle) % 2 == (j // cycle) % 2:
|
||||
rows[i][j] = f"\033[91m{rows[i][j]}\033[0m"
|
||||
|
||||
for row in rows:
|
||||
self.logger.info("".join(row))
|
||||
print("\n".join("".join(row) for row in rows))
|
||||
|
||||
self.logger.info(f"values to fit: {values}")
|
||||
|
||||
logging.info(f"values to fit: {values}")
|
||||
|
||||
# version 1:
|
||||
#
|
||||
@@ -122,8 +121,7 @@ class Solver(BaseSolver):
|
||||
+ 2 * radius * (radius + 1) // 2 * A
|
||||
+ 2 * radius * (radius - 1) // 2 * B
|
||||
+ sum(counts[i][j] for i, j in ((0, 2), (-1, 2), (2, 0), (2, -1)))
|
||||
+ sum(counts[i][j] for i, j in ((0, 1), (0, 3), (-1, 1), (-1, 3)))
|
||||
* (radius + 1)
|
||||
+ sum(counts[i][j] for i, j in ((0, 1), (0, 3), (-1, 1), (-1, 3))) * (radius + 1)
|
||||
+ sum(counts[i][j] for i, j in ((1, 1), (1, 3), (-2, 1), (-2, 3))) * radius
|
||||
)
|
||||
print(f"answer 2 (v1) is {answer_2}")
|
||||
@@ -147,4 +145,5 @@ class Solver(BaseSolver):
|
||||
a, b, c = (y1 + y3) // 2 - y2, 2 * y2 - (3 * y1 + y3) // 2, y1
|
||||
|
||||
n = (26501365 - rhombus) // cycle
|
||||
yield a * n * n + b * n + c
|
||||
answer_2 = a * n * n + b * n + c
|
||||
print(f"answer 2 (v2) is {answer_2}")
|
||||
|
@@ -1,20 +1,23 @@
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import string
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
|
||||
def _name(i: int) -> str:
|
||||
if len(lines) < 26:
|
||||
return string.ascii_uppercase[i]
|
||||
return f"B{i:04d}"
|
||||
|
||||
|
||||
def build_supports(
|
||||
bricks: list[tuple[tuple[int, int, int], tuple[int, int, int]]],
|
||||
) -> tuple[dict[int, set[int]], dict[int, set[int]]]:
|
||||
@@ -39,9 +42,7 @@ class Solver(BaseSolver):
|
||||
|
||||
# 2. compute the bricks that supports any brick
|
||||
supported_by: dict[int, set[int]] = {}
|
||||
supports: dict[int, set[int]] = {
|
||||
i_brick: set() for i_brick in range(len(bricks))
|
||||
}
|
||||
supports: dict[int, set[int]] = {i_brick: set() for i_brick in range(len(bricks))}
|
||||
for i_brick, ((sx, sy, sz), (ex, ey, ez)) in enumerate(bricks):
|
||||
name = _name(i_brick)
|
||||
|
||||
@@ -50,7 +51,7 @@ class Solver(BaseSolver):
|
||||
for x, y in itertools.product(range(sx, ex + 1), range(sy, ey + 1))
|
||||
if (v := levels[x, y, sz - 1]) != -1
|
||||
}
|
||||
self.logger.info(
|
||||
logging.info(
|
||||
f"{name} supported by {', '.join(map(_name, supported_by[i_brick]))}"
|
||||
)
|
||||
|
||||
@@ -59,6 +60,7 @@ class Solver(BaseSolver):
|
||||
|
||||
return supported_by, supports
|
||||
|
||||
|
||||
bricks: list[tuple[tuple[int, int, int], tuple[int, int, int]]] = []
|
||||
for line in lines:
|
||||
bricks.append(
|
||||
@@ -73,10 +75,11 @@ class Solver(BaseSolver):
|
||||
supported_by, supports = build_supports(bricks)
|
||||
|
||||
# part 1
|
||||
yield len(bricks) - sum(
|
||||
answer_1 = len(bricks) - sum(
|
||||
any(len(supported_by[supported]) == 1 for supported in supports_to)
|
||||
for supports_to in supports.values()
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
falling_in_chain: dict[int, set[int]] = {}
|
||||
@@ -97,13 +100,12 @@ class Solver(BaseSolver):
|
||||
|
||||
for d_brick in to_disintegrate:
|
||||
for supported in supports[d_brick]:
|
||||
supported_by_copy[supported] = supported_by_copy[supported] - {
|
||||
d_brick
|
||||
}
|
||||
supported_by_copy[supported] = supported_by_copy[supported] - {d_brick}
|
||||
|
||||
if not supported_by_copy[supported]:
|
||||
to_disintegrate_v.add(supported)
|
||||
|
||||
to_disintegrate = to_disintegrate_v
|
||||
|
||||
yield sum(len(falling) for falling in falling_in_chain.values())
|
||||
answer_2 = sum(len(falling) for falling in falling_in_chain.values())
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,11 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator, Literal, Sequence, TypeAlias, cast
|
||||
from typing import Literal, Sequence, TypeAlias, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
|
||||
DirectionType: TypeAlias = Literal[">", "<", "^", "v", ".", "#"]
|
||||
|
||||
@@ -31,7 +35,6 @@ def neighbors(
|
||||
Compute neighbors of the given node, ignoring the given set of nodes and considering
|
||||
that you can go uphill on slopes.
|
||||
"""
|
||||
n_rows, n_cols = len(grid), len(grid[0])
|
||||
i, j = node
|
||||
|
||||
for di, dj in Neighbors[grid[i][j]]:
|
||||
@@ -100,9 +103,7 @@ def compute_direct_links(
|
||||
return direct
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def longest_path_length(
|
||||
self,
|
||||
links: dict[tuple[int, int], list[tuple[tuple[int, int], int]]],
|
||||
start: tuple[int, int],
|
||||
target: tuple[int, int],
|
||||
@@ -128,29 +129,29 @@ class Solver(BaseSolver):
|
||||
if reach not in path
|
||||
)
|
||||
|
||||
self.logger.info(f"processed {nodes} nodes")
|
||||
logging.info(f"processed {nodes} nodes")
|
||||
|
||||
return max_distance
|
||||
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = cast(list[Sequence[DirectionType]], input.splitlines())
|
||||
|
||||
lines = cast(list[Sequence[DirectionType]], sys.stdin.read().splitlines())
|
||||
n_rows, n_cols = len(lines), len(lines[0])
|
||||
start = (0, 1)
|
||||
target = (len(lines) - 1, len(lines[0]) - 2)
|
||||
|
||||
|
||||
direct_links: dict[tuple[int, int], list[tuple[tuple[int, int], int]]] = {
|
||||
start: [reachable(lines, start, target)]
|
||||
}
|
||||
direct_links.update(
|
||||
compute_direct_links(lines, direct_links[start][0][0], target)
|
||||
)
|
||||
direct_links.update(compute_direct_links(lines, direct_links[start][0][0], target))
|
||||
|
||||
# part 1
|
||||
yield self.longest_path_length(direct_links, start, target)
|
||||
answer_1 = longest_path_length(direct_links, start, target)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
reverse_links: dict[tuple[int, int], list[tuple[tuple[int, int], int]]] = (
|
||||
defaultdict(list)
|
||||
reverse_links: dict[tuple[int, int], list[tuple[tuple[int, int], int]]] = defaultdict(
|
||||
list
|
||||
)
|
||||
for origin, links in direct_links.items():
|
||||
for destination, distance in links:
|
||||
@@ -162,4 +163,5 @@ class Solver(BaseSolver):
|
||||
for k in direct_links.keys() | reverse_links.keys()
|
||||
}
|
||||
|
||||
yield self.longest_path_length(links, start, target)
|
||||
answer_2 = longest_path_length(links, start, target)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,14 +1,9 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
from sympy import solve, symbols
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
positions = np.array(
|
||||
[[int(c) for c in line.split("@")[0].strip().split(", ")] for line in lines]
|
||||
@@ -18,9 +13,7 @@ class Solver(BaseSolver):
|
||||
)
|
||||
|
||||
# part 1
|
||||
low, high = (
|
||||
[7, 27] if len(positions) <= 10 else [200000000000000, 400000000000000]
|
||||
)
|
||||
low, high = [7, 27] if len(positions) <= 10 else [200000000000000, 400000000000000]
|
||||
|
||||
count = 0
|
||||
for i1, (p1, v1) in enumerate(zip(positions, velocities)):
|
||||
@@ -38,7 +31,9 @@ class Solver(BaseSolver):
|
||||
c = p + np.expand_dims(t, 1) * r
|
||||
count += np.all((low <= c) & (c <= high), axis=1).sum()
|
||||
|
||||
yield count
|
||||
|
||||
answer_1 = count
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
# equation
|
||||
@@ -59,10 +54,10 @@ class Solver(BaseSolver):
|
||||
)
|
||||
equations = []
|
||||
for i1, ti in zip(range(n), ts):
|
||||
for p, d, pi, di in zip(
|
||||
(x, y, z), (vx, vy, vz), positions[i1], velocities[i1]
|
||||
):
|
||||
for p, d, pi, di in zip((x, y, z), (vx, vy, vz), positions[i1], velocities[i1]):
|
||||
equations.append(p + ti * d - pi - ti * di)
|
||||
|
||||
r = solve(equations, [x, y, z, vx, vy, vz] + list(ts), dict=True)[0]
|
||||
yield r[x] + r[y] + r[z]
|
||||
|
||||
answer_2 = r[x] + r[y] + r[z]
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,19 +1,14 @@
|
||||
# pyright: reportUnknownMemberType=false
|
||||
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
import networkx as nx
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
components = {
|
||||
(p := line.split(": "))[0]: p[1].split() for line in input.splitlines()
|
||||
(p := line.split(": "))[0]: p[1].split() for line in sys.stdin.read().splitlines()
|
||||
}
|
||||
|
||||
graph: "nx.Graph[str]" = nx.Graph()
|
||||
targets = {t for c in components for t in components[c] if t not in components}
|
||||
|
||||
graph = nx.Graph()
|
||||
graph.add_edges_from((u, v) for u, vs in components.items() for v in vs)
|
||||
|
||||
cut = nx.minimum_edge_cut(graph)
|
||||
@@ -22,4 +17,9 @@ class Solver(BaseSolver):
|
||||
c1, c2 = nx.connected_components(graph)
|
||||
|
||||
# part 1
|
||||
yield len(c1) * len(c2)
|
||||
answer_1 = len(c1) * len(c2)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
answer_2 = ...
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,15 +1,10 @@
|
||||
import string
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
NOT_A_SYMBOL = "." + string.digits
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
values: list[int] = []
|
||||
gears: dict[tuple[int, int], list[int]] = defaultdict(list)
|
||||
@@ -49,5 +44,10 @@ class Solver(BaseSolver):
|
||||
# continue starting from the end of the number
|
||||
j = k
|
||||
|
||||
yield sum(values)
|
||||
yield sum(v1 * v2 for v1, v2 in filter(lambda vs: len(vs) == 2, gears.values()))
|
||||
# part 1
|
||||
answer_1 = sum(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}")
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -11,9 +9,7 @@ class Card:
|
||||
values: list[int]
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
cards: list[Card] = []
|
||||
for line in lines:
|
||||
@@ -30,7 +26,8 @@ class Solver(BaseSolver):
|
||||
winnings = [sum(1 for n in card.values if n in card.numbers) for card in cards]
|
||||
|
||||
# part 1
|
||||
yield sum(2 ** (winning - 1) for winning in winnings if winning > 0)
|
||||
answer_1 = sum(2 ** (winning - 1) for winning in winnings if winning > 0)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
card2cards = {i: list(range(i + 1, i + w + 1)) for i, w in enumerate(winnings)}
|
||||
@@ -41,4 +38,4 @@ class Solver(BaseSolver):
|
||||
for j in card2cards[i]:
|
||||
card2values[j] += card2values[i]
|
||||
|
||||
yield sum(card2values.values())
|
||||
print(f"answer 2 is {sum(card2values.values())}")
|
||||
|
@@ -1,6 +1,5 @@
|
||||
from typing import Any, Iterator, Sequence
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
from typing import Sequence
|
||||
|
||||
MAP_ORDER = [
|
||||
"seed",
|
||||
@@ -13,6 +12,55 @@ MAP_ORDER = [
|
||||
"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(
|
||||
values: tuple[int, int], map: list[tuple[int, int, int]]
|
||||
@@ -63,71 +111,19 @@ def find_range(
|
||||
return ranges
|
||||
|
||||
|
||||
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]]:
|
||||
def find_location_ranges(seeds: Sequence[tuple[int, int]]) -> Sequence[tuple[int, int]]:
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
# 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))
|
||||
answer_1 = min(start for start, _ in find_location_ranges(seeds_p1))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# # 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))
|
||||
answer_2 = min(start for start, _ in find_location_ranges(seeds_p2))
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import math
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
|
||||
|
||||
def extreme_times_to_beat(time: int, distance: int) -> tuple[int, int]:
|
||||
@@ -27,23 +25,23 @@ def extreme_times_to_beat(time: int, distance: int) -> tuple[int, int]:
|
||||
return t1, t2
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
# part 1
|
||||
times = list(map(int, lines[0].split()[1:]))
|
||||
distances = list(map(int, lines[1].split()[1:]))
|
||||
yield math.prod(
|
||||
answer_1 = math.prod(
|
||||
t2 - t1 + 1
|
||||
for t1, t2 in (
|
||||
extreme_times_to_beat(time, distance)
|
||||
for time, distance in zip(times, distances)
|
||||
)
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
time = int(lines[0].split(":")[1].strip().replace(" ", ""))
|
||||
distance = int(lines[1].split(":")[1].strip().replace(" ", ""))
|
||||
t1, t2 = extreme_times_to_beat(time, distance)
|
||||
yield t2 - t1 + 1
|
||||
answer_2 = t2 - t1 + 1
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import sys
|
||||
from collections import Counter, defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class HandTypes:
|
||||
@@ -34,17 +32,18 @@ def extract_key(hand: str, values: dict[str, int], joker: str = "0") -> tuple[in
|
||||
)
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
cards = [(t[0], int(t[1])) for line in lines if (t := line.split())]
|
||||
|
||||
|
||||
# part 1
|
||||
values = {card: value for value, card in enumerate("23456789TJQKA")}
|
||||
cards.sort(key=lambda cv: extract_key(cv[0], values=values))
|
||||
yield sum(rank * value for rank, (_, value) in enumerate(cards, start=1))
|
||||
answer_1 = sum(rank * value for rank, (_, value) in enumerate(cards, start=1))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
values = {card: value for value, card in enumerate("J23456789TQKA")}
|
||||
cards.sort(key=lambda cv: extract_key(cv[0], values=values, joker="J"))
|
||||
yield sum(rank * value for rank, (_, value) in enumerate(cards, start=1))
|
||||
answer_2 = sum(rank * value for rank, (_, value) in enumerate(cards, start=1))
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,13 +1,8 @@
|
||||
import itertools
|
||||
import math
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
sequence = lines[0]
|
||||
nodes = {
|
||||
@@ -16,6 +11,7 @@ class Solver(BaseSolver):
|
||||
if (p := line.split(" = "))
|
||||
}
|
||||
|
||||
|
||||
def path(start: str):
|
||||
path = [start]
|
||||
it_seq = iter(itertools.cycle(sequence))
|
||||
@@ -23,8 +19,11 @@ class Solver(BaseSolver):
|
||||
path.append(nodes[path[-1]][next(it_seq)])
|
||||
return path
|
||||
|
||||
|
||||
# part 1
|
||||
yield len(path(next(node for node in nodes if node.endswith("A")))) - 1
|
||||
answer_1 = len(path(next(node for node in nodes if node.endswith("A")))) - 1
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
yield math.lcm(*(len(path(node)) - 1 for node in nodes if node.endswith("A")))
|
||||
answer_2 = math.lcm(*(len(path(node)) - 1 for node in nodes if node.endswith("A")))
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,11 +1,6 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
data = [[int(c) for c in line.split()] for line in lines]
|
||||
|
||||
@@ -14,9 +9,7 @@ class Solver(BaseSolver):
|
||||
for values in data:
|
||||
diffs = [values]
|
||||
while any(d != 0 for d in diffs[-1]):
|
||||
diffs.append(
|
||||
[rhs - lhs for lhs, rhs in zip(diffs[-1][:-1], diffs[-1][1:])]
|
||||
)
|
||||
diffs.append([rhs - lhs for lhs, rhs in zip(diffs[-1][:-1], diffs[-1][1:])])
|
||||
|
||||
rhs: list[int] = [0]
|
||||
lhs: list[int] = [0]
|
||||
@@ -28,7 +21,9 @@ class Solver(BaseSolver):
|
||||
left_values.append(lhs[-1])
|
||||
|
||||
# part 1
|
||||
yield sum(right_values)
|
||||
answer_1 = sum(right_values)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
yield sum(left_values)
|
||||
answer_2 = sum(left_values)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,17 +1,14 @@
|
||||
import sys
|
||||
from collections import Counter
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
values = list(map(int, input.split()))
|
||||
values = list(map(int, sys.stdin.read().strip().split()))
|
||||
|
||||
column_1 = sorted(values[::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)
|
||||
|
||||
answer_1 = sum(abs(v1 - v2) for v1, v2 in zip(column_1, column_2, strict=True))
|
||||
answer_2 = sum(value * counter_2.get(value, 0) for value in column_1)
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,10 +1,6 @@
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
|
||||
|
||||
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)]
|
||||
|
||||
@@ -12,12 +8,15 @@ class Solver(BaseSolver):
|
||||
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))
|
||||
)
|
||||
return any(is_safe(level[:i] + level[i + 1 :]) for i in range(0, len(level)))
|
||||
|
||||
levels = [[int(c) for c in r.split()] for r in input.splitlines()]
|
||||
|
||||
yield sum(is_safe(level) for level in levels)
|
||||
yield sum(is_safe(level) or is_any_safe(level) for level in levels)
|
||||
levels = [[int(c) for c in r.split()] for r in sys.stdin.read().strip().splitlines()]
|
||||
|
||||
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}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,15 +1,13 @@
|
||||
import re
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
import sys
|
||||
from typing import Iterator
|
||||
|
||||
|
||||
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):
|
||||
yield int(m.group(1)) * int(m.group(2))
|
||||
|
||||
|
||||
def valid_memory_blocks(line: str) -> Iterator[str]:
|
||||
accumulate = True
|
||||
while line:
|
||||
@@ -26,5 +24,11 @@ class Solver(BaseSolver):
|
||||
else:
|
||||
line = ""
|
||||
|
||||
yield sum(extract_multiply(input))
|
||||
yield sum(sum(extract_multiply(block)) for block in valid_memory_blocks(input))
|
||||
|
||||
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}")
|
||||
|
@@ -1,37 +1,10 @@
|
||||
import itertools as it
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
n = len(lines)
|
||||
answer_2 = ...
|
||||
|
||||
yield 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)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
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))
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,65 +1,10 @@
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
def in_correct_order(update: list[int], requirements: dict[int, set[int]]) -> bool:
|
||||
return all(
|
||||
not any(value_2 in requirements[value] for value_2 in update[i_value:])
|
||||
for i_value, value in enumerate(update)
|
||||
)
|
||||
answer_2 = ...
|
||||
|
||||
|
||||
def to_correct_order(
|
||||
update: list[int],
|
||||
requirements: dict[int, set[int]],
|
||||
max_update_length: int | None = None,
|
||||
) -> list[int]:
|
||||
# copy requirements to update
|
||||
requirements = {
|
||||
value: {predecessor for predecessor in predecessors if predecessor in update}
|
||||
for value, predecessors in requirements.items()
|
||||
if value in update
|
||||
}
|
||||
|
||||
max_update_length = max_update_length or len(update)
|
||||
|
||||
update = []
|
||||
while requirements and len(update) < max_update_length:
|
||||
value = next(
|
||||
value for value, predecessors in requirements.items() if not predecessors
|
||||
)
|
||||
|
||||
update.append(value)
|
||||
del requirements[value]
|
||||
|
||||
for predecessors in requirements.values():
|
||||
if value in predecessors:
|
||||
predecessors.remove(value)
|
||||
|
||||
return update
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
part1, part2 = input.split("\n\n")
|
||||
|
||||
requirements: dict[int, set[int]] = defaultdict(set)
|
||||
for line in part1.splitlines():
|
||||
v1, v2 = line.split("|")
|
||||
requirements[int(v2)].add(int(v1))
|
||||
|
||||
updates = [list(map(int, line.split(","))) for line in part2.splitlines()]
|
||||
|
||||
yield sum(
|
||||
update[len(update) // 2]
|
||||
for update in updates
|
||||
if in_correct_order(update, requirements)
|
||||
)
|
||||
|
||||
yield sum(
|
||||
to_correct_order(update, requirements, len(update) // 2 + 1)[-1]
|
||||
for update in updates
|
||||
if not in_correct_order(update, requirements)
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,122 +1,10 @@
|
||||
import itertools as it
|
||||
from typing import Any, Iterator, TypeAlias
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
NodeType: TypeAlias = tuple[tuple[int, int], tuple[int, int]]
|
||||
EdgesType: TypeAlias = dict[NodeType, tuple[NodeType, set[tuple[int, int]]]]
|
||||
answer_1 = ...
|
||||
|
||||
ROTATE = {(-1, 0): (0, 1), (0, 1): (1, 0), (1, 0): (0, -1), (0, -1): (-1, 0)}
|
||||
answer_2 = ...
|
||||
|
||||
START_NODE: NodeType = ((-2, -2), (-1, 0))
|
||||
FINAL_POS: tuple[int, int] = (-1, -1)
|
||||
|
||||
|
||||
def move(
|
||||
lines: list[str], pos: tuple[int, int], dir: tuple[int, int]
|
||||
) -> tuple[tuple[int, int] | None, set[tuple[int, int]]]:
|
||||
n_rows, n_cols = len(lines), len(lines[0])
|
||||
row, col = pos
|
||||
|
||||
marked: set[tuple[int, int]] = set()
|
||||
final_pos: tuple[int, int] | None = None
|
||||
|
||||
while True:
|
||||
marked.add((row, col))
|
||||
|
||||
if not (0 <= row + dir[0] < n_rows and 0 <= col + dir[1] < n_cols):
|
||||
final_pos = None
|
||||
break
|
||||
|
||||
if lines[row + dir[0]][col + dir[1]] != ".":
|
||||
final_pos = (row, col)
|
||||
break
|
||||
|
||||
row += dir[0]
|
||||
col += dir[1]
|
||||
|
||||
return final_pos, marked
|
||||
|
||||
|
||||
def compute_graph(lines: list[str], start_node: NodeType):
|
||||
n_rows, n_cols = len(lines), len(lines[0])
|
||||
|
||||
edges: EdgesType = {}
|
||||
|
||||
start_pos, start_dir = start_node
|
||||
end_pos, marked = move(lines, start_pos, start_dir)
|
||||
assert end_pos is not None
|
||||
edges[START_NODE] = ((end_pos, start_dir), marked)
|
||||
|
||||
for row, col in it.product(range(n_rows), range(n_cols)):
|
||||
if lines[row][col] != "#":
|
||||
continue
|
||||
|
||||
for start_pos, start_dir in (
|
||||
((row - 1, col), (1, 0)),
|
||||
((row + 1, col), (-1, 0)),
|
||||
((row, col - 1), (0, 1)),
|
||||
((row, col + 1), (0, -1)),
|
||||
):
|
||||
if 0 <= start_pos[0] < n_rows and 0 <= start_pos[1] < n_cols:
|
||||
end_pos, marked = move(lines, start_pos, ROTATE[start_dir])
|
||||
|
||||
edges[start_pos, start_dir] = (
|
||||
(end_pos or FINAL_POS, ROTATE[start_dir]),
|
||||
marked,
|
||||
)
|
||||
|
||||
return edges
|
||||
|
||||
|
||||
def is_loop(lines: list[str], edges: EdgesType, position: tuple[int, int]):
|
||||
row, col = position
|
||||
current_node = START_NODE
|
||||
found: set[NodeType] = set()
|
||||
|
||||
while current_node[0] != FINAL_POS and current_node not in found:
|
||||
found.add(current_node)
|
||||
|
||||
target_node, edge_marked = edges[current_node]
|
||||
|
||||
if (row, col) in edge_marked:
|
||||
# need to break the edge
|
||||
target_dir = target_node[1]
|
||||
end_pos, _ = move(
|
||||
lines, (row - target_dir[0], col - target_dir[1]), ROTATE[target_dir]
|
||||
)
|
||||
current_node = (end_pos or FINAL_POS, ROTATE[target_dir])
|
||||
else:
|
||||
current_node = target_node
|
||||
|
||||
return current_node in found
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
# read lines
|
||||
lines = input.splitlines()
|
||||
|
||||
# find and delete original position
|
||||
start_pos = next(
|
||||
(i, j)
|
||||
for i, row in enumerate(lines)
|
||||
for j, col in enumerate(row)
|
||||
if col == "^"
|
||||
)
|
||||
lines[start_pos[0]] = lines[start_pos[0]].replace("^", ".")
|
||||
|
||||
# compute edges from the map
|
||||
edges = compute_graph(lines, (start_pos, (-1, 0)))
|
||||
|
||||
# part 1
|
||||
marked: set[tuple[int, int]] = set()
|
||||
current_node = START_NODE
|
||||
|
||||
while current_node[0] != FINAL_POS:
|
||||
current_node, current_marked = edges[current_node]
|
||||
marked = marked.union(current_marked)
|
||||
|
||||
yield len(marked)
|
||||
|
||||
yield sum(is_loop(lines, edges, pos) for pos in marked if pos != start_pos)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,50 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
def evaluate(
|
||||
target: int, numbers: list[int], concatenate: bool = False, current: int = 0
|
||||
) -> bool:
|
||||
if not numbers:
|
||||
return current == target
|
||||
answer_2 = ...
|
||||
|
||||
if current > target:
|
||||
return False
|
||||
|
||||
head, *tail = numbers
|
||||
|
||||
if evaluate(target, tail, concatenate, current + head) or evaluate(
|
||||
target, tail, concatenate, current * head
|
||||
):
|
||||
return True
|
||||
|
||||
if not concatenate:
|
||||
return False
|
||||
|
||||
return evaluate(target, tail, concatenate, int(str(current) + str(head)))
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
targets = {
|
||||
int(part[0]): list(map(int, part[1].strip().split()))
|
||||
for line in input.splitlines()
|
||||
if (part := line.split(":"))
|
||||
}
|
||||
|
||||
yield sum(
|
||||
target
|
||||
for target, numbers in self.progress.wrap(
|
||||
targets.items(), total=len(targets)
|
||||
)
|
||||
if evaluate(target, numbers)
|
||||
)
|
||||
|
||||
yield sum(
|
||||
target
|
||||
for target, numbers in self.progress.wrap(
|
||||
targets.items(), total=len(targets)
|
||||
)
|
||||
if evaluate(target, numbers, True)
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,76 +1,10 @@
|
||||
import itertools as it
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator, cast
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
def compute_antinodes(
|
||||
a1: tuple[int, int],
|
||||
a2: tuple[int, int],
|
||||
n_rows: int,
|
||||
n_cols: int,
|
||||
min_distance: int = 1,
|
||||
max_distance: int | None = 1,
|
||||
):
|
||||
if a1[0] > a2[0]:
|
||||
a1, a2 = a2, a1
|
||||
answer_2 = ...
|
||||
|
||||
d_row, d_col = a2[0] - a1[0], a2[1] - a1[1]
|
||||
|
||||
points: list[tuple[int, int]] = []
|
||||
|
||||
for c in range(min_distance, (max_distance or n_rows) + 1):
|
||||
row_1, col_1 = a1[0] - c * d_row, a1[1] - c * d_col
|
||||
row_2, col_2 = a2[0] + c * d_row, a2[1] + c * d_col
|
||||
|
||||
valid_1, valid_2 = (
|
||||
0 <= row_1 < n_rows and 0 <= col_1 < n_cols,
|
||||
0 <= row_2 < n_rows and 0 <= col_2 < n_cols,
|
||||
)
|
||||
|
||||
if not valid_1 and not valid_2:
|
||||
break
|
||||
|
||||
if valid_1:
|
||||
points.append((row_1, col_1))
|
||||
if valid_2:
|
||||
points.append((row_2, col_2))
|
||||
|
||||
return tuple(points)
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
n_rows, n_cols = len(lines), len(lines[0])
|
||||
|
||||
antennas: dict[str, list[tuple[int, int]]] = defaultdict(list)
|
||||
for i, j in it.product(range(n_rows), range(n_cols)):
|
||||
if lines[i][j] != ".":
|
||||
antennas[lines[i][j]].append((i, j))
|
||||
|
||||
yield len(
|
||||
cast(set[tuple[int, int]], set()).union(
|
||||
it.chain(
|
||||
*(
|
||||
compute_antinodes(a1, a2, n_rows, n_cols)
|
||||
for antennas_of_frequency in antennas.values()
|
||||
for a1, a2 in it.permutations(antennas_of_frequency, 2)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
yield len(
|
||||
cast(set[tuple[int, int]], set()).union(
|
||||
it.chain(
|
||||
*(
|
||||
compute_antinodes(a1, a2, n_rows, n_cols, 0, None)
|
||||
for antennas_of_frequency in antennas.values()
|
||||
for a1, a2 in it.permutations(antennas_of_frequency, 2)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,7 +1,10 @@
|
||||
from typing import Any, Iterator
|
||||
import sys
|
||||
|
||||
from ..base import BaseSolver
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@@ -1,114 +1,14 @@
|
||||
import argparse
|
||||
import importlib
|
||||
import json
|
||||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable, Iterator, Literal, Sequence, TextIO, TypeVar
|
||||
|
||||
from tqdm import tqdm
|
||||
|
||||
from .base import BaseSolver
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def dump_api_message(
|
||||
type: Literal["log", "answer", "progress-start", "progress-step", "progress-end"],
|
||||
content: Any,
|
||||
file: TextIO = sys.stdout,
|
||||
):
|
||||
print(
|
||||
json.dumps(
|
||||
{"type": type, "time": datetime.now().isoformat(), "content": content}
|
||||
),
|
||||
flush=True,
|
||||
file=file,
|
||||
)
|
||||
|
||||
|
||||
class LoggerAPIHandler(logging.Handler):
|
||||
def __init__(self, output: TextIO = sys.stdout):
|
||||
super().__init__()
|
||||
self.output = output
|
||||
|
||||
def emit(self, record: logging.LogRecord):
|
||||
dump_api_message(
|
||||
"log", {"level": record.levelname, "message": record.getMessage()}
|
||||
)
|
||||
|
||||
|
||||
class ProgressAPI:
|
||||
def __init__(
|
||||
self,
|
||||
min_step: int = 1,
|
||||
min_time: timedelta = timedelta(milliseconds=100),
|
||||
output: TextIO = sys.stdout,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.counter = 0
|
||||
self.output = output
|
||||
self.min_step = min_step
|
||||
self.min_time = min_time
|
||||
|
||||
def wrap(
|
||||
self, values: Sequence[_T] | Iterable[_T], total: int | None = None
|
||||
) -> Iterator[_T]:
|
||||
total = total or len(values) # type: ignore
|
||||
|
||||
current = self.counter
|
||||
self.counter += 1
|
||||
|
||||
dump_api_message("progress-start", {"counter": current, "total": total})
|
||||
|
||||
try:
|
||||
percent = 0
|
||||
time = datetime.now()
|
||||
|
||||
for i_value, value in enumerate(values):
|
||||
yield value
|
||||
|
||||
if datetime.now() - time < self.min_time:
|
||||
continue
|
||||
|
||||
time = datetime.now()
|
||||
|
||||
c_percent = round(i_value / total * 100)
|
||||
|
||||
if c_percent >= percent + self.min_step:
|
||||
dump_api_message(
|
||||
"progress-step", {"counter": current, "percent": c_percent}
|
||||
)
|
||||
percent = c_percent
|
||||
finally:
|
||||
dump_api_message(
|
||||
"progress-end",
|
||||
{"counter": current},
|
||||
)
|
||||
|
||||
|
||||
class ProgressTQDM:
|
||||
def wrap(
|
||||
self, values: Sequence[_T] | Iterable[_T], total: int | None = None
|
||||
) -> Iterator[_T]:
|
||||
return iter(tqdm(values, total=total))
|
||||
|
||||
|
||||
class ProgressNone:
|
||||
def wrap(
|
||||
self, values: Sequence[_T] | Iterable[_T], total: int | None = None
|
||||
) -> Iterator[_T]:
|
||||
return iter(values)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser("Holt59 Advent-Of-Code Runner")
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help="verbose mode")
|
||||
parser.add_argument("-t", "--test", action="store_true", help="test mode")
|
||||
parser.add_argument("-a", "--api", action="store_true", help="API mode")
|
||||
parser.add_argument(
|
||||
"-u", "--user", type=str, default="holt59", help="user input to use"
|
||||
)
|
||||
@@ -131,7 +31,6 @@ def main():
|
||||
args = parser.parse_args()
|
||||
|
||||
verbose: bool = args.verbose
|
||||
api: bool = args.api
|
||||
test: bool = args.test
|
||||
stdin: bool = args.stdin
|
||||
user: str = args.user
|
||||
@@ -141,10 +40,8 @@ def main():
|
||||
day: int = args.day
|
||||
|
||||
# TODO: change this
|
||||
logging.basicConfig(
|
||||
level=logging.INFO if verbose or api else logging.WARNING,
|
||||
handlers=[LoggerAPIHandler()] if api else None,
|
||||
)
|
||||
if verbose:
|
||||
os.environ["AOC_VERBOSE"] = "True"
|
||||
|
||||
if input_path is None:
|
||||
input_path = Path(__file__).parent.joinpath(
|
||||
@@ -152,55 +49,11 @@ def main():
|
||||
)
|
||||
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"),
|
||||
verbose=verbose,
|
||||
year=year,
|
||||
day=day,
|
||||
progress=ProgressAPI()
|
||||
if api
|
||||
else ProgressTQDM()
|
||||
if verbose
|
||||
else ProgressNone(), # type: ignore
|
||||
outputs=not api,
|
||||
)
|
||||
|
||||
data: str
|
||||
if stdin:
|
||||
data = sys.stdin.read()
|
||||
importlib.import_module(f".{year}.day{day}", __package__)
|
||||
else:
|
||||
with open(input_path) as fp:
|
||||
data = fp.read()
|
||||
sys.stdin = fp
|
||||
importlib.import_module(f".{year}.day{day}", __package__)
|
||||
|
||||
start = datetime.now()
|
||||
last = start
|
||||
|
||||
it = solver.solve(data.strip())
|
||||
|
||||
if it is None:
|
||||
solver.logger.error(f"no implementation for {year} day {day}")
|
||||
exit()
|
||||
|
||||
for i_answer, answer in enumerate(it):
|
||||
current = datetime.now()
|
||||
|
||||
if api:
|
||||
dump_api_message(
|
||||
"answer",
|
||||
{
|
||||
"answer": i_answer + 1,
|
||||
"value": answer,
|
||||
"answerTime_s": (current - last).total_seconds(),
|
||||
"totalTime_s": (current - start).total_seconds(),
|
||||
},
|
||||
)
|
||||
else:
|
||||
print(
|
||||
f"answer {i_answer + 1} is {answer} (found in {(current - last).total_seconds():.2f}s)"
|
||||
)
|
||||
|
||||
last = current
|
||||
sys.stdin = sys.__stdin__
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user