Compare commits
15 Commits
2024/day4
...
377e501d34
Author | SHA1 | Date | |
---|---|---|---|
|
377e501d34 | ||
|
d1f4f5bed0 | ||
|
03a950c485 | ||
|
22b1513271 | ||
|
1f5b21a13a | ||
|
8c707c00ba | ||
|
ae4f42517c | ||
|
17432f7ac6 | ||
|
664dcfe7ba | ||
|
a9bcf9ef8f | ||
|
1caf93b38b | ||
|
f9a3dee20b | ||
|
1a1ff0c64d | ||
|
d7621d09b5 | ||
|
b89d29e880 |
16
poetry.lock
generated
16
poetry.lock
generated
@@ -1245,6 +1245,20 @@ 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"
|
||||
@@ -1281,4 +1295,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "b643261f91a781d77735e05f6d2ac1002867600c2df6393a9d1a15f5e1189109"
|
||||
content-hash = "c91bc307ff4a5b3e8cd1976ebea211c9749fe09d563dd80861f70ce26826cda9"
|
||||
|
@@ -23,6 +23,7 @@ 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,10 +1,12 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
line = sys.stdin.read().strip()
|
||||
|
||||
floor = 0
|
||||
floors = [(floor := floor + (1 if c == "(" else -1)) for c in line]
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
print(f"answer 1 is {floors[-1]}")
|
||||
print(f"answer 2 is {floors.index(-1)}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
floor = 0
|
||||
floors = [(floor := floor + (1 if c == "(" else -1)) for c in input]
|
||||
|
||||
yield floors[-1]
|
||||
yield floors.index(-1)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import itertools
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
line = sys.stdin.read().strip()
|
||||
from ..base import BaseSolver
|
||||
|
||||
# see http://www.se16.info/js/lands2.htm for the explanation of 'atoms' (or elements)
|
||||
#
|
||||
@@ -9,7 +9,7 @@ line = sys.stdin.read().strip()
|
||||
# CodeGolf answer https://codegolf.stackexchange.com/a/8479/42148
|
||||
|
||||
# fmt: off
|
||||
atoms = [
|
||||
ATOMS: list[tuple[str, tuple[int, ...]]] = [
|
||||
("22", (0, )), # 0
|
||||
("13112221133211322112211213322112", (71, 90, 0, 19, 2, )), # 1
|
||||
("312211322212221121123222112", (1, )), # 2
|
||||
@@ -105,7 +105,7 @@ atoms = [
|
||||
]
|
||||
# fmt: on
|
||||
|
||||
starters = [
|
||||
STARTERS = [
|
||||
"1",
|
||||
"11",
|
||||
"21",
|
||||
@@ -122,27 +122,26 @@ 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))
|
||||
|
||||
|
||||
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}")
|
||||
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)
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import itertools
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def is_valid(p: str) -> bool:
|
||||
@@ -40,10 +42,8 @@ def find_next_password(p: str) -> str:
|
||||
return p
|
||||
|
||||
|
||||
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}")
|
||||
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))
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import json
|
||||
import sys
|
||||
from typing import TypeAlias
|
||||
from typing import Any, Iterator, TypeAlias
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
JsonObject: TypeAlias = dict[str, "JsonObject"] | list["JsonObject"] | int | str
|
||||
|
||||
@@ -18,10 +19,9 @@ def json_sum(value: JsonObject, ignore: str | None = None) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
data: JsonObject = json.load(sys.stdin)
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
data: JsonObject = json.loads(input)
|
||||
|
||||
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}")
|
||||
yield json_sum(data)
|
||||
yield json_sum(data, "red")
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import itertools
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Literal, cast
|
||||
from typing import Any, Iterator, 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)
|
||||
@@ -17,25 +18,23 @@ def max_change_in_happiness(happiness: dict[str, dict[str, int]]) -> int:
|
||||
)
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
happiness: dict[str, dict[str, int]] = defaultdict(dict)
|
||||
for line in lines:
|
||||
u1, gain_or_loose, hap, u2 = cast(
|
||||
tuple[str, Literal["gain", "lose"], int, str],
|
||||
parse.parse( # type: ignore
|
||||
"{} would {} {:d} happiness units by sitting next to {}.", line
|
||||
),
|
||||
)
|
||||
happiness[u1][u2] = hap if gain_or_loose == "gain" else -hap
|
||||
happiness: dict[str, dict[str, int]] = defaultdict(dict)
|
||||
for line in lines:
|
||||
u1, gain_or_loose, hap, u2 = cast(
|
||||
tuple[str, Literal["gain", "lose"], int, str],
|
||||
parse.parse( # type: ignore
|
||||
"{} would {} {:d} happiness units by sitting next to {}.", line
|
||||
),
|
||||
)
|
||||
happiness[u1][u2] = hap if gain_or_loose == "gain" else -hap
|
||||
|
||||
yield max_change_in_happiness(happiness)
|
||||
for guest in list(happiness):
|
||||
happiness["me"][guest] = 0
|
||||
happiness[guest]["me"] = 0
|
||||
|
||||
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
|
||||
|
||||
answer_2 = max_change_in_happiness(happiness)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield max_change_in_happiness(happiness)
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal, cast
|
||||
from typing import Any, Iterator, Literal, cast
|
||||
|
||||
import parse # type: ignore
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Reindeer:
|
||||
@@ -13,50 +14,50 @@ class Reindeer:
|
||||
rest_time: int
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
reindeers: list[Reindeer] = []
|
||||
for line in lines:
|
||||
reindeer, speed, speed_time, rest_time = cast(
|
||||
tuple[str, int, int, int],
|
||||
parse.parse( # type: ignore
|
||||
"{} can fly {:d} km/s for {:d} seconds, "
|
||||
"but then must rest for {:d} seconds.",
|
||||
line,
|
||||
),
|
||||
)
|
||||
reindeers.append(
|
||||
Reindeer(name=reindeer, speed=speed, fly_time=speed_time, rest_time=rest_time)
|
||||
)
|
||||
reindeers: list[Reindeer] = []
|
||||
for line in lines:
|
||||
reindeer, speed, speed_time, rest_time = cast(
|
||||
tuple[str, int, int, int],
|
||||
parse.parse( # type: ignore
|
||||
"{} can fly {:d} km/s for {:d} seconds, "
|
||||
"but then must rest for {:d} seconds.",
|
||||
line,
|
||||
),
|
||||
)
|
||||
reindeers.append(
|
||||
Reindeer(
|
||||
name=reindeer, speed=speed, fly_time=speed_time, rest_time=rest_time
|
||||
)
|
||||
)
|
||||
|
||||
target = 1000 if len(reindeers) <= 2 else 2503
|
||||
target = 1000 if len(reindeers) <= 2 else 2503
|
||||
|
||||
states: dict[Reindeer, tuple[Literal["resting", "flying"], int]] = {
|
||||
reindeer: ("resting", 0) for reindeer in reindeers
|
||||
}
|
||||
distances: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
||||
points: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
||||
states: dict[Reindeer, tuple[Literal["resting", "flying"], int]] = {
|
||||
reindeer: ("resting", 0) for reindeer in reindeers
|
||||
}
|
||||
distances: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
||||
points: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
||||
|
||||
for time in range(target):
|
||||
for reindeer in reindeers:
|
||||
if states[reindeer][0] == "flying":
|
||||
distances[reindeer] += reindeer.speed
|
||||
for time in self.progress.wrap(range(target)):
|
||||
for reindeer in reindeers:
|
||||
if states[reindeer][0] == "flying":
|
||||
distances[reindeer] += reindeer.speed
|
||||
|
||||
top_distance = max(distances.values())
|
||||
for reindeer in reindeers:
|
||||
if distances[reindeer] == top_distance:
|
||||
points[reindeer] += 1
|
||||
top_distance = max(distances.values())
|
||||
for reindeer in reindeers:
|
||||
if distances[reindeer] == top_distance:
|
||||
points[reindeer] += 1
|
||||
|
||||
for reindeer in reindeers:
|
||||
if states[reindeer][1] == time:
|
||||
if states[reindeer][0] == "resting":
|
||||
states[reindeer] = ("flying", time + reindeer.fly_time)
|
||||
else:
|
||||
states[reindeer] = ("resting", time + reindeer.rest_time)
|
||||
for reindeer in reindeers:
|
||||
if states[reindeer][1] == time:
|
||||
if states[reindeer][0] == "resting":
|
||||
states[reindeer] = ("flying", time + reindeer.fly_time)
|
||||
else:
|
||||
states[reindeer] = ("resting", time + reindeer.rest_time)
|
||||
|
||||
|
||||
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}")
|
||||
yield max(distances.values())
|
||||
yield max(points.values()) - 1
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import math
|
||||
import sys
|
||||
from typing import Sequence, cast
|
||||
from typing import Any, Iterator, Sequence, cast
|
||||
|
||||
import parse # type: ignore
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def score(ingredients: list[list[int]], teaspoons: Sequence[int]) -> int:
|
||||
return math.prod(
|
||||
@@ -18,40 +19,38 @@ def score(ingredients: list[list[int]], teaspoons: Sequence[int]) -> int:
|
||||
)
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
ingredients: list[list[int]] = []
|
||||
for line in lines:
|
||||
_, *scores = cast(
|
||||
tuple[str, int, int, int, int, int],
|
||||
parse.parse( # type: ignore
|
||||
"{}: capacity {:d}, durability {:d}, flavor {:d}, "
|
||||
"texture {:d}, calories {:d}",
|
||||
line,
|
||||
),
|
||||
)
|
||||
ingredients.append(scores)
|
||||
|
||||
total_teaspoons = 100
|
||||
calories: list[int] = []
|
||||
scores: list[int] = []
|
||||
|
||||
for a in range(total_teaspoons + 1):
|
||||
for b in range(total_teaspoons + 1 - a):
|
||||
for c in range(total_teaspoons + 1 - a - b):
|
||||
teaspoons = (a, b, c, total_teaspoons - a - b - c)
|
||||
|
||||
scores.append(score(ingredients, teaspoons))
|
||||
calories.append(
|
||||
sum(
|
||||
ingredient[-1] * teaspoon
|
||||
for ingredient, teaspoon in zip(ingredients, teaspoons)
|
||||
)
|
||||
ingredients: list[list[int]] = []
|
||||
for line in lines:
|
||||
_, *scores = cast(
|
||||
tuple[str, int, int, int, int, int],
|
||||
parse.parse( # type: ignore
|
||||
"{}: capacity {:d}, durability {:d}, flavor {:d}, "
|
||||
"texture {:d}, calories {:d}",
|
||||
line,
|
||||
),
|
||||
)
|
||||
ingredients.append(scores)
|
||||
|
||||
total_teaspoons = 100
|
||||
calories: list[int] = []
|
||||
scores: list[int] = []
|
||||
|
||||
answer_1 = max(scores)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
for a in range(total_teaspoons + 1):
|
||||
for b in range(total_teaspoons + 1 - a):
|
||||
for c in range(total_teaspoons + 1 - a - b):
|
||||
teaspoons = (a, b, c, total_teaspoons - a - b - c)
|
||||
|
||||
answer_2 = max(score for score, calory in zip(scores, calories) if calory == 500)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
scores.append(score(ingredients, teaspoons))
|
||||
calories.append(
|
||||
sum(
|
||||
ingredient[-1] * teaspoon
|
||||
for ingredient, teaspoon in zip(ingredients, teaspoons)
|
||||
)
|
||||
)
|
||||
|
||||
yield max(scores)
|
||||
yield max(score for score, calory in zip(scores, calories) if calory == 500)
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import operator as op
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Callable
|
||||
from typing import Any, Callable, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
MFCSAM: dict[str, int] = {
|
||||
"children": 3,
|
||||
@@ -17,18 +18,10 @@ MFCSAM: dict[str, int] = {
|
||||
"perfumes": 1,
|
||||
}
|
||||
|
||||
lines = sys.stdin.readlines()
|
||||
|
||||
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:
|
||||
def match(
|
||||
aunts: list[dict[str, int]], operators: dict[str, Callable[[int, int], bool]]
|
||||
) -> int:
|
||||
return next(
|
||||
i
|
||||
for i, aunt in enumerate(aunts, start=1)
|
||||
@@ -36,16 +29,29 @@ def match(operators: dict[str, Callable[[int, int], bool]]) -> int:
|
||||
)
|
||||
|
||||
|
||||
answer_1 = match(defaultdict(lambda: op.eq))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
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}")
|
||||
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,
|
||||
defaultdict(
|
||||
lambda: op.eq,
|
||||
trees=op.gt,
|
||||
cats=op.gt,
|
||||
pomeranians=op.lt,
|
||||
goldfish=op.lt,
|
||||
),
|
||||
)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import sys
|
||||
from typing import Iterator
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def iter_combinations(value: int, containers: list[int]) -> Iterator[tuple[int, ...]]:
|
||||
@@ -16,15 +17,18 @@ def iter_combinations(value: int, containers: list[int]) -> Iterator[tuple[int,
|
||||
yield (containers[i],) + combination
|
||||
|
||||
|
||||
containers = [int(c) for c in sys.stdin.read().split()]
|
||||
total = 25 if len(containers) <= 5 else 150
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
containers = [int(c) for c in input.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)
|
||||
]
|
||||
|
||||
answer_1 = len(combinations)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
yield len(combinations)
|
||||
|
||||
min_containers = min(len(combination) for combination in combinations)
|
||||
|
||||
answer_2 = sum(1 for combination in combinations if len(combination) == min_containers)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
min_containers = min(len(combination) for combination in combinations)
|
||||
yield sum(
|
||||
1 for combination in combinations if len(combination) == min_containers
|
||||
)
|
||||
|
@@ -1,66 +1,66 @@
|
||||
import itertools
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
import numpy as np
|
||||
from numpy.typing import NDArray
|
||||
|
||||
grid0 = np.array([[c == "#" for c in line] for line in sys.stdin.read().splitlines()])
|
||||
from ..base import BaseSolver
|
||||
|
||||
# add an always off circle around
|
||||
grid0 = np.concatenate(
|
||||
[
|
||||
np.zeros((grid0.shape[0] + 2, 1), dtype=bool),
|
||||
np.concatenate(
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
grid0 = np.array([[c == "#" for c in line] for line in input.splitlines()])
|
||||
|
||||
# add an always off circle around
|
||||
grid0 = np.concatenate(
|
||||
[
|
||||
np.zeros((1, grid0.shape[1]), dtype=bool),
|
||||
grid0,
|
||||
np.zeros((1, grid0.shape[1]), dtype=bool),
|
||||
]
|
||||
),
|
||||
np.zeros((grid0.shape[0] + 2, 1), dtype=bool),
|
||||
],
|
||||
axis=1,
|
||||
)
|
||||
np.zeros((grid0.shape[0] + 2, 1), dtype=bool),
|
||||
np.concatenate(
|
||||
[
|
||||
np.zeros((1, grid0.shape[1]), dtype=bool),
|
||||
grid0,
|
||||
np.zeros((1, grid0.shape[1]), dtype=bool),
|
||||
]
|
||||
),
|
||||
np.zeros((grid0.shape[0] + 2, 1), dtype=bool),
|
||||
],
|
||||
axis=1,
|
||||
)
|
||||
|
||||
moves = list(itertools.product([-1, 0, 1], repeat=2))
|
||||
moves.remove((0, 0))
|
||||
moves = list(itertools.product([-1, 0, 1], repeat=2))
|
||||
moves.remove((0, 0))
|
||||
|
||||
jjs, iis = np.meshgrid(
|
||||
np.arange(1, grid0.shape[0] - 1, dtype=int),
|
||||
np.arange(1, grid0.shape[1] - 1, dtype=int),
|
||||
)
|
||||
iis, jjs = iis.flatten(), jjs.flatten()
|
||||
jjs, iis = np.meshgrid(
|
||||
np.arange(1, grid0.shape[0] - 1, dtype=int),
|
||||
np.arange(1, grid0.shape[1] - 1, dtype=int),
|
||||
)
|
||||
iis, jjs = iis.flatten(), jjs.flatten()
|
||||
|
||||
ins = iis[:, None] + np.array(moves)[:, 0]
|
||||
jns = jjs[:, None] + np.array(moves)[:, 1]
|
||||
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]
|
||||
|
||||
def game_of_life(grid: NDArray[np.bool_]) -> NDArray[np.bool_]:
|
||||
neighbors_on = grid[ins, jns].sum(axis=1)
|
||||
cells_on = grid[iis, jjs]
|
||||
grid = np.zeros_like(grid)
|
||||
grid[iis, jjs] = (neighbors_on == 3) | (cells_on & (neighbors_on == 2))
|
||||
|
||||
grid = np.zeros_like(grid)
|
||||
grid[iis, jjs] = (neighbors_on == 3) | (cells_on & (neighbors_on == 2))
|
||||
return grid
|
||||
|
||||
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()
|
||||
|
||||
grid = grid0
|
||||
n_steps = 4 if len(grid) < 10 else 100
|
||||
for _ in range(n_steps):
|
||||
grid = game_of_life(grid)
|
||||
n_steps = 5 if len(grid) < 10 else 100
|
||||
grid = grid0
|
||||
for _ in range(n_steps):
|
||||
grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True
|
||||
grid = game_of_life(grid)
|
||||
|
||||
answer_1 = grid.sum()
|
||||
print(f"answer 1 is {answer_1}")
|
||||
grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True
|
||||
|
||||
|
||||
n_steps = 5 if len(grid) < 10 else 100
|
||||
grid = grid0
|
||||
for _ in range(n_steps):
|
||||
grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True
|
||||
grid = game_of_life(grid)
|
||||
|
||||
grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True
|
||||
|
||||
answer_2 = sum(cell for line in grid for cell in line)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield sum(cell for line in grid for cell in line)
|
||||
|
@@ -1,56 +1,58 @@
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
replacements_s, molecule = sys.stdin.read().split("\n\n")
|
||||
|
||||
REPLACEMENTS: dict[str, list[str]] = defaultdict(list)
|
||||
for replacement_s in replacements_s.splitlines():
|
||||
p = replacement_s.split(" => ")
|
||||
REPLACEMENTS[p[0]].append(p[1])
|
||||
molecule = molecule.strip()
|
||||
|
||||
generated = [
|
||||
molecule[:i] + replacement + molecule[i + len(symbol) :]
|
||||
for symbol, replacements in REPLACEMENTS.items()
|
||||
for replacement in replacements
|
||||
for i in range(len(molecule))
|
||||
if molecule[i:].startswith(symbol)
|
||||
]
|
||||
|
||||
answer_1 = len(set(generated))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
inversion: dict[str, str] = {
|
||||
replacement: symbol
|
||||
for symbol, replacements in REPLACEMENTS.items()
|
||||
for replacement in replacements
|
||||
}
|
||||
|
||||
# there is actually only one way to create the molecule, and we can greedily replace
|
||||
# tokens with their replacements, e.g., if H => OH then we can replace OH by H directly
|
||||
# without thinking
|
||||
|
||||
count = 0
|
||||
while molecule != "e":
|
||||
i = 0
|
||||
m2 = ""
|
||||
while i < len(molecule):
|
||||
found = False
|
||||
for replacement in inversion:
|
||||
if molecule[i:].startswith(replacement):
|
||||
m2 += inversion[replacement]
|
||||
i += len(replacement)
|
||||
count += 1
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
m2 += molecule[i]
|
||||
i += 1
|
||||
|
||||
# print(m2)
|
||||
molecule = m2
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
answer_2 = count
|
||||
print(f"answer 2 is {count}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
replacements_s, molecule = input.split("\n\n")
|
||||
|
||||
REPLACEMENTS: dict[str, list[str]] = defaultdict(list)
|
||||
for replacement_s in replacements_s.splitlines():
|
||||
p = replacement_s.split(" => ")
|
||||
REPLACEMENTS[p[0]].append(p[1])
|
||||
molecule = molecule.strip()
|
||||
|
||||
generated = [
|
||||
molecule[:i] + replacement + molecule[i + len(symbol) :]
|
||||
for symbol, replacements in REPLACEMENTS.items()
|
||||
for replacement in replacements
|
||||
for i in range(len(molecule))
|
||||
if molecule[i:].startswith(symbol)
|
||||
]
|
||||
|
||||
yield len(set(generated))
|
||||
|
||||
inversion: dict[str, str] = {
|
||||
replacement: symbol
|
||||
for symbol, replacements in REPLACEMENTS.items()
|
||||
for replacement in replacements
|
||||
}
|
||||
|
||||
# there is actually only one way to create the molecule, and we can greedily replace
|
||||
# tokens with their replacements, e.g., if H => OH then we can replace OH by H directly
|
||||
# without thinking
|
||||
|
||||
count = 0
|
||||
while molecule != "e":
|
||||
i = 0
|
||||
m2 = ""
|
||||
while i < len(molecule):
|
||||
found = False
|
||||
for replacement in inversion:
|
||||
if molecule[i:].startswith(replacement):
|
||||
m2 += inversion[replacement]
|
||||
i += len(replacement)
|
||||
count += 1
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
m2 += molecule[i]
|
||||
i += 1
|
||||
|
||||
# print(m2)
|
||||
molecule = m2
|
||||
|
||||
yield count
|
||||
|
@@ -1,20 +1,24 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
import numpy as np
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
length, width, height = np.array(
|
||||
[[int(c) for c in line.split("x")] for line in lines]
|
||||
).T
|
||||
|
||||
lw, wh, hl = (length * width, width * height, height * length)
|
||||
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()]
|
||||
).T
|
||||
|
||||
answer_1 = np.sum(2 * (lw + wh + hl) + np.min(np.stack([lw, wh, hl]), axis=0))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
lw, wh, hl = (length * width, width * height, height * length)
|
||||
|
||||
answer_2 = np.sum(
|
||||
length * width * height
|
||||
+ 2 * np.min(np.stack([length + width, length + height, height + width]), axis=0)
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield np.sum(2 * (lw + wh + hl) + np.min(np.stack([lw, wh, hl]), axis=0))
|
||||
|
||||
yield np.sum(
|
||||
length * width * height
|
||||
+ 2
|
||||
* np.min(
|
||||
np.stack([length + width, length + height, height + width]), axis=0
|
||||
)
|
||||
)
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import itertools
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
target = int(sys.stdin.read())
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def presents(n: int, elf: int, max: int = target) -> int:
|
||||
def presents(n: int, elf: int, max: int) -> int:
|
||||
count = 0
|
||||
k = 1
|
||||
while k * k < n:
|
||||
@@ -21,8 +21,9 @@ def presents(n: int, elf: int, max: int = target) -> int:
|
||||
return count
|
||||
|
||||
|
||||
answer_1 = next(n for n in itertools.count(1) if presents(n, 10) >= target)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
target = int(input)
|
||||
|
||||
answer_2 = next(n for n in itertools.count(1) if presents(n, 11, 50) >= target)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
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)
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import itertools
|
||||
import sys
|
||||
from math import ceil
|
||||
from typing import TypeAlias
|
||||
from typing import Any, Iterator, TypeAlias
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
Modifier: TypeAlias = tuple[str, int, int, int]
|
||||
|
||||
@@ -33,34 +34,31 @@ RINGS: list[Modifier] = [
|
||||
]
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
player_hp = 100
|
||||
player_hp = 100
|
||||
|
||||
boss_attack = int(lines[1].split(":")[1].strip())
|
||||
boss_armor = int(lines[2].split(":")[1].strip())
|
||||
boss_hp = int(lines[0].split(":")[1].strip())
|
||||
boss_attack = int(lines[1].split(":")[1].strip())
|
||||
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]:
|
||||
continue
|
||||
|
||||
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]:
|
||||
continue
|
||||
cost, player_attack, player_armor = (
|
||||
sum(equipment[1:][k] for equipment in equipments) for k in range(3)
|
||||
)
|
||||
|
||||
cost, player_attack, player_armor = (
|
||||
sum(equipment[1:][k] for equipment in equipments) for k in range(3)
|
||||
)
|
||||
if ceil(boss_hp / max(1, player_attack - boss_armor)) <= ceil(
|
||||
player_hp / max(1, boss_attack - player_armor)
|
||||
):
|
||||
min_cost = min(cost, min_cost)
|
||||
else:
|
||||
max_cost = max(cost, max_cost)
|
||||
|
||||
if ceil(boss_hp / max(1, player_attack - boss_armor)) <= ceil(
|
||||
player_hp / max(1, boss_attack - player_armor)
|
||||
):
|
||||
min_cost = min(cost, min_cost)
|
||||
else:
|
||||
max_cost = max(cost, max_cost)
|
||||
|
||||
|
||||
answer_1 = min_cost
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = max_cost
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield min_cost
|
||||
yield max_cost
|
||||
|
@@ -1,8 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import heapq
|
||||
import sys
|
||||
from typing import Literal, TypeAlias, cast
|
||||
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
PlayerType: TypeAlias = Literal["player", "boss"]
|
||||
SpellType: TypeAlias = Literal["magic missile", "drain", "shield", "poison", "recharge"]
|
||||
@@ -62,17 +63,6 @@ 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
|
||||
@@ -88,6 +78,16 @@ 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,23 +155,28 @@ def play(
|
||||
return winning_node
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
player_hp = 50
|
||||
player_mana = 500
|
||||
player_armor = 0
|
||||
player_hp = 50
|
||||
player_mana = 500
|
||||
player_armor = 0
|
||||
|
||||
boss_hp = int(lines[0].split(":")[1].strip())
|
||||
boss_attack = int(lines[1].split(":")[1].strip())
|
||||
boss_hp = int(lines[0].split(":")[1].strip())
|
||||
boss_attack = int(lines[1].split(":")[1].strip())
|
||||
|
||||
answer_1 = sum(
|
||||
c
|
||||
for _, c in play(player_hp, player_mana, player_armor, boss_hp, boss_attack, False)
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
yield sum(
|
||||
c
|
||||
for _, c in play(
|
||||
player_hp, player_mana, player_armor, boss_hp, boss_attack, False
|
||||
)
|
||||
)
|
||||
|
||||
# 1242 (not working)
|
||||
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}")
|
||||
# 1242 (not working)
|
||||
yield sum(
|
||||
c
|
||||
for _, c in play(
|
||||
player_hp, player_mana, player_armor, boss_hp, boss_attack, True
|
||||
)
|
||||
)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
line = sys.stdin.read().strip()
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def process(directions: str) -> dict[tuple[int, int], int]:
|
||||
@@ -27,8 +27,7 @@ def process(directions: str) -> dict[tuple[int, int], int]:
|
||||
return counts
|
||||
|
||||
|
||||
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}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
yield len(process(input))
|
||||
yield len(process(input[::2]) | process(input[1::2]))
|
||||
|
@@ -1,16 +1,20 @@
|
||||
import hashlib
|
||||
import itertools
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
line = sys.stdin.read().strip()
|
||||
from ..base import BaseSolver
|
||||
|
||||
it = iter(itertools.count(1))
|
||||
answer_1 = next(
|
||||
i for i in it if hashlib.md5(f"{line}{i}".encode()).hexdigest().startswith("00000")
|
||||
)
|
||||
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}")
|
||||
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")
|
||||
)
|
||||
yield next(
|
||||
i
|
||||
for i in it
|
||||
if hashlib.md5(f"{input}{i}".encode()).hexdigest().startswith("000000")
|
||||
)
|
||||
|
@@ -1,4 +1,6 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
VOWELS = "aeiou"
|
||||
FORBIDDEN = {"ab", "cd", "pq", "xy"}
|
||||
@@ -27,10 +29,8 @@ def is_nice_2(s: str) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
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}")
|
||||
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))
|
||||
|
@@ -1,33 +1,32 @@
|
||||
import sys
|
||||
from typing import Literal, cast
|
||||
from typing import Any, Iterator, Literal, cast
|
||||
|
||||
import numpy as np
|
||||
import parse # type: ignore
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
lights_1 = np.zeros((1000, 1000), dtype=bool)
|
||||
lights_2 = np.zeros((1000, 1000), dtype=int)
|
||||
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
|
||||
)
|
||||
ex, ey = ex + 1, ey + 1
|
||||
|
||||
match action:
|
||||
case "turn on":
|
||||
lights_1[sx:ex, sy:ey] = True
|
||||
lights_2[sx:ex, sy:ey] += 1
|
||||
case "turn off":
|
||||
lights_1[sx:ex, sy:ey] = False
|
||||
lights_2[sx:ex, sy:ey] = np.maximum(lights_2[sx:ex, sy:ey] - 1, 0)
|
||||
case "toggle":
|
||||
lights_1[sx:ex, sy:ey] = ~lights_1[sx:ex, sy:ey]
|
||||
lights_2[sx:ex, sy:ey] += 2
|
||||
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():
|
||||
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
|
||||
)
|
||||
ex, ey = ex + 1, ey + 1
|
||||
|
||||
answer_1 = lights_1.sum()
|
||||
print(f"answer 1 is {answer_1}")
|
||||
match action:
|
||||
case "turn on":
|
||||
lights_1[sx:ex, sy:ey] = True
|
||||
lights_2[sx:ex, sy:ey] += 1
|
||||
case "turn off":
|
||||
lights_1[sx:ex, sy:ey] = False
|
||||
lights_2[sx:ex, sy:ey] = np.maximum(lights_2[sx:ex, sy:ey] - 1, 0)
|
||||
case "toggle":
|
||||
lights_1[sx:ex, sy:ey] = ~lights_1[sx:ex, sy:ey]
|
||||
lights_2[sx:ex, sy:ey] += 2
|
||||
|
||||
answer_2 = lights_2.sum()
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield lights_1.sum()
|
||||
yield lights_2.sum()
|
||||
|
@@ -1,11 +1,7 @@
|
||||
import logging
|
||||
import operator
|
||||
import os
|
||||
import sys
|
||||
from typing import Callable
|
||||
from typing import Any, Callable, Iterator
|
||||
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
from ..base import BaseSolver
|
||||
|
||||
OPERATORS = {
|
||||
"AND": operator.and_,
|
||||
@@ -36,48 +32,6 @@ def value_of(key: str) -> tuple[str, Callable[[dict[str, int]], int]]:
|
||||
return key, lambda values: values[key]
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
signals: Signals = {}
|
||||
values: dict[str, int] = {"": 0}
|
||||
|
||||
for line in lines:
|
||||
command, signal = line.split(" -> ")
|
||||
|
||||
if command.startswith("NOT"):
|
||||
name = command.split(" ")[1]
|
||||
signals[signal] = (
|
||||
(name, ""),
|
||||
(lambda values, _n=name: values[_n], lambda _v: 0),
|
||||
lambda a, _b: ~a,
|
||||
)
|
||||
|
||||
elif not any(command.find(name) >= 0 for name in OPERATORS):
|
||||
try:
|
||||
values[signal] = int(command)
|
||||
except ValueError:
|
||||
signals[signal] = (
|
||||
(command, ""),
|
||||
(lambda values, _c=command: values[_c], lambda _v: 0),
|
||||
lambda a, _b: a,
|
||||
)
|
||||
|
||||
else:
|
||||
op: Callable[[int, int], int] = zero_op
|
||||
lhs_s, rhs_s = "", ""
|
||||
|
||||
for name in OPERATORS:
|
||||
if command.find(name) >= 0:
|
||||
op = OPERATORS[name]
|
||||
lhs_s, rhs_s = command.split(f" {name} ")
|
||||
break
|
||||
|
||||
lhs_s, lhs_fn = value_of(lhs_s)
|
||||
rhs_s, rhs_fn = value_of(rhs_s)
|
||||
|
||||
signals[signal] = ((lhs_s, rhs_s), (lhs_fn, rhs_fn), op)
|
||||
|
||||
|
||||
def process(
|
||||
signals: Signals,
|
||||
values: dict[str, int],
|
||||
@@ -91,11 +45,52 @@ def process(
|
||||
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}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any] | None:
|
||||
lines = input.splitlines()
|
||||
|
||||
values_2 = process(signals.copy(), values | {"b": values_1["a"]})
|
||||
answer_2 = values_2["a"]
|
||||
print(f"answer 2 is {answer_2}")
|
||||
signals: Signals = {}
|
||||
values: dict[str, int] = {"": 0}
|
||||
|
||||
for line in lines:
|
||||
command, signal = line.split(" -> ")
|
||||
|
||||
if command.startswith("NOT"):
|
||||
name = command.split(" ")[1]
|
||||
signals[signal] = (
|
||||
(name, ""),
|
||||
(lambda values, _n=name: values[_n], lambda _v: 0),
|
||||
lambda a, _b: ~a,
|
||||
)
|
||||
|
||||
elif not any(command.find(name) >= 0 for name in OPERATORS):
|
||||
try:
|
||||
values[signal] = int(command)
|
||||
except ValueError:
|
||||
signals[signal] = (
|
||||
(command, ""),
|
||||
(lambda values, _c=command: values[_c], lambda _v: 0),
|
||||
lambda a, _b: a,
|
||||
)
|
||||
|
||||
else:
|
||||
op: Callable[[int, int], int] = zero_op
|
||||
lhs_s, rhs_s = "", ""
|
||||
|
||||
for name in OPERATORS:
|
||||
if command.find(name) >= 0:
|
||||
op = OPERATORS[name]
|
||||
lhs_s, rhs_s = command.split(f" {name} ")
|
||||
break
|
||||
|
||||
lhs_s, lhs_fn = value_of(lhs_s)
|
||||
rhs_s, rhs_fn = value_of(rhs_s)
|
||||
|
||||
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"]
|
||||
|
@@ -1,35 +1,32 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
answer_1 = sum(
|
||||
# left and right quotes (not in memory)
|
||||
2
|
||||
# each \\ adds one character in the literals (compared to memory)
|
||||
+ line.count(R"\\")
|
||||
# each \" adds one character in the literals (compared to memory)
|
||||
+ line[1:-1].count(R"\"")
|
||||
# each \xFF adds 3 characters in the literals (compared to memory), but we must not
|
||||
# count A\\x (A != \), but we must count A\\\x (A != \) - in practice we should also
|
||||
# avoid \\\\x, etc., but this does not occur in the examples and the actual input
|
||||
+ 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(
|
||||
# left and right quotes (not in memory)
|
||||
2
|
||||
# each \\ adds one character in the literals (compared to memory)
|
||||
+ line.count(R"\\")
|
||||
# each \" adds one character in the literals (compared to memory)
|
||||
+ line[1:-1].count(R"\"")
|
||||
# each \xFF adds 3 characters in the literals (compared to memory), but we must not
|
||||
# count A\\x (A != \), but we must count A\\\x (A != \) - in practice we should also
|
||||
# avoid \\\\x, etc., but this does not occur in the examples and the actual input
|
||||
+ 3 * (line.count(R"\x") - line.count(R"\\x") + line.count(R"\\\x"))
|
||||
for line in lines
|
||||
)
|
||||
|
||||
answer_2 = sum(
|
||||
# needs to wrap in quotes (2 characters)
|
||||
2
|
||||
# needs to escape every \ with an extra \
|
||||
+ line.count("\\")
|
||||
# needs to escape every " with an extra \ (including the first and last ones)
|
||||
+ line.count('"')
|
||||
for line in lines
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield sum(
|
||||
# needs to wrap in quotes (2 characters)
|
||||
2
|
||||
# needs to escape every \ with an extra \
|
||||
+ line.count("\\")
|
||||
# needs to escape every " with an extra \ (including the first and last ones)
|
||||
+ line.count('"')
|
||||
for line in lines
|
||||
)
|
||||
|
@@ -1,27 +1,28 @@
|
||||
import itertools
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import cast
|
||||
from typing import Any, Iterator, cast
|
||||
|
||||
import parse # type: ignore
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
distances: dict[str, dict[str, int]] = defaultdict(dict)
|
||||
for line in lines:
|
||||
origin, destination, length = cast(
|
||||
tuple[str, str, int],
|
||||
parse.parse("{} to {} = {:d}", line), # type: ignore
|
||||
)
|
||||
distances[origin][destination] = distances[destination][origin] = length
|
||||
|
||||
distance_of_routes = {
|
||||
route: sum(distances[o][d] for o, d in zip(route[:-1], route[1:]))
|
||||
for route in map(tuple, itertools.permutations(distances))
|
||||
}
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
answer_1 = min(distance_of_routes.values())
|
||||
print(f"answer 1 is {answer_1}")
|
||||
distances: dict[str, dict[str, int]] = defaultdict(dict)
|
||||
for line in lines:
|
||||
origin, destination, length = cast(
|
||||
tuple[str, str, int],
|
||||
parse.parse("{} to {} = {:d}", line), # type: ignore
|
||||
)
|
||||
distances[origin][destination] = distances[destination][origin] = length
|
||||
|
||||
answer_2 = max(distance_of_routes.values())
|
||||
print(f"answer 2 is {answer_2}")
|
||||
distance_of_routes = {
|
||||
route: sum(distances[o][d] for o, d in zip(route[:-1], route[1:]))
|
||||
for route in map(tuple, itertools.permutations(distances))
|
||||
}
|
||||
|
||||
yield min(distance_of_routes.values())
|
||||
yield max(distance_of_routes.values())
|
||||
|
@@ -1,90 +1,96 @@
|
||||
import sys
|
||||
from typing import Any
|
||||
from typing import Any, Iterator
|
||||
|
||||
import numpy as np
|
||||
import parse # type: ignore
|
||||
from numpy.typing import NDArray
|
||||
|
||||
|
||||
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():
|
||||
d = abs(sx - bx) + abs(sy - by) # closest
|
||||
|
||||
no_beacons_row_l.append(sx - np.arange(0, d - abs(sy - row) + 1)) # type: ignore
|
||||
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
|
||||
|
||||
return len(no_beacons_row)
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def part2_intervals(
|
||||
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], xy_max: int
|
||||
) -> tuple[int, int, int]:
|
||||
from tqdm import trange
|
||||
class Solver(BaseSolver):
|
||||
def part1(
|
||||
self, 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():
|
||||
d = abs(sx - bx) + abs(sy - by) # closest
|
||||
|
||||
no_beacons_row_l.append(sx - np.arange(0, d - abs(sy - row) + 1)) # type: ignore
|
||||
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
|
||||
|
||||
return len(no_beacons_row)
|
||||
|
||||
def part2_intervals(
|
||||
self, 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)):
|
||||
its: list[tuple[int, int]] = []
|
||||
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
||||
d = abs(sx - bx) + abs(sy - by)
|
||||
dx = d - abs(sy - y)
|
||||
|
||||
if dx >= 0:
|
||||
its.append((max(0, sx - dx), min(sx + dx, xy_max)))
|
||||
|
||||
its = sorted(its)
|
||||
_, e = its[0]
|
||||
|
||||
for si, ei in its[1:]:
|
||||
if si > e + 1:
|
||||
return si - 1, y, 4_000_000 * (si - 1) + y
|
||||
if ei > e:
|
||||
e = ei
|
||||
|
||||
return (0, 0, 0)
|
||||
|
||||
def part2_cplex(
|
||||
self, sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], xy_max: int
|
||||
) -> tuple[int, int, int]:
|
||||
from docplex.mp.model import Model
|
||||
|
||||
m = Model()
|
||||
|
||||
x, y = m.continuous_var_list(2, ub=xy_max, name=["x", "y"])
|
||||
|
||||
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)
|
||||
dx = d - abs(sy - y)
|
||||
m.add_constraint(
|
||||
m.abs(x - sx) + m.abs(y - sy) >= d + 1, ctname=f"ct_{sx}_{sy}"
|
||||
) # type: ignore
|
||||
|
||||
if dx >= 0:
|
||||
its.append((max(0, sx - dx), min(sx + dx, xy_max)))
|
||||
m.set_objective("min", x + y)
|
||||
|
||||
its = sorted(its)
|
||||
_, e = its[0]
|
||||
s = m.solve()
|
||||
assert s is not None
|
||||
|
||||
for si, ei in its[1:]:
|
||||
if si > e + 1:
|
||||
return si - 1, y, 4_000_000 * (si - 1) + y
|
||||
if ei > e:
|
||||
e = ei
|
||||
vx = int(s.get_value(x))
|
||||
vy = int(s.get_value(y))
|
||||
return vx, vy, 4_000_000 * vx + vy
|
||||
|
||||
return (0, 0, 0)
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]] = {}
|
||||
|
||||
def part2_cplex(
|
||||
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], xy_max: int
|
||||
) -> tuple[int, int, int]:
|
||||
from docplex.mp.model import Model
|
||||
for line in lines:
|
||||
r: dict[str, str] = parse.parse( # type: ignore
|
||||
"Sensor at x={sx}, y={sy}: closest beacon is at x={bx}, y={by}", line
|
||||
)
|
||||
sensor_to_beacon[int(r["sx"]), int(r["sy"])] = (int(r["bx"]), int(r["by"]))
|
||||
|
||||
m = Model()
|
||||
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
|
||||
|
||||
x, y = m.continuous_var_list(2, ub=xy_max, name=["x", "y"])
|
||||
yield self.part1(sensor_to_beacon, row)
|
||||
|
||||
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, ctname=f"ct_{sx}_{sy}") # type: ignore
|
||||
|
||||
m.set_objective("min", x + y)
|
||||
|
||||
s = m.solve()
|
||||
assert s is not None
|
||||
|
||||
vx = int(s.get_value(x))
|
||||
vy = int(s.get_value(y))
|
||||
return vx, vy, 4_000_000 * vx + vy
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]] = {}
|
||||
|
||||
for line in lines:
|
||||
r: dict[str, str] = parse.parse( # type: ignore
|
||||
"Sensor at x={sx}, y={sy}: closest beacon is at x={bx}, y={by}", line
|
||||
)
|
||||
sensor_to_beacon[int(r["sx"]), int(r["sy"])] = (int(r["bx"]), int(r["by"]))
|
||||
|
||||
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
|
||||
|
||||
print(f"answer 1 is {part1(sensor_to_beacon, row)}")
|
||||
|
||||
# x, y, a2 = part2_cplex(sensor_to_beacon, xy_max)
|
||||
x, y, a2 = part2_intervals(sensor_to_beacon, xy_max)
|
||||
print(f"answer 2 is {a2} (x={x}, y={y})")
|
||||
# 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
|
||||
|
@@ -1,27 +1,9 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
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",
|
||||
)
|
||||
)
|
||||
}
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def find_values(lookups: dict[str, int]) -> list[int]:
|
||||
def find_values(lines: list[str], lookups: dict[str, int]) -> list[int]:
|
||||
values: list[int] = []
|
||||
|
||||
for line in filter(bool, lines):
|
||||
@@ -41,5 +23,27 @@ def find_values(lookups: dict[str, int]) -> list[int]:
|
||||
return values
|
||||
|
||||
|
||||
print(f"answer 1 is {sum(find_values(lookups_1))}")
|
||||
print(f"answer 2 is {sum(find_values(lookups_2))}")
|
||||
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))
|
||||
|
@@ -1,100 +1,100 @@
|
||||
import os
|
||||
import sys
|
||||
from typing import Literal, cast
|
||||
from typing import Any, Iterator, Literal, cast
|
||||
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
from ..base import BaseSolver
|
||||
|
||||
Symbol = Literal["|", "-", "L", "J", "7", "F", ".", "S"]
|
||||
|
||||
lines: list[list[Symbol]] = [
|
||||
[cast(Symbol, symbol) for symbol in line] for line in sys.stdin.read().splitlines()
|
||||
]
|
||||
|
||||
# find starting point
|
||||
si, sj = next(
|
||||
(i, j)
|
||||
for i in range(len(lines))
|
||||
for j in range(len(lines[0]))
|
||||
if lines[i][j] == "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()
|
||||
]
|
||||
|
||||
# find one of the two outputs
|
||||
ni, nj = si, sj
|
||||
for ni, nj, chars in (
|
||||
(si - 1, sj, "|7F"),
|
||||
(si + 1, sj, "|LJ"),
|
||||
(si, sj - 1, "-LF"),
|
||||
(si, sj + 1, "-J7"),
|
||||
):
|
||||
if lines[ni][nj] in chars:
|
||||
break
|
||||
# find starting point
|
||||
si, sj = next(
|
||||
(i, j)
|
||||
for i in range(len(lines))
|
||||
for j in range(len(lines[0]))
|
||||
if lines[i][j] == "S"
|
||||
)
|
||||
|
||||
# part 1 - find the loop (re-used in part 2)
|
||||
loop = [(si, sj), (ni, nj)]
|
||||
while True:
|
||||
pi, pj = loop[-2]
|
||||
i, j = loop[-1]
|
||||
# find one of the two outputs
|
||||
ni, nj = si, sj
|
||||
for ni, nj, chars in (
|
||||
(si - 1, sj, "|7F"),
|
||||
(si + 1, sj, "|LJ"),
|
||||
(si, sj - 1, "-LF"),
|
||||
(si, sj + 1, "-J7"),
|
||||
):
|
||||
if lines[ni][nj] in chars:
|
||||
break
|
||||
|
||||
sym = lines[i][j]
|
||||
# part 1 - find the loop (re-used in part 2)
|
||||
loop = [(si, sj), (ni, nj)]
|
||||
while True:
|
||||
pi, pj = loop[-2]
|
||||
i, j = loop[-1]
|
||||
|
||||
if sym == "|" and pi > i or sym in "JL" and pi == i:
|
||||
i -= 1
|
||||
elif sym == "|" and pi < i or sym in "7F" and pi == i:
|
||||
i += 1
|
||||
elif sym == "-" and pj > j or sym in "J7" and pj == j:
|
||||
j -= 1
|
||||
elif sym == "-" and pj < j or sym in "LF" and pj == j:
|
||||
j += 1
|
||||
sym = lines[i][j]
|
||||
|
||||
if (i, j) == (si, sj):
|
||||
break
|
||||
if sym == "|" and pi > i or sym in "JL" and pi == i:
|
||||
i -= 1
|
||||
elif sym == "|" and pi < i or sym in "7F" and pi == i:
|
||||
i += 1
|
||||
elif sym == "-" and pj > j or sym in "J7" and pj == j:
|
||||
j -= 1
|
||||
elif sym == "-" and pj < j or sym in "LF" and pj == j:
|
||||
j += 1
|
||||
|
||||
loop.append((i, j))
|
||||
|
||||
answer_1 = len(loop) // 2
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
|
||||
# replace S by an appropriate character for the loop below
|
||||
di1, dj1 = loop[1][0] - loop[0][0], loop[1][1] - loop[0][1]
|
||||
di2, dj2 = loop[0][0] - loop[-1][0], loop[0][1] - loop[-1][1]
|
||||
mapping: dict[tuple[int, int], dict[tuple[int, int], Symbol]] = {
|
||||
(0, 1): {(0, 1): "-", (-1, 0): "F", (1, 0): "L"},
|
||||
(0, -1): {(0, -1): "-", (-1, 0): "7", (1, 0): "J"},
|
||||
(1, 0): {(1, 0): "|", (0, 1): "7", (0, -1): "F"},
|
||||
(-1, 0): {(-1, 0): "|", (0, -1): "L", (0, 1): "J"},
|
||||
}
|
||||
lines[si][sj] = mapping[di1, dj1][di2, dj2]
|
||||
|
||||
# find the points inside the loop using an adaptation of ray casting for a discrete
|
||||
# grid (https://stackoverflow.com/a/218081/2666289)
|
||||
#
|
||||
# use a set for faster '... in loop' check
|
||||
#
|
||||
loop_s = set(loop)
|
||||
inside: set[tuple[int, int]] = set()
|
||||
for i in range(len(lines)):
|
||||
cnt = 0
|
||||
for j in range(len(lines[0])):
|
||||
if (i, j) not in loop_s and cnt % 2 == 1:
|
||||
inside.add((i, j))
|
||||
|
||||
if (i, j) in loop_s and lines[i][j] in "|LJ":
|
||||
cnt += 1
|
||||
|
||||
if VERBOSE:
|
||||
for i in range(len(lines)):
|
||||
for j in range(len(lines[0])):
|
||||
if (i, j) == (si, sj):
|
||||
print("\033[91mS\033[0m", end="")
|
||||
elif (i, j) in loop:
|
||||
print(lines[i][j], end="")
|
||||
elif (i, j) in inside:
|
||||
print("\033[92mI\033[0m", end="")
|
||||
else:
|
||||
print(".", end="")
|
||||
print()
|
||||
break
|
||||
|
||||
answer_2 = len(inside)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
loop.append((i, j))
|
||||
|
||||
yield len(loop) // 2
|
||||
|
||||
# part 2
|
||||
|
||||
# replace S by an appropriate character for the loop below
|
||||
di1, dj1 = loop[1][0] - loop[0][0], loop[1][1] - loop[0][1]
|
||||
di2, dj2 = loop[0][0] - loop[-1][0], loop[0][1] - loop[-1][1]
|
||||
mapping: dict[tuple[int, int], dict[tuple[int, int], Symbol]] = {
|
||||
(0, 1): {(0, 1): "-", (-1, 0): "F", (1, 0): "L"},
|
||||
(0, -1): {(0, -1): "-", (-1, 0): "7", (1, 0): "J"},
|
||||
(1, 0): {(1, 0): "|", (0, 1): "7", (0, -1): "F"},
|
||||
(-1, 0): {(-1, 0): "|", (0, -1): "L", (0, 1): "J"},
|
||||
}
|
||||
lines[si][sj] = mapping[di1, dj1][di2, dj2]
|
||||
|
||||
# find the points inside the loop using an adaptation of ray casting for a discrete
|
||||
# grid (https://stackoverflow.com/a/218081/2666289)
|
||||
#
|
||||
# use a set for faster '... in loop' check
|
||||
#
|
||||
loop_s = set(loop)
|
||||
inside: set[tuple[int, int]] = set()
|
||||
for i in range(len(lines)):
|
||||
cnt = 0
|
||||
for j in range(len(lines[0])):
|
||||
if (i, j) not in loop_s and cnt % 2 == 1:
|
||||
inside.add((i, j))
|
||||
|
||||
if (i, j) in loop_s and lines[i][j] in "|LJ":
|
||||
cnt += 1
|
||||
|
||||
if self.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"
|
||||
elif (i, j) in loop:
|
||||
s += lines[i][j]
|
||||
elif (i, j) in inside:
|
||||
s += "\033[92mI\033[0m"
|
||||
else:
|
||||
s += "."
|
||||
self.logger.info(s)
|
||||
|
||||
yield len(inside)
|
||||
|
@@ -1,41 +1,42 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
import numpy as np
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
data = np.array([[c == "#" for c in line] for line in lines])
|
||||
|
||||
rows = {c for c in range(data.shape[0]) if not data[c, :].any()}
|
||||
columns = {c for c in range(data.shape[1]) if not data[:, c].any()}
|
||||
|
||||
galaxies_y, galaxies_x = np.where(data) # type: ignore
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def compute_total_distance(expansion: int) -> int:
|
||||
distances: list[int] = []
|
||||
for g1 in range(len(galaxies_y)):
|
||||
x1, y1 = int(galaxies_x[g1]), int(galaxies_y[g1])
|
||||
for g2 in range(g1 + 1, len(galaxies_y)):
|
||||
x2, y2 = int(galaxies_x[g2]), int(galaxies_y[g2])
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
dx = sum(
|
||||
1 + (expansion - 1) * (x in columns)
|
||||
for x in range(min(x1, x2), max(x1, x2))
|
||||
)
|
||||
dy = sum(
|
||||
1 + (expansion - 1) * (y in rows)
|
||||
for y in range(min(y1, y2), max(y1, y2))
|
||||
)
|
||||
data = np.array([[c == "#" for c in line] for line in lines])
|
||||
|
||||
distances.append(dx + dy)
|
||||
return sum(distances)
|
||||
rows = {c for c in range(data.shape[0]) if not data[c, :].any()}
|
||||
columns = {c for c in range(data.shape[1]) if not data[:, c].any()}
|
||||
|
||||
galaxies_y, galaxies_x = np.where(data) # type: ignore
|
||||
|
||||
# part 1
|
||||
answer_1 = compute_total_distance(2)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
def compute_total_distance(expansion: int) -> int:
|
||||
distances: list[int] = []
|
||||
for g1 in range(len(galaxies_y)):
|
||||
x1, y1 = int(galaxies_x[g1]), int(galaxies_y[g1])
|
||||
for g2 in range(g1 + 1, len(galaxies_y)):
|
||||
x2, y2 = int(galaxies_x[g2]), int(galaxies_y[g2])
|
||||
|
||||
# part 2
|
||||
answer_2 = compute_total_distance(1000000)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
dx = sum(
|
||||
1 + (expansion - 1) * (x in columns)
|
||||
for x in range(min(x1, x2), max(x1, x2))
|
||||
)
|
||||
dy = sum(
|
||||
1 + (expansion - 1) * (y in rows)
|
||||
for y in range(min(y1, y2), max(y1, y2))
|
||||
)
|
||||
|
||||
distances.append(dx + dy)
|
||||
return sum(distances)
|
||||
|
||||
# part 1
|
||||
yield compute_total_distance(2)
|
||||
|
||||
# part 2
|
||||
yield compute_total_distance(1000000)
|
||||
|
@@ -1,9 +1,7 @@
|
||||
import os
|
||||
import sys
|
||||
from functools import lru_cache
|
||||
from typing import Iterable
|
||||
from typing import Any, Iterable, Iterator
|
||||
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
@lru_cache
|
||||
@@ -77,31 +75,29 @@ def compute_possible_arrangements(
|
||||
)
|
||||
|
||||
|
||||
def compute_all_possible_arrangements(lines: Iterable[str], repeat: int) -> int:
|
||||
count = 0
|
||||
class Solver(BaseSolver):
|
||||
def compute_all_possible_arrangements(
|
||||
self, lines: Iterable[str], repeat: int
|
||||
) -> int:
|
||||
count = 0
|
||||
|
||||
if VERBOSE:
|
||||
from tqdm import tqdm
|
||||
for i_line, line in enumerate(lines):
|
||||
self.logger.info(f"processing line {i_line}: {line}...")
|
||||
parts = line.split(" ")
|
||||
count += compute_possible_arrangements(
|
||||
tuple(
|
||||
filter(len, "?".join(parts[0] for _ in range(repeat)).split("."))
|
||||
),
|
||||
tuple(int(c) for c in parts[1].split(",")) * repeat,
|
||||
)
|
||||
|
||||
lines = tqdm(lines)
|
||||
return count
|
||||
|
||||
for line in lines:
|
||||
parts = line.split(" ")
|
||||
count += compute_possible_arrangements(
|
||||
tuple(filter(len, "?".join(parts[0] for _ in range(repeat)).split("."))),
|
||||
tuple(int(c) for c in parts[1].split(",")) * repeat,
|
||||
)
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
return count
|
||||
# part 1
|
||||
yield self.compute_all_possible_arrangements(lines, 1)
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
|
||||
# part 1
|
||||
answer_1 = compute_all_possible_arrangements(lines, 1)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
answer_2 = compute_all_possible_arrangements(lines, 5)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
# part 2
|
||||
yield self.compute_all_possible_arrangements(lines, 5)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import sys
|
||||
from typing import Callable, Literal
|
||||
from typing import Any, Callable, Iterator, Literal
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def split(block: list[str], axis: Literal[0, 1], count: int) -> int:
|
||||
@@ -25,19 +26,18 @@ def split(block: list[str], axis: Literal[0, 1], count: int) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
blocks = [block.splitlines() for block in sys.stdin.read().split("\n\n")]
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
blocks = [block.splitlines() for block in input.split("\n\n")]
|
||||
|
||||
# part 1
|
||||
yield sum(
|
||||
split(block, axis=1, count=0) + 100 * split(block, axis=0, count=0)
|
||||
for block in blocks
|
||||
)
|
||||
|
||||
# part 1
|
||||
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
|
||||
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}")
|
||||
# part 2
|
||||
yield sum(
|
||||
split(block, axis=1, count=1) + 100 * split(block, axis=0, count=1)
|
||||
for block in blocks
|
||||
)
|
||||
|
@@ -1,10 +1,9 @@
|
||||
import sys
|
||||
from typing import TypeAlias
|
||||
from typing import Any, Iterator, TypeAlias
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
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]]
|
||||
@@ -34,35 +33,38 @@ def cycle(rocks: RockGrid) -> RockGrid:
|
||||
return rocks
|
||||
|
||||
|
||||
rocks = slide_rocks_top([[c for c in r] for r in rocks0])
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
rocks0 = [list(line) for line in input.splitlines()]
|
||||
|
||||
# part 1
|
||||
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}")
|
||||
rocks = slide_rocks_top([[c for c in r] for r in rocks0])
|
||||
|
||||
# part 2
|
||||
rocks = rocks0
|
||||
# part 1
|
||||
yield sum(
|
||||
(len(rocks) - i) * sum(1 for c in row if c == "O")
|
||||
for i, row in enumerate(rocks)
|
||||
)
|
||||
|
||||
N = 1000000000
|
||||
cycles: list[RockGrid] = []
|
||||
i_cycle: int = -1
|
||||
for i_cycle in range(N):
|
||||
rocks = cycle(rocks)
|
||||
# part 2
|
||||
rocks = rocks0
|
||||
|
||||
if any(rocks == c for c in cycles):
|
||||
break
|
||||
N = 1000000000
|
||||
cycles: list[RockGrid] = []
|
||||
i_cycle: int = -1
|
||||
for i_cycle in range(N):
|
||||
rocks = cycle(rocks)
|
||||
|
||||
cycles.append([[c for c in r] for r in rocks])
|
||||
if any(rocks == c for c in cycles):
|
||||
break
|
||||
|
||||
cycle_start = next(i for i in range(len(cycles)) if (rocks == cycles[i]))
|
||||
cycle_length = i_cycle - cycle_start
|
||||
cycles.append([[c for c in r] for r in rocks])
|
||||
|
||||
ci = cycle_start + (N - cycle_start) % cycle_length - 1
|
||||
cycle_start = next(i for i in range(len(cycles)) if (rocks == cycles[i]))
|
||||
cycle_length = i_cycle - cycle_start
|
||||
|
||||
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}")
|
||||
ci = cycle_start + (N - cycle_start) % cycle_length - 1
|
||||
|
||||
yield sum(
|
||||
(len(rocks) - i) * sum(1 for c in row if c == "O")
|
||||
for i, row in enumerate(cycles[ci])
|
||||
)
|
||||
|
@@ -1,31 +1,33 @@
|
||||
import sys
|
||||
from functools import reduce
|
||||
from typing import Any, Iterator
|
||||
|
||||
steps = sys.stdin.read().strip().split(",")
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def _hash(s: str) -> int:
|
||||
return reduce(lambda v, u: ((v + ord(u)) * 17) % 256, s, 0)
|
||||
|
||||
|
||||
# part 1
|
||||
answer_1 = sum(map(_hash, steps))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
steps = input.split(",")
|
||||
|
||||
# part 2
|
||||
boxes: list[dict[str, int]] = [{} for _ in range(256)]
|
||||
# part 1
|
||||
yield sum(map(_hash, steps))
|
||||
|
||||
for step in steps:
|
||||
if (i := step.find("=")) >= 0:
|
||||
label, length = step[:i], int(step[i + 1 :])
|
||||
boxes[_hash(label)][label] = length
|
||||
else:
|
||||
label = step[:-1]
|
||||
boxes[_hash(label)].pop(label, None)
|
||||
# part 2
|
||||
boxes: list[dict[str, int]] = [{} for _ in range(256)]
|
||||
|
||||
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}")
|
||||
for step in steps:
|
||||
if (i := step.find("=")) >= 0:
|
||||
label, length = step[:i], int(step[i + 1 :])
|
||||
boxes[_hash(label)][label] = length
|
||||
else:
|
||||
label = step[:-1]
|
||||
boxes[_hash(label)].pop(label, None)
|
||||
|
||||
yield 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)
|
||||
)
|
||||
|
@@ -1,8 +1,6 @@
|
||||
import os
|
||||
import sys
|
||||
from typing import Literal, TypeAlias, cast
|
||||
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
from ..base import BaseSolver
|
||||
|
||||
CellType: TypeAlias = Literal[".", "|", "-", "\\", "/"]
|
||||
Direction: TypeAlias = Literal["R", "L", "U", "D"]
|
||||
@@ -78,33 +76,33 @@ def propagate(
|
||||
return beams
|
||||
|
||||
|
||||
layout: list[list[CellType]] = [
|
||||
[cast(CellType, col) for col in row] for row in sys.stdin.read().splitlines()
|
||||
]
|
||||
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()
|
||||
]
|
||||
|
||||
beams = propagate(layout, (0, 0), "R")
|
||||
|
||||
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)
|
||||
|
||||
# part 1
|
||||
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])
|
||||
cases: list[tuple[tuple[int, int], Direction]] = []
|
||||
|
||||
# part 2
|
||||
n_rows, n_cols = len(layout), len(layout[0])
|
||||
cases: list[tuple[tuple[int, int], Direction]] = []
|
||||
for row in range(n_rows):
|
||||
cases.append(((row, 0), "R"))
|
||||
cases.append(((row, n_cols - 1), "L"))
|
||||
for col in range(n_cols):
|
||||
cases.append(((0, col), "D"))
|
||||
cases.append(((n_rows - 1, col), "U"))
|
||||
|
||||
for row in range(n_rows):
|
||||
cases.append(((row, 0), "R"))
|
||||
cases.append(((row, n_cols - 1), "L"))
|
||||
for col in range(n_cols):
|
||||
cases.append(((0, col), "D"))
|
||||
cases.append(((n_rows - 1, col), "U"))
|
||||
|
||||
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}")
|
||||
yield max(
|
||||
sum(sum(map(bool, row)) for row in propagate(layout, start, direction))
|
||||
for start, direction in cases
|
||||
)
|
||||
|
@@ -1,13 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import heapq
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal, TypeAlias
|
||||
from typing import Any, Iterator, Literal, TypeAlias
|
||||
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
from ..base import BaseSolver
|
||||
|
||||
Direction: TypeAlias = Literal[">", "<", "^", "v"]
|
||||
|
||||
@@ -32,202 +30,204 @@ MAPPINGS: dict[Direction, tuple[int, int, Direction]] = {
|
||||
}
|
||||
|
||||
|
||||
def print_shortest_path(
|
||||
grid: list[list[int]],
|
||||
target: tuple[int, int],
|
||||
per_cell: dict[tuple[int, int], list[tuple[Label, int]]],
|
||||
):
|
||||
assert len(per_cell[target]) == 1
|
||||
label = per_cell[target][0][0]
|
||||
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]]],
|
||||
):
|
||||
assert len(per_cell[target]) == 1
|
||||
label = per_cell[target][0][0]
|
||||
|
||||
path: list[Label] = []
|
||||
while True:
|
||||
path.insert(0, label)
|
||||
if label.parent is None:
|
||||
break
|
||||
label = label.parent
|
||||
path: list[Label] = []
|
||||
while True:
|
||||
path.insert(0, label)
|
||||
if label.parent is None:
|
||||
break
|
||||
label = label.parent
|
||||
|
||||
p_grid = [[str(c) for c in r] for r in grid]
|
||||
p_grid = [[str(c) for c in r] for r in grid]
|
||||
|
||||
for i in range(len(grid)):
|
||||
for j in range(len(grid[0])):
|
||||
if per_cell[i, j]:
|
||||
p_grid[i][j] = f"\033[94m{grid[i][j]}\033[0m"
|
||||
for i in range(len(grid)):
|
||||
for j in range(len(grid[0])):
|
||||
if per_cell[i, j]:
|
||||
p_grid[i][j] = f"\033[94m{grid[i][j]}\033[0m"
|
||||
|
||||
prev_label = path[0]
|
||||
for label in path[1:]:
|
||||
for r in range(
|
||||
min(prev_label.row, label.row), max(prev_label.row, label.row) + 1
|
||||
):
|
||||
for c in range(
|
||||
min(prev_label.col, label.col),
|
||||
max(prev_label.col, label.col) + 1,
|
||||
prev_label = path[0]
|
||||
for label in path[1:]:
|
||||
for r in range(
|
||||
min(prev_label.row, label.row), max(prev_label.row, label.row) + 1
|
||||
):
|
||||
if (r, c) != (prev_label.row, prev_label.col):
|
||||
p_grid[r][c] = f"\033[93m{grid[r][c]}\033[0m"
|
||||
for c in range(
|
||||
min(prev_label.col, label.col),
|
||||
max(prev_label.col, label.col) + 1,
|
||||
):
|
||||
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
|
||||
prev_label = label
|
||||
|
||||
p_grid[0][0] = f"\033[92m{grid[0][0]}\033[0m"
|
||||
p_grid[0][0] = f"\033[92m{grid[0][0]}\033[0m"
|
||||
|
||||
print("\n".join("".join(row) for row in p_grid))
|
||||
for row in p_grid:
|
||||
self.logger.info("".join(row))
|
||||
|
||||
def shortest_many_paths(self, grid: list[list[int]]) -> dict[tuple[int, int], int]:
|
||||
n_rows, n_cols = len(grid), len(grid[0])
|
||||
|
||||
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]] = {}
|
||||
|
||||
visited: dict[tuple[int, int], tuple[Label, int]] = {}
|
||||
queue: list[tuple[int, Label]] = [
|
||||
(0, Label(row=n_rows - 1, col=n_cols - 1, direction="^", count=0))
|
||||
]
|
||||
|
||||
queue: list[tuple[int, Label]] = [
|
||||
(0, Label(row=n_rows - 1, col=n_cols - 1, direction="^", count=0))
|
||||
]
|
||||
while queue and len(visited) != n_rows * n_cols:
|
||||
distance, label = heapq.heappop(queue)
|
||||
|
||||
while queue and len(visited) != n_rows * n_cols:
|
||||
distance, label = heapq.heappop(queue)
|
||||
|
||||
if (label.row, label.col) in visited:
|
||||
continue
|
||||
|
||||
visited[label.row, label.col] = (label, distance)
|
||||
|
||||
for direction, (c_row, c_col, i_direction) in MAPPINGS.items():
|
||||
if label.direction == i_direction:
|
||||
continue
|
||||
else:
|
||||
row, col = (label.row + c_row, label.col + c_col)
|
||||
|
||||
# exclude labels outside the grid or with too many moves in the same
|
||||
# direction
|
||||
if row not in range(0, n_rows) or col not in range(0, n_cols):
|
||||
if (label.row, label.col) in visited:
|
||||
continue
|
||||
|
||||
heapq.heappush(
|
||||
queue,
|
||||
(
|
||||
visited[label.row, label.col] = (label, distance)
|
||||
|
||||
for direction, (c_row, c_col, i_direction) in MAPPINGS.items():
|
||||
if label.direction == i_direction:
|
||||
continue
|
||||
else:
|
||||
row, col = (label.row + c_row, label.col + c_col)
|
||||
|
||||
# exclude labels outside the grid or with too many moves in the same
|
||||
# direction
|
||||
if row not in range(0, n_rows) or col not in range(0, n_cols):
|
||||
continue
|
||||
|
||||
heapq.heappush(
|
||||
queue,
|
||||
(
|
||||
distance
|
||||
+ sum(
|
||||
grid[r][c]
|
||||
for r in range(min(row, label.row), max(row, label.row) + 1)
|
||||
for c in range(min(col, label.col), max(col, label.col) + 1)
|
||||
)
|
||||
- grid[row][col],
|
||||
Label(
|
||||
row=row,
|
||||
col=col,
|
||||
direction=direction,
|
||||
count=0,
|
||||
parent=label,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
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,
|
||||
lower_bounds: dict[tuple[int, int], int],
|
||||
) -> int:
|
||||
n_rows, n_cols = len(grid), len(grid[0])
|
||||
|
||||
target = (len(grid) - 1, len(grid[0]) - 1)
|
||||
|
||||
# for each tuple (row, col, direction, count), the associated label when visited
|
||||
visited: dict[tuple[int, int, str, int], Label] = {}
|
||||
|
||||
# list of all visited labels for a cell (with associated distance)
|
||||
per_cell: dict[tuple[int, int], list[tuple[Label, int]]] = defaultdict(list)
|
||||
|
||||
# need to add two start labels, otherwise one of the two possible direction will
|
||||
# not be possible
|
||||
queue: list[tuple[int, int, Label]] = [
|
||||
(lower_bounds[0, 0], 0, Label(row=0, col=0, direction="^", count=0)),
|
||||
(lower_bounds[0, 0], 0, Label(row=0, col=0, direction="<", count=0)),
|
||||
]
|
||||
|
||||
while queue:
|
||||
_, distance, label = heapq.heappop(queue)
|
||||
|
||||
if (label.row, label.col, label.direction, label.count) in visited:
|
||||
continue
|
||||
|
||||
visited[label.row, label.col, label.direction, label.count] = label
|
||||
per_cell[label.row, label.col].append((label, distance))
|
||||
|
||||
if (label.row, label.col) == target:
|
||||
break
|
||||
|
||||
for direction, (c_row, c_col, i_direction) in MAPPINGS.items():
|
||||
# cannot move in the opposite direction
|
||||
if label.direction == i_direction:
|
||||
continue
|
||||
|
||||
# other direction, move 'min_straight' in the new direction
|
||||
elif label.direction != direction:
|
||||
row, col, count = (
|
||||
label.row + min_straight * c_row,
|
||||
label.col + min_straight * c_col,
|
||||
min_straight,
|
||||
)
|
||||
|
||||
# same direction, too many count
|
||||
elif label.count == max_straight:
|
||||
continue
|
||||
|
||||
# same direction, keep going and increment count
|
||||
else:
|
||||
row, col, count = (
|
||||
label.row + c_row,
|
||||
label.col + c_col,
|
||||
label.count + 1,
|
||||
)
|
||||
# exclude labels outside the grid or with too many moves in the same
|
||||
# direction
|
||||
if row not in range(0, n_rows) or col not in range(0, n_cols):
|
||||
continue
|
||||
|
||||
distance_to = (
|
||||
distance
|
||||
+ sum(
|
||||
grid[r][c]
|
||||
for r in range(min(row, label.row), max(row, label.row) + 1)
|
||||
for c in range(min(col, label.col), max(col, label.col) + 1)
|
||||
)
|
||||
- grid[row][col],
|
||||
Label(
|
||||
row=row,
|
||||
col=col,
|
||||
direction=direction,
|
||||
count=0,
|
||||
parent=label,
|
||||
- grid[label.row][label.col]
|
||||
)
|
||||
|
||||
heapq.heappush(
|
||||
queue,
|
||||
(
|
||||
distance_to + lower_bounds[row, col],
|
||||
distance_to,
|
||||
Label(
|
||||
row=row,
|
||||
col=col,
|
||||
direction=direction,
|
||||
count=count,
|
||||
parent=label,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
return {(r, c): visited[r, c][1] for r in range(n_rows) for c in range(n_cols)}
|
||||
|
||||
|
||||
def shortest_path(
|
||||
grid: list[list[int]],
|
||||
min_straight: int,
|
||||
max_straight: int,
|
||||
lower_bounds: dict[tuple[int, int], int],
|
||||
) -> int:
|
||||
n_rows, n_cols = len(grid), len(grid[0])
|
||||
|
||||
target = (len(grid) - 1, len(grid[0]) - 1)
|
||||
|
||||
# for each tuple (row, col, direction, count), the associated label when visited
|
||||
visited: dict[tuple[int, int, str, int], Label] = {}
|
||||
|
||||
# list of all visited labels for a cell (with associated distance)
|
||||
per_cell: dict[tuple[int, int], list[tuple[Label, int]]] = defaultdict(list)
|
||||
|
||||
# need to add two start labels, otherwise one of the two possible direction will
|
||||
# not be possible
|
||||
queue: list[tuple[int, int, Label]] = [
|
||||
(lower_bounds[0, 0], 0, Label(row=0, col=0, direction="^", count=0)),
|
||||
(lower_bounds[0, 0], 0, Label(row=0, col=0, direction="<", count=0)),
|
||||
]
|
||||
|
||||
while queue:
|
||||
_, distance, label = heapq.heappop(queue)
|
||||
|
||||
if (label.row, label.col, label.direction, label.count) in visited:
|
||||
continue
|
||||
|
||||
visited[label.row, label.col, label.direction, label.count] = label
|
||||
per_cell[label.row, label.col].append((label, distance))
|
||||
|
||||
if (label.row, label.col) == target:
|
||||
break
|
||||
|
||||
for direction, (c_row, c_col, i_direction) in MAPPINGS.items():
|
||||
# cannot move in the opposite direction
|
||||
if label.direction == i_direction:
|
||||
continue
|
||||
|
||||
# other direction, move 'min_straight' in the new direction
|
||||
elif label.direction != direction:
|
||||
row, col, count = (
|
||||
label.row + min_straight * c_row,
|
||||
label.col + min_straight * c_col,
|
||||
min_straight,
|
||||
)
|
||||
|
||||
# same direction, too many count
|
||||
elif label.count == max_straight:
|
||||
continue
|
||||
if self.verbose:
|
||||
self.print_shortest_path(grid, target, per_cell)
|
||||
|
||||
# same direction, keep going and increment count
|
||||
else:
|
||||
row, col, count = (
|
||||
label.row + c_row,
|
||||
label.col + c_col,
|
||||
label.count + 1,
|
||||
)
|
||||
# exclude labels outside the grid or with too many moves in the same
|
||||
# direction
|
||||
if row not in range(0, n_rows) or col not in range(0, n_cols):
|
||||
continue
|
||||
return per_cell[target][0][1]
|
||||
|
||||
distance_to = (
|
||||
distance
|
||||
+ sum(
|
||||
grid[r][c]
|
||||
for r in range(min(row, label.row), max(row, label.row) + 1)
|
||||
for c in range(min(col, label.col), max(col, label.col) + 1)
|
||||
)
|
||||
- grid[label.row][label.col]
|
||||
)
|
||||
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)
|
||||
|
||||
heapq.heappush(
|
||||
queue,
|
||||
(
|
||||
distance_to + lower_bounds[row, col],
|
||||
distance_to,
|
||||
Label(
|
||||
row=row,
|
||||
col=col,
|
||||
direction=direction,
|
||||
count=count,
|
||||
parent=label,
|
||||
),
|
||||
),
|
||||
)
|
||||
# part 1
|
||||
yield self.shortest_path(data, 1, 3, lower_bounds=estimates)
|
||||
|
||||
if VERBOSE:
|
||||
print_shortest_path(grid, target, per_cell)
|
||||
|
||||
return per_cell[target][0][1]
|
||||
|
||||
|
||||
data = [[int(c) for c in r] for r in sys.stdin.read().splitlines()]
|
||||
estimates = shortest_many_paths(data)
|
||||
|
||||
# part 1
|
||||
answer_1 = shortest_path(data, 1, 3, lower_bounds=estimates)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# part 2
|
||||
answer_2 = shortest_path(data, 4, 10, lower_bounds=estimates)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
# part 2
|
||||
yield self.shortest_path(data, 4, 10, lower_bounds=estimates)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import sys
|
||||
from typing import Literal, TypeAlias, cast
|
||||
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
Direction: TypeAlias = Literal["R", "L", "U", "D"]
|
||||
|
||||
@@ -33,22 +34,23 @@ def polygon(values: list[tuple[Direction, int]]) -> tuple[list[tuple[int, int]],
|
||||
return corners, perimeter
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
# part 1
|
||||
yield area(
|
||||
*polygon(
|
||||
[(cast(Direction, (p := line.split())[0]), int(p[1])) for line in lines]
|
||||
)
|
||||
)
|
||||
|
||||
# part 1
|
||||
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
|
||||
answer_2 = area(
|
||||
*polygon(
|
||||
[
|
||||
(DIRECTIONS[int((h := line.split()[-1])[-2])], int(h[2:-2], 16))
|
||||
for line in lines
|
||||
]
|
||||
)
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
# part 2
|
||||
yield area(
|
||||
*polygon(
|
||||
[
|
||||
(DIRECTIONS[int((h := line.split()[-1])[-2])], int(h[2:-2], 16))
|
||||
for line in lines
|
||||
]
|
||||
)
|
||||
)
|
||||
|
@@ -1,13 +1,8 @@
|
||||
import logging
|
||||
import operator
|
||||
import os
|
||||
import sys
|
||||
from math import prod
|
||||
from typing import Literal, TypeAlias, cast
|
||||
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
from ..base import BaseSolver
|
||||
|
||||
Category: TypeAlias = Literal["x", "m", "a", "s"]
|
||||
Part: TypeAlias = dict[Category, int]
|
||||
@@ -22,119 +17,118 @@ Check: TypeAlias = tuple[Category, Literal["<", ">"], int] | None
|
||||
Workflow: TypeAlias = list[tuple[Check, str]]
|
||||
|
||||
|
||||
def accept(workflows: dict[str, Workflow], part: Part) -> bool:
|
||||
workflow = "in"
|
||||
decision: bool | None = None
|
||||
class Solver(BaseSolver):
|
||||
def accept(self, workflows: dict[str, Workflow], part: Part) -> bool:
|
||||
workflow = "in"
|
||||
decision: bool | None = None
|
||||
|
||||
while decision is None:
|
||||
for check, target in workflows[workflow]:
|
||||
passed = check is None
|
||||
if check is not None:
|
||||
category, sense, value = check
|
||||
passed = OPERATORS[sense](part[category], value)
|
||||
while decision is None:
|
||||
for check, target in workflows[workflow]:
|
||||
passed = check is None
|
||||
if check is not None:
|
||||
category, sense, value = check
|
||||
passed = OPERATORS[sense](part[category], value)
|
||||
|
||||
if passed:
|
||||
if target in workflows:
|
||||
workflow = target
|
||||
else:
|
||||
decision = target == "A"
|
||||
break
|
||||
if passed:
|
||||
if target in workflows:
|
||||
workflow = target
|
||||
else:
|
||||
decision = target == "A"
|
||||
break
|
||||
|
||||
return decision
|
||||
return decision
|
||||
|
||||
def propagate(self, workflows: dict[str, Workflow], start: PartWithBounds) -> int:
|
||||
def _fmt(meta: PartWithBounds) -> str:
|
||||
return "{" + ", ".join(f"{k}={v}" for k, v in meta.items()) + "}"
|
||||
|
||||
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()) + "}"
|
||||
|
||||
def transfer_or_accept(
|
||||
target: str, meta: PartWithBounds, queue: list[tuple[PartWithBounds, str]]
|
||||
) -> int:
|
||||
count = 0
|
||||
if target in workflows:
|
||||
logging.info(f" transfer to {target}")
|
||||
queue.append((meta, target))
|
||||
elif target == "A":
|
||||
count = prod((high - low + 1) for low, high in meta.values())
|
||||
logging.info(f" accepted ({count})")
|
||||
else:
|
||||
logging.info(" rejected")
|
||||
return count
|
||||
|
||||
accepted = 0
|
||||
queue: list[tuple[PartWithBounds, str]] = [(start, "in")]
|
||||
|
||||
n_iterations = 0
|
||||
|
||||
while queue:
|
||||
n_iterations += 1
|
||||
meta, workflow = queue.pop()
|
||||
logging.info(f"{workflow}: {_fmt(meta)}")
|
||||
for check, target in workflows[workflow]:
|
||||
if check is None:
|
||||
logging.info(" end-of-workflow")
|
||||
accepted += transfer_or_accept(target, meta, queue)
|
||||
continue
|
||||
|
||||
category, sense, value = check
|
||||
bounds, op = meta[category], OPERATORS[sense]
|
||||
|
||||
logging.info(f" checking {_fmt(meta)} against {category} {sense} {value}")
|
||||
|
||||
if not op(bounds[0], value) and not op(bounds[1], value):
|
||||
logging.info(" reject, always false")
|
||||
continue
|
||||
|
||||
if op(meta[category][0], value) and op(meta[category][1], value):
|
||||
logging.info(" accept, always true")
|
||||
accepted += transfer_or_accept(target, meta, queue)
|
||||
break
|
||||
|
||||
meta2 = meta.copy()
|
||||
low, high = meta[category]
|
||||
if sense == "<":
|
||||
meta[category], meta2[category] = (value, high), (low, value - 1)
|
||||
def transfer_or_accept(
|
||||
target: str, meta: PartWithBounds, queue: list[tuple[PartWithBounds, str]]
|
||||
) -> int:
|
||||
count = 0
|
||||
if target in workflows:
|
||||
self.logger.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})")
|
||||
else:
|
||||
meta[category], meta2[category] = (low, value), (value + 1, high)
|
||||
logging.info(f" split {_fmt(meta2)} ({target}), {_fmt(meta)}")
|
||||
self.logger.info(" rejected")
|
||||
return count
|
||||
|
||||
accepted += transfer_or_accept(target, meta2, queue)
|
||||
accepted = 0
|
||||
queue: list[tuple[PartWithBounds, str]] = [(start, "in")]
|
||||
|
||||
logging.info(f"run took {n_iterations} iterations")
|
||||
return accepted
|
||||
n_iterations = 0
|
||||
|
||||
while queue:
|
||||
n_iterations += 1
|
||||
meta, workflow = queue.pop()
|
||||
self.logger.info(f"{workflow}: {_fmt(meta)}")
|
||||
for check, target in workflows[workflow]:
|
||||
if check is None:
|
||||
self.logger.info(" end-of-workflow")
|
||||
accepted += transfer_or_accept(target, meta, queue)
|
||||
continue
|
||||
|
||||
workflows_s, parts_s = sys.stdin.read().strip().split("\n\n")
|
||||
category, sense, value = check
|
||||
bounds, op = meta[category], OPERATORS[sense]
|
||||
|
||||
workflows: dict[str, Workflow] = {}
|
||||
for workflow_s in workflows_s.split("\n"):
|
||||
name, block_s = workflow_s.split("{")
|
||||
workflows[name] = []
|
||||
self.logger.info(
|
||||
f" checking {_fmt(meta)} against {category} {sense} {value}"
|
||||
)
|
||||
|
||||
for block in block_s[:-1].split(","):
|
||||
check: Check
|
||||
if (i := block.find(":")) >= 0:
|
||||
check = (
|
||||
cast(Category, block[0]),
|
||||
cast(Literal["<", ">"], block[1]),
|
||||
int(block[2:i]),
|
||||
)
|
||||
target = block[i + 1 :]
|
||||
else:
|
||||
check, target = None, block
|
||||
workflows[name].append((check, target))
|
||||
if not op(bounds[0], value) and not op(bounds[1], value):
|
||||
self.logger.info(" reject, always false")
|
||||
continue
|
||||
|
||||
# part 1
|
||||
parts: list[Part] = [
|
||||
{cast(Category, s[0]): int(s[2:]) for s in part_s[1:-1].split(",")}
|
||||
for part_s in parts_s.split("\n")
|
||||
]
|
||||
answer_1 = sum(sum(part.values()) for part in parts if accept(workflows, part))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
if op(meta[category][0], value) and op(meta[category][1], value):
|
||||
self.logger.info(" accept, always true")
|
||||
accepted += transfer_or_accept(target, meta, queue)
|
||||
break
|
||||
|
||||
meta2 = meta.copy()
|
||||
low, high = meta[category]
|
||||
if sense == "<":
|
||||
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)}")
|
||||
|
||||
# part 2
|
||||
answer_2 = propagate(
|
||||
workflows, {cast(Category, c): (1, 4000) for c in ["x", "m", "a", "s"]}
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
accepted += transfer_or_accept(target, meta2, queue)
|
||||
|
||||
self.logger.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: dict[str, Workflow] = {}
|
||||
for workflow_s in workflows_s.split("\n"):
|
||||
name, block_s = workflow_s.split("{")
|
||||
workflows[name] = []
|
||||
|
||||
for block in block_s[:-1].split(","):
|
||||
check: Check
|
||||
if (i := block.find(":")) >= 0:
|
||||
check = (
|
||||
cast(Category, block[0]),
|
||||
cast(Literal["<", ">"], block[1]),
|
||||
int(block[2:i]),
|
||||
)
|
||||
target = block[i + 1 :]
|
||||
else:
|
||||
check, target = None, block
|
||||
workflows[name].append((check, target))
|
||||
|
||||
# part 1
|
||||
parts: list[Part] = [
|
||||
{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))
|
||||
|
||||
# part 2
|
||||
yield self.propagate(
|
||||
workflows, {cast(Category, c): (1, 4000) for c in ["x", "m", "a", "s"]}
|
||||
)
|
||||
|
@@ -1,43 +1,43 @@
|
||||
import math
|
||||
import sys
|
||||
from typing import Literal, TypeAlias, cast
|
||||
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
CubeType: TypeAlias = Literal["red", "blue", "green"]
|
||||
|
||||
MAX_CUBES: dict[CubeType, int] = {"red": 12, "green": 13, "blue": 14}
|
||||
|
||||
# parse games
|
||||
lines = sys.stdin.read().splitlines()
|
||||
games: dict[int, list[dict[CubeType, int]]] = {}
|
||||
for line in filter(bool, lines):
|
||||
id_part, sets_part = line.split(":")
|
||||
|
||||
games[int(id_part.split(" ")[-1])] = [
|
||||
{
|
||||
cast(CubeType, s[1]): int(s[0])
|
||||
for cube_draw in cube_set_s.strip().split(", ")
|
||||
if (s := cube_draw.split(" "))
|
||||
}
|
||||
for cube_set_s in sets_part.strip().split(";")
|
||||
]
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
games: dict[int, list[dict[CubeType, int]]] = {}
|
||||
for line in filter(bool, lines):
|
||||
id_part, sets_part = line.split(":")
|
||||
|
||||
# part 1
|
||||
answer_1 = sum(
|
||||
id
|
||||
for id, set_of_cubes in games.items()
|
||||
if all(
|
||||
n_cubes <= MAX_CUBES[cube]
|
||||
for cube_set in set_of_cubes
|
||||
for cube, n_cubes in cube_set.items()
|
||||
)
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
games[int(id_part.split(" ")[-1])] = [
|
||||
{
|
||||
cast(CubeType, s[1]): int(s[0])
|
||||
for cube_draw in cube_set_s.strip().split(", ")
|
||||
if (s := cube_draw.split(" "))
|
||||
}
|
||||
for cube_set_s in sets_part.strip().split(";")
|
||||
]
|
||||
|
||||
# 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
|
||||
)
|
||||
for set_of_cubes in games.values()
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield sum(
|
||||
id
|
||||
for id, set_of_cubes in games.items()
|
||||
if all(
|
||||
n_cubes <= MAX_CUBES[cube]
|
||||
for cube_set in set_of_cubes
|
||||
for cube, n_cubes in cube_set.items()
|
||||
)
|
||||
)
|
||||
|
||||
yield sum(
|
||||
math.prod(
|
||||
max(cube_set.get(cube, 0) for cube_set in set_of_cubes)
|
||||
for cube in MAX_CUBES
|
||||
)
|
||||
for set_of_cubes in games.values()
|
||||
)
|
||||
|
@@ -1,161 +1,172 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from math import lcm
|
||||
from typing import Literal, TypeAlias
|
||||
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
from typing import Any, Iterator, Literal, TypeAlias
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
ModuleType: TypeAlias = Literal["broadcaster", "conjunction", "flip-flop"]
|
||||
PulseType: TypeAlias = Literal["high", "low"]
|
||||
|
||||
modules: dict[str, tuple[ModuleType, list[str]]] = {}
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
class Solver(BaseSolver):
|
||||
_modules: dict[str, tuple[ModuleType, list[str]]]
|
||||
|
||||
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(
|
||||
self,
|
||||
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}
|
||||
)
|
||||
|
||||
self.logger.info("starting process... ")
|
||||
|
||||
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})
|
||||
while pulses:
|
||||
input, name, pulse = pulses.pop(0)
|
||||
self.logger.info(f"{input} -{pulse}-> {name}")
|
||||
counts[pulse] += 1
|
||||
|
||||
logging.info("starting process... ")
|
||||
inputs[name][pulse] += 1
|
||||
|
||||
while pulses:
|
||||
input, name, pulse = pulses.pop(0)
|
||||
logging.info(f"{input} -{pulse}-> {name}")
|
||||
counts[pulse] += 1
|
||||
|
||||
inputs[name][pulse] += 1
|
||||
|
||||
if name not in modules:
|
||||
continue
|
||||
|
||||
type, outputs = modules[name]
|
||||
|
||||
if type == "broadcaster":
|
||||
...
|
||||
|
||||
elif type == "flip-flop":
|
||||
if pulse == "high":
|
||||
if name not in self._modules:
|
||||
continue
|
||||
|
||||
if flip_flop_states[name] == "off":
|
||||
flip_flop_states[name] = "on"
|
||||
pulse = "high"
|
||||
type, outputs = self._modules[name]
|
||||
|
||||
if type == "broadcaster":
|
||||
...
|
||||
|
||||
elif type == "flip-flop":
|
||||
if pulse == "high":
|
||||
continue
|
||||
|
||||
if flip_flop_states[name] == "off":
|
||||
flip_flop_states[name] = "on"
|
||||
pulse = "high"
|
||||
else:
|
||||
flip_flop_states[name] = "off"
|
||||
pulse = "low"
|
||||
|
||||
else:
|
||||
flip_flop_states[name] = "off"
|
||||
pulse = "low"
|
||||
conjunction_states[name][input] = pulse
|
||||
|
||||
else:
|
||||
conjunction_states[name][input] = pulse
|
||||
if all(state == "high" for state in conjunction_states[name].values()):
|
||||
pulse = "low"
|
||||
else:
|
||||
pulse = "high"
|
||||
|
||||
if all(state == "high" for state in conjunction_states[name].values()):
|
||||
pulse = "low"
|
||||
pulses.extend((name, output, pulse) for output in outputs)
|
||||
|
||||
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:
|
||||
pulse = "high"
|
||||
self._modules[name[1:]] = (
|
||||
"conjunction" if name.startswith("&") else "flip-flop",
|
||||
outputs,
|
||||
)
|
||||
|
||||
pulses.extend((name, output, pulse) for output in 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():
|
||||
if type == "conjunction":
|
||||
shape = "diamond"
|
||||
elif type == "flip-flop":
|
||||
shape = "box"
|
||||
else:
|
||||
shape = "circle"
|
||||
fp.write(f"{name} [shape={shape}];\n")
|
||||
for name, (type, outputs) in self._modules.items():
|
||||
for output in outputs:
|
||||
fp.write(f"{name} -> {output};\n")
|
||||
fp.write("}\n")
|
||||
|
||||
return counts, inputs
|
||||
# part 1
|
||||
flip_flop_states: dict[str, Literal["on", "off"]] = {
|
||||
name: "off"
|
||||
for name, (type, _) in self._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()
|
||||
if type == "conjunction"
|
||||
}
|
||||
counts: dict[PulseType, int] = {"low": 0, "high": 0}
|
||||
for _ in range(1000):
|
||||
result, _ = self._process(
|
||||
("button", "broadcaster", "low"), flip_flop_states, conjunction_states
|
||||
)
|
||||
for pulse in ("low", "high"):
|
||||
counts[pulse] += result[pulse]
|
||||
yield counts["low"] * counts["high"]
|
||||
|
||||
# part 2
|
||||
|
||||
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 modules.items():
|
||||
if type == "conjunction":
|
||||
shape = "diamond"
|
||||
elif type == "flip-flop":
|
||||
shape = "box"
|
||||
else:
|
||||
shape = "circle"
|
||||
fp.write(f"{name} [shape={shape}];\n")
|
||||
for name, (type, outputs) in modules.items():
|
||||
for output in outputs:
|
||||
fp.write(f"{name} -> {output};\n")
|
||||
fp.write("}\n")
|
||||
# reset states
|
||||
for name in flip_flop_states:
|
||||
flip_flop_states[name] = "off"
|
||||
|
||||
# part 1
|
||||
flip_flop_states: dict[str, Literal["on", "off"]] = {
|
||||
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 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, _ = process(
|
||||
("button", "broadcaster", "low"), flip_flop_states, conjunction_states
|
||||
)
|
||||
for pulse in ("low", "high"):
|
||||
counts[pulse] += result[pulse]
|
||||
answer_1 = counts["low"] * counts["high"]
|
||||
print(f"answer 1 is {answer_1}")
|
||||
for name in conjunction_states:
|
||||
for input in conjunction_states[name]:
|
||||
conjunction_states[name][input] = "low"
|
||||
|
||||
# part 2
|
||||
# find the conjunction connected to rx
|
||||
to_rx = [
|
||||
name for name, (_, outputs) in self._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"
|
||||
), "can only handle conjunction as input to rx"
|
||||
|
||||
# reset states
|
||||
for name in flip_flop_states:
|
||||
flip_flop_states[name] = "off"
|
||||
to_rx_inputs = [
|
||||
name for name, (_, outputs) in self._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
|
||||
), "can only handle inversion as second-order inputs to rx"
|
||||
|
||||
for name in conjunction_states:
|
||||
for input in conjunction_states[name]:
|
||||
conjunction_states[name][input] = "low"
|
||||
count = 1
|
||||
cycles: dict[str, int] = {}
|
||||
second: dict[str, int] = {}
|
||||
while len(second) != len(to_rx_inputs):
|
||||
_, inputs = self._process(
|
||||
("button", "broadcaster", "low"), flip_flop_states, conjunction_states
|
||||
)
|
||||
|
||||
# find the conjunction connected to rx
|
||||
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 (
|
||||
modules[to_rx[0]][0] == "conjunction"
|
||||
), "can only handle conjunction as input to rx"
|
||||
for node in to_rx_inputs:
|
||||
if inputs[node]["low"] == 1:
|
||||
if node not in cycles:
|
||||
cycles[node] = count
|
||||
elif node not in second:
|
||||
second[node] = count
|
||||
|
||||
to_rx_inputs = [name for name, (_, outputs) in modules.items() if to_rx[0] in outputs]
|
||||
assert all(
|
||||
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
|
||||
|
||||
assert all(
|
||||
second[k] == cycles[k] * 2 for k in to_rx_inputs
|
||||
), "cannot only handle cycles starting at the beginning"
|
||||
|
||||
count = 1
|
||||
cycles: dict[str, int] = {}
|
||||
second: dict[str, int] = {}
|
||||
while len(second) != len(to_rx_inputs):
|
||||
_, inputs = process(
|
||||
("button", "broadcaster", "low"), flip_flop_states, conjunction_states
|
||||
)
|
||||
|
||||
for node in to_rx_inputs:
|
||||
if inputs[node]["low"] == 1:
|
||||
if node not in cycles:
|
||||
cycles[node] = count
|
||||
elif node not in second:
|
||||
second[node] = count
|
||||
|
||||
count += 1
|
||||
|
||||
assert all(
|
||||
second[k] == cycles[k] * 2 for k in to_rx_inputs
|
||||
), "cannot only handle cycles starting at the beginning"
|
||||
|
||||
answer_2 = lcm(*cycles.values())
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield lcm(*cycles.values())
|
||||
|
@@ -1,9 +1,6 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def reachable(
|
||||
@@ -21,129 +18,133 @@ def reachable(
|
||||
return tiles
|
||||
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
# part 1
|
||||
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
|
||||
#
|
||||
# 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
|
||||
#
|
||||
cycle = len(map)
|
||||
rhombus = (len(map) - 3) // 2 + 1
|
||||
|
||||
values: list[int] = []
|
||||
values.append(len(tiles := reachable(map, {start}, rhombus)))
|
||||
values.append(len(tiles := reachable(map, tiles, cycle)))
|
||||
values.append(len(tiles := reachable(map, tiles, cycle)))
|
||||
|
||||
if logging.root.getEffectiveLevel() == logging.INFO:
|
||||
n_rows, n_cols = len(map), len(map[0])
|
||||
|
||||
rows = [
|
||||
[
|
||||
map[i % n_rows][j % n_cols] if (i, j) not in tiles else "O"
|
||||
for j in range(-2 * cycle, 3 * cycle)
|
||||
]
|
||||
for i in range(-2 * cycle, 3 * cycle)
|
||||
]
|
||||
|
||||
for i in range(len(rows)):
|
||||
for j in range(len(rows[i])):
|
||||
if (i // cycle) % 2 == (j // cycle) % 2:
|
||||
rows[i][j] = f"\033[91m{rows[i][j]}\033[0m"
|
||||
|
||||
print("\n".join("".join(row) for row in rows))
|
||||
|
||||
|
||||
logging.info(f"values to fit: {values}")
|
||||
|
||||
# version 1:
|
||||
#
|
||||
# after 3 cycles, the figure looks like the following:
|
||||
#
|
||||
# I M D
|
||||
# I J A K D
|
||||
# H A F A L
|
||||
# C E A K B
|
||||
# C G B
|
||||
#
|
||||
# after 4 cycles, the figure looks like the following:
|
||||
#
|
||||
# I M D
|
||||
# I J A K D
|
||||
# I J A B A K D
|
||||
# H A B A B A L
|
||||
# C E A B A N F
|
||||
# C E A N F
|
||||
# C G F
|
||||
#
|
||||
# the 'radius' of the rhombus is the number of cycles minus 1
|
||||
#
|
||||
# the 4 'corner' (M, H, L, G) are counted once, the blocks with a corner triangle (D, I,
|
||||
# C, B) are each counted radius times, the blocks with everything but one corner (J, K,
|
||||
# E, N) are each counted radius - 1 times
|
||||
#
|
||||
# there are two versions of the whole block, A and B in the above (or odd and even),
|
||||
# depending on the number of cycles, either A or B will be in the center
|
||||
#
|
||||
|
||||
counts = [
|
||||
[
|
||||
sum(
|
||||
(i, j) in tiles
|
||||
for i in range(ci * cycle, (ci + 1) * cycle)
|
||||
for j in range(cj * cycle, (cj + 1) * cycle)
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
map = input.splitlines()
|
||||
start = next(
|
||||
(i, j)
|
||||
for i in range(len(map))
|
||||
for j in range(len(map[i]))
|
||||
if map[i][j] == "S"
|
||||
)
|
||||
for cj in range(-2, 3)
|
||||
]
|
||||
for ci in range(-2, 3)
|
||||
]
|
||||
|
||||
radius = (26501365 - rhombus) // cycle - 1
|
||||
A = counts[2][2] if radius % 2 == 0 else counts[2][1]
|
||||
B = counts[2][2] if radius % 2 == 1 else counts[2][1]
|
||||
answer_2 = (
|
||||
(radius + 1) * A
|
||||
+ radius * B
|
||||
+ 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 ((1, 1), (1, 3), (-2, 1), (-2, 3))) * radius
|
||||
)
|
||||
print(f"answer 2 (v1) is {answer_2}")
|
||||
# part 1
|
||||
yield len(reachable(map, {start}, 6 if len(map) < 20 else 64))
|
||||
|
||||
# version 2: fitting a polynomial
|
||||
#
|
||||
# the value we are interested in (26501365) can be written as R + K * C where R is the
|
||||
# step at which we find the first rhombus, and K the repeat step, so instead of fitting
|
||||
# for X values (R, R + K, R + 2 K), we are going to fit for (0, 1, 2), giving us much
|
||||
# simpler equation for the a, b and c coefficient
|
||||
#
|
||||
# we get:
|
||||
# - (a * 0² + b * 0 + c) = y1 => c = y1
|
||||
# - (a * 1² + b * 1 + c) = y2 => a + b = y2 - y1
|
||||
# => b = y2 - y1 - a
|
||||
# - (a * 2² + b * 2 + c) = y3 => 4a + 2b = y3 - y1
|
||||
# => 4a + 2(y2 - y1 - a) = y3 - y1
|
||||
# => a = (y1 + y3) / 2 - y2
|
||||
#
|
||||
y1, y2, y3 = values
|
||||
a, b, c = (y1 + y3) // 2 - y2, 2 * y2 - (3 * y1 + y3) // 2, y1
|
||||
# part 2
|
||||
|
||||
n = (26501365 - rhombus) // cycle
|
||||
answer_2 = a * n * n + b * n + c
|
||||
print(f"answer 2 (v2) is {answer_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
|
||||
#
|
||||
# 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
|
||||
#
|
||||
cycle = len(map)
|
||||
rhombus = (len(map) - 3) // 2 + 1
|
||||
|
||||
values: list[int] = []
|
||||
values.append(len(tiles := reachable(map, {start}, rhombus)))
|
||||
values.append(len(tiles := reachable(map, tiles, cycle)))
|
||||
values.append(len(tiles := reachable(map, tiles, cycle)))
|
||||
|
||||
if self.verbose:
|
||||
n_rows, n_cols = len(map), len(map[0])
|
||||
|
||||
rows = [
|
||||
[
|
||||
map[i % n_rows][j % n_cols] if (i, j) not in tiles else "O"
|
||||
for j in range(-2 * cycle, 3 * cycle)
|
||||
]
|
||||
for i in range(-2 * cycle, 3 * cycle)
|
||||
]
|
||||
|
||||
for i in range(len(rows)):
|
||||
for j in range(len(rows[i])):
|
||||
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))
|
||||
|
||||
self.logger.info(f"values to fit: {values}")
|
||||
|
||||
# version 1:
|
||||
#
|
||||
# after 3 cycles, the figure looks like the following:
|
||||
#
|
||||
# I M D
|
||||
# I J A K D
|
||||
# H A F A L
|
||||
# C E A K B
|
||||
# C G B
|
||||
#
|
||||
# after 4 cycles, the figure looks like the following:
|
||||
#
|
||||
# I M D
|
||||
# I J A K D
|
||||
# I J A B A K D
|
||||
# H A B A B A L
|
||||
# C E A B A N F
|
||||
# C E A N F
|
||||
# C G F
|
||||
#
|
||||
# the 'radius' of the rhombus is the number of cycles minus 1
|
||||
#
|
||||
# the 4 'corner' (M, H, L, G) are counted once, the blocks with a corner triangle (D, I,
|
||||
# C, B) are each counted radius times, the blocks with everything but one corner (J, K,
|
||||
# E, N) are each counted radius - 1 times
|
||||
#
|
||||
# there are two versions of the whole block, A and B in the above (or odd and even),
|
||||
# depending on the number of cycles, either A or B will be in the center
|
||||
#
|
||||
|
||||
counts = [
|
||||
[
|
||||
sum(
|
||||
(i, j) in tiles
|
||||
for i in range(ci * cycle, (ci + 1) * cycle)
|
||||
for j in range(cj * cycle, (cj + 1) * cycle)
|
||||
)
|
||||
for cj in range(-2, 3)
|
||||
]
|
||||
for ci in range(-2, 3)
|
||||
]
|
||||
|
||||
radius = (26501365 - rhombus) // cycle - 1
|
||||
A = counts[2][2] if radius % 2 == 0 else counts[2][1]
|
||||
B = counts[2][2] if radius % 2 == 1 else counts[2][1]
|
||||
answer_2 = (
|
||||
(radius + 1) * A
|
||||
+ radius * B
|
||||
+ 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 ((1, 1), (1, 3), (-2, 1), (-2, 3))) * radius
|
||||
)
|
||||
print(f"answer 2 (v1) is {answer_2}")
|
||||
|
||||
# version 2: fitting a polynomial
|
||||
#
|
||||
# the value we are interested in (26501365) can be written as R + K * C where R is the
|
||||
# step at which we find the first rhombus, and K the repeat step, so instead of fitting
|
||||
# for X values (R, R + K, R + 2 K), we are going to fit for (0, 1, 2), giving us much
|
||||
# simpler equation for the a, b and c coefficient
|
||||
#
|
||||
# we get:
|
||||
# - (a * 0² + b * 0 + c) = y1 => c = y1
|
||||
# - (a * 1² + b * 1 + c) = y2 => a + b = y2 - y1
|
||||
# => b = y2 - y1 - a
|
||||
# - (a * 2² + b * 2 + c) = y3 => 4a + 2b = y3 - y1
|
||||
# => 4a + 2(y2 - y1 - a) = y3 - y1
|
||||
# => a = (y1 + y3) / 2 - y2
|
||||
#
|
||||
y1, y2, y3 = values
|
||||
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
|
||||
|
@@ -1,111 +1,109 @@
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import string
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
def _name(i: int) -> str:
|
||||
if len(lines) < 26:
|
||||
return string.ascii_uppercase[i]
|
||||
return f"B{i:04d}"
|
||||
|
||||
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]]]:
|
||||
# 1. compute locations where a brick of sand will land after falling by processing
|
||||
# them in sorted order of bottom z location
|
||||
levels: dict[tuple[int, int, int], int] = defaultdict(lambda: -1)
|
||||
for i_brick, ((sx, sy, sz), (ex, ey, ez)) in enumerate(bricks):
|
||||
assert sx <= ex and sy <= ey and sz <= ez
|
||||
|
||||
xs, ys = range(sx, ex + 1), range(sy, ey + 1)
|
||||
|
||||
def build_supports(
|
||||
bricks: list[tuple[tuple[int, int, int], tuple[int, int, int]]],
|
||||
) -> tuple[dict[int, set[int]], dict[int, set[int]]]:
|
||||
# 1. compute locations where a brick of sand will land after falling by processing
|
||||
# them in sorted order of bottom z location
|
||||
levels: dict[tuple[int, int, int], int] = defaultdict(lambda: -1)
|
||||
for i_brick, ((sx, sy, sz), (ex, ey, ez)) in enumerate(bricks):
|
||||
assert sx <= ex and sy <= ey and sz <= ez
|
||||
for z in range(sz - 1, 0, -1):
|
||||
if any(levels[x, y, z] >= 0 for x, y in itertools.product(xs, ys)):
|
||||
break
|
||||
sz, ez = sz - 1, ez - 1
|
||||
|
||||
xs, ys = range(sx, ex + 1), range(sy, ey + 1)
|
||||
bricks[i_brick] = ((sx, sy, sz), (ex, ey, ez))
|
||||
zs = range(sz, ez + 1)
|
||||
|
||||
for z in range(sz - 1, 0, -1):
|
||||
if any(levels[x, y, z] >= 0 for x, y in itertools.product(xs, ys)):
|
||||
break
|
||||
sz, ez = sz - 1, ez - 1
|
||||
for x, y, z in itertools.product(xs, ys, zs):
|
||||
levels[x, y, z] = i_brick
|
||||
|
||||
bricks[i_brick] = ((sx, sy, sz), (ex, ey, ez))
|
||||
zs = range(sz, ez + 1)
|
||||
# 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))
|
||||
}
|
||||
for i_brick, ((sx, sy, sz), (ex, ey, ez)) in enumerate(bricks):
|
||||
name = _name(i_brick)
|
||||
|
||||
for x, y, z in itertools.product(xs, ys, zs):
|
||||
levels[x, y, z] = i_brick
|
||||
supported_by[i_brick] = {
|
||||
v
|
||||
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(
|
||||
f"{name} supported by {', '.join(map(_name, supported_by[i_brick]))}"
|
||||
)
|
||||
|
||||
# 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))}
|
||||
for i_brick, ((sx, sy, sz), (ex, ey, ez)) in enumerate(bricks):
|
||||
name = _name(i_brick)
|
||||
for support in supported_by[i_brick]:
|
||||
supports[support].add(i_brick)
|
||||
|
||||
supported_by[i_brick] = {
|
||||
v
|
||||
for x, y in itertools.product(range(sx, ex + 1), range(sy, ey + 1))
|
||||
if (v := levels[x, y, sz - 1]) != -1
|
||||
}
|
||||
logging.info(
|
||||
f"{name} supported by {', '.join(map(_name, supported_by[i_brick]))}"
|
||||
return supported_by, supports
|
||||
|
||||
bricks: list[tuple[tuple[int, int, int], tuple[int, int, int]]] = []
|
||||
for line in lines:
|
||||
bricks.append(
|
||||
(
|
||||
tuple(int(c) for c in line.split("~")[0].split(",")), # type: ignore
|
||||
tuple(int(c) for c in line.split("~")[1].split(",")), # type: ignore
|
||||
)
|
||||
)
|
||||
|
||||
# sort bricks by bottom z position to compute supports
|
||||
bricks = sorted(bricks, key=lambda b: b[0][-1])
|
||||
supported_by, supports = build_supports(bricks)
|
||||
|
||||
# part 1
|
||||
yield len(bricks) - sum(
|
||||
any(len(supported_by[supported]) == 1 for supported in supports_to)
|
||||
for supports_to in supports.values()
|
||||
)
|
||||
|
||||
for support in supported_by[i_brick]:
|
||||
supports[support].add(i_brick)
|
||||
# part 2
|
||||
falling_in_chain: dict[int, set[int]] = {}
|
||||
for i_brick in range(len(bricks)):
|
||||
to_disintegrate: set[int] = {
|
||||
supported
|
||||
for supported in supports[i_brick]
|
||||
if len(supported_by[supported]) == 1
|
||||
}
|
||||
|
||||
return supported_by, supports
|
||||
supported_by_copy = dict(supported_by)
|
||||
|
||||
falling_in_chain[i_brick] = set()
|
||||
while to_disintegrate:
|
||||
falling_in_chain[i_brick].update(to_disintegrate)
|
||||
|
||||
bricks: list[tuple[tuple[int, int, int], tuple[int, int, int]]] = []
|
||||
for line in lines:
|
||||
bricks.append(
|
||||
(
|
||||
tuple(int(c) for c in line.split("~")[0].split(",")), # type: ignore
|
||||
tuple(int(c) for c in line.split("~")[1].split(",")), # type: ignore
|
||||
)
|
||||
)
|
||||
to_disintegrate_v: set[int] = set()
|
||||
|
||||
# sort bricks by bottom z position to compute supports
|
||||
bricks = sorted(bricks, key=lambda b: b[0][-1])
|
||||
supported_by, supports = build_supports(bricks)
|
||||
for d_brick in to_disintegrate:
|
||||
for supported in supports[d_brick]:
|
||||
supported_by_copy[supported] = supported_by_copy[supported] - {
|
||||
d_brick
|
||||
}
|
||||
|
||||
# part 1
|
||||
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}")
|
||||
if not supported_by_copy[supported]:
|
||||
to_disintegrate_v.add(supported)
|
||||
|
||||
# part 2
|
||||
falling_in_chain: dict[int, set[int]] = {}
|
||||
for i_brick in range(len(bricks)):
|
||||
to_disintegrate: set[int] = {
|
||||
supported
|
||||
for supported in supports[i_brick]
|
||||
if len(supported_by[supported]) == 1
|
||||
}
|
||||
to_disintegrate = to_disintegrate_v
|
||||
|
||||
supported_by_copy = dict(supported_by)
|
||||
|
||||
falling_in_chain[i_brick] = set()
|
||||
while to_disintegrate:
|
||||
falling_in_chain[i_brick].update(to_disintegrate)
|
||||
|
||||
to_disintegrate_v: set[int] = set()
|
||||
|
||||
for d_brick in to_disintegrate:
|
||||
for supported in supports[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
|
||||
|
||||
answer_2 = sum(len(falling) for falling in falling_in_chain.values())
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield sum(len(falling) for falling in falling_in_chain.values())
|
||||
|
@@ -1,11 +1,7 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Literal, Sequence, TypeAlias, cast
|
||||
from typing import Any, Iterator, Literal, Sequence, TypeAlias, cast
|
||||
|
||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||
from ..base import BaseSolver
|
||||
|
||||
DirectionType: TypeAlias = Literal[">", "<", "^", "v", ".", "#"]
|
||||
|
||||
@@ -35,6 +31,7 @@ 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]]:
|
||||
@@ -103,65 +100,66 @@ def compute_direct_links(
|
||||
return direct
|
||||
|
||||
|
||||
def longest_path_length(
|
||||
links: dict[tuple[int, int], list[tuple[tuple[int, int], int]]],
|
||||
start: tuple[int, int],
|
||||
target: tuple[int, int],
|
||||
) -> int:
|
||||
max_distance: int = -1
|
||||
queue: list[tuple[tuple[int, int], int, frozenset[tuple[int, int]]]] = [
|
||||
(start, 0, frozenset({start}))
|
||||
]
|
||||
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],
|
||||
) -> int:
|
||||
max_distance: int = -1
|
||||
queue: list[tuple[tuple[int, int], int, frozenset[tuple[int, int]]]] = [
|
||||
(start, 0, frozenset({start}))
|
||||
]
|
||||
|
||||
nodes = 0
|
||||
while queue:
|
||||
node, distance, path = queue.pop()
|
||||
nodes = 0
|
||||
while queue:
|
||||
node, distance, path = queue.pop()
|
||||
|
||||
nodes += 1
|
||||
nodes += 1
|
||||
|
||||
if node == target:
|
||||
max_distance = max(distance, max_distance)
|
||||
continue
|
||||
if node == target:
|
||||
max_distance = max(distance, max_distance)
|
||||
continue
|
||||
|
||||
queue.extend(
|
||||
(reach, distance + length, path | {reach})
|
||||
for reach, length in links.get(node, [])
|
||||
if reach not in path
|
||||
queue.extend(
|
||||
(reach, distance + length, path | {reach})
|
||||
for reach, length in links.get(node, [])
|
||||
if reach not in path
|
||||
)
|
||||
|
||||
self.logger.info(f"processed {nodes} nodes")
|
||||
|
||||
return max_distance
|
||||
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = cast(list[Sequence[DirectionType]], input.splitlines())
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
logging.info(f"processed {nodes} nodes")
|
||||
# part 1
|
||||
yield self.longest_path_length(direct_links, start, target)
|
||||
|
||||
return max_distance
|
||||
# part 2
|
||||
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:
|
||||
if origin != start:
|
||||
reverse_links[destination].append((origin, distance))
|
||||
|
||||
links = {
|
||||
k: direct_links.get(k, []) + reverse_links.get(k, [])
|
||||
for k in direct_links.keys() | reverse_links.keys()
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
# part 1
|
||||
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
|
||||
)
|
||||
for origin, links in direct_links.items():
|
||||
for destination, distance in links:
|
||||
if origin != start:
|
||||
reverse_links[destination].append((origin, distance))
|
||||
|
||||
links = {
|
||||
k: direct_links.get(k, []) + reverse_links.get(k, [])
|
||||
for k in direct_links.keys() | reverse_links.keys()
|
||||
}
|
||||
|
||||
answer_2 = longest_path_length(links, start, target)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield self.longest_path_length(links, start, target)
|
||||
|
@@ -1,63 +1,68 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
import numpy as np
|
||||
from sympy import solve, symbols
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
positions = np.array(
|
||||
[[int(c) for c in line.split("@")[0].strip().split(", ")] for line in lines]
|
||||
)
|
||||
velocities = np.array(
|
||||
[[int(c) for c in line.split("@")[1].strip().split(", ")] for line in lines]
|
||||
)
|
||||
|
||||
# part 1
|
||||
low, high = [7, 27] if len(positions) <= 10 else [200000000000000, 400000000000000]
|
||||
|
||||
count = 0
|
||||
for i1, (p1, v1) in enumerate(zip(positions, velocities)):
|
||||
p, r = p1[:2], v1[:2]
|
||||
|
||||
q, s = positions[i1 + 1 :, :2], velocities[i1 + 1 :, :2]
|
||||
|
||||
rs = np.cross(r, s)
|
||||
|
||||
q, s, rs = q[m := (rs != 0)], s[m], rs[m]
|
||||
t = np.cross((q - p), s) / rs
|
||||
u = np.cross((q - p), r) / rs
|
||||
|
||||
t, u = t[m := ((t >= 0) & (u >= 0))], u[m]
|
||||
c = p + np.expand_dims(t, 1) * r
|
||||
count += np.all((low <= c) & (c <= high), axis=1).sum()
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
answer_1 = count
|
||||
print(f"answer 1 is {answer_1}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
# part 2
|
||||
# equation
|
||||
# p1 + t1 * v1 == p0 + t1 * v0
|
||||
# p2 + t2 * v2 == p0 + t2 * v0
|
||||
# p3 + t3 * v3 == p0 + t3 * v0
|
||||
# ...
|
||||
# pn + tn * vn == p0 + tn * v0
|
||||
#
|
||||
positions = np.array(
|
||||
[[int(c) for c in line.split("@")[0].strip().split(", ")] for line in lines]
|
||||
)
|
||||
velocities = np.array(
|
||||
[[int(c) for c in line.split("@")[1].strip().split(", ")] for line in lines]
|
||||
)
|
||||
|
||||
# we can solve with only 3 lines since each lines contains 3
|
||||
# equations (x / y / z), so 3 lines give 9 equations and 9
|
||||
# variables: position (3), velocities (3) and times (3).
|
||||
n = 3
|
||||
# part 1
|
||||
low, high = (
|
||||
[7, 27] if len(positions) <= 10 else [200000000000000, 400000000000000]
|
||||
)
|
||||
|
||||
x, y, z, vx, vy, vz, *ts = symbols(
|
||||
"x y z vx vy vz " + " ".join(f"t{i}" for i in range(n + 1))
|
||||
)
|
||||
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]):
|
||||
equations.append(p + ti * d - pi - ti * di)
|
||||
count = 0
|
||||
for i1, (p1, v1) in enumerate(zip(positions, velocities)):
|
||||
p, r = p1[:2], v1[:2]
|
||||
|
||||
r = solve(equations, [x, y, z, vx, vy, vz] + list(ts), dict=True)[0]
|
||||
q, s = positions[i1 + 1 :, :2], velocities[i1 + 1 :, :2]
|
||||
|
||||
answer_2 = r[x] + r[y] + r[z]
|
||||
print(f"answer 2 is {answer_2}")
|
||||
rs = np.cross(r, s)
|
||||
|
||||
q, s, rs = q[m := (rs != 0)], s[m], rs[m]
|
||||
t = np.cross((q - p), s) / rs
|
||||
u = np.cross((q - p), r) / rs
|
||||
|
||||
t, u = t[m := ((t >= 0) & (u >= 0))], u[m]
|
||||
c = p + np.expand_dims(t, 1) * r
|
||||
count += np.all((low <= c) & (c <= high), axis=1).sum()
|
||||
|
||||
yield count
|
||||
|
||||
# part 2
|
||||
# equation
|
||||
# p1 + t1 * v1 == p0 + t1 * v0
|
||||
# p2 + t2 * v2 == p0 + t2 * v0
|
||||
# p3 + t3 * v3 == p0 + t3 * v0
|
||||
# ...
|
||||
# pn + tn * vn == p0 + tn * v0
|
||||
#
|
||||
|
||||
# we can solve with only 3 lines since each lines contains 3
|
||||
# equations (x / y / z), so 3 lines give 9 equations and 9
|
||||
# variables: position (3), velocities (3) and times (3).
|
||||
n = 3
|
||||
|
||||
x, y, z, vx, vy, vz, *ts = symbols(
|
||||
"x y z vx vy vz " + " ".join(f"t{i}" for i in range(n + 1))
|
||||
)
|
||||
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]
|
||||
):
|
||||
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]
|
||||
|
@@ -1,25 +1,23 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
import networkx as nx
|
||||
|
||||
components = {
|
||||
(p := line.split(": "))[0]: p[1].split() for line in sys.stdin.read().splitlines()
|
||||
}
|
||||
from ..base import BaseSolver
|
||||
|
||||
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)
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
components = {
|
||||
(p := line.split(": "))[0]: p[1].split() for line in input.splitlines()
|
||||
}
|
||||
|
||||
cut = nx.minimum_edge_cut(graph)
|
||||
graph.remove_edges_from(cut)
|
||||
graph: "nx.Graph[str]" = nx.Graph()
|
||||
graph.add_edges_from((u, v) for u, vs in components.items() for v in vs)
|
||||
|
||||
c1, c2 = nx.connected_components(graph)
|
||||
cut = nx.minimum_edge_cut(graph)
|
||||
graph.remove_edges_from(cut)
|
||||
|
||||
# part 1
|
||||
answer_1 = len(c1) * len(c2)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
c1, c2 = nx.connected_components(graph)
|
||||
|
||||
# part 2
|
||||
answer_2 = ...
|
||||
print(f"answer 2 is {answer_2}")
|
||||
# part 1
|
||||
yield len(c1) * len(c2)
|
||||
|
@@ -1,53 +1,53 @@
|
||||
import string
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
NOT_A_SYMBOL = "." + string.digits
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
values: list[int] = []
|
||||
gears: dict[tuple[int, int], list[int]] = defaultdict(list)
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
j = 0
|
||||
while j < len(line):
|
||||
# skip everything until a digit is found (start of a number)
|
||||
if line[j] not in string.digits:
|
||||
j += 1
|
||||
continue
|
||||
values: list[int] = []
|
||||
gears: dict[tuple[int, int], list[int]] = defaultdict(list)
|
||||
|
||||
# extract the range of the number and its value
|
||||
k = j + 1
|
||||
while k < len(line) and line[k] in string.digits:
|
||||
k += 1
|
||||
for i, line in enumerate(lines):
|
||||
j = 0
|
||||
while j < len(line):
|
||||
# skip everything until a digit is found (start of a number)
|
||||
if line[j] not in string.digits:
|
||||
j += 1
|
||||
continue
|
||||
|
||||
value = int(line[j:k])
|
||||
# extract the range of the number and its value
|
||||
k = j + 1
|
||||
while k < len(line) and line[k] in string.digits:
|
||||
k += 1
|
||||
|
||||
# lookup around the number if there is a symbol - we go through the number
|
||||
# itself but that should not matter since it only contains digits
|
||||
found = False
|
||||
for i2 in range(max(0, i - 1), min(i + 1, len(lines) - 1) + 1):
|
||||
for j2 in range(max(0, j - 1), min(k, len(line) - 1) + 1):
|
||||
assert i2 >= 0 and i2 < len(lines)
|
||||
assert j2 >= 0 and j2 < len(line)
|
||||
value = int(line[j:k])
|
||||
|
||||
if lines[i2][j2] not in NOT_A_SYMBOL:
|
||||
found = True
|
||||
# lookup around the number if there is a symbol - we go through the number
|
||||
# itself but that should not matter since it only contains digits
|
||||
found = False
|
||||
for i2 in range(max(0, i - 1), min(i + 1, len(lines) - 1) + 1):
|
||||
for j2 in range(max(0, j - 1), min(k, len(line) - 1) + 1):
|
||||
assert i2 >= 0 and i2 < len(lines)
|
||||
assert j2 >= 0 and j2 < len(line)
|
||||
|
||||
if lines[i2][j2] == "*":
|
||||
gears[i2, j2].append(value)
|
||||
if lines[i2][j2] not in NOT_A_SYMBOL:
|
||||
found = True
|
||||
|
||||
if found:
|
||||
values.append(value)
|
||||
if lines[i2][j2] == "*":
|
||||
gears[i2, j2].append(value)
|
||||
|
||||
# continue starting from the end of the number
|
||||
j = k
|
||||
if found:
|
||||
values.append(value)
|
||||
|
||||
# part 1
|
||||
answer_1 = sum(values)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
# continue starting from the end of the number
|
||||
j = k
|
||||
|
||||
# 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}")
|
||||
yield sum(values)
|
||||
yield sum(v1 * v2 for v1, v2 in filter(lambda vs: len(vs) == 2, gears.values()))
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -9,33 +11,34 @@ class Card:
|
||||
values: list[int]
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
cards: list[Card] = []
|
||||
for line in lines:
|
||||
id_part, e_part = line.split(":")
|
||||
numbers_s, values_s = e_part.split("|")
|
||||
cards.append(
|
||||
Card(
|
||||
id=int(id_part.split()[1]),
|
||||
numbers=[int(v.strip()) for v in numbers_s.strip().split()],
|
||||
values=[int(v.strip()) for v in values_s.strip().split()],
|
||||
)
|
||||
)
|
||||
cards: list[Card] = []
|
||||
for line in lines:
|
||||
id_part, e_part = line.split(":")
|
||||
numbers_s, values_s = e_part.split("|")
|
||||
cards.append(
|
||||
Card(
|
||||
id=int(id_part.split()[1]),
|
||||
numbers=[int(v.strip()) for v in numbers_s.strip().split()],
|
||||
values=[int(v.strip()) for v in values_s.strip().split()],
|
||||
)
|
||||
)
|
||||
|
||||
winnings = [sum(1 for n in card.values if n in card.numbers) for card in cards]
|
||||
winnings = [sum(1 for n in card.values if n in card.numbers) for card in cards]
|
||||
|
||||
# part 1
|
||||
answer_1 = sum(2 ** (winning - 1) for winning in winnings if winning > 0)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
# part 1
|
||||
yield sum(2 ** (winning - 1) for winning in winnings if winning > 0)
|
||||
|
||||
# part 2
|
||||
card2cards = {i: list(range(i + 1, i + w + 1)) for i, w in enumerate(winnings)}
|
||||
card2values = {i: 0 for i in range(len(cards))}
|
||||
# part 2
|
||||
card2cards = {i: list(range(i + 1, i + w + 1)) for i, w in enumerate(winnings)}
|
||||
card2values = {i: 0 for i in range(len(cards))}
|
||||
|
||||
for i in range(len(cards)):
|
||||
card2values[i] += 1
|
||||
for j in card2cards[i]:
|
||||
card2values[j] += card2values[i]
|
||||
for i in range(len(cards)):
|
||||
card2values[i] += 1
|
||||
for j in card2cards[i]:
|
||||
card2values[j] += card2values[i]
|
||||
|
||||
print(f"answer 2 is {sum(card2values.values())}")
|
||||
yield sum(card2values.values())
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import sys
|
||||
from typing import Sequence
|
||||
from typing import Any, Iterator, Sequence
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
MAP_ORDER = [
|
||||
"seed",
|
||||
@@ -12,55 +13,6 @@ 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]]
|
||||
@@ -111,19 +63,71 @@ def find_range(
|
||||
return ranges
|
||||
|
||||
|
||||
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
|
||||
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]]] = {}
|
||||
|
||||
# part 1 - use find_range() with range of length 1
|
||||
seeds_p1 = [(int(s), 1) for s in lines[0].split(":")[1].strip().split()]
|
||||
answer_1 = min(start for start, _ in find_location_ranges(seeds_p1))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
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
|
||||
|
||||
# # 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])]
|
||||
answer_2 = min(start for start, _ in find_location_ranges(seeds_p2))
|
||||
print(f"answer 2 is {answer_2}")
|
||||
# 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))
|
||||
|
||||
# # 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))
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import math
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def extreme_times_to_beat(time: int, distance: int) -> tuple[int, int]:
|
||||
@@ -25,23 +27,23 @@ def extreme_times_to_beat(time: int, distance: int) -> tuple[int, int]:
|
||||
return t1, t2
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
# part 1
|
||||
times = list(map(int, lines[0].split()[1:]))
|
||||
distances = list(map(int, lines[1].split()[1:]))
|
||||
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 1
|
||||
times = list(map(int, lines[0].split()[1:]))
|
||||
distances = list(map(int, lines[1].split()[1:]))
|
||||
yield math.prod(
|
||||
t2 - t1 + 1
|
||||
for t1, t2 in (
|
||||
extreme_times_to_beat(time, distance)
|
||||
for time, distance in zip(times, distances)
|
||||
)
|
||||
)
|
||||
|
||||
# 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)
|
||||
answer_2 = t2 - t1 + 1
|
||||
print(f"answer 2 is {answer_2}")
|
||||
# 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
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import sys
|
||||
from collections import Counter, defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class HandTypes:
|
||||
@@ -32,18 +34,17 @@ def extract_key(hand: str, values: dict[str, int], joker: str = "0") -> tuple[in
|
||||
)
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
cards = [(t[0], int(t[1])) for line in lines if (t := line.split())]
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.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))
|
||||
|
||||
# part 1
|
||||
values = {card: value for value, card in enumerate("23456789TJQKA")}
|
||||
cards.sort(key=lambda cv: extract_key(cv[0], values=values))
|
||||
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"))
|
||||
answer_2 = sum(rank * value for rank, (_, value) in enumerate(cards, start=1))
|
||||
print(f"answer 2 is {answer_2}")
|
||||
# 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))
|
||||
|
@@ -1,29 +1,30 @@
|
||||
import itertools
|
||||
import math
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
sequence = lines[0]
|
||||
nodes = {
|
||||
p[0]: {d: n for d, n in zip("LR", p[1].strip("()").split(", "))}
|
||||
for line in lines[2:]
|
||||
if (p := line.split(" = "))
|
||||
}
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def path(start: str):
|
||||
path = [start]
|
||||
it_seq = iter(itertools.cycle(sequence))
|
||||
while not path[-1].endswith("Z"):
|
||||
path.append(nodes[path[-1]][next(it_seq)])
|
||||
return path
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
sequence = lines[0]
|
||||
nodes = {
|
||||
p[0]: {d: n for d, n in zip("LR", p[1].strip("()").split(", "))}
|
||||
for line in lines[2:]
|
||||
if (p := line.split(" = "))
|
||||
}
|
||||
|
||||
# part 1
|
||||
answer_1 = len(path(next(node for node in nodes if node.endswith("A")))) - 1
|
||||
print(f"answer 1 is {answer_1}")
|
||||
def path(start: str):
|
||||
path = [start]
|
||||
it_seq = iter(itertools.cycle(sequence))
|
||||
while not path[-1].endswith("Z"):
|
||||
path.append(nodes[path[-1]][next(it_seq)])
|
||||
return path
|
||||
|
||||
# part 2
|
||||
answer_2 = math.lcm(*(len(path(node)) - 1 for node in nodes if node.endswith("A")))
|
||||
print(f"answer 2 is {answer_2}")
|
||||
# part 1
|
||||
yield len(path(next(node for node in nodes if node.endswith("A")))) - 1
|
||||
|
||||
# part 2
|
||||
yield math.lcm(*(len(path(node)) - 1 for node in nodes if node.endswith("A")))
|
||||
|
@@ -1,29 +1,34 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
data = [[int(c) for c in line.split()] for line in lines]
|
||||
|
||||
right_values: list[int] = []
|
||||
left_values: list[int] = []
|
||||
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:])])
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
rhs: list[int] = [0]
|
||||
lhs: list[int] = [0]
|
||||
for cx in range(len(diffs) - 1):
|
||||
rhs.append(diffs[-cx - 2][-1] + rhs[cx])
|
||||
lhs.append(diffs[-cx - 2][0] - lhs[cx])
|
||||
data = [[int(c) for c in line.split()] for line in lines]
|
||||
|
||||
right_values.append(rhs[-1])
|
||||
left_values.append(lhs[-1])
|
||||
right_values: list[int] = []
|
||||
left_values: list[int] = []
|
||||
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:])]
|
||||
)
|
||||
|
||||
# part 1
|
||||
answer_1 = sum(right_values)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
rhs: list[int] = [0]
|
||||
lhs: list[int] = [0]
|
||||
for cx in range(len(diffs) - 1):
|
||||
rhs.append(diffs[-cx - 2][-1] + rhs[cx])
|
||||
lhs.append(diffs[-cx - 2][0] - lhs[cx])
|
||||
|
||||
# part 2
|
||||
answer_2 = sum(left_values)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
right_values.append(rhs[-1])
|
||||
left_values.append(lhs[-1])
|
||||
|
||||
# part 1
|
||||
yield sum(right_values)
|
||||
|
||||
# part 2
|
||||
yield sum(left_values)
|
||||
|
@@ -1,14 +1,17 @@
|
||||
import sys
|
||||
from collections import Counter
|
||||
from typing import Any, Iterator
|
||||
|
||||
values = list(map(int, sys.stdin.read().strip().split()))
|
||||
from ..base import BaseSolver
|
||||
|
||||
column_1 = sorted(values[::2])
|
||||
column_2 = sorted(values[1::2])
|
||||
counter_2 = Counter(column_2)
|
||||
|
||||
answer_1 = sum(abs(v1 - v2) for v1, v2 in zip(column_1, column_2, strict=True))
|
||||
answer_2 = sum(value * counter_2.get(value, 0) for value in column_1)
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
values = list(map(int, input.split()))
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
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)
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,22 +1,23 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def is_safe(level: list[int]) -> bool:
|
||||
diff = [a - b for a, b in zip(level[:-1], level[1:], strict=True)]
|
||||
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)]
|
||||
|
||||
return sum(d > 0 for d in diff) in (0, len(diff)) and all(
|
||||
1 <= abs(d) <= 3 for d in diff
|
||||
)
|
||||
return sum(d > 0 for d in diff) in (0, len(diff)) and all(
|
||||
1 <= abs(d) <= 3 for d in diff
|
||||
)
|
||||
|
||||
def is_any_safe(level: list[int]) -> bool:
|
||||
return any(
|
||||
is_safe(level[:i] + level[i + 1 :]) for i in range(0, len(level))
|
||||
)
|
||||
|
||||
def is_any_safe(level: list[int]) -> bool:
|
||||
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()]
|
||||
|
||||
|
||||
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}")
|
||||
yield sum(is_safe(level) for level in levels)
|
||||
yield sum(is_safe(level) or is_any_safe(level) for level in levels)
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,34 +1,30 @@
|
||||
import re
|
||||
import sys
|
||||
from typing import Iterator
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
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))
|
||||
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:
|
||||
if accumulate:
|
||||
if (dont_i := line.find("don't()")) != -1:
|
||||
yield line[:dont_i]
|
||||
line, accumulate = line[dont_i:], False
|
||||
else:
|
||||
yield line
|
||||
line = ""
|
||||
else:
|
||||
if (do_i := line.find("do()")) != -1:
|
||||
line, accumulate = line[do_i:], True
|
||||
else:
|
||||
line = ""
|
||||
|
||||
def valid_memory_blocks(line: str) -> Iterator[str]:
|
||||
accumulate = True
|
||||
while line:
|
||||
if accumulate:
|
||||
if (dont_i := line.find("don't()")) != -1:
|
||||
yield line[:dont_i]
|
||||
line, accumulate = line[dont_i:], False
|
||||
else:
|
||||
yield line
|
||||
line = ""
|
||||
else:
|
||||
if (do_i := line.find("do()")) != -1:
|
||||
line, accumulate = line[do_i:], True
|
||||
else:
|
||||
line = ""
|
||||
|
||||
|
||||
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}")
|
||||
yield sum(extract_multiply(input))
|
||||
yield sum(sum(extract_multiply(block)) for block in valid_memory_blocks(input))
|
||||
|
@@ -1,46 +1,37 @@
|
||||
import sys
|
||||
import itertools as it
|
||||
from typing import Any, Iterator
|
||||
|
||||
import numpy as np
|
||||
from ..base import BaseSolver
|
||||
|
||||
lines = np.array(list(map(list, sys.stdin.read().strip().splitlines())))
|
||||
print(lines)
|
||||
n = lines.shape[0]
|
||||
|
||||
answer_1 = 0
|
||||
for i in range(n):
|
||||
line = "".join(lines[i, :])
|
||||
answer_1 += line.count("XMAS") + line.count("SAMX")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
n = len(lines)
|
||||
|
||||
column = "".join(lines[:, i])
|
||||
answer_1 += column.count("XMAS") + column.count("SAMX")
|
||||
|
||||
diag = "".join(np.diagonal(lines, i))
|
||||
answer_1 += diag.count("XMAS") + diag.count("SAMX")
|
||||
|
||||
diag = "".join(np.diagonal(lines[::-1, :], i))
|
||||
answer_1 += diag.count("XMAS") + diag.count("SAMX")
|
||||
|
||||
if i != 0:
|
||||
diag = "".join(np.diagonal(lines, -i))
|
||||
answer_1 += diag.count("XMAS") + diag.count("SAMX")
|
||||
|
||||
diag = "".join(np.diagonal(lines[::-1, :], -i))
|
||||
answer_1 += diag.count("XMAS") + diag.count("SAMX")
|
||||
|
||||
answer_2 = sum(
|
||||
"".join(
|
||||
(
|
||||
lines[i - 1, j - 1],
|
||||
lines[i - 1, j + 1],
|
||||
lines[i + 1, j - 1],
|
||||
lines[i + 1, j + 1],
|
||||
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)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
in {"MSMS", "SSMM", "MMSS", "SMSM"}
|
||||
for i in range(1, n - 1)
|
||||
for j in range(1, n - 1)
|
||||
if lines[i, j] == "A"
|
||||
)
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
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))
|
||||
)
|
||||
|
@@ -1,10 +1,65 @@
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
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)
|
||||
)
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {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)
|
||||
)
|
||||
|
@@ -1,10 +1,122 @@
|
||||
import sys
|
||||
import itertools as it
|
||||
from typing import Any, Iterator, TypeAlias
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
NodeType: TypeAlias = tuple[tuple[int, int], tuple[int, int]]
|
||||
EdgesType: TypeAlias = dict[NodeType, tuple[NodeType, set[tuple[int, int]]]]
|
||||
|
||||
answer_2 = ...
|
||||
ROTATE = {(-1, 0): (0, 1), (0, 1): (1, 0), (1, 0): (0, -1), (0, -1): (-1, 0)}
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {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)
|
||||
|
@@ -1,10 +1,50 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
def evaluate(
|
||||
target: int, numbers: list[int], concatenate: bool = False, current: int = 0
|
||||
) -> bool:
|
||||
if not numbers:
|
||||
return current == target
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {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)
|
||||
)
|
||||
|
@@ -1,10 +1,76 @@
|
||||
import sys
|
||||
import itertools as it
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator, cast
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
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
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {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)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
answer_1 = ...
|
||||
|
||||
answer_2 = ...
|
||||
|
||||
print(f"answer 1 is {answer_1}")
|
||||
print(f"answer 2 is {answer_2}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
|
@@ -1,14 +1,114 @@
|
||||
import argparse
|
||||
import importlib
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import logging.handlers
|
||||
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"
|
||||
)
|
||||
@@ -31,6 +131,7 @@ 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
|
||||
@@ -40,8 +141,10 @@ def main():
|
||||
day: int = args.day
|
||||
|
||||
# TODO: change this
|
||||
if verbose:
|
||||
os.environ["AOC_VERBOSE"] = "True"
|
||||
logging.basicConfig(
|
||||
level=logging.INFO if verbose or api else logging.WARNING,
|
||||
handlers=[LoggerAPIHandler()] if api else None,
|
||||
)
|
||||
|
||||
if input_path is None:
|
||||
input_path = Path(__file__).parent.joinpath(
|
||||
@@ -49,11 +152,55 @@ 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:
|
||||
importlib.import_module(f".{year}.day{day}", __package__)
|
||||
data = sys.stdin.read()
|
||||
else:
|
||||
with open(input_path) as fp:
|
||||
sys.stdin = fp
|
||||
importlib.import_module(f".{year}.day{day}", __package__)
|
||||
data = fp.read()
|
||||
|
||||
sys.stdin = sys.__stdin__
|
||||
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
|
||||
|
37
src/holt59/aoc/base.py
Normal file
37
src/holt59/aoc/base.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from abc import abstractmethod
|
||||
from logging import Logger
|
||||
from typing import Any, Final, Iterable, Iterator, Protocol, Sequence, TypeVar, overload
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
class ProgressHandler(Protocol):
|
||||
@overload
|
||||
def wrap(self, values: Sequence[_T]) -> Iterator[_T]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def wrap(self, values: Iterable[_T], total: int) -> Iterator[_T]:
|
||||
...
|
||||
|
||||
|
||||
class BaseSolver:
|
||||
def __init__(
|
||||
self,
|
||||
logger: Logger,
|
||||
verbose: bool,
|
||||
year: int,
|
||||
day: int,
|
||||
progress: ProgressHandler,
|
||||
outputs: bool = False,
|
||||
):
|
||||
self.logger: Final = logger
|
||||
self.verbose: Final = verbose
|
||||
self.year: Final = year
|
||||
self.day: Final = day
|
||||
self.progress: Final = progress
|
||||
self.outputs = outputs
|
||||
|
||||
@abstractmethod
|
||||
def solve(self, input: str) -> Iterator[Any] | None:
|
||||
...
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,130 @@
|
||||
..............................#....#..........................................#....................#...........#..##..............
|
||||
.......#............................#..........#...................................................#...................#........#.
|
||||
......................................#..................#.................#....................................#...#............#
|
||||
........#...................#.....................................................................................#...............
|
||||
.............................#........................#.......................#........#............................#.............
|
||||
........................##......................................#...............#...................................#.............
|
||||
...#................#.......................#....#........................#.............................#.........................
|
||||
..............................................................#............##......#.......#..#.....................#.....#.......
|
||||
.........................#..#............##.......#.........#....................#.......................##...............#....#..
|
||||
..............#....................................................................#....#........................................#
|
||||
.................#............#.....#.....#............................................................#......................#...
|
||||
.#.................#.................#.....................................#......................................................
|
||||
...........##..#.......#.........#.#......#..................#.....................#.....#........................................
|
||||
....................................................#........#........#....................................#......................
|
||||
........#........#..........#..........................................................#.##.........#..........................#..
|
||||
...........#............................#............##.............................#...............................#............#
|
||||
............#......#...............#...........#................................................#.................................
|
||||
............#..#.#......#............................................................................#.##....#............#.......
|
||||
................#..............#......#...#........................#.......#.........................#............................
|
||||
..........................................................................#............#........#......#.#......#.............#...
|
||||
..........#...#...........................................................#......#..........................................#.....
|
||||
.......................................#....#..............#...............#....#............#.............#......................
|
||||
...........#............................................#................................#..............#.........................
|
||||
..##...............#.................................................................................#.......#.........#.........#
|
||||
..........#......#......#.........................................#..........#...##.................................#........#....
|
||||
..........#.......................................................................#..........#...............##.#....#............
|
||||
....#.............#..#......#..........................................................#......................#...................
|
||||
.......................................................................#.............##......................#............#.......
|
||||
............#..#.........#...............................................................................#........................
|
||||
...............##..#..................#....#....................................................................#......#..........
|
||||
......#...............................................................................#..#...............#........................
|
||||
.....#............................................................................#...............#......#........................
|
||||
#.......................#.........................#...........#....#...#.......#..................................................
|
||||
.#....................#..............#........................#....................#........###............#.#.#............#.....
|
||||
.......#...#...................#................................................................................#........##.......
|
||||
............#.................#.........................................#.....................................#...................
|
||||
..................#...........#...................................................................................................
|
||||
......................#.......#...............................#......#...........................#..................#.............
|
||||
...#..........#......#..........#.....................................................#.....#.................................#...
|
||||
......#.......#..............................#..............#...............................#...............................#.....
|
||||
....................................................................#..........#.....#.#...#................................#.....
|
||||
..............................#...#.....................##.........................................#.#...................#........
|
||||
........................#....#..............#....................................................................................#
|
||||
............................#......##...........................#...........#...............................#.....................
|
||||
.....................................................................................#............................................
|
||||
#.....................#.................#...#.......##....................#.##.....#.#.......................#....................
|
||||
....................#............................#.............................................................................#..
|
||||
........................#.....................................#.............#..............#......................................
|
||||
...............#....#...................#............................#..#...#.....................................................
|
||||
.#...........#......#...........##.#...........^.....#........................#..........#.................#......................
|
||||
...#.##.........#.#......................................#...............#............#......#....................................
|
||||
.....................................................#......................#.........#.......................#...................
|
||||
............................................##............#....................#............................#.....................
|
||||
.............................#.........................##.............#...........................................................
|
||||
........................................#.............................................................................#...........
|
||||
.........................................#...............#........#..........................#.#........................#.........
|
||||
............................................#.................#...............................#...........#.#.......#.............
|
||||
....................#.............#.........................#..........................#............................#.............
|
||||
.#.................................#..........#..#.......................................................#........................
|
||||
..................#..............................................#............##.....#......................................#..#..
|
||||
.......#...........................................#..................#...........................................................
|
||||
.#.................................................................#....................................................#.........
|
||||
...........#.#........#....#...............#.........................................#........................#.........#.........
|
||||
...............#......#...........#....#....#...............................#..#......#........#.....................#......#.....
|
||||
.......................................................#.........#.............................#..................#...............
|
||||
#........................#.........#..........................#................................#...#.....................#.......#
|
||||
.............................#....................................................................................................
|
||||
...#..................#....#.............................................................................................#........
|
||||
..................#........#...............#............................................#..........#..#........................#..
|
||||
..#...................#..................................#.......#......................#.................................#......#
|
||||
.........................................#.#......##..........................#.........#..#....................................#.
|
||||
..........#....#............#.....##....................................................................................#.........
|
||||
.....#.........##.................................................................................................................
|
||||
.............................................................................................................#.............#......
|
||||
...........#.......#..............#...#............#.......................#...#......#.....................................#.....
|
||||
.......#...................#...........#.....#...#..........................#...........#.........#.............................#.
|
||||
..............#.....................#..........................................................#..................................
|
||||
..#............#.................................................#.....#.....................#....#.............................#.
|
||||
.................#..................................#....#...............................#........................................
|
||||
.......#..#...........#.#..........##.#.#......................................##.#.....................................#....#....
|
||||
.....#.#...#......#.............................................................#.........#..#.....................#.............#
|
||||
.....................................................#..........................................#.......................#.........
|
||||
........................#....................#................................#......#...........................#......#.........
|
||||
..........#...........................................................#.....#.#....#...............................#...........#..
|
||||
........#...............#..............#......#..............................................................#.............#......
|
||||
...................................................................................................................#..............
|
||||
.....#..................................................................................##...................#.......#.....#......
|
||||
...#..#..............#....................................#..#.#..........#.......................................................
|
||||
#..................#......##.......#..............................................................................................
|
||||
........................................#....#.........................................................................#..........
|
||||
#.................................#...................................................#.........#.#...............................
|
||||
..........................#.....#.#.....................#..........#...................#..#...........................#.....#.....
|
||||
..........................#..............................#.......#................................................................
|
||||
.........................#............................#.........#.........#............#............#.#..........................#
|
||||
.#.#.....................#.....................................#............#.........#..........#.............#.................#
|
||||
.........#..........................................#.........................................................#...................
|
||||
..........................................................#.........................#....#...#......#........................#....
|
||||
.......#.#.........#.............................................#.................#...................#.................#.#......
|
||||
............#..#........#..#...........#.........................................................#..............#...#.............
|
||||
..........................#..............#...................................................................#....................
|
||||
......#.....................#...............#......................................#......#.................#.....................
|
||||
.........................................................................#.#...........................#..#....#..................
|
||||
...........#.............................#...#....#...............................................................................
|
||||
.................................#.....#...............#....#.#.....................................#.........................#...
|
||||
.#.......#..............#....#..............................................................................#...............##....
|
||||
.................................#............................#................#...#.........#...................................#
|
||||
...........#......#.......#..............#.........................#.#.....................................#.......#..............
|
||||
.....................#....................................#...................#...................................................
|
||||
...........#...................#.....................................................................................#........#...
|
||||
..................#...#....................................................................................#..#.........#.........
|
||||
.............#.................................................#.........#.#.#...............................................#....
|
||||
..................#............................................#.......................................................#..........
|
||||
..#......#...........#............................................#...........#............#......#.....................#.........
|
||||
......#.......................##..........#................................................#...................................#..
|
||||
.......................................................................#.........#........#....#........................#......#..
|
||||
..........#...........#...#....................................#.#.............#.#............................#...#.....#.........
|
||||
.##...............................#......#.....................................................#.........................#......#.
|
||||
.#.#...#.................................................#......#....................#...........................................#
|
||||
.........................#..................#.............................#......................................#................
|
||||
...................................#.......................#..................................#.........#.........................
|
||||
.........................#...........#...............#........#..................................#...........#..............#.....
|
||||
...............................#.......................................#.......#.....#.................#....#..............#......
|
||||
...#...........#.....#................................................#..................#........#.............................#.
|
||||
..............#........#................................#...#................................#.......#.......#........#........#..
|
||||
......#....#...................................#............#.........#..#.##..................##....................#......#.....
|
||||
.....#...........................#................##.............#.#...........................................#..................
|
||||
#....................................................................#..#......................#..........#.......................
|
||||
...........#...#..............#.........#....................#.....................#........#.......#...#.#............#..........
|
||||
.......#..............#........#..##............#...............#.............#.#.......................#.#......#....#...........
|
||||
....#........#..................#...............#....................#...#...............................#..........#....#..##....
|
||||
|
@@ -0,0 +1,850 @@
|
||||
408407: 40 770 70 8 1
|
||||
1222314027: 4 40 6 66 2 4 7 8 5 519
|
||||
79632370: 12 6 79 8 369 1
|
||||
173138715990: 7 7 6 4 2 692 6 2 8 2 2 45
|
||||
820782447: 273 593 984 3 5 489 2
|
||||
6344588: 1 866 33 1 222 50
|
||||
244480: 1 59 6 86 8 816 45 66
|
||||
30431590286: 745 872 3 68 6 4 1 442
|
||||
1178358827: 530 4 7 554 827
|
||||
5472884572198: 380 5 3 71 85 5 36 94 1
|
||||
1290391: 1 17 308 11 2
|
||||
2279613269: 729 6 8 9 177 73 3
|
||||
2590613018: 29 91 47 433 4 1 5
|
||||
7528390101668: 17 8 939 47 83 744 71
|
||||
102796125: 6 8 5 1 3 454 961 2 5
|
||||
1199: 6 982 208
|
||||
108411: 9 1 6 98 8 7 12 9 71 36 4
|
||||
3589: 1 8 15 2 11 24 37
|
||||
16052: 60 6 2 98 3 45 9 1 3 71 6
|
||||
2795761: 706 396 1
|
||||
1307185: 197 7 4 6 8 17 1 9 9 1 55
|
||||
943: 920 3 20
|
||||
89520: 75 58 52 3 4 5 16
|
||||
9492769728: 6 325 2 234 564 8 1 3 9
|
||||
168032403: 1 89 3 3 465 27 77 17
|
||||
11444723221: 894 11 90 2 64 3 18
|
||||
3987348: 45 3 88 61 47 840
|
||||
399956: 99 897 74 4 72
|
||||
1724814019: 1 8 6 9 5 36 6 78 585 1 8
|
||||
484578732: 5 724 15 92 57 97 1
|
||||
120769: 9 99 55 2 74
|
||||
27834744915: 4 99 4 5 61 96 7 2 9 12
|
||||
1105: 852 6 247
|
||||
449470: 633 710 42
|
||||
7827644: 220 6 593 4 3
|
||||
53663904: 9 7 467 19 96
|
||||
55595: 2 7 3 538 95
|
||||
3050832153: 6 95 2 49 535 6
|
||||
8458892982058: 469 938 499 36 1 5 1 7
|
||||
9820815338177: 98 208 15 338 180
|
||||
86872304424: 45 288 6 11 93 2 14 2 2
|
||||
289200: 14 5 2 3 40 2
|
||||
4683023: 88 4 72 9 7 21 7 42 5 72
|
||||
1661: 9 2 788 127 652
|
||||
164507151418: 95 6 1 9 8 8 554 4 1 2 7 9
|
||||
23684882: 7 486 3 7 696
|
||||
93392: 1 9 3 348 47
|
||||
4472531682: 447 2 5 316 82
|
||||
13106: 6 26 97 127 28 4 1 69
|
||||
57641397: 12 32 131 13 98
|
||||
1175: 116 8 7
|
||||
167479498237: 3 13 62 656 6 6 863
|
||||
1436391: 53 5 92 4 8 75 8 8 3
|
||||
3078721: 8 3 91 16 5 4 87 321 9
|
||||
5580949230: 9 30 6 94 85 9 56 5 9 21
|
||||
12288573: 527 7 74 1 73 5 4 5 57 9
|
||||
2525017: 40 801 4 328 78
|
||||
153888: 43 5 49 9 730 96
|
||||
8869: 7 612 267 7 2 1
|
||||
60059346: 873 3 857 8 36 1 41 8
|
||||
896553742361: 3 838 7 2 5 3 302 7 363
|
||||
10924696802: 9 101 2 5 22 60 80 6 2
|
||||
15976400: 2 7 8 8 4 40 3 4 1 50 22 4
|
||||
930984: 860 9 5 5 3 28 7 48 8 3
|
||||
255420: 18 22 7 113 495
|
||||
604546: 529 593 87 5 43
|
||||
14895576: 7 8 5 7 420 9 8 9 3 762
|
||||
78330028659: 83 67 87 5 60 2 8 662
|
||||
2600107133221: 9 46 938 98 79 299
|
||||
4825632122: 8 668 2 602 1 75 47
|
||||
1360891: 765 747 9 81 8
|
||||
5967: 46 5 4 4 68
|
||||
920353480: 9 4 23 9 751 27 61 62
|
||||
6197067629: 9 7 195 560 1 878 585
|
||||
5203411241: 904 5 68 9 3 1 8 94 71
|
||||
170607390: 2 828 6 92 736 3 24 1
|
||||
5898240: 41 726 769 5 5
|
||||
1674650700: 83 7 414 89 505
|
||||
42447: 424 45 2
|
||||
59264679614: 9 6 5 264 67 9 537 77
|
||||
3475000: 3 38 2 7 3 1 999
|
||||
93749040857: 88 1 9 7 11 342 4 8 57
|
||||
3502961033286: 21 5 1 5 75 1 67 171 66
|
||||
87: 5 3 6
|
||||
435638: 7 2 4 2 804 1 518
|
||||
8507129374: 98 920 109 1 86
|
||||
4014337: 2 24 4 7 4 407 2 24 8 6 9
|
||||
762: 22 8 4 93 6
|
||||
5207537: 67 9 4 9 49 39 62
|
||||
494: 6 2 1 38
|
||||
287567280: 4 8 40 6 95 13 539
|
||||
861393517: 771 90 39 3 503 10 7
|
||||
101961: 3 748 45 6 973
|
||||
3330002: 490 7 67 97 4
|
||||
10592773: 80 63 996 93 73
|
||||
21000096: 56 800 72 78 6
|
||||
6955758: 3 3 83 49 674 1 7 1
|
||||
901589763: 3 424 914 651 7 3 12 8
|
||||
8136221: 738 68 7 3 2 28 92 2 1
|
||||
20737132707: 6 25 6 8 39 3 3 6 8 6 3
|
||||
16843229: 698 65 25 883 7
|
||||
5790: 90 7 8 3 9 944 2 3 9 3
|
||||
557: 53 1 26
|
||||
41: 5 7 6
|
||||
46052474: 2 7 74 11 54 31 72 5 9
|
||||
1232874: 24 3 344 60 5 35 1 1
|
||||
836: 3 3 5 57 11 198
|
||||
1318742655: 9 7 10 70 460 9 5 41 79
|
||||
2213400387345: 372 70 85 387 344
|
||||
447538349: 3 75 5 5 198 13 45 348
|
||||
421305: 1 4 21 30 2
|
||||
475727494328: 5 875 976 84 2 3 81 1 9
|
||||
522664: 9 87 40 63 8 94 62 5 4
|
||||
984484419769: 98 448 4 419 7 66
|
||||
28440766430: 623 70 456 46 432
|
||||
410257: 9 24 444
|
||||
6224579: 71 17 573 9 80
|
||||
2624: 1 8 5 1 64
|
||||
485102: 70 462 3 5 2
|
||||
3387672: 833 8 72 486 33 3 5 4
|
||||
13939339950: 72 6 8 4 233 25 6
|
||||
36188570398: 7 30 5 21 60 95 177
|
||||
9450: 86 17 428 5 1
|
||||
179686799: 57 574 9 8 5 568 2
|
||||
9857: 70 2 9 9 62 61
|
||||
101711: 2 7 6 6 3 8 668 45
|
||||
1514: 19 91 2 400 9
|
||||
49522: 4 144 332 11 378
|
||||
20507987520: 512 6 8 4 7 875 19 2
|
||||
49920136: 1 80 39 160 67 69
|
||||
15280764: 3 820 40 22 7 732
|
||||
17959201: 361 5 7 2 22 3 8 1 5 7 2 4
|
||||
297052: 1 6 5 457 4
|
||||
1066322: 73 49 8 23 67 324
|
||||
7987968737287: 496 8 79 553 80 8 8 9 7
|
||||
3725738880: 631 2 600 36 28 5 984
|
||||
33867960: 62 8 6 9 9 1 6 78 2 35 1 6
|
||||
12958044319: 719 882 1 9 3 18 1 9 19
|
||||
1369536: 1 1 430 3 6 25 266 3 7
|
||||
520: 7 88 9 5
|
||||
195279357: 38 3 6 2 6 82 9 5 9 56
|
||||
644193313: 4 8 3 6 8 140 7 4 4 22 5
|
||||
337: 9 216 7 99 5
|
||||
217374327: 96 3 1 6 9 4 37 3 419 57
|
||||
177168: 1 77 9 1 2 497 22 2 4 3
|
||||
511826: 945 3 2 80 89 76
|
||||
271368121049: 92 53 3 1 662 443
|
||||
121564804: 2 328 5 10 2 9 4 9 4 7 2 2
|
||||
72253: 7 2 123 87 45
|
||||
4948000293: 5 173 5 4 7 286 10 2 81
|
||||
8271: 7 24 8 5 83 592 9 1 39 9
|
||||
4885180322: 508 87 2 59 8 6 10
|
||||
597626214290: 8 25 464 58 751 9 41
|
||||
1153566: 8 393 742 10 565
|
||||
3488312704: 7 5 424 8 304 8 6 99 3
|
||||
576841707040: 795 205 403 18 40
|
||||
22511: 26 1 2 82 5 2 304 887 1
|
||||
13859: 529 924 526 7 4
|
||||
2969996810: 3 815 4 2 963 942 806
|
||||
128767: 1 6 4 8 6 8 20 8 5 1 56
|
||||
213042: 6 679 321 211 776
|
||||
54007: 744 2 9 4 433 6
|
||||
89929: 390 46 33 5 64
|
||||
11305189: 5 25 4 2 68 94
|
||||
3580256: 9 49 1 81 59
|
||||
4143480910: 31 15 5 85 88 903 7
|
||||
28323629: 2 1 1 7 92 224 8 362 9
|
||||
1408961706: 8 79 7 3 4 2 111 8 9 704
|
||||
126793: 939 1 15 2 66 652 213
|
||||
82831317: 3 380 8 7 245
|
||||
26737: 7 709 3 6 6 4 4 845
|
||||
82265531: 7 832 394 552 8
|
||||
455486995: 6 523 7 9 4 576 19
|
||||
2450459092: 2 9 9 4 7 5 2 607 10 9 1 1
|
||||
5222992320: 9 909 60 108 4 364 65
|
||||
140045802459: 273 7 5 45 802 433 26
|
||||
170593: 4 2 83 57 6 277
|
||||
18811455237: 9 4 2 1 145 3 1 4 9 74 1 8
|
||||
497002: 15 481 97 5 29
|
||||
11026974: 484 8 5 79 7 6 86 3 6
|
||||
2379942152: 83 706 94 78 284
|
||||
569: 81 352 6 81 49
|
||||
23630: 647 35 17 70 97 54 2
|
||||
7168521: 6 516 1 82 1 1 11
|
||||
41597309: 7 33 671 6 195 58
|
||||
51386: 50 81 5 571
|
||||
330445804866: 67 4 363 49 793 48 67
|
||||
797893: 71 3 84 839 52
|
||||
3952185: 98 99 4 60 653 86
|
||||
22660416505: 55 412 334 82 508
|
||||
11666: 33 333 2 596 70 9
|
||||
39908156: 1 28 5 14 717 98 6
|
||||
25878142856: 48 77 7 4 2 140 2 856
|
||||
83584512: 61 2 8 388 44
|
||||
511729: 730 5 2 52 7
|
||||
31059736924: 45 69 914 4 592 923 1
|
||||
129340224: 413 1 751 104 4
|
||||
499307: 68 9 81 8 33 7 9
|
||||
8757: 3 5 68 7 69
|
||||
5438484: 8 13 7 4 2 3 78 63 228
|
||||
13986: 9 226 190 31 7 804
|
||||
209498244: 571 69 10 6 2 443
|
||||
10560: 34 34 51 3 9 159 503 6
|
||||
529331671: 1 84 63 602 712 2 70
|
||||
7990488064: 896 80 891 62 2
|
||||
321138: 4 978 3 1 52 9 57 40 3 9
|
||||
3767895689: 6 28 95 51 759 599
|
||||
2315938: 321 6 4 8 5 239 12 6 2 8
|
||||
3125737510: 96 454 18 84 54 324
|
||||
10368172359241: 648 5 4 2 154 2 98 4 4 1
|
||||
445292: 4 1 1 29 5 9 7 207 42 8
|
||||
133644146231: 44 34 6 5 86 2 5 2 890 7
|
||||
560709349225: 5 8 7 95 83 82 16 4 166
|
||||
376533465: 92 9 491 5 371
|
||||
77130302295: 8 68 19 985 9 435 757
|
||||
20905: 6 9 3 101 1
|
||||
814649598: 9 6 307 52 1 4 2 15 1 63
|
||||
790078389: 89 1 79 790 78 387
|
||||
89129901: 89 6 6 7 3 2 3 255 661
|
||||
576: 2 9 14 319 5
|
||||
2104319: 9 470 2 6 9 5 81 2 2 7 9
|
||||
2904080: 3 2 19 6 2 9 2 9 481 6 85
|
||||
29893521: 8 46 9 7 5 3 3 582 9 5 4 6
|
||||
66945: 6 795 7 81 2
|
||||
255203011: 1 372 129 1 942 17
|
||||
2662327666: 7 664 67 230 89 1 662
|
||||
5357852124: 267 11 78 2 2 3 212 4
|
||||
103049: 2 3 4 55 8 87 1
|
||||
14755216: 53 9 3 14 981 52 13 3
|
||||
257122: 257 118 4
|
||||
10217900: 417 7 4 70 5
|
||||
136110748: 272 5 662 445 49
|
||||
3581: 71 280 6 9
|
||||
711: 59 18 489 69 76
|
||||
14580: 85 4 6 47 9 913 5 720 5
|
||||
12388730941: 9 3 2 6 9 8 389 5 1 846 2
|
||||
1322760556: 4 4 53 45 8 267 287
|
||||
1846678542: 13 9 8 7 1 1 66 785 44
|
||||
1091375766: 9 278 2 38 1 3 2 767
|
||||
733146778471: 6 2 8 9 8 9 2 751 1 1 97 6
|
||||
28737222081: 84 394 3 5 64 681
|
||||
76319806367: 780 5 9 9 9 797 4 9 1 96
|
||||
46274856: 4 1 804 572 59
|
||||
44101024: 228 47 6 271 43 16
|
||||
353939245524: 7 119 1 3 5 3 436 7 7 8 5
|
||||
22399091925: 297 4 2 5 852 5 3 5 6 44
|
||||
4086: 2 7 2 74 93
|
||||
536326: 4 355 49 903 29
|
||||
93362: 2 5 37 14 851
|
||||
11038817: 43 66 804 574 82 936
|
||||
44229148720: 22 5 1 572 813 58 80 4
|
||||
2982: 99 6 4 6 7
|
||||
122909973: 3 2 7 9 73 2 1 3 999 3 6
|
||||
772005: 527 1 8 481 3
|
||||
52734797555: 2 89 475 9 55 667 1 17
|
||||
596168243784: 3 16 1 9 2 69 82 43 781
|
||||
770: 7 5 684 9 2
|
||||
5956343: 7 46 41 34 2 45 51
|
||||
873108: 1 5 41 5 1 4 843 9 2 9 8 6
|
||||
617469: 86 1 40 7 7 62 8
|
||||
793460: 4 3 1 8 292 785 73 537
|
||||
146380: 5 28 4 183 664 300
|
||||
97948555854: 2 8 4 6 5 8 4 4 691 5 1 86
|
||||
76146205: 7 61 4 620 3
|
||||
235951: 983 4 4 1 6
|
||||
4100537553282: 64 94 4 1 8 7 213 3 2 85
|
||||
35482272: 51 5 397 3 532
|
||||
13291590297: 9 794 6 31 2 9 4 8 8 7 5 4
|
||||
1433303694: 73 9 7 512 6 5 32 493
|
||||
1580045514: 770 6 38 613 9
|
||||
7160046529: 800 9 745 1 2 6 10 86 2
|
||||
47470486: 21 55 218 5 5 20 5 6 8 7
|
||||
2262482802: 707 4 8 82 1 6 4 3 1 1 7 2
|
||||
702062: 195 9 4 29 35
|
||||
190725307756: 4 54 7 6 76 79 6 7 756
|
||||
82460: 303 2 22 9 3
|
||||
1518239: 983 830 832 574 9
|
||||
23055889402: 7 4 46 4 2 61 917 8 487
|
||||
354363961: 484 47 7 89 73 61
|
||||
2089734: 64 22 69 4 8 3 88
|
||||
3779961539: 99 98 16 8 487
|
||||
5458: 8 9 3 3 4 7 8
|
||||
407677166: 750 4 7 807 6 7 31 4 8 2
|
||||
39308163: 694 8 8 885 3
|
||||
126747155: 6 132 154 2 8 251 7
|
||||
20176891625: 806 276 59 87 907
|
||||
20085: 5 3 48 998 19 59
|
||||
418114382: 5 7 48 764 267 91 2
|
||||
2059684553: 752 9 77 370 95
|
||||
713077098: 99 8 3 3 2 32 7 7 3 3 80 7
|
||||
6594374: 22 637 43 7 3
|
||||
1562987934: 13 6 14 938 7 6 2 89 2
|
||||
101849060331: 1 3 5 4 1 457 8 1 2 25 9 9
|
||||
58979170: 5 230 584 6 91 2 8 15 8
|
||||
1495368078: 98 5 702 516 9 19 4
|
||||
511346649: 3 4 7 5 215 3 6 11 8 3 8 2
|
||||
235445568573139: 4 2 4 99 2 554 573 138
|
||||
7540930176: 6 9 310 8 16 2 68 1
|
||||
3906210: 23 79 43 3 890
|
||||
67364136525: 9 6 767 513 82 4 82 4
|
||||
510557215: 9 64 9 885 97 1 548 69
|
||||
341079556: 39 32 8 8 81 82 58
|
||||
38723838: 88 44 3 84 1
|
||||
8068: 42 43 4 844
|
||||
22133800: 6 153 1 9 3 343 45 8
|
||||
1315578: 2 6 1 74 5 3 280 6 6 6 4 3
|
||||
12239357: 3 770 152 8 26 12 220
|
||||
354910581706: 77 4 9 851 5 897 6 616
|
||||
454: 9 16 8 4 2 30 88
|
||||
1142: 4 72 567 101
|
||||
9885: 3 95 25 5 8
|
||||
28615342: 496 41 67 123 21 3 4
|
||||
1029787: 7 147 7 42 45
|
||||
13830: 7 6 9 740 2 2 1 124 3 39
|
||||
2413: 1 2 45 3 5
|
||||
19963791352: 3 9 30 4 3 2 92 1 927 1 2
|
||||
22316186966528: 32 199 688 833 832
|
||||
848710: 6 6 213 569 18 53 2
|
||||
5844: 7 7 347 978 8
|
||||
124818070161: 6 9 3 4 3 369 8 4 5 502 9
|
||||
1528188: 9 806 6 3 1 4 6 26 4 2 4
|
||||
38255: 42 3 852 1 3
|
||||
98612039402: 5 892 6 34 7 680 955
|
||||
28108634550: 593 60 5 500 79 49
|
||||
5749: 804 89 65 2 3
|
||||
5983513731: 318 294 220 96 86 8 8
|
||||
844265928: 882 676 1 2 354 2
|
||||
4754: 26 181 3 3 42
|
||||
403: 7 7 1 8 6 19 194 99
|
||||
20938590: 50 15 5 4 92 19 82 73
|
||||
223318: 5 5 6 8 9 4 6 5 774 972 9
|
||||
1996699: 2 5 786 221 247 1 692
|
||||
24795: 9 908 51 108 23 45
|
||||
598170468: 3 249 3 4 1 1 2 8 8 6 6
|
||||
8848672: 442 433 7 2 1
|
||||
315477: 4 9 9 73 186 995 60
|
||||
12122640: 20 9 93 94 3 720
|
||||
275664384: 36 2 8 6 2 8 102 4 153
|
||||
23372414: 72 380 8 35 9 4 64
|
||||
7008001: 936 7 92 838 6 8 4
|
||||
37585141: 375 35 49 61 53 2
|
||||
1514: 1 1 1 8 84
|
||||
5460480346: 6 28 4 80 6 18 346
|
||||
3541188: 843 2 5 1 838 2 8 621 7
|
||||
4949142: 72 3 29 7 79 26
|
||||
845: 5 92 7 4 374
|
||||
4422080: 51 161 293 8 65
|
||||
13000: 3 216 9 2 22
|
||||
16076280: 5 8 39 69 120
|
||||
25992255181724: 69 1 6 73 879 271 6 97
|
||||
42367599: 10 2 6 1 5 8 6 3 370 9 6
|
||||
377666: 327 613 4 4 59 7
|
||||
612575190949: 6 7 9 1 4 7 4 3 9 10 9 949
|
||||
215605: 53 3 5 77 8
|
||||
25732162: 4 975 68 78 2 97
|
||||
141265153: 397 93 71 50 3
|
||||
4832698: 6 2 64 864 700
|
||||
1799: 561 2 3 671 3
|
||||
614467: 176 451 980 8
|
||||
2469943: 382 7 869 5 8 2 8 7 49
|
||||
18191713494361: 9 8 642 3 769 571 35 4
|
||||
434950474717: 7 7 2 41 24 661 34 718
|
||||
45990013: 40 771 5 213 15 7 13
|
||||
4130365516679: 90 1 82 653 458 92 81
|
||||
37308684: 79 198 561 8 9 49 83
|
||||
1628: 9 41 4 81 70
|
||||
22675440: 5 327 7 6 5 5 66 4 72 4 6
|
||||
93431688605: 93 43 168 860 8
|
||||
16157: 55 4 484 15 590
|
||||
116564580: 9 1 3 5 5 8 9 5 3 99 5 68
|
||||
237636110: 410 644 9 67 43
|
||||
76969775: 6 3 3 580 2 72 713
|
||||
218447713: 2 8 78 45 2 714
|
||||
315041383: 28 6 7 69 4 3 2 196 37
|
||||
47840846537: 6 7 6 541 207 89 1 39
|
||||
988503654: 28 70 781 7 62 365 4
|
||||
116232654: 19 372 6 643 9
|
||||
111735066: 9 3 8 8 8 68 8 524 7 3 4 2
|
||||
1817575499: 861 823 3 2 26 415 1
|
||||
63390696446: 4 65 6 5 9 28 1 9 64 43 1
|
||||
738580012: 29 4 82 322 6 6 9 61
|
||||
1242147: 7 5 40 661 683 800
|
||||
43738589966: 4 373 85 8 90 8 5 2 9 1 4
|
||||
139972: 98 2 91 14 882 7
|
||||
154495527: 14 11 4 954 6 6 7
|
||||
51988: 1 518 2 6 6
|
||||
498078: 5 1 568 7 85 88
|
||||
8471: 3 172 5 17 43
|
||||
1098720: 41 71 7 5 117 3 3 88 90
|
||||
3220993: 590 954 66 2 991
|
||||
206545581: 6 20 78 6 55 89 4 9 8 4
|
||||
7841176: 4 36 196 699 476
|
||||
5646390883: 467 490 59 90 883
|
||||
3654642584: 9 396 90 58 6 256 2 5
|
||||
130942625902: 23 5 4 8 2 6 995 5 91
|
||||
22045656: 1 764 81 42 356
|
||||
627478410: 742 57 8 845
|
||||
603: 4 596 4
|
||||
4957495: 77 34 641
|
||||
13595839975496: 2 52 93 9 936 8 7 1 376
|
||||
761098063: 69 9 871 30 5 980 63
|
||||
131724535: 58 52 72 697 721
|
||||
3159: 9 6 6 9 4 601 385 5 7 1
|
||||
150715356: 2 6 66 4 6 5 43 328 6 6 6
|
||||
3652185: 3 71 68 7 7 9 4 86 6 361
|
||||
45630261: 9 994 38 73 51
|
||||
1259546885792: 419 136 4 2 1 477 63
|
||||
77263789755: 7 7 438 2 3 3 829 6 8 6
|
||||
6312980161295: 99 9 920 5 4 3 4 954 6
|
||||
874: 6 810 56
|
||||
206794512427: 1 2 5 1 4 7 3 4 20 1 402 5
|
||||
4241075177959: 29 914 71 9 86 4 8 2 8 4
|
||||
1268233188: 6 4 78 4 7 2 5 1 8 7 7 244
|
||||
254643: 3 391 646 49 67
|
||||
57552457531: 793 827 5 5 29 33
|
||||
243360: 45 766 20 3
|
||||
21575: 1 17 1 63 16 2
|
||||
4788137: 969 61 755 1 49 7
|
||||
551226080: 491 924 243 4 5
|
||||
54389104: 24 682 3 52 77
|
||||
174052800193: 6 8 8 1 4 5 9 4 6 34 4 196
|
||||
2029492: 3 245 7 547 5 47 74
|
||||
31953: 7 99 14 3 110
|
||||
15969771: 1 5 7 3 38 8 243 3 6 3 4 2
|
||||
6419328101: 8 5 118 2 640 68 1 23 8
|
||||
263669759: 9 3 86 768 672 8 3 24 4
|
||||
7732933: 721 3 3 7 1 85 47 1 3 6 7
|
||||
13049008058275: 4 16 9 626 7 6 5 582 7 5
|
||||
582119: 9 572 17 4 866 72 8
|
||||
267454667: 5 57 4 8 25 5 3 61 1 5 1 7
|
||||
6499467: 3 4 5 3 80 8 8 40 4 8 2 95
|
||||
78041738: 859 9 73 1 740
|
||||
1799761: 1 4 3 4 65 5 4 5 6 1 61
|
||||
581955071: 726 49 57 287 2
|
||||
430510057: 643 670 37 5 89 999
|
||||
3219088050: 208 28 1 31 23 5 5 762
|
||||
38129143: 7 2 37 8 1 92 5 2 36 4 6
|
||||
13920902: 46 301 67 35 4 97 7
|
||||
134581504: 4 94 979 357 1
|
||||
3215357: 2 782 8 425 354
|
||||
923: 27 890 2 4
|
||||
1211518464: 8 173 56 5 4 6 4 9 6 8 83
|
||||
12704960836: 2 16 1 252 1 486 7 4 35
|
||||
439941006: 47 8 8 2 7 1 20 5 2 33 4 9
|
||||
7777528: 8 903 4 85 28
|
||||
121083382: 4 74 764 8 72 3 17
|
||||
9958: 7 88 452 752 5
|
||||
34561803: 5 6 94 13 422 1 6
|
||||
10334766686: 492 7 922 3 6 87
|
||||
150289: 1 879 66 2 77 4 366
|
||||
580552: 579 531 2 28 989
|
||||
7410837: 340 7 562 8 38
|
||||
18540512: 3 610 1 6 267 7 8 6 7 3 2
|
||||
46119777: 28 854 581 10 9
|
||||
461179926912: 1 2 593 223 762 913
|
||||
5714938: 98 2 6 967 635 4 5 9 8 5
|
||||
5550: 26 909 5 794 81
|
||||
8394624803: 5 84 131 2 2 18 2 803
|
||||
1399: 5 2 290 90 4 3 205 9 3 1
|
||||
341160006: 2 5 4 34 737 138 7 40
|
||||
11205967: 87 447 916 42 2 184
|
||||
131378720351: 4 62 27 6 4 2 314 781
|
||||
16932035756: 7 6 330 4 354 2 7 9 1 76
|
||||
720: 14 1 6 71 629
|
||||
2282737137: 19 2 67 343 163 1 54
|
||||
2813767: 4 163 6 117 6 3 8 1 4 1 2
|
||||
3068463: 51 6 8 461
|
||||
1176192: 1 1 3 83 9 631 325 17
|
||||
136012: 8 889 17 5 4 37
|
||||
5879975453: 4 1 5 4 7 359 6 1 37 4 5 3
|
||||
491608106: 8 83 18 330 3 280 909
|
||||
1307268678: 2 32 81 25 2 9 221 457
|
||||
12671: 370 3 76 4 1 806
|
||||
8029550: 75 76 48 603 976
|
||||
1410706997550: 653 1 9 452 6 15 8 4 5 6
|
||||
3916639: 3 1 9 4 6 817 258 5 6 65
|
||||
11530851: 5 24 2 5 7 9 549 7 2 741
|
||||
115721: 96 5 10 1 533 938 2
|
||||
98385277: 4 4 53 476 909 276
|
||||
10133: 45 6 17 76 7 5 46 3
|
||||
41756: 7 5 69 7 50 6
|
||||
2144: 7 7 98 67 2
|
||||
1446603: 6 63 72 63 8 886 651
|
||||
139819152: 744 9 44 5 422
|
||||
3828: 335 275 8 4 4 3 5 6 7 8 9
|
||||
129353136666: 41 478 96 66 631 36
|
||||
3438094: 343 269 253 281 8 53
|
||||
7326: 18 9 15 2 18
|
||||
15617004: 22 2 9 302 679 7
|
||||
30917: 2 593 26 76 8
|
||||
6092883: 7 2 7 6 9 1 34 1 64 7 6 8
|
||||
551603000: 52 9 5 8 6 543 6 1 650 4
|
||||
123918: 9 1 2 644 9 6
|
||||
39657: 77 2 7 25 982
|
||||
124840: 12 41 8 4 87 1 68 503
|
||||
179966207842: 85 8 9 6 51 97 4 19 6 4 5
|
||||
718854: 46 9 3 6 664 8 86 63
|
||||
926545746: 9 265 457 4 6
|
||||
674908382899: 41 1 66 9 9 1 6 3 289 9
|
||||
1755015393918: 540 5 13 3 5 3 8 7 6 9 1 8
|
||||
107210: 7 7 93 189 20
|
||||
40936: 1 297 84 3 21 5 8 2 2 1 7
|
||||
35354: 3 21 87 1 11 673 5
|
||||
1576785524: 5 6 6 2 7 8 21 5 8 9 3 521
|
||||
43218: 432 11 4 6
|
||||
3322: 8 21 4 32 3
|
||||
440344195: 8 9 788 8 17 8 3 81 53 5
|
||||
1128: 8 3 33 24 72
|
||||
36615177: 91 574 73 40 7
|
||||
195: 9 8 66 51 6
|
||||
141600555813: 14 39 5 6 90 82 9 5 4 6 7
|
||||
1181669: 980 13 17 7 2
|
||||
761996: 76 15 4 9 6
|
||||
976585475: 673 517 95 79 82
|
||||
3548311955: 51 694 89 11 952
|
||||
9732638: 8 9 541 34 513
|
||||
45902666880: 5 70 3 8 7 759 7 8 515 9
|
||||
125069814644: 6 461 461 822 5 8 943
|
||||
710696: 53 59 785 917 8
|
||||
465599: 7 2 919 4 19 8 5 5 2 1
|
||||
259967957955: 9 8 647 814 5 41 50 5
|
||||
3845565568: 881 5 873 56 2 4 1
|
||||
190592649449: 7 37 38 8 8 8 649 447
|
||||
756057: 4 461 4 3 5 56 46 2 34
|
||||
111361216: 28 5 2 5 2 68 5 9 5 1 46
|
||||
110088843: 27 5 14 8 4 840
|
||||
4387653: 39 71 8 367 570 52 1
|
||||
2305381: 2 305 3 82
|
||||
15439: 765 6 23 41 2
|
||||
46691409877612: 58 3 6 42 59 3 347 8 14
|
||||
10402885471: 30 448 42 516 26 3 5
|
||||
367703352437: 664 879 45 5 7 1 2 434
|
||||
432397019: 661 59 6 617 60
|
||||
161713440: 156 799 97 180 7 122
|
||||
200727: 9 52 206 50 32 5 3
|
||||
4664366888: 466 1 436 68 85
|
||||
4225790: 7 25 86 222 5 874
|
||||
313967: 5 1 19 64 3 4 51 11
|
||||
25424010: 5 5 376 48 10 13
|
||||
869235228: 285 1 105 7 5 74 391 1
|
||||
6422495: 6 9 90 88 1 695
|
||||
231217: 61 78 176 6 338 74 6 4
|
||||
175: 3 54 13
|
||||
356649: 51 873 57 8 9
|
||||
20662785: 76 941 982 1 2 285
|
||||
668781667: 723 8 959 954
|
||||
213680962: 18 6 89 8 5 2 3 94 1 570
|
||||
409: 9 57 83 6 7 244
|
||||
9100212539: 68 941 66 2 539
|
||||
4425754: 1 7 85 37 5 43 635 11
|
||||
7446: 95 5 7 666 94 1
|
||||
24189389894: 7 3 3 2 87 3 55 8 446 96
|
||||
73966855: 6 66 257 6 18 4 74 1 3 7
|
||||
2754514531958: 459 6 514 5 31 958 2 1
|
||||
2461425673: 659 498 48 4 933
|
||||
5055058199: 2 9 9 29 82 10 7 7 7 23
|
||||
9960104: 322 50 69 14 44
|
||||
1154: 60 9 92 473 8 41
|
||||
76056154128: 9 4 1 216 8 21 87 412 8
|
||||
1690148990: 1 640 7 3 9 8 6 3 50 95 2
|
||||
170115: 67 4 85 1 9 30 464 17 6
|
||||
853869660: 1 9 4 87 30 6 8 9 4 4 8 8
|
||||
6792: 81 71 2 8 9 95 574 9 8
|
||||
6716711692856: 6 9 482 748 6 8 58 855
|
||||
41172790: 1 40 524 73 790
|
||||
17788396: 37 5 2 21 38 2 2 6 4 27 4
|
||||
190790305: 8 293 365 223 426
|
||||
1193801: 4 4 651 611 940
|
||||
2422522291: 346 7 520 2 283 8
|
||||
13308: 64 957 5 83 12
|
||||
4293863484: 612 818 9 84 953
|
||||
149017: 5 98 660 4 43 33 9 7 3 4
|
||||
72: 9 7 2 38 13
|
||||
226993231: 97 10 26 60 70 17 90
|
||||
11072: 5 8 19 5 1
|
||||
4978271: 71 7 8 269 5
|
||||
992998: 3 22 4 7 3 44 1 9 12 368
|
||||
313930837: 313 92 8 2 835
|
||||
102854: 1 399 5 5 51 7 849 1
|
||||
409530: 549 36 7 21 9
|
||||
792211: 4 3 922 1 1
|
||||
160645: 7 63 394 3 55 31 9 7 19
|
||||
1951: 4 33 4 7 120 7 4 3 1 8 2 8
|
||||
38349229: 18 5 5 9 22 4 4 7 247 7 2
|
||||
24060: 3 8 4 8 92 113 104 1
|
||||
239912: 655 214 276 60 8
|
||||
2823624050: 98 9 3 8 4 8 41 763 8
|
||||
61741: 686 9 1
|
||||
12031786809: 911 1 3 1 99 6 4 11 5 3 8
|
||||
34865617: 899 524 70 74 814
|
||||
1249568: 15 76 259 3 895 544
|
||||
1047251: 70 15 5 992 691
|
||||
162436904: 203 4 1 8 426 2 1 42 8 3
|
||||
2284918477: 923 643 3 4 521 7 3 6
|
||||
12166392: 423 8 84 1 4 8 14 84
|
||||
303534248989: 6 72 629 6 806 9 8 386
|
||||
2099524667214: 72 90 9 4 581 2 402 8
|
||||
21542441041: 8 9 7 8 561 2 200 9 7 7 3
|
||||
6383320: 68 561 97 836 1 8
|
||||
19518067475: 49 77 21 61 8 848
|
||||
398142456: 8 2 6 241 1 4 6 6 15 4 9 2
|
||||
4538716967: 74 9 6 58 2 6 1 9 91 965
|
||||
216597935617: 30 313 806 243 4 63 5
|
||||
74528982: 431 3 9 8 6 3 4 4 8 74 2
|
||||
26089807308: 5 207 2 9 252 499 3 10
|
||||
2094995: 3 4 314 397 503
|
||||
347304287: 5 988 58 285
|
||||
134526480: 13 93 63 7 7 5 4 1 60 44
|
||||
47110131597: 673 70 131 59 7
|
||||
33282630423: 528 295 715 6 63
|
||||
63099288: 1 4 1 15 4 58 1 3 4 1 84 7
|
||||
18351027: 25 810 5 302 3
|
||||
15327: 3 509 29 402 77
|
||||
718477: 79 8 3 9 8
|
||||
156918: 6 4 53 296 33 5
|
||||
13721761037: 163 741 7 4 4 9 5 6 284
|
||||
3725196162: 73 63 81 610 5 54
|
||||
84577739: 4 88 47 2 4 815 3 299
|
||||
185618730: 1 4 4 925 79 1 3 1 8 7
|
||||
1163675588: 4 3 7 14 8 1 4 84 31 9 7 5
|
||||
379019877: 80 30 5 4 944 98
|
||||
228721777: 9 29 1 6 9 2 1 9 1 2 984 5
|
||||
6294235021: 86 9 7 6 9 67 1 9 4 18 3 3
|
||||
158746596: 90 467 95 5 32 3
|
||||
18697431689: 41 57 4 7 15 84 2 9
|
||||
858844: 483 886 725 759 2
|
||||
55947: 6 782 71
|
||||
28295433: 19 541 362 17 4
|
||||
1998070830: 8 476 5 583 72 4 6 45
|
||||
5046835618: 787 27 62 26 9 618
|
||||
1639: 1 50 7 3 2 6 7 665 9 9 2 7
|
||||
7414: 13 34 776 9 8
|
||||
12135987: 720 36 52 7 6 590 9
|
||||
3592173389074: 98 61 8 9 55 64 846 6
|
||||
148153: 16 5 3 85 21
|
||||
19227110478: 6 6 7 608 6 4 4 6 50 71 4
|
||||
1261831906: 98 58 2 64 7 908
|
||||
8451760895: 4 2 4 9 7 879 707 89 8
|
||||
37032: 2 2 2 96 635 2 1 2 1 47 6
|
||||
24846: 7 273 1 573 4
|
||||
100699: 37 214 401 28 1 14 5
|
||||
774056700558: 1 767 252 6 47 94 73 6
|
||||
4290567: 5 297 810
|
||||
30499049925897: 11 9 9 9 6 8 85 305 896
|
||||
6607390: 277 6 238 9 30 473
|
||||
161281: 72 28 8 1 1
|
||||
214267392: 66 9 585 6 8 4
|
||||
3746933: 61 7 70 6 753 5 1
|
||||
13362239: 90 432 825 992 1
|
||||
355: 3 3 39 4
|
||||
3528: 21 16 56 9
|
||||
48083163: 8 290 2 580 1
|
||||
125953633: 8 231 527 6 35
|
||||
49621348: 492 74 1 346 3 48
|
||||
328566351: 84 628 2 9 11 233 2 2
|
||||
897549442: 997 277 9 54 88
|
||||
1518061: 758 2 8 2 62
|
||||
11728: 2 1 36 770 582 75 8
|
||||
440725865: 8 6 35 5 314 5 54 3 1 6 7
|
||||
17245369: 6 303 70 1 70 7 1 88 9
|
||||
1128025843: 8 2 736 30 5 5 798 46
|
||||
1387547: 6 330 7 17 543 987
|
||||
1735611512: 779 88 805 753 2 1 5
|
||||
59018886: 83 4 9 7 6 99 8 83 97 1 9
|
||||
14972265: 8 56 23 763 77 87
|
||||
98394814: 9 26 7 9 861 9 1 3 1 4
|
||||
62171: 8 54 987 975 2
|
||||
747726641: 392 89 25 987 1 9 8 7
|
||||
193535: 4 9 96 8 7
|
||||
8491504: 925 1 925 459 4
|
||||
19943778: 11 1 41 335 121 3 555
|
||||
1233154550: 89 517 335 7 762 8
|
||||
104363998: 88 61 7 639 98
|
||||
1211277625: 68 4 95 330 9 4 4 7 62 5
|
||||
22727771: 250 4 504 638 18
|
||||
272163135216: 704 355 94 4 23 42
|
||||
21950784286: 7 6 9 3 3 270 4 48 2 2 6 6
|
||||
472: 2 95 5 85 90 67 125 3
|
||||
1915: 5 65 727 108 755
|
||||
16669773: 7 984 623 6 9 3
|
||||
105548118179: 387 439 345 9 62 182
|
||||
6718: 3 1 3 11 43 56
|
||||
84500240: 989 89 192 5 80
|
||||
20729408: 4 1 182 32 1 4 127
|
||||
2059431192: 5 1 9 324 9 1 376 3 8 9 7
|
||||
6661244: 239 306 61 91 501
|
||||
912840: 77 40 195 6 40
|
||||
8462797: 82 5 905 7 8 528 6 3 8 6
|
||||
518913992: 3 5 1 4 1 25 6 4 6 615 9 2
|
||||
53096: 9 8 726 824
|
||||
3277428146350: 3 27 74 207 74 463 4 8
|
||||
688291: 593 829 484 33 11
|
||||
3394: 4 6 9 36 52 4
|
||||
1035101750: 419 73 43 9 787
|
||||
21467269505: 4 95 5 76 388 3 4 8 3 3 7
|
||||
13893516625: 28 199 248 222 8 3 22
|
||||
19972600: 9 29 56 63 1 597
|
||||
6298749: 1 3 9 2 8 282 3 2 8 461 9
|
||||
22: 1 8 1 1 13
|
||||
9070658525: 3 1 82 8 82 3 64 9 924 8
|
||||
14490586: 2 4 30 84 53 9 4 95 2 6 3
|
||||
706408: 4 53 12 22 411
|
||||
57200: 7 679 28 1 1 5 16
|
||||
1940489988: 4 4 7 3 5 6 7 22 899 88
|
||||
317776438: 642 707 72 34 7
|
||||
91211400641: 900 95 4 381 74 16 2 7
|
||||
186: 171 8 7
|
||||
11660572695: 3 721 7 73 17 317
|
||||
12904914: 3 6 7 1 7 3 1 4 9 7 93 141
|
||||
21267013269: 365 2 35 30 1 58 59 94
|
||||
1730625: 949 777 4 625
|
||||
36784: 98 5 75 31 3
|
||||
145262: 309 6 1 830 37
|
||||
888036: 2 9 9 4 8 311 12 8 3 4 9 5
|
||||
107616111478: 442 11 34 927 651
|
||||
292000: 81 5 6 7 1 6 1 980 8 4 4
|
||||
3765: 2 787 420 46 3
|
||||
46121893264: 9 4 9 90 9 45 6 6 1 2 4 40
|
||||
5795330413: 81 4 2 9 607 25 60 9 6 4
|
||||
36648062: 2 4 144 45 62
|
||||
6836468: 683 55 8 76 94
|
||||
1436309: 64 9 4 783 310
|
||||
2196829: 540 8 5 1 2 58 3 811 2
|
||||
11799356411: 4 9 5 4 7 8 156 558 9 5
|
||||
1971634891: 3 7 123 2 21 6 4 7 2 2 4 3
|
||||
9397275528: 12 791 1 9 7 5 99
|
||||
274978673783: 55 862 11 617 58
|
||||
13841: 3 4 4 78 165
|
||||
120688822: 15 62 83 805 148 2
|
||||
225387750: 4 4 3 1 9 3 5 5 8 875 6 7
|
||||
393785: 3 880 55 37 180 67
|
||||
1762656: 65 6 2 439 5 24
|
||||
220788852: 29 2 54 7 88 51
|
||||
43526370: 4 765 16 889 930
|
||||
737100420299: 9 9 70 975 7 6 4 5 60 5
|
||||
157961882: 3 2 987 8 76 959 7 5 4 5
|
||||
1802: 7 5 2 21 8 93 720
|
||||
7346461: 6 898 394 54 461
|
||||
8503740: 9 5 2 92 85 7
|
||||
5919855997: 8 2 333 7 584 855 998
|
||||
390937659: 92 7 8 9 667 2 5 1 25 6 3
|
||||
365744: 516 29 3 822 4 8
|
||||
8057514: 7 5 45 5 7 5 16
|
||||
28017586: 8 98 7 5 95 8 30 95 8 7
|
||||
202759256: 4 134 40 70 9 9 6
|
||||
471150: 39 470 469 4 43 25
|
||||
513685: 2 8 7 5 61 220 71
|
||||
1543834258722: 380 736 6 71 5 1 92 5
|
||||
3384988870: 5 8 4 4 1 3 8 6 721 5 2 4
|
||||
4764: 35 4 5 5 3 7 7 59 1 1 74 4
|
||||
1184974: 28 19 42 8 6 53
|
||||
4000143113: 35 8 703 238 556
|
||||
418: 4 1 7 2
|
||||
9557037: 61 1 4 391 3 992
|
||||
105661643: 573 5 4 94 49
|
||||
23214254: 382 4 9 6 252
|
||||
5444: 46 4 4 3 4 7
|
||||
737349: 94 5 32 8 49
|
||||
5122916: 2 48 71 51 919
|
||||
5821000: 79 736 63 2 6 5 3
|
||||
8301678: 77 18 155 73 561
|
||||
1501597072: 7 6 4 9 787 265 102 5 5
|
||||
718052598: 491 7 6 4 4 8 4 4 454 9
|
||||
178497: 4 1 6 11 6 413 52 4 99 3
|
||||
540: 7 9 10 4 1 18
|
||||
9103497120: 9 400 713 1 97 2 680 9
|
||||
5630913090: 782 8 570 1 90
|
||||
1054463962: 43 4 6 361 56 4 95 9 63
|
||||
20116902: 8 244 15 7 721 4 74 2
|
||||
898710: 70 26 102 126 581
|
||||
14793708485071: 959 6 539 3 91 9 530
|
||||
50330139441: 99 270 49 2 507
|
||||
6807041: 41 527 3 5 21 836
|
||||
1031: 76 27 1
|
||||
93138: 9 218 95 8
|
||||
258123: 57 44 4 658 5 700
|
||||
46365920: 89 374 3 2 3 7 5 3 6 1 7 1
|
||||
11859124: 2 5 28 798 55 8 2 6 90 2
|
||||
23250824: 539 32 674 4 1 8 2 496
|
||||
545806394: 3 7 8 1 5 484 6 8 1 9 6 49
|
||||
10832: 185 8 21 23 4 7 53 7 76
|
||||
100339266: 8 67 26 72 64
|
||||
7412898: 92 66 8 91 7
|
||||
257523634800: 9 566 32 964 9 815 60
|
||||
185426200: 1 8 5 4 261 4 3 50 1 1 9
|
||||
10433434471: 92 177 451 86 468
|
||||
216571: 7 8 6 865 734 6 9
|
||||
1104: 2 52 6 2 1
|
||||
5318188: 23 2 317 671 513
|
||||
1007609179: 34 7 1 45 141 3 29
|
||||
323629: 7 1 5 8 1 378 9
|
||||
3521036: 6 698 5 963 2 69
|
||||
95660213: 1 7 948 98 2 620 13
|
||||
64638: 6 46 37 1
|
||||
17385402: 173 84 48 7 904 3 3 8
|
||||
1616421504: 5 498 7 6 7 1 9 2 3 7 5 3
|
||||
715025: 71 502 5
|
||||
20047048: 1 260 158 488 8
|
||||
216208384: 802 4 62 556 448
|
||||
4235581375: 4 7 9 451 8 92 7 137 4 2
|
||||
8823357451: 80 64 74 328 71 11
|
||||
3406644959: 8 8 9 6 2 643 7 324 4 95
|
||||
3356625: 670 433 892 5
|
||||
516787406: 2 33 832 55 9 9 4 5 4 1 6
|
||||
14601: 21 1 7 6 3 975
|
||||
39390: 508 39 72 6 1
|
||||
57737790013: 60 1 911 5 6 5 4 495 13
|
||||
75067696812: 21 5 88 1 8 869 4 812
|
||||
28721: 2 3 5 68 5 33
|
||||
299147159: 19 72 7 9 61 925 7 308
|
||||
47958: 6 290 162 2 4
|
||||
4810465342: 87 463 55 335 5
|
||||
1084622973: 2 8 8 4 6 2 3 5 7 963 5 2
|
||||
940412473: 7 23 54 540 3 3 6 27 4 2
|
||||
1251718: 6 69 2 25 72 9 1 9 252 3
|
||||
2276640: 4 9 5 86 93 2 9 6 31 6 8
|
||||
363950: 908 5 8 5 553
|
||||
963651: 6 4 938 980 663 8
|
||||
4385541040633: 515 946 85 40 633
|
||||
1498665: 4 6 23 174 87 3
|
||||
4678232686: 8 584 2 60 3 268 1 7
|
||||
682407: 6 822 74 122 13
|
||||
3718652394: 6 1 53 86 5 2 391
|
||||
22789229870: 5 426 42 298 70
|
||||
128646776484: 4 4 2 7 8 699 2 4 18 49
|
||||
52417206331: 6 798 6 771 331
|
||||
3040286: 538 593 1 389 1 4 2 6
|
||||
198047: 9 45 27 9 2 499 713 1 5
|
||||
21645166: 7 1 6 7 91 1 276 2 2 347
|
||||
22206000: 6 6 72 3 95 731 749 6 5
|
||||
1402633431: 253 924 2 39 6
|
||||
24223828: 928 8 647 3 7 4
|
||||
5140898383: 72 714 9 83 1 8 3
|
||||
11209432: 70 8 2 70 24 33 1
|
||||
5551629375: 9 7 9 7 782 7 5 5 9 8 612
|
||||
119196: 80 70 29 3 1 7 299 1 1 3
|
||||
15894764944: 595 667 504 4 494 2
|
||||
1285987: 47 342 7 1 8
|
||||
9717593: 94 28 289 59 5
|
||||
263: 5 41 52 6
|
||||
153682480: 630 9 497 89 3 56 5
|
||||
1937661: 8 157 993 4 86 2 79
|
||||
84669705: 851 449 5 801 81
|
||||
875552: 8 751 99 50 303
|
||||
96459: 160 1 7 6 6
|
||||
562626: 92 95 54 3 6
|
||||
19640: 24 55 8 2
|
||||
67784330: 4 4 71 36 95 5 8 2 2 2 8 7
|
||||
213868: 25 4 842
|
||||
105733: 4 15 30 28 54
|
||||
|
@@ -0,0 +1,50 @@
|
||||
.........................p........................
|
||||
......................h....C............M.........
|
||||
..............................p....U..............
|
||||
..5..................p............................
|
||||
..6z...........................................C..
|
||||
...............c...........zV.....................
|
||||
...5.....c........................................
|
||||
.Z.............h........S...z....9................
|
||||
.O............................9...z........M..C...
|
||||
..O....5..............................F..M..C.....
|
||||
..Z.........5.c...............M....V..............
|
||||
........ZO................q.......................
|
||||
s...O................h..Uq.....7V...........4.....
|
||||
.q.g..............c.............p.......4.........
|
||||
............hZ.............................4G.....
|
||||
6s...........................U.Q.....3............
|
||||
.......6.................9.......Q.............3..
|
||||
....s..D.........................6................
|
||||
.............................................FL...
|
||||
..................................................
|
||||
..g...D.........q.....f.......Q...F....L......7...
|
||||
...............2.........f.............V.L...4....
|
||||
...................2.s...................f3......G
|
||||
....g...........................v......7P.........
|
||||
..2..g.............d.....v...........P.......1....
|
||||
..............u.........f.............L........G..
|
||||
.........l.D....u...............d........o..P.....
|
||||
..................8...............9..1......o...7.
|
||||
............l.....................................
|
||||
...................l...S...........F.......o..U...
|
||||
.......................u...S......................
|
||||
..........l....u...............m...........P....G.
|
||||
......................................1.8.......o.
|
||||
..................................................
|
||||
..................v.......S................0......
|
||||
.............v........d.....1.....................
|
||||
..................................................
|
||||
..........D....................................0..
|
||||
...................m.............H..........0.....
|
||||
...................................d......0.......
|
||||
..................................................
|
||||
....Q.............................................
|
||||
................................H.................
|
||||
........................H....................8....
|
||||
..................................................
|
||||
..................................................
|
||||
.........................................8........
|
||||
.......................H3.........................
|
||||
............................m.....................
|
||||
................................m.................
|
||||
|
@@ -0,0 +1,28 @@
|
||||
47|53
|
||||
97|13
|
||||
97|61
|
||||
97|47
|
||||
75|29
|
||||
61|13
|
||||
75|53
|
||||
29|13
|
||||
97|29
|
||||
53|29
|
||||
61|53
|
||||
97|53
|
||||
61|29
|
||||
47|13
|
||||
75|47
|
||||
97|75
|
||||
47|61
|
||||
75|61
|
||||
47|29
|
||||
75|13
|
||||
53|13
|
||||
|
||||
75,47,61,53,29
|
||||
97,61,53,29,13
|
||||
75,29,13
|
||||
75,97,47,61,53
|
||||
61,13,29
|
||||
97,13,75,29,47
|
||||
|
@@ -0,0 +1,10 @@
|
||||
....#.....
|
||||
.........#
|
||||
..........
|
||||
..#.......
|
||||
.......#..
|
||||
..........
|
||||
.#..^.....
|
||||
........#.
|
||||
#.........
|
||||
......#...
|
||||
|
@@ -0,0 +1,9 @@
|
||||
190: 10 19
|
||||
3267: 81 40 27
|
||||
83: 17 5
|
||||
156: 15 6
|
||||
7290: 6 8 6 15
|
||||
161011: 16 10 13
|
||||
192: 17 8 14
|
||||
21037: 9 7 18 13
|
||||
292: 11 6 16 20
|
||||
|
@@ -0,0 +1,12 @@
|
||||
............
|
||||
........0...
|
||||
.....0......
|
||||
.......0....
|
||||
....0.......
|
||||
......A.....
|
||||
............
|
||||
............
|
||||
........A...
|
||||
.........A..
|
||||
............
|
||||
............
|
||||
|
Reference in New Issue
Block a user