Compare commits

..

2 Commits

Author SHA1 Message Date
Mikaël Capelle
1b4dd32898 Refactor 2021 for new UI.
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2024-12-08 14:03:34 +01:00
Mikaël Capelle
cd96140378 Start fixing 2022 for new API. 2024-12-08 13:55:25 +01:00
37 changed files with 821 additions and 836 deletions

View File

@ -1,14 +1,17 @@
import sys
from typing import Any, Iterator
lines = sys.stdin.read().splitlines()
from ..base import BaseSolver
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
values = [int(line) for line in lines]
# part 1
answer_1 = sum(v2 > v1 for v1, v2 in zip(values[:-1], values[1:]))
print(f"answer 1 is {answer_1}")
yield sum(v2 > v1 for v1, v2 in zip(values[:-1], values[1:]))
# part 2
runnings = [sum(values[i : i + 3]) for i in range(len(values) - 2)]
answer_2 = sum(v2 > v1 for v1, v2 in zip(runnings[:-1], runnings[1:]))
print(f"answer 2 is {answer_2}")
yield sum(v2 > v1 for v1, v2 in zip(runnings[:-1], runnings[1:]))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +1,19 @@
import sys
from math import prod
from typing import Literal, TypeAlias, cast
from typing import Any, Iterator, Literal, TypeAlias, cast
lines = sys.stdin.read().splitlines()
from ..base import BaseSolver
Command: TypeAlias = Literal["forward", "up", "down"]
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
commands: list[tuple[Command, int]] = [
(cast(Command, (p := line.split())[0]), int(p[1])) for line in lines
]
def depth_and_position(use_aim: bool):
aim, pos, depth = 0, 0, 0
for command, value in commands:
@ -31,11 +34,5 @@ def depth_and_position(use_aim: bool):
return depth, pos
# part 1
answer_1 = prod(depth_and_position(False))
print(f"answer 1 is {answer_1}")
# part 2
answer_2 = prod(depth_and_position(True))
print(f"answer 2 is {answer_2}")
yield prod(depth_and_position(False))
yield prod(depth_and_position(True))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
import sys
from collections import Counter
from typing import Literal
from typing import Any, Iterator, Literal
from ..base import BaseSolver
def generator_rating(
@ -20,20 +21,23 @@ def generator_rating(
return values[0]
lines = sys.stdin.read().splitlines()
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
# part 1
most_and_least_common = [
tuple(Counter(line[col] for line in lines).most_common(2)[m][0] for m in range(2))
tuple(
Counter(line[col] for line in lines).most_common(2)[m][0]
for m in range(2)
)
for col in range(len(lines[0]))
]
gamma_rate = int("".join(most for most, _ in most_and_least_common), base=2)
epsilon_rate = int("".join(least for _, least in most_and_least_common), base=2)
print(f"answer 1 is {gamma_rate * epsilon_rate}")
yield gamma_rate * epsilon_rate
# part 2
oxygen_generator_rating = int(generator_rating(lines, True, "1"), base=2)
co2_scrubber_rating = int(generator_rating(lines, False, "0"), base=2)
answer_2 = oxygen_generator_rating * co2_scrubber_rating
print(f"answer 2 is {answer_2}")
yield oxygen_generator_rating * co2_scrubber_rating

View File

@ -1,8 +1,13 @@
import sys
from typing import Any, Iterator
import numpy as np
lines = sys.stdin.read().splitlines()
from ..base import BaseSolver
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
numbers = [int(c) for c in lines[0].split(",")]
@ -26,7 +31,9 @@ for round, number in enumerate(numbers):
if winning_rounds[index][0] > 0:
continue
if np.any(np.all(marked[index], axis=0) | np.all(marked[index], axis=1)):
if np.any(
np.all(marked[index], axis=0) | np.all(marked[index], axis=1)
):
winning_rounds[index] = (
round,
number * int(np.sum(boards[index][~marked[index]])),
@ -38,8 +45,8 @@ for round, number in enumerate(numbers):
# part 1
(_, score) = min(winning_rounds, key=lambda w: w[0])
print(f"answer 1 is {score}")
yield score
# part 2
(_, score) = max(winning_rounds, key=lambda w: w[0])
print(f"answer 2 is {score}")
yield score

View File

@ -1,8 +1,13 @@
import sys
from typing import Any, Iterator
import numpy as np
lines: list[str] = sys.stdin.read().splitlines()
from ..base import BaseSolver
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
sections: list[tuple[tuple[int, int], tuple[int, int]]] = [
(
@ -20,10 +25,8 @@ sections: list[tuple[tuple[int, int], tuple[int, int]]] = [
np_sections = np.array(sections).reshape(-1, 4)
x_min, x_max, y_min, y_max = (
min(np_sections[:, 0].min(), np_sections[:, 2].min()),
x_max, y_max = (
max(np_sections[:, 0].max(), np_sections[:, 2].max()),
min(np_sections[:, 1].min(), np_sections[:, 3].min()),
max(np_sections[:, 1].max(), np_sections[:, 3].max()),
)
@ -41,8 +44,5 @@ for (x1, y1), (x2, y2) in sections:
for i, j in zip(y_rng, x_rng):
counts_2[i, j] += 1
answer_1 = (counts_1 >= 2).sum()
print(f"answer 1 is {answer_1}")
answer_2 = (counts_2 >= 2).sum()
print(f"answer 2 is {answer_2}")
yield (counts_1 >= 2).sum()
yield (counts_2 >= 2).sum()

View File

@ -1,6 +1,11 @@
import sys
from typing import Any, Iterator
values = [int(c) for c in sys.stdin.read().strip().split(",")]
from ..base import BaseSolver
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
values = [int(c) for c in input.split(",")]
days = 256
lanterns = {day: 0 for day in range(days)}
@ -12,10 +17,5 @@ for day in range(days):
for day2 in range(day + 9, days, 7):
lanterns[day2] += lanterns[day]
# part 1
answer_1 = sum(v for k, v in lanterns.items() if k < 80) + len(values)
print(f"answer 1 is {answer_1}")
# part 2
answer_2 = sum(lanterns.values()) + len(values)
print(f"answer 2 is {answer_2}")
yield sum(v for k, v in lanterns.items() if k < 80) + len(values)
yield sum(lanterns.values()) + len(values)

View File

@ -1,19 +1,22 @@
import sys
from typing import Any, Iterator
positions = [int(c) for c in sys.stdin.read().strip().split(",")]
from ..base import BaseSolver
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
positions = [int(c) for c in input.split(",")]
min_position, max_position = min(positions), max(positions)
# part 1
answer_1 = min(
yield min(
sum(abs(p - position) for p in positions)
for position in range(min_position, max_position + 1)
)
print(f"answer 1 is {answer_1}")
# part 2
answer_2 = min(
yield min(
sum(abs(p - position) * (abs(p - position) + 1) // 2 for p in positions)
for position in range(min_position, max_position + 1)
)
print(f"answer 2 is {answer_2}")

View File

@ -1,8 +1,7 @@
import itertools
import os
import sys
from typing import Any, Iterator
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
from ..base import BaseSolver
digits = {
"abcefg": 0,
@ -17,14 +16,18 @@ digits = {
"abcdfg": 9,
}
lines = sys.stdin.read().splitlines()
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = input.splitlines()
# part 1
lengths = {len(k) for k, v in digits.items() if v in (1, 4, 7, 8)}
answer_1 = sum(
len(p) in lengths for line in lines for p in line.split("|")[1].strip().split()
yield sum(
len(p) in lengths
for line in lines
for p in line.split("|")[1].strip().split()
)
print(f"answer 1 is {answer_1}")
# part 2
values: list[int] = []
@ -49,7 +52,9 @@ for line in lines:
bd = [u for u in per_length[4][0] if u not in cf]
# the 3 digits of length 5 have a, d and g in common
adg = [u for u in per_length[5][0] if all(u in pe for pe in per_length[5][1:])]
adg = [
u for u in per_length[5][0] if all(u in pe for pe in per_length[5][1:])
]
# we can remove a
dg = [u for u in adg if u != a]
@ -77,11 +82,8 @@ for line in lines:
digit = "".join(sorted(mapping[c] for c in number))
value = 10 * value + digits[digit]
if VERBOSE:
print(value)
self.logger.info(f"value for '{line}' is {value}")
values.append(value)
answer_2 = sum(values)
print(f"answer 2 is {answer_2}")
yield sum(values)

View File

@ -1,18 +1,18 @@
import sys
from math import prod
from typing import Any, Iterator
values = [[int(c) for c in row] for row in sys.stdin.read().splitlines()]
n_rows, n_cols = len(values), len(values[0])
from ..base import BaseSolver
def neighbors(point: tuple[int, int]):
def neighbors(point: tuple[int, int], n_rows: int, n_cols: int):
i, j = point
for di, dj in ((-1, 0), (+1, 0), (0, -1), (0, +1)):
if 0 <= i + di < n_rows and 0 <= j + dj < n_cols:
yield (i + di, j + dj)
def basin(start: tuple[int, int]) -> set[tuple[int, int]]:
def basin(values: list[list[int]], start: tuple[int, int]) -> set[tuple[int, int]]:
n_rows, n_cols = len(values), len(values[0])
visited: set[tuple[int, int]] = set()
queue = [start]
@ -23,22 +23,25 @@ def basin(start: tuple[int, int]) -> set[tuple[int, int]]:
continue
visited.add((i, j))
queue.extend(neighbors((i, j)))
queue.extend(neighbors((i, j), n_rows, n_cols))
return visited
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
values = [[int(c) for c in row] for row in input.splitlines()]
n_rows, n_cols = len(values), len(values[0])
low_points = [
(i, j)
for i in range(n_rows)
for j in range(n_cols)
if all(values[ti][tj] > values[i][j] for ti, tj in neighbors((i, j)))
if all(
values[ti][tj] > values[i][j]
for ti, tj in neighbors((i, j), n_rows, n_cols)
)
]
# part 1
answer_1 = sum(values[i][j] + 1 for i, j in low_points)
print(f"answer 1 is {answer_1}")
# part 2
answer_2 = prod(sorted(len(basin(point)) for point in low_points)[-3:])
print(f"answer 2 is {answer_2}")
yield sum(values[i][j] + 1 for i, j in low_points)
yield prod(sorted(len(basin(values, point)) for point in low_points)[-3:])

View File

@ -1,3 +1,4 @@
import itertools as it
from typing import Any, Iterator
import numpy as np
@ -20,9 +21,7 @@ class Solver(BaseSolver):
no_beacons_row_l.append(sx + np.arange(0, d - abs(sy - row) + 1)) # type: ignore
beacons_at_row = set(bx for (bx, by) in sensor_to_beacon.values() if by == row)
no_beacons_row = set(np.concatenate(no_beacons_row_l)).difference(
beacons_at_row
) # type: ignore
no_beacons_row = set(it.chain(*no_beacons_row_l)).difference(beacons_at_row) # type: ignore
return len(no_beacons_row)
@ -92,5 +91,5 @@ class Solver(BaseSolver):
# 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})")
self.logger.info(f"answer 2 is {a2} (x={x}, y={y})")
yield a2

View File

@ -3,7 +3,6 @@ from __future__ import annotations
import heapq
import itertools
import re
import sys
from collections import defaultdict
from typing import Any, FrozenSet, Iterator, NamedTuple
@ -38,8 +37,8 @@ def breadth_first_search(pipes: dict[str, Pipe], pipe: Pipe) -> dict[Pipe, int]:
Runs a BFS from the given pipe and return the shortest distance (in term of hops)
to all other pipes.
"""
queue = [(0, pipe_1)]
visited = set()
queue = [(0, pipe)]
visited: set[Pipe] = set()
distances: dict[Pipe, int] = {}
while len(distances) < len(pipes):
@ -124,8 +123,9 @@ def part_2(
# === MAIN ===
lines = sys.stdin.read().splitlines()
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = [line.strip() for line in input.splitlines()]
pipes: dict[str, Pipe] = {}
for line in lines:
@ -152,9 +152,8 @@ for pipe_1 in pipes.values():
# valves with flow
relevant_pipes = frozenset(pipe for pipe in pipes.values() if pipe.flow > 0)
# 1651, 1653
print(part_1(pipes["AA"], 30, distances, relevant_pipes))
yield part_1(pipes["AA"], 30, distances, relevant_pipes)
# 1707, 2223
print(part_2(pipes["AA"], 26, distances, relevant_pipes))
yield part_2(pipes["AA"], 26, distances, relevant_pipes)

View File

@ -1,14 +1,16 @@
import sys
from typing import Any, Iterator, Sequence, TypeVar
from typing import Any, Iterator, Sequence, TypeAlias, TypeVar
import numpy as np
from numpy.typing import NDArray
from ..base import BaseSolver
T = TypeVar("T")
Tower: TypeAlias = NDArray[np.bool]
def print_tower(tower: np.ndarray, out: str = "#"):
def print_tower(tower: Tower, out: str = "#"):
print("-" * (tower.shape[1] + 2))
non_empty = False
for row in reversed(range(1, tower.shape[0])):
@ -19,7 +21,7 @@ def print_tower(tower: np.ndarray, out: str = "#"):
print("+" + "-" * tower.shape[1] + "+")
def tower_height(tower: np.ndarray) -> int:
def tower_height(tower: Tower) -> int:
return int(tower.shape[0] - tower[::-1, :].argmax(axis=0).min() - 1)
@ -47,8 +49,8 @@ def build_tower(
n_rocks: int,
jets: str,
early_stop: bool = False,
init: np.ndarray = np.ones(WIDTH, dtype=bool),
) -> tuple[np.ndarray, int, int, dict[int, int]]:
init: Tower = np.ones(WIDTH, dtype=bool),
) -> tuple[Tower, int, int, dict[int, int]]:
tower = EMPTY_BLOCKS.copy()
tower[0, :] = init
@ -97,14 +99,13 @@ def build_tower(
return tower, rock_count, done_at.get((i_rock, i_jet), -1), heights
line = sys.stdin.read().strip()
tower, *_ = build_tower(2022, line)
answer_1 = tower_height(tower)
print(f"answer 1 is {answer_1}")
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
tower, *_ = build_tower(2022, input)
yield tower_height(tower)
TOTAL_ROCKS = 1_000_000_000_000
tower_1, n_rocks_1, prev_1, heights_1 = build_tower(TOTAL_ROCKS, line, True)
_tower_1, n_rocks_1, prev_1, heights_1 = build_tower(TOTAL_ROCKS, input, True)
assert prev_1 > 0
# 2767 1513
@ -118,5 +119,4 @@ remaining_height = (
heights_1[prev_1 + remaining_rocks % n_repeat_rocks] - heights_1[prev_1]
)
answer_2 = base_height + (n_repeat_towers + 1) * repeat_height + remaining_height
print(f"answer 2 is {answer_2}")
yield base_height + (n_repeat_towers + 1) * repeat_height + remaining_height

View File

@ -1,14 +1,16 @@
import sys
from typing import Any, Iterator
import numpy as np
from ..base import BaseSolver
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
xyz = np.asarray(
[
tuple(int(x) for x in row.split(",")) # type: ignore
for row in sys.stdin.read().splitlines()
for row in input.splitlines()
]
)
@ -17,14 +19,14 @@ xyz = xyz - xyz.min(axis=0) + 1
cubes = np.zeros(xyz.max(axis=0) + 3, dtype=bool)
cubes[xyz[:, 0], xyz[:, 1], xyz[:, 2]] = True
n_dims = len(cubes.shape)
faces = [(-1, 0, 0), (1, 0, 0), (0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1)]
answer_1 = sum(
1 for x, y, z in xyz for dx, dy, dz in faces if not cubes[x + dx, y + dy, z + dz]
yield sum(
1
for x, y, z in xyz
for dx, dy, dz in faces
if not cubes[x + dx, y + dy, z + dz]
)
print(f"answer 1 is {answer_1}")
visited = np.zeros_like(cubes, dtype=bool)
queue = [(0, 0, 0)]
@ -40,7 +42,9 @@ while queue:
for dx, dy, dz in faces:
nx, ny, nz = x + dx, y + dy, z + dz
if not all(n >= 0 and n < cubes.shape[i] for i, n in enumerate((nx, ny, nz))):
if not all(
n >= 0 and n < cubes.shape[i] for i, n in enumerate((nx, ny, nz))
):
continue
if visited[nx, ny, nz]:
@ -50,4 +54,5 @@ while queue:
n_faces += 1
else:
queue.append((nx, ny, nz))
print(f"answer 2 is {n_faces}")
yield n_faces

View File

@ -1,4 +1,3 @@
import sys
from typing import Any, Iterator, Literal
import numpy as np
@ -64,29 +63,6 @@ def dominates(lhs: State, rhs: State):
)
lines = sys.stdin.read().splitlines()
blueprints: list[dict[Reagent, IntOfReagent]] = []
for line in lines:
r: list[int] = parse.parse( # type: ignore
"Blueprint {}: "
"Each ore robot costs {:d} ore. "
"Each clay robot costs {:d} ore. "
"Each obsidian robot costs {:d} ore and {:d} clay. "
"Each geode robot costs {:d} ore and {:d} obsidian.",
line,
)
blueprints.append(
{
"ore": {"ore": r[1]},
"clay": {"ore": r[2]},
"obsidian": {"ore": r[3], "clay": r[4]},
"geode": {"ore": r[5], "obsidian": r[6]},
}
)
def run(blueprint: dict[Reagent, dict[Reagent, int]], max_time: int) -> int:
# since we can only build one robot per time, we do not need more than X robots
# of type K where X is the maximum number of K required among all robots, e.g.,
@ -175,11 +151,31 @@ def run(blueprint: dict[Reagent, dict[Reagent, int]], max_time: int) -> int:
return max(state.reagents["geode"] for state in state_after_t[max_time])
answer_1 = sum(
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
blueprints: list[dict[Reagent, IntOfReagent]] = []
for line in input.splitlines():
r: list[int] = parse.parse( # type: ignore
"Blueprint {}: "
"Each ore robot costs {:d} ore. "
"Each clay robot costs {:d} ore. "
"Each obsidian robot costs {:d} ore and {:d} clay. "
"Each geode robot costs {:d} ore and {:d} obsidian.",
line,
)
blueprints.append(
{
"ore": {"ore": r[1]},
"clay": {"ore": r[2]},
"obsidian": {"ore": r[3], "clay": r[4]},
"geode": {"ore": r[5], "obsidian": r[6]},
}
)
yield sum(
(i_blueprint + 1) * run(blueprint, 24)
for i_blueprint, blueprint in enumerate(blueprints)
)
print(f"answer 1 is {answer_1}")
answer_2 = run(blueprints[0], 32) * run(blueprints[1], 32) * run(blueprints[2], 32)
print(f"answer 2 is {answer_2}")
yield (run(blueprints[0], 32) * run(blueprints[1], 32) * run(blueprints[2], 32))

View File

@ -1,6 +1,5 @@
from __future__ import annotations
import sys
from typing import Any, Iterator
from ..base import BaseSolver
@ -68,10 +67,9 @@ def decrypt(numbers: list[Number], key: int, rounds: int) -> int:
)
numbers = [Number(int(x)) for i, x in enumerate(sys.stdin.readlines())]
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
numbers = [Number(int(x)) for x in input.splitlines()]
answer_1 = decrypt(numbers, 1, 1)
print(f"answer 1 is {answer_1}")
answer_2 = decrypt(numbers, 811589153, 10)
print(f"answer 2 is {answer_2}")
yield decrypt(numbers, 1, 1)
yield decrypt(numbers, 811589153, 10)

View File

@ -1,5 +1,4 @@
import operator
import sys
from typing import Any, Callable, Iterator
from ..base import BaseSolver
@ -79,7 +78,9 @@ def invert(
return monkeys
lines = sys.stdin.read().splitlines()
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = [line.strip() for line in input.splitlines()]
monkeys: dict[str, int | tuple[str, str, str]] = {}
@ -98,12 +99,10 @@ for line in lines:
op_monkeys.add(name)
answer_1 = compute(monkeys.copy(), "root")
print(f"answer 1 is {answer_1}")
yield compute(monkeys.copy(), "root")
# assume the second operand of 'root' can be computed, and the first one depends on
# humn, which is the case is my input and the test input
assert isinstance(monkeys["root"], tuple)
p1, _, p2 = monkeys["root"] # type: ignore
answer_2 = compute(invert(monkeys, "humn", compute(monkeys.copy(), p2)), "humn")
print(f"answer 2 is {answer_2}")
yield compute(invert(monkeys, "humn", compute(monkeys.copy(), p2)), "humn")

View File

@ -1,5 +1,4 @@
import re
import sys
from typing import Any, Callable, Iterator
import numpy as np
@ -12,7 +11,9 @@ TILE_FROM_CHAR = {" ": VOID, ".": EMPTY, "#": WALL}
SCORES = {"E": 0, "S": 1, "W": 2, "N": 3}
board_map_s, direction_s = sys.stdin.read().split("\n\n")
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
board_map_s, direction_s = input.split("\n\n")
# board
board_lines = board_map_s.splitlines()
@ -25,16 +26,19 @@ board = np.array(
)
directions = [
int(p1) if p2 else p1 for p1, p2 in re.findall(R"(([0-9])+|L|R)", direction_s)
int(p1) if p2 else p1
for p1, p2 in re.findall(R"(([0-9])+|L|R)", direction_s)
]
# find on each row and column the first and last non-void
row_first_non_void = np.argmax(board != VOID, axis=1)
row_last_non_void = board.shape[1] - np.argmax(board[:, ::-1] != VOID, axis=1) - 1
row_last_non_void = (
board.shape[1] - np.argmax(board[:, ::-1] != VOID, axis=1) - 1
)
col_first_non_void = np.argmax(board != VOID, axis=0)
col_last_non_void = board.shape[0] - np.argmax(board[::-1, :] != VOID, axis=0) - 1
col_last_non_void = (
board.shape[0] - np.argmax(board[::-1, :] != VOID, axis=0) - 1
)
faces = np.zeros_like(board)
size = np.gcd(board.shape[0], board.shape[1])
@ -105,7 +109,6 @@ else:
},
}
def wrap_part_1(y0: int, x0: int, r0: str) -> tuple[int, int, str]:
if r0 == "E":
return y0, row_first_non_void[y0], r0
@ -118,14 +121,14 @@ def wrap_part_1(y0: int, x0: int, r0: str) -> tuple[int, int, str]:
assert False
def wrap_part_2(y0: int, x0: int, r0: str) -> tuple[int, int, str]:
cube = faces[y0, x0]
assert r0 in faces_wrap[cube]
return faces_wrap[cube][r0](y0, x0)
def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int, str]:
def run(
wrap: Callable[[int, int, str], tuple[int, int, str]],
) -> tuple[int, int, str]:
y0 = 0
x0 = np.where(board[0] == EMPTY)[0][0]
r0 = "E"
@ -134,7 +137,9 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
if isinstance(direction, int):
while direction > 0:
if r0 == "E":
xi = np.where(board[y0, x0 + 1 : x0 + direction + 1] == WALL)[0]
xi = np.where(
board[y0, x0 + 1 : x0 + direction + 1] == WALL
)[0]
if len(xi):
x0 = x0 + xi[0]
direction = 0
@ -150,10 +155,14 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
x0 = row_last_non_void[y0]
direction = 0
else:
direction = direction - (row_last_non_void[y0] - x0) - 1
direction = (
direction - (row_last_non_void[y0] - x0) - 1
)
y0, x0, r0 = y0_t, x0_t, r0_t
elif r0 == "S":
yi = np.where(board[y0 + 1 : y0 + direction + 1, x0] == WALL)[0]
yi = np.where(
board[y0 + 1 : y0 + direction + 1, x0] == WALL
)[0]
if len(yi):
y0 = y0 + yi[0]
direction = 0
@ -169,7 +178,9 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
y0 = col_last_non_void[x0]
direction = 0
else:
direction = direction - (col_last_non_void[x0] - y0) - 1
direction = (
direction - (col_last_non_void[x0] - y0) - 1
)
y0, x0, r0 = y0_t, x0_t, r0_t
elif r0 == "W":
left = max(x0 - direction - 1, 0)
@ -177,7 +188,10 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
if len(xi):
x0 = left + xi[-1] + 1
direction = 0
elif x0 - direction >= 0 and board[y0, x0 - direction] == EMPTY:
elif (
x0 - direction >= 0
and board[y0, x0 - direction] == EMPTY
):
x0 = x0 - direction
direction = 0
else:
@ -186,7 +200,9 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
x0 = row_first_non_void[y0]
direction = 0
else:
direction = direction - (x0 - row_first_non_void[y0]) - 1
direction = (
direction - (x0 - row_first_non_void[y0]) - 1
)
y0, x0, r0 = y0_t, x0_t, r0_t
elif r0 == "N":
top = max(y0 - direction - 1, 0)
@ -194,7 +210,10 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
if len(yi):
y0 = top + yi[-1] + 1
direction = 0
elif y0 - direction >= 0 and board[y0 - direction, x0] == EMPTY:
elif (
y0 - direction >= 0
and board[y0 - direction, x0] == EMPTY
):
y0 = y0 - direction
direction = 0
else:
@ -203,7 +222,9 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
y0 = col_first_non_void[x0]
direction = 0
else:
direction = direction - (y0 - col_first_non_void[x0]) - 1
direction = (
direction - (y0 - col_first_non_void[x0]) - 1
)
y0, x0, r0 = y0_t, x0_t, r0_t
else:
r0 = {
@ -215,11 +236,8 @@ def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int
return y0, x0, r0
y1, x1, r1 = run(wrap_part_1)
answer_1 = 1000 * (1 + y1) + 4 * (1 + x1) + SCORES[r1]
print(f"answer 1 is {answer_1}")
yield 1000 * (1 + y1) + 4 * (1 + x1) + SCORES[r1]
y2, x2, r2 = run(wrap_part_2)
answer_2 = 1000 * (1 + y2) + 4 * (1 + x2) + SCORES[r2]
print(f"answer 2 is {answer_2}")
yield 1000 * (1 + y2) + 4 * (1 + x2) + SCORES[r2]

View File

@ -1,5 +1,4 @@
import itertools
import sys
from collections import defaultdict
from typing import Any, Iterator
@ -21,7 +20,7 @@ DIRECTIONS: Directions = [
def min_max_yx(positions: set[tuple[int, int]]) -> tuple[int, int, int, int]:
ys, xs = {y for y, x in positions}, {x for y, x in positions}
ys, xs = {y for y, _x in positions}, {x for _y, x in positions}
return min(ys), min(xs), max(ys), max(xs)
@ -72,9 +71,11 @@ def round(
directions.append(directions.pop(0))
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
POSITIONS = {
(i, j)
for i, row in enumerate(sys.stdin.read().splitlines())
for i, row in enumerate(input.splitlines())
for j, col in enumerate(row)
if col == "#"
}
@ -82,14 +83,15 @@ POSITIONS = {
# === part 1 ===
p1, d1 = POSITIONS.copy(), DIRECTIONS.copy()
for r in range(10):
for _ in range(10):
round(p1, d1)
min_y, min_x, max_y, max_x = min_max_yx(p1)
answer_1 = sum(
(y, x) not in p1 for y in range(min_y, max_y + 1) for x in range(min_x, max_x + 1)
yield sum(
(y, x) not in p1
for y in range(min_y, max_y + 1)
for x in range(min_x, max_x + 1)
)
print(f"answer 1 is {answer_1}")
# === part 2 ===
@ -103,4 +105,4 @@ while True:
if backup == p2:
break
print(f"answer 2 is {answer_2}")
yield answer_2

View File

@ -1,12 +1,14 @@
import heapq
import math
import sys
from collections import defaultdict
from typing import Any, Iterator
from ..base import BaseSolver
lines = sys.stdin.read().splitlines()
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = [line.strip() for line in input.splitlines()]
winds = {
(i - 1, j - 1, lines[i][j])
@ -18,8 +20,12 @@ winds = {
n_rows, n_cols = len(lines) - 2, len(lines[0]) - 2
CYCLE = math.lcm(n_rows, n_cols)
east_winds = [{j for j in range(n_cols) if (i, j, ">") in winds} for i in range(n_rows)]
west_winds = [{j for j in range(n_cols) if (i, j, "<") in winds} for i in range(n_rows)]
east_winds = [
{j for j in range(n_cols) if (i, j, ">") in winds} for i in range(n_rows)
]
west_winds = [
{j for j in range(n_cols) if (i, j, "<") in winds} for i in range(n_rows)
]
north_winds = [
{i for i in range(n_rows) if (i, j, "^") in winds} for j in range(n_cols)
]
@ -27,13 +33,14 @@ south_winds = [
{i for i in range(n_rows) if (i, j, "v") in winds} for j in range(n_cols)
]
def run(start: tuple[int, int], start_cycle: int, end: tuple[int, int]):
def heuristic(y: int, x: int) -> int:
return abs(end[0] - y) + abs(end[1] - x)
# (distance + heuristic, distance, (start_pos, cycle))
queue = [(heuristic(start[0], start[1]), 0, ((start[0], start[1]), start_cycle))]
queue = [
(heuristic(start[0], start[1]), 0, ((start[0], start[1]), start_cycle))
]
visited: set[tuple[tuple[int, int], int]] = set()
distances: dict[tuple[int, int], dict[int, int]] = defaultdict(lambda: {})
@ -57,13 +64,17 @@ def run(start: tuple[int, int], start_cycle: int, end: tuple[int, int]):
n_cycle = (cycle + 1) % CYCLE
if (ty, tx) == end:
heapq.heappush(queue, (distance + 1, distance + 1, ((ty, tx), n_cycle)))
heapq.heappush(
queue, (distance + 1, distance + 1, ((ty, tx), n_cycle))
)
break
if ((ty, tx), n_cycle) in visited:
continue
if (ty, tx) != start and (ty < 0 or tx < 0 or ty >= n_rows or tx >= n_cols):
if (ty, tx) != start and (
ty < 0 or tx < 0 or ty >= n_rows or tx >= n_cols
):
continue
if (ty, tx) != start:
@ -78,12 +89,17 @@ def run(start: tuple[int, int], start_cycle: int, end: tuple[int, int]):
heapq.heappush(
queue,
((heuristic(ty, tx) + distance + 1, distance + 1, ((ty, tx), n_cycle))),
(
(
heuristic(ty, tx) + distance + 1,
distance + 1,
((ty, tx), n_cycle),
)
),
)
return distances, next(iter(distances[end].values()))
start = (
-1,
next(j for j in range(1, len(lines[0]) - 1) if lines[0][j] == ".") - 1,
@ -94,8 +110,8 @@ end = (
)
distances_1, forward_1 = run(start, 0, end)
print(f"answer 1 is {forward_1}")
yield forward_1
distances_2, return_1 = run(end, next(iter(distances_1[end].keys())), start)
distances_3, forward_2 = run(start, next(iter(distances_2[start].keys())), end)
print(f"answer 2 is {forward_1 + return_1 + forward_2}")
_distances_3, forward_2 = run(start, next(iter(distances_2[start].keys())), end)
yield forward_1 + return_1 + forward_2

View File

@ -1,13 +1,14 @@
import sys
from typing import Any, Iterator
from ..base import BaseSolver
lines = sys.stdin.read().splitlines()
class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]:
lines = [line.strip() for line in input.splitlines()]
coeffs = {"2": 2, "1": 1, "0": 0, "-": -1, "=": -2}
def snafu2number(number: str) -> int:
value = 0
for c in number:
@ -15,7 +16,6 @@ def snafu2number(number: str) -> int:
value += coeffs[c]
return value
def number2snafu(number: int) -> str:
values = ["0", "1", "2", "=", "-"]
res = ""
@ -25,6 +25,4 @@ def number2snafu(number: int) -> str:
number = number // 5 + int(mod >= 3)
return "".join(reversed(res))
answer_1 = number2snafu(sum(map(snafu2number, lines)))
print(f"answer 1 is {answer_1}")
yield number2snafu(sum(map(snafu2number, lines)))

View File

@ -179,7 +179,7 @@ def main():
start = datetime.now()
last = start
it = solver.solve(data.strip())
it = solver.solve(data.rstrip())
if it is None:
solver.logger.error(f"no implementation for {year} day {day}")