Refactor code for API #3
@ -1,10 +1,12 @@
|
|||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
from ..base import BaseSolver
|
||||||
|
|
||||||
floor = 0
|
|
||||||
floors = [(floor := floor + (1 if c == "(" else -1)) for c in line]
|
|
||||||
|
|
||||||
|
|
||||||
print(f"answer 1 is {floors[-1]}")
|
class Solver(BaseSolver):
|
||||||
print(f"answer 2 is {floors.index(-1)}")
|
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 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)
|
# 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
|
# CodeGolf answer https://codegolf.stackexchange.com/a/8479/42148
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
atoms = [
|
ATOMS: list[tuple[str, tuple[int, ...]]] = [
|
||||||
("22", (0, )), # 0
|
("22", (0, )), # 0
|
||||||
("13112221133211322112211213322112", (71, 90, 0, 19, 2, )), # 1
|
("13112221133211322112211213322112", (71, 90, 0, 19, 2, )), # 1
|
||||||
("312211322212221121123222112", (1, )), # 2
|
("312211322212221121123222112", (1, )), # 2
|
||||||
@ -105,7 +105,7 @@ atoms = [
|
|||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
starters = [
|
STARTERS = [
|
||||||
"1",
|
"1",
|
||||||
"11",
|
"11",
|
||||||
"21",
|
"21",
|
||||||
@ -122,27 +122,26 @@ def look_and_say_length(s: str, n: int) -> int:
|
|||||||
if n == 0:
|
if n == 0:
|
||||||
return len(s)
|
return len(s)
|
||||||
|
|
||||||
if s in starters:
|
if s in STARTERS:
|
||||||
return look_and_say_length(
|
return look_and_say_length(
|
||||||
"".join(f"{len(list(g))}{k}" for k, g in itertools.groupby(s)), n - 1
|
"".join(f"{len(list(g))}{k}" for k, g in itertools.groupby(s)), n - 1
|
||||||
)
|
)
|
||||||
|
|
||||||
counts = {i: 0 for i in range(len(atoms))}
|
counts = {i: 0 for i in range(len(ATOMS))}
|
||||||
idx = next(i for i, (a, _) in enumerate(atoms) if s == a)
|
idx = next(i for i, (a, _) in enumerate(ATOMS) if s == a)
|
||||||
counts[idx] = 1
|
counts[idx] = 1
|
||||||
|
|
||||||
for _ in range(n):
|
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 i in counts:
|
||||||
for j in atoms[i][1]:
|
for j in ATOMS[i][1]:
|
||||||
c2[j] += counts[i]
|
c2[j] += counts[i]
|
||||||
counts = c2
|
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)
|
class Solver(BaseSolver):
|
||||||
print(f"answer 1 is {answer_1}")
|
def solve(self, input: str) -> Iterator[Any] | None:
|
||||||
|
yield look_and_say_length(input, 40)
|
||||||
answer_2 = look_and_say_length(line, 50)
|
yield look_and_say_length(input, 50)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def is_valid(p: str) -> bool:
|
def is_valid(p: str) -> bool:
|
||||||
@ -40,10 +42,8 @@ def find_next_password(p: str) -> str:
|
|||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
answer_1 = find_next_password(line)
|
answer_1 = find_next_password(input)
|
||||||
print(f"answer 1 is {answer_1}")
|
yield answer_1
|
||||||
|
yield find_next_password(increment(answer_1))
|
||||||
answer_2 = find_next_password(increment(answer_1))
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import sys
|
from typing import Any, Iterator, TypeAlias
|
||||||
from typing import TypeAlias
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
JsonObject: TypeAlias = dict[str, "JsonObject"] | list["JsonObject"] | int | str
|
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
|
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)
|
yield json_sum(data)
|
||||||
print(f"answer 1 is {answer_1}")
|
yield json_sum(data, "red")
|
||||||
|
|
||||||
answer_2 = json_sum(data, "red")
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Literal, cast
|
from typing import Any, Iterator, Literal, cast
|
||||||
|
|
||||||
import parse # type: ignore
|
import parse # type: ignore
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def max_change_in_happiness(happiness: dict[str, dict[str, int]]) -> int:
|
def max_change_in_happiness(happiness: dict[str, dict[str, int]]) -> int:
|
||||||
guests = list(happiness)
|
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)
|
happiness: dict[str, dict[str, int]] = defaultdict(dict)
|
||||||
for line in lines:
|
for line in lines:
|
||||||
u1, gain_or_loose, hap, u2 = cast(
|
u1, gain_or_loose, hap, u2 = cast(
|
||||||
tuple[str, Literal["gain", "lose"], int, str],
|
tuple[str, Literal["gain", "lose"], int, str],
|
||||||
parse.parse( # type: ignore
|
parse.parse( # type: ignore
|
||||||
"{} would {} {:d} happiness units by sitting next to {}.", line
|
"{} would {} {:d} happiness units by sitting next to {}.", line
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
happiness[u1][u2] = hap if gain_or_loose == "gain" else -hap
|
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)
|
yield 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}")
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import sys
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Literal, cast
|
from typing import Any, Iterator, Literal, cast
|
||||||
|
|
||||||
import parse # type: ignore
|
import parse # type: ignore
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Reindeer:
|
class Reindeer:
|
||||||
@ -13,50 +14,50 @@ class Reindeer:
|
|||||||
rest_time: int
|
rest_time: int
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
reindeers: list[Reindeer] = []
|
reindeers: list[Reindeer] = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
reindeer, speed, speed_time, rest_time = cast(
|
reindeer, speed, speed_time, rest_time = cast(
|
||||||
tuple[str, int, int, int],
|
tuple[str, int, int, int],
|
||||||
parse.parse( # type: ignore
|
parse.parse( # type: ignore
|
||||||
"{} can fly {:d} km/s for {:d} seconds, "
|
"{} can fly {:d} km/s for {:d} seconds, "
|
||||||
"but then must rest for {:d} seconds.",
|
"but then must rest for {:d} seconds.",
|
||||||
line,
|
line,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
reindeers.append(
|
reindeers.append(
|
||||||
Reindeer(name=reindeer, speed=speed, fly_time=speed_time, rest_time=rest_time)
|
Reindeer(
|
||||||
)
|
name=reindeer, speed=speed, fly_time=speed_time, rest_time=rest_time
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
target = 1000 if len(reindeers) <= 2 else 2503
|
target = 1000 if len(reindeers) <= 2 else 2503
|
||||||
|
|
||||||
states: dict[Reindeer, tuple[Literal["resting", "flying"], int]] = {
|
states: dict[Reindeer, tuple[Literal["resting", "flying"], int]] = {
|
||||||
reindeer: ("resting", 0) for reindeer in reindeers
|
reindeer: ("resting", 0) for reindeer in reindeers
|
||||||
}
|
}
|
||||||
distances: dict[Reindeer, int] = {reindeer: 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}
|
points: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
||||||
|
|
||||||
for time in range(target):
|
for time in self.progress.wrap(range(target)):
|
||||||
for reindeer in reindeers:
|
for reindeer in reindeers:
|
||||||
if states[reindeer][0] == "flying":
|
if states[reindeer][0] == "flying":
|
||||||
distances[reindeer] += reindeer.speed
|
distances[reindeer] += reindeer.speed
|
||||||
|
|
||||||
top_distance = max(distances.values())
|
top_distance = max(distances.values())
|
||||||
for reindeer in reindeers:
|
for reindeer in reindeers:
|
||||||
if distances[reindeer] == top_distance:
|
if distances[reindeer] == top_distance:
|
||||||
points[reindeer] += 1
|
points[reindeer] += 1
|
||||||
|
|
||||||
for reindeer in reindeers:
|
for reindeer in reindeers:
|
||||||
if states[reindeer][1] == time:
|
if states[reindeer][1] == time:
|
||||||
if states[reindeer][0] == "resting":
|
if states[reindeer][0] == "resting":
|
||||||
states[reindeer] = ("flying", time + reindeer.fly_time)
|
states[reindeer] = ("flying", time + reindeer.fly_time)
|
||||||
else:
|
else:
|
||||||
states[reindeer] = ("resting", time + reindeer.rest_time)
|
states[reindeer] = ("resting", time + reindeer.rest_time)
|
||||||
|
|
||||||
|
yield max(distances.values())
|
||||||
answer_1 = max(distances.values())
|
yield max(points.values()) - 1
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = max(points.values()) - 1
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import math
|
import math
|
||||||
import sys
|
from typing import Any, Iterator, Sequence, cast
|
||||||
from typing import Sequence, cast
|
|
||||||
|
|
||||||
import parse # type: ignore
|
import parse # type: ignore
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def score(ingredients: list[list[int]], teaspoons: Sequence[int]) -> int:
|
def score(ingredients: list[list[int]], teaspoons: Sequence[int]) -> int:
|
||||||
return math.prod(
|
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]] = []
|
ingredients: list[list[int]] = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
_, *scores = cast(
|
_, *scores = cast(
|
||||||
tuple[str, int, int, int, int, int],
|
tuple[str, int, int, int, int, int],
|
||||||
parse.parse( # type: ignore
|
parse.parse( # type: ignore
|
||||||
"{}: capacity {:d}, durability {:d}, flavor {:d}, "
|
"{}: capacity {:d}, durability {:d}, flavor {:d}, "
|
||||||
"texture {:d}, calories {:d}",
|
"texture {:d}, calories {:d}",
|
||||||
line,
|
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.append(scores)
|
||||||
|
|
||||||
|
total_teaspoons = 100
|
||||||
|
calories: list[int] = []
|
||||||
|
scores: list[int] = []
|
||||||
|
|
||||||
answer_1 = max(scores)
|
for a in range(total_teaspoons + 1):
|
||||||
print(f"answer 1 is {answer_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)
|
scores.append(score(ingredients, teaspoons))
|
||||||
print(f"answer 2 is {answer_2}")
|
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 operator as op
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Callable
|
from typing import Any, Callable, Iterator
|
||||||
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
MFCSAM: dict[str, int] = {
|
MFCSAM: dict[str, int] = {
|
||||||
"children": 3,
|
"children": 3,
|
||||||
@ -17,18 +18,10 @@ MFCSAM: dict[str, int] = {
|
|||||||
"perfumes": 1,
|
"perfumes": 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
lines = sys.stdin.readlines()
|
|
||||||
|
|
||||||
aunts: list[dict[str, int]] = [
|
def match(
|
||||||
{
|
aunts: list[dict[str, int]], operators: dict[str, Callable[[int, int], bool]]
|
||||||
match[1]: int(match[2])
|
) -> int:
|
||||||
for match in re.findall(R"((?P<compound>[^:, ]+): (?P<quantity>\d+))", line)
|
|
||||||
}
|
|
||||||
for line in lines
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def match(operators: dict[str, Callable[[int, int], bool]]) -> int:
|
|
||||||
return next(
|
return next(
|
||||||
i
|
i
|
||||||
for i, aunt in enumerate(aunts, start=1)
|
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))
|
class Solver(BaseSolver):
|
||||||
print(f"answer 1 is {answer_1}")
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
answer_2 = match(
|
aunts: list[dict[str, int]] = [
|
||||||
defaultdict(
|
{
|
||||||
lambda: op.eq,
|
match[1]: int(match[2])
|
||||||
trees=op.gt,
|
for match in re.findall(
|
||||||
cats=op.gt,
|
R"((?P<compound>[^:, ]+): (?P<quantity>\d+))", line
|
||||||
pomeranians=op.lt,
|
)
|
||||||
goldfish=op.lt,
|
}
|
||||||
)
|
for line in lines
|
||||||
)
|
]
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
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 Any, Iterator
|
||||||
from typing import Iterator
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
|
|
||||||
def iter_combinations(value: int, containers: list[int]) -> Iterator[tuple[int, ...]]:
|
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
|
yield (containers[i],) + combination
|
||||||
|
|
||||||
|
|
||||||
containers = [int(c) for c in sys.stdin.read().split()]
|
class Solver(BaseSolver):
|
||||||
total = 25 if len(containers) <= 5 else 150
|
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)
|
yield len(combinations)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
min_containers = min(len(combination) for combination in combinations)
|
min_containers = min(len(combination) for combination in combinations)
|
||||||
|
yield sum(
|
||||||
answer_2 = sum(1 for combination in combinations if len(combination) == min_containers)
|
1 for combination in combinations if len(combination) == min_containers
|
||||||
print(f"answer 2 is {answer_2}")
|
)
|
||||||
|
@ -1,66 +1,66 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
from typing import Any, Iterator
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.typing import NDArray
|
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(
|
class Solver(BaseSolver):
|
||||||
[
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
np.zeros((grid0.shape[0] + 2, 1), dtype=bool),
|
grid0 = np.array([[c == "#" for c in line] for line in input.splitlines()])
|
||||||
np.concatenate(
|
|
||||||
|
# add an always off circle around
|
||||||
|
grid0 = np.concatenate(
|
||||||
[
|
[
|
||||||
np.zeros((1, grid0.shape[1]), dtype=bool),
|
np.zeros((grid0.shape[0] + 2, 1), dtype=bool),
|
||||||
grid0,
|
np.concatenate(
|
||||||
np.zeros((1, grid0.shape[1]), dtype=bool),
|
[
|
||||||
]
|
np.zeros((1, grid0.shape[1]), dtype=bool),
|
||||||
),
|
grid0,
|
||||||
np.zeros((grid0.shape[0] + 2, 1), dtype=bool),
|
np.zeros((1, grid0.shape[1]), dtype=bool),
|
||||||
],
|
]
|
||||||
axis=1,
|
),
|
||||||
)
|
np.zeros((grid0.shape[0] + 2, 1), dtype=bool),
|
||||||
|
],
|
||||||
|
axis=1,
|
||||||
|
)
|
||||||
|
|
||||||
moves = list(itertools.product([-1, 0, 1], repeat=2))
|
moves = list(itertools.product([-1, 0, 1], repeat=2))
|
||||||
moves.remove((0, 0))
|
moves.remove((0, 0))
|
||||||
|
|
||||||
jjs, iis = np.meshgrid(
|
jjs, iis = np.meshgrid(
|
||||||
np.arange(1, grid0.shape[0] - 1, dtype=int),
|
np.arange(1, grid0.shape[0] - 1, dtype=int),
|
||||||
np.arange(1, grid0.shape[1] - 1, dtype=int),
|
np.arange(1, grid0.shape[1] - 1, dtype=int),
|
||||||
)
|
)
|
||||||
iis, jjs = iis.flatten(), jjs.flatten()
|
iis, jjs = iis.flatten(), jjs.flatten()
|
||||||
|
|
||||||
ins = iis[:, None] + np.array(moves)[:, 0]
|
ins = iis[:, None] + np.array(moves)[:, 0]
|
||||||
jns = jjs[:, None] + np.array(moves)[:, 1]
|
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_]:
|
grid = np.zeros_like(grid)
|
||||||
neighbors_on = grid[ins, jns].sum(axis=1)
|
grid[iis, jjs] = (neighbors_on == 3) | (cells_on & (neighbors_on == 2))
|
||||||
cells_on = grid[iis, jjs]
|
|
||||||
|
|
||||||
grid = np.zeros_like(grid)
|
return grid
|
||||||
grid[iis, jjs] = (neighbors_on == 3) | (cells_on & (neighbors_on == 2))
|
|
||||||
|
|
||||||
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 = 5 if len(grid) < 10 else 100
|
||||||
n_steps = 4 if len(grid) < 10 else 100
|
grid = grid0
|
||||||
for _ in range(n_steps):
|
for _ in range(n_steps):
|
||||||
grid = game_of_life(grid)
|
grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True
|
||||||
|
grid = game_of_life(grid)
|
||||||
|
|
||||||
answer_1 = grid.sum()
|
grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
|
yield sum(cell for line in grid for cell in line)
|
||||||
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}")
|
|
||||||
|
@ -1,56 +1,58 @@
|
|||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
replacements_s, molecule = sys.stdin.read().split("\n\n")
|
from ..base import BaseSolver
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
answer_2 = count
|
class Solver(BaseSolver):
|
||||||
print(f"answer 2 is {count}")
|
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
|
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))
|
lw, wh, hl = (length * width, width * height, height * length)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = np.sum(
|
yield np.sum(2 * (lw + wh + hl) + np.min(np.stack([lw, wh, hl]), axis=0))
|
||||||
length * width * height
|
|
||||||
+ 2 * np.min(np.stack([length + width, length + height, height + width]), axis=0)
|
yield np.sum(
|
||||||
)
|
length * width * height
|
||||||
print(f"answer 2 is {answer_2}")
|
+ 2
|
||||||
|
* np.min(
|
||||||
|
np.stack([length + width, length + height, height + width]), axis=0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import itertools
|
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
|
count = 0
|
||||||
k = 1
|
k = 1
|
||||||
while k * k < n:
|
while k * k < n:
|
||||||
@ -21,8 +21,9 @@ def presents(n: int, elf: int, max: int = target) -> int:
|
|||||||
return count
|
return count
|
||||||
|
|
||||||
|
|
||||||
answer_1 = next(n for n in itertools.count(1) if presents(n, 10) >= target)
|
class Solver(BaseSolver):
|
||||||
print(f"answer 1 is {answer_1}")
|
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)
|
yield next(n for n in itertools.count(1) if presents(n, 10, target) >= target)
|
||||||
print(f"answer 2 is {answer_2}")
|
yield next(n for n in itertools.count(1) if presents(n, 11, 50) >= target)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
|
||||||
from math import ceil
|
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]
|
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_attack = int(lines[1].split(":")[1].strip())
|
||||||
boss_armor = int(lines[2].split(":")[1].strip())
|
boss_armor = int(lines[2].split(":")[1].strip())
|
||||||
boss_hp = int(lines[0].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
|
cost, player_attack, player_armor = (
|
||||||
for equipments in itertools.product(WEAPONS, ARMORS, RINGS, RINGS):
|
sum(equipment[1:][k] for equipment in equipments) for k in range(3)
|
||||||
if equipments[-1][0] != "" and equipments[-2] == equipments[-1]:
|
)
|
||||||
continue
|
|
||||||
|
|
||||||
cost, player_attack, player_armor = (
|
if ceil(boss_hp / max(1, player_attack - boss_armor)) <= ceil(
|
||||||
sum(equipment[1:][k] for equipment in equipments) for k in range(3)
|
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(
|
yield min_cost
|
||||||
player_hp / max(1, boss_attack - player_armor)
|
yield max_cost
|
||||||
):
|
|
||||||
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}")
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import heapq
|
import heapq
|
||||||
import sys
|
from typing import Any, Iterator, Literal, TypeAlias, cast
|
||||||
from typing import Literal, TypeAlias, cast
|
|
||||||
|
from ..base import BaseSolver
|
||||||
|
|
||||||
PlayerType: TypeAlias = Literal["player", "boss"]
|
PlayerType: TypeAlias = Literal["player", "boss"]
|
||||||
SpellType: TypeAlias = Literal["magic missile", "drain", "shield", "poison", "recharge"]
|
SpellType: TypeAlias = Literal["magic missile", "drain", "shield", "poison", "recharge"]
|
||||||
@ -62,17 +63,6 @@ def play(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
visited.add((player, player_hp, player_mana, player_armor, boss_hp, buffs))
|
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]] = []
|
new_buffs: list[tuple[BuffType, int]] = []
|
||||||
for buff, length in buffs:
|
for buff, length in buffs:
|
||||||
length = length - 1
|
length = length - 1
|
||||||
@ -88,6 +78,16 @@ def play(
|
|||||||
if length > 0:
|
if length > 0:
|
||||||
new_buffs.append((buff, length))
|
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)
|
buffs = tuple(new_buffs)
|
||||||
|
|
||||||
if player == "boss":
|
if player == "boss":
|
||||||
@ -155,23 +155,28 @@ def play(
|
|||||||
return winning_node
|
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_hp = 50
|
||||||
player_mana = 500
|
player_mana = 500
|
||||||
player_armor = 0
|
player_armor = 0
|
||||||
|
|
||||||
boss_hp = int(lines[0].split(":")[1].strip())
|
boss_hp = int(lines[0].split(":")[1].strip())
|
||||||
boss_attack = int(lines[1].split(":")[1].strip())
|
boss_attack = int(lines[1].split(":")[1].strip())
|
||||||
|
|
||||||
answer_1 = sum(
|
yield sum(
|
||||||
c
|
c
|
||||||
for _, c in play(player_hp, player_mana, player_armor, boss_hp, boss_attack, False)
|
for _, c in play(
|
||||||
)
|
player_hp, player_mana, player_armor, boss_hp, boss_attack, False
|
||||||
print(f"answer 1 is {answer_1}")
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# 1242 (not working)
|
# 1242 (not working)
|
||||||
answer_2 = sum(
|
yield sum(
|
||||||
c for _, c in play(player_hp, player_mana, player_armor, boss_hp, boss_attack, True)
|
c
|
||||||
)
|
for _, c in play(
|
||||||
print(f"answer 2 is {answer_2}")
|
player_hp, player_mana, player_armor, boss_hp, boss_attack, True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import sys
|
|
||||||
from collections import defaultdict
|
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]:
|
def process(directions: str) -> dict[tuple[int, int], int]:
|
||||||
@ -27,8 +27,7 @@ def process(directions: str) -> dict[tuple[int, int], int]:
|
|||||||
return counts
|
return counts
|
||||||
|
|
||||||
|
|
||||||
answer_1 = len(process(line))
|
class Solver(BaseSolver):
|
||||||
print(f"answer 1 is {answer_1}")
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
yield len(process(input))
|
||||||
answer_2 = len(process(line[::2]) | process(line[1::2]))
|
yield len(process(input[::2]) | process(input[1::2]))
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
import itertools
|
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(
|
class Solver(BaseSolver):
|
||||||
i for i in it if hashlib.md5(f"{line}{i}".encode()).hexdigest().startswith("000000")
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
)
|
it = iter(itertools.count(1))
|
||||||
print(f"answer 2 is {answer_2}")
|
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"
|
VOWELS = "aeiou"
|
||||||
FORBIDDEN = {"ab", "cd", "pq", "xy"}
|
FORBIDDEN = {"ab", "cd", "pq", "xy"}
|
||||||
@ -27,10 +29,8 @@ def is_nice_2(s: str) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
answer_1 = sum(map(is_nice_1, lines))
|
lines = input.splitlines()
|
||||||
print(f"answer 1 is {answer_1}")
|
yield sum(map(is_nice_1, lines))
|
||||||
|
yield sum(map(is_nice_2, lines))
|
||||||
answer_2 = sum(map(is_nice_2, lines))
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,33 +1,32 @@
|
|||||||
import sys
|
from typing import Any, Iterator, Literal, cast
|
||||||
from typing import Literal, cast
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import parse # type: ignore
|
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:
|
class Solver(BaseSolver):
|
||||||
case "turn on":
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
lights_1[sx:ex, sy:ey] = True
|
lights_1 = np.zeros((1000, 1000), dtype=bool)
|
||||||
lights_2[sx:ex, sy:ey] += 1
|
lights_2 = np.zeros((1000, 1000), dtype=int)
|
||||||
case "turn off":
|
for line in input.splitlines():
|
||||||
lights_1[sx:ex, sy:ey] = False
|
action, sx, sy, ex, ey = cast(
|
||||||
lights_2[sx:ex, sy:ey] = np.maximum(lights_2[sx:ex, sy:ey] - 1, 0)
|
tuple[Literal["turn on", "turn off", "toggle"], int, int, int, int],
|
||||||
case "toggle":
|
parse.parse("{} {:d},{:d} through {:d},{:d}", line), # type: ignore
|
||||||
lights_1[sx:ex, sy:ey] = ~lights_1[sx:ex, sy:ey]
|
)
|
||||||
lights_2[sx:ex, sy:ey] += 2
|
ex, ey = ex + 1, ey + 1
|
||||||
|
|
||||||
answer_1 = lights_1.sum()
|
match action:
|
||||||
print(f"answer 1 is {answer_1}")
|
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()
|
yield lights_1.sum()
|
||||||
print(f"answer 2 is {answer_2}")
|
yield lights_2.sum()
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import logging
|
|
||||||
import operator
|
import operator
|
||||||
import os
|
from typing import Any, Callable, Iterator
|
||||||
import sys
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
|
||||||
|
|
||||||
OPERATORS = {
|
OPERATORS = {
|
||||||
"AND": operator.and_,
|
"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]
|
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(
|
def process(
|
||||||
signals: Signals,
|
signals: Signals,
|
||||||
values: dict[str, int],
|
values: dict[str, int],
|
||||||
@ -91,11 +45,52 @@ def process(
|
|||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
values_1 = process(signals.copy(), values.copy())
|
class Solver(BaseSolver):
|
||||||
logging.info("\n" + "\n".join(f"{k}: {values_1[k]}" for k in sorted(values_1)))
|
def solve(self, input: str) -> Iterator[Any] | None:
|
||||||
answer_1 = values_1["a"]
|
lines = input.splitlines()
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
values_2 = process(signals.copy(), values | {"b": values_1["a"]})
|
signals: Signals = {}
|
||||||
answer_2 = values_2["a"]
|
values: dict[str, int] = {"": 0}
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
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
|
from typing import Any, Iterator
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
from ..base import BaseSolver
|
||||||
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
class Solver(BaseSolver):
|
||||||
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
|
lines = input.splitlines()
|
||||||
|
|
||||||
answer_1 = sum(
|
yield sum(
|
||||||
# left and right quotes (not in memory)
|
# left and right quotes (not in memory)
|
||||||
2
|
2
|
||||||
# each \\ adds one character in the literals (compared to memory)
|
# each \\ adds one character in the literals (compared to memory)
|
||||||
+ line.count(R"\\")
|
+ line.count(R"\\")
|
||||||
# each \" adds one character in the literals (compared to memory)
|
# each \" adds one character in the literals (compared to memory)
|
||||||
+ line[1:-1].count(R"\"")
|
+ line[1:-1].count(R"\"")
|
||||||
# each \xFF adds 3 characters in the literals (compared to memory), but we must not
|
# 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
|
# 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
|
# 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"))
|
+ 3 * (line.count(R"\x") - line.count(R"\\x") + line.count(R"\\\x"))
|
||||||
for line in lines
|
for line in lines
|
||||||
)
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = sum(
|
yield sum(
|
||||||
# needs to wrap in quotes (2 characters)
|
# needs to wrap in quotes (2 characters)
|
||||||
2
|
2
|
||||||
# needs to escape every \ with an extra \
|
# needs to escape every \ with an extra \
|
||||||
+ line.count("\\")
|
+ line.count("\\")
|
||||||
# needs to escape every " with an extra \ (including the first and last ones)
|
# needs to escape every " with an extra \ (including the first and last ones)
|
||||||
+ line.count('"')
|
+ line.count('"')
|
||||||
for line in lines
|
for line in lines
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {answer_2}")
|
|
||||||
|
@ -1,27 +1,28 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import cast
|
from typing import Any, Iterator, cast
|
||||||
|
|
||||||
import parse # type: ignore
|
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 = {
|
class Solver(BaseSolver):
|
||||||
route: sum(distances[o][d] for o, d in zip(route[:-1], route[1:]))
|
def solve(self, input: str) -> Iterator[Any]:
|
||||||
for route in map(tuple, itertools.permutations(distances))
|
lines = input.splitlines()
|
||||||
}
|
|
||||||
|
|
||||||
answer_1 = min(distance_of_routes.values())
|
distances: dict[str, dict[str, int]] = defaultdict(dict)
|
||||||
print(f"answer 1 is {answer_1}")
|
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())
|
distance_of_routes = {
|
||||||
print(f"answer 2 is {answer_2}")
|
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())
|
||||||
|
@ -178,7 +178,14 @@ def main():
|
|||||||
|
|
||||||
start = datetime.now()
|
start = datetime.now()
|
||||||
last = start
|
last = start
|
||||||
for i_answer, answer in enumerate(solver.solve(data.strip())):
|
|
||||||
|
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()
|
current = datetime.now()
|
||||||
|
|
||||||
if api:
|
if api:
|
||||||
|
@ -7,10 +7,12 @@ _T = TypeVar("_T")
|
|||||||
|
|
||||||
class ProgressHandler(Protocol):
|
class ProgressHandler(Protocol):
|
||||||
@overload
|
@overload
|
||||||
def wrap(self, values: Sequence[_T]) -> Iterator[_T]: ...
|
def wrap(self, values: Sequence[_T]) -> Iterator[_T]:
|
||||||
|
...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def wrap(self, values: Iterable[_T], total: int) -> Iterator[_T]: ...
|
def wrap(self, values: Iterable[_T], total: int) -> Iterator[_T]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
class BaseSolver:
|
class BaseSolver:
|
||||||
@ -31,4 +33,5 @@ class BaseSolver:
|
|||||||
self.outputs = outputs
|
self.outputs = outputs
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def solve(self, input: str) -> Iterator[Any]: ...
|
def solve(self, input: str) -> Iterator[Any] | None:
|
||||||
|
...
|
||||||
|
Loading…
Reference in New Issue
Block a user