Refactor code for API #3
@ -1,7 +1,12 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
blocks = sys.stdin.read().split("\n\n")
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
blocks = input.split("\n\n")
|
||||
values = sorted(sum(map(int, block.split())) for block in blocks)
|
||||
|
||||
print(f"answer 1 is {values[-1]}")
|
||||
print(f"answer 2 is {sum(values[-3:])}")
|
||||
yield values[-1]
|
||||
yield sum(values[-3:])
|
||||
|
@ -1,10 +1,13 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
cycle = 1
|
||||
x = 1
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
|
||||
cycle, x = 1, 1
|
||||
values = {cycle: x}
|
||||
|
||||
for line in lines:
|
||||
@ -23,16 +26,18 @@ for line in lines:
|
||||
values[cycle] = x
|
||||
|
||||
answer_1 = sum(c * values[c] for c in range(20, max(values.keys()) + 1, 40))
|
||||
print(f"answer 1 is {answer_1}")
|
||||
yield answer_1
|
||||
|
||||
|
||||
for i in range(6):
|
||||
for j in range(40):
|
||||
v = values[1 + i * 40 + j]
|
||||
|
||||
if j >= v - 1 and j <= v + 1:
|
||||
print("#", end="")
|
||||
else:
|
||||
print(".", end="")
|
||||
|
||||
print()
|
||||
yield (
|
||||
"\n"
|
||||
+ "\n".join(
|
||||
"".join(
|
||||
"#"
|
||||
if j >= (v := values[1 + i * 40 + j]) - 1 and j <= v + 1
|
||||
else "."
|
||||
for j in range(40)
|
||||
)
|
||||
for i in range(6)
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
|
@ -1,7 +1,8 @@
|
||||
import copy
|
||||
import sys
|
||||
from functools import reduce
|
||||
from typing import Callable, Final, Mapping, Sequence
|
||||
from typing import Any, Callable, Final, Iterator, Mapping, Sequence
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Monkey:
|
||||
@ -119,13 +120,14 @@ def monkey_business(inspects: dict[Monkey, int]) -> int:
|
||||
return sorted_levels[-2] * sorted_levels[-1]
|
||||
|
||||
|
||||
monkeys = [parse_monkey(block.splitlines()) for block in sys.stdin.read().split("\n\n")]
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
monkeys = [parse_monkey(block.splitlines()) for block in input.split("\n\n")]
|
||||
|
||||
# case 1: we simply divide the worry by 3 after applying the monkey worry operation
|
||||
answer_1 = monkey_business(
|
||||
yield monkey_business(
|
||||
run(copy.deepcopy(monkeys), 20, me_worry_fn=lambda w: w // 3)
|
||||
)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
# case 2: to keep reasonable level values, we can use a modulo operation, we need to
|
||||
# use the product of all "divisible by" test so that the test remains valid
|
||||
@ -136,7 +138,10 @@ print(f"answer 1 is {answer_1}")
|
||||
# we use the product of all test value
|
||||
#
|
||||
total_test_value = reduce(lambda w, m: w * m.test_value, monkeys, 1)
|
||||
answer_2 = monkey_business(
|
||||
run(copy.deepcopy(monkeys), 10_000, me_worry_fn=lambda w: w % total_test_value)
|
||||
yield monkey_business(
|
||||
run(
|
||||
copy.deepcopy(monkeys),
|
||||
10_000,
|
||||
me_worry_fn=lambda w: w % total_test_value,
|
||||
)
|
||||
)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
|
@ -1,6 +1,7 @@
|
||||
import heapq
|
||||
import sys
|
||||
from typing import Callable, Iterator, TypeVar
|
||||
from typing import Any, Callable, Iterator, TypeVar
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
Node = TypeVar("Node")
|
||||
|
||||
@ -68,30 +69,6 @@ def make_path(parents: dict[Node, Node], start: Node, end: Node) -> list[Node] |
|
||||
return list(reversed(path))
|
||||
|
||||
|
||||
def print_path(path: list[tuple[int, int]], n_rows: int, n_cols: int) -> None:
|
||||
end = path[-1]
|
||||
|
||||
graph = [["." for _c in range(n_cols)] for _r in range(n_rows)]
|
||||
graph[end[0]][end[1]] = "E"
|
||||
|
||||
for i in range(0, len(path) - 1):
|
||||
cr, cc = path[i]
|
||||
nr, nc = path[i + 1]
|
||||
|
||||
if cr == nr and nc == cc - 1:
|
||||
graph[cr][cc] = "<"
|
||||
elif cr == nr and nc == cc + 1:
|
||||
graph[cr][cc] = ">"
|
||||
elif cr == nr - 1 and nc == cc:
|
||||
graph[cr][cc] = "v"
|
||||
elif cr == nr + 1 and nc == cc:
|
||||
graph[cr][cc] = "^"
|
||||
else:
|
||||
assert False, "{} -> {} infeasible".format(path[i], path[i + 1])
|
||||
|
||||
print("\n".join("".join(row) for row in graph))
|
||||
|
||||
|
||||
def neighbors(
|
||||
grid: list[list[int]], node: tuple[int, int], up: bool
|
||||
) -> Iterator[tuple[int, int]]:
|
||||
@ -118,7 +95,34 @@ def neighbors(
|
||||
|
||||
# === main code ===
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def print_path(self, path: list[tuple[int, int]], n_rows: int, n_cols: int) -> None:
|
||||
end = path[-1]
|
||||
|
||||
graph = [["." for _c in range(n_cols)] for _r in range(n_rows)]
|
||||
graph[end[0]][end[1]] = "E"
|
||||
|
||||
for i in range(0, len(path) - 1):
|
||||
cr, cc = path[i]
|
||||
nr, nc = path[i + 1]
|
||||
|
||||
if cr == nr and nc == cc - 1:
|
||||
graph[cr][cc] = "<"
|
||||
elif cr == nr and nc == cc + 1:
|
||||
graph[cr][cc] = ">"
|
||||
elif cr == nr - 1 and nc == cc:
|
||||
graph[cr][cc] = "v"
|
||||
elif cr == nr + 1 and nc == cc:
|
||||
graph[cr][cc] = "^"
|
||||
else:
|
||||
assert False, "{} -> {} infeasible".format(path[i], path[i + 1])
|
||||
|
||||
for row in graph:
|
||||
self.logger.info("".join(row))
|
||||
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
grid = [[ord(cell) - ord("a") for cell in line] for line in lines]
|
||||
|
||||
@ -145,19 +149,20 @@ assert end is not None
|
||||
grid[start[0]][start[1]] = 0
|
||||
grid[end[0]][end[1]] = ord("z") - ord("a")
|
||||
|
||||
|
||||
lengths_1, parents_1 = dijkstra(
|
||||
start=start, neighbors=lambda n: neighbors(grid, n, True), cost=lambda lhs, rhs: 1
|
||||
start=start,
|
||||
neighbors=lambda n: neighbors(grid, n, True),
|
||||
cost=lambda lhs, rhs: 1,
|
||||
)
|
||||
path_1 = make_path(parents_1, start, end)
|
||||
assert path_1 is not None
|
||||
|
||||
print_path(path_1, n_rows=len(grid), n_cols=len(grid[0]))
|
||||
self.print_path(path_1, n_rows=len(grid), n_cols=len(grid[0]))
|
||||
yield lengths_1[end] - 1
|
||||
|
||||
print(f"answer 1 is {lengths_1[end] - 1}")
|
||||
|
||||
lengths_2, parents_2 = dijkstra(
|
||||
start=end, neighbors=lambda n: neighbors(grid, n, False), cost=lambda lhs, rhs: 1
|
||||
lengths_2, _ = dijkstra(
|
||||
start=end,
|
||||
neighbors=lambda n: neighbors(grid, n, False),
|
||||
cost=lambda lhs, rhs: 1,
|
||||
)
|
||||
answer_2 = min(lengths_2.get(start, float("inf")) for start in start_s)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield min(lengths_2.get(start, float("inf")) for start in start_s)
|
||||
|
@ -1,11 +1,8 @@
|
||||
import json
|
||||
import sys
|
||||
from functools import cmp_to_key
|
||||
from typing import TypeAlias, cast
|
||||
from typing import Any, Iterator, TypeAlias, cast
|
||||
|
||||
blocks = sys.stdin.read().strip().split("\n\n")
|
||||
|
||||
pairs = [tuple(json.loads(p) for p in block.split("\n")) for block in blocks]
|
||||
from ..base import BaseSolver
|
||||
|
||||
Packet: TypeAlias = list[int | list["Packet"]]
|
||||
|
||||
@ -28,8 +25,12 @@ def compare(lhs: Packet, rhs: Packet) -> int:
|
||||
return len(rhs) - len(lhs)
|
||||
|
||||
|
||||
answer_1 = sum(i + 1 for i, (lhs, rhs) in enumerate(pairs) if compare(lhs, rhs) > 0)
|
||||
print(f"answer_1 is {answer_1}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
blocks = input.split("\n\n")
|
||||
pairs = [tuple(json.loads(p) for p in block.split("\n")) for block in blocks]
|
||||
|
||||
yield sum(i + 1 for i, (lhs, rhs) in enumerate(pairs) if compare(lhs, rhs) > 0)
|
||||
|
||||
dividers = [[[2]], [[6]]]
|
||||
|
||||
@ -38,4 +39,4 @@ packets.extend(dividers)
|
||||
packets = list(reversed(sorted(packets, key=cmp_to_key(compare))))
|
||||
|
||||
d_index = [packets.index(d) + 1 for d in dividers]
|
||||
print(f"answer 2 is {d_index[0] * d_index[1]}")
|
||||
yield d_index[0] * d_index[1]
|
||||
|
@ -1,6 +1,7 @@
|
||||
import sys
|
||||
from enum import Enum, auto
|
||||
from typing import Callable, cast
|
||||
from typing import Any, Callable, Iterator, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Cell(Enum):
|
||||
@ -12,26 +13,6 @@ class Cell(Enum):
|
||||
return {Cell.AIR: ".", Cell.ROCK: "#", Cell.SAND: "O"}[self]
|
||||
|
||||
|
||||
def print_blocks(blocks: dict[tuple[int, int], Cell]):
|
||||
"""
|
||||
Print the given set of blocks on a grid.
|
||||
|
||||
Args:
|
||||
blocks: Set of blocks to print.
|
||||
"""
|
||||
x_min, y_min, x_max, y_max = (
|
||||
min(x for x, _ in blocks),
|
||||
0,
|
||||
max(x for x, _ in blocks),
|
||||
max(y for _, y in blocks),
|
||||
)
|
||||
|
||||
for y in range(y_min, y_max + 1):
|
||||
print(
|
||||
"".join(str(blocks.get((x, y), Cell.AIR)) for x in range(x_min, x_max + 1))
|
||||
)
|
||||
|
||||
|
||||
def flow(
|
||||
blocks: dict[tuple[int, int], Cell],
|
||||
stop_fn: Callable[[int, int], bool],
|
||||
@ -84,19 +65,44 @@ def flow(
|
||||
|
||||
# === inputs ===
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def print_blocks(self, blocks: dict[tuple[int, int], Cell]):
|
||||
"""
|
||||
Print the given set of blocks on a grid.
|
||||
|
||||
Args:
|
||||
blocks: Set of blocks to print.
|
||||
"""
|
||||
x_min, y_min, x_max, y_max = (
|
||||
min(x for x, _ in blocks),
|
||||
0,
|
||||
max(x for x, _ in blocks),
|
||||
max(y for _, y in blocks),
|
||||
)
|
||||
|
||||
for y in range(y_min, y_max + 1):
|
||||
self.logger.info(
|
||||
"".join(
|
||||
str(blocks.get((x, y), Cell.AIR)) for x in range(x_min, x_max + 1)
|
||||
)
|
||||
)
|
||||
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
|
||||
paths: list[list[tuple[int, int]]] = []
|
||||
for line in lines:
|
||||
parts = line.split(" -> ")
|
||||
paths.append(
|
||||
[
|
||||
cast(tuple[int, int], tuple(int(c.strip()) for c in part.split(",")))
|
||||
cast(
|
||||
tuple[int, int], tuple(int(c.strip()) for c in part.split(","))
|
||||
)
|
||||
for part in parts
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
blocks: dict[tuple[int, int], Cell] = {}
|
||||
for path in paths:
|
||||
for start, end in zip(path[:-1], path[1:]):
|
||||
@ -109,24 +115,17 @@ for path in paths:
|
||||
for y in range(y_start, y_end):
|
||||
blocks[x, y] = Cell.ROCK
|
||||
|
||||
print_blocks(blocks)
|
||||
print()
|
||||
self.print_blocks(blocks)
|
||||
|
||||
x_min, y_min, x_max, y_max = (
|
||||
min(x for x, _ in blocks),
|
||||
0,
|
||||
max(x for x, _ in blocks),
|
||||
max(y for _, y in blocks),
|
||||
)
|
||||
y_max = max(y for _, y in blocks)
|
||||
|
||||
# === part 1 ===
|
||||
|
||||
blocks_1 = flow(
|
||||
blocks.copy(), stop_fn=lambda x, y: y > y_max, fill_fn=lambda x, y: Cell.AIR
|
||||
)
|
||||
print_blocks(blocks_1)
|
||||
print(f"answer 1 is {sum(v == Cell.SAND for v in blocks_1.values())}")
|
||||
print()
|
||||
self.print_blocks(blocks_1)
|
||||
yield sum(v == Cell.SAND for v in blocks_1.values())
|
||||
|
||||
# === part 2 ===
|
||||
|
||||
@ -136,5 +135,5 @@ blocks_2 = flow(
|
||||
fill_fn=lambda x, y: Cell.AIR if y < y_max + 2 else Cell.ROCK,
|
||||
)
|
||||
blocks_2[500, 0] = Cell.SAND
|
||||
print_blocks(blocks_2)
|
||||
print(f"answer 2 is {sum(v == Cell.SAND for v in blocks_2.values())}")
|
||||
self.print_blocks(blocks_2)
|
||||
yield sum(v == Cell.SAND for v in blocks_2.values())
|
||||
|
@ -1,4 +1,4 @@
|
||||
import sys
|
||||
import itertools as it
|
||||
from typing import Any, Iterator
|
||||
|
||||
import numpy as np
|
||||
@ -21,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)
|
||||
|
||||
@ -62,8 +60,9 @@ class Solver(BaseSolver):
|
||||
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
||||
d = abs(sx - bx) + abs(sy - by)
|
||||
m.add_constraint(
|
||||
m.abs(x - sx) + m.abs(y - sy) >= d + 1, ctname=f"ct_{sx}_{sy}"
|
||||
) # type: ignore
|
||||
m.abs(x - sx) + m.abs(y - sy) >= d + 1, # type: ignore
|
||||
ctname=f"ct_{sx}_{sy}",
|
||||
)
|
||||
|
||||
m.set_objective("min", x + y)
|
||||
|
||||
@ -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
|
||||
|
@ -3,12 +3,13 @@ from __future__ import annotations
|
||||
import heapq
|
||||
import itertools
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import FrozenSet, NamedTuple
|
||||
from typing import Any, FrozenSet, Iterator, NamedTuple
|
||||
|
||||
from tqdm import tqdm
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Pipe(NamedTuple):
|
||||
name: str
|
||||
@ -36,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):
|
||||
@ -122,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:
|
||||
@ -150,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)
|
||||
|
@ -1,12 +1,16 @@
|
||||
import sys
|
||||
from typing import 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])):
|
||||
@ -17,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)
|
||||
|
||||
|
||||
@ -45,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
|
||||
|
||||
@ -95,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
|
||||
@ -116,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
|
||||
|
@ -1,11 +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()
|
||||
]
|
||||
)
|
||||
|
||||
@ -14,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)]
|
||||
@ -37,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]:
|
||||
@ -47,4 +54,5 @@ while queue:
|
||||
n_faces += 1
|
||||
else:
|
||||
queue.append((nx, ny, nz))
|
||||
print(f"answer 2 is {n_faces}")
|
||||
|
||||
yield n_faces
|
||||
|
@ -1,10 +1,11 @@
|
||||
import sys
|
||||
from typing import Any, Literal
|
||||
from typing import Any, Iterator, Literal
|
||||
|
||||
import numpy as np
|
||||
import parse # pyright: ignore[reportMissingTypeStubs]
|
||||
from numpy.typing import NDArray
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
Reagent = Literal["ore", "clay", "obsidian", "geode"]
|
||||
REAGENTS: tuple[Reagent, ...] = (
|
||||
"ore",
|
||||
@ -62,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.,
|
||||
@ -173,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))
|
||||
|
@ -1,4 +1,6 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def score_1(ux: int, vx: int) -> int:
|
||||
@ -33,7 +35,9 @@ def score_2(ux: int, vx: int) -> int:
|
||||
return (ux + vx - 1) % 3 + 1 + vx * 3
|
||||
|
||||
|
||||
lines = sys.stdin.readlines()
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = input.splitlines()
|
||||
|
||||
# the solution relies on replacing rock / paper / scissor by values 0 / 1 / 2 and using
|
||||
# modulo-3 arithmetic
|
||||
@ -47,7 +51,7 @@ lines = sys.stdin.readlines()
|
||||
values = [(ord(row[0]) - ord("A"), ord(row[2]) - ord("X")) for row in lines]
|
||||
|
||||
# part 1 - 13526
|
||||
print(f"answer 1 is {sum(score_1(*v) for v in values)}")
|
||||
yield sum(score_1(*v) for v in values)
|
||||
|
||||
# part 2 - 14204
|
||||
print(f"answer 2 is {sum(score_2(*v) for v in values)}")
|
||||
yield sum(score_2(*v) for v in values)
|
||||
|
@ -1,6 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Number:
|
||||
@ -65,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)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import operator
|
||||
import sys
|
||||
from typing import Callable
|
||||
from typing import Any, Callable, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def compute(monkeys: dict[str, int | tuple[str, str, str]], monkey: str) -> int:
|
||||
@ -77,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]] = {}
|
||||
|
||||
@ -96,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")
|
||||
|
@ -1,16 +1,19 @@
|
||||
import re
|
||||
import sys
|
||||
from typing import Callable
|
||||
from typing import Any, Callable, Iterator
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
VOID, EMPTY, WALL = 0, 1, 2
|
||||
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()
|
||||
@ -23,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])
|
||||
@ -103,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
|
||||
@ -116,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"
|
||||
@ -132,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
|
||||
@ -148,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
|
||||
@ -167,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)
|
||||
@ -175,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:
|
||||
@ -184,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)
|
||||
@ -192,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:
|
||||
@ -201,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 = {
|
||||
@ -213,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]
|
||||
|
@ -1,6 +1,8 @@
|
||||
import itertools
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
Directions = list[
|
||||
tuple[
|
||||
@ -18,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)
|
||||
|
||||
|
||||
@ -69,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 == "#"
|
||||
}
|
||||
@ -79,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 ===
|
||||
|
||||
@ -100,4 +105,4 @@ while True:
|
||||
if backup == p2:
|
||||
break
|
||||
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield answer_2
|
||||
|
@ -1,9 +1,14 @@
|
||||
import heapq
|
||||
import math
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
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 = [line.strip() for line in input.splitlines()]
|
||||
|
||||
winds = {
|
||||
(i - 1, j - 1, lines[i][j])
|
||||
@ -15,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)
|
||||
]
|
||||
@ -24,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: {})
|
||||
|
||||
@ -54,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:
|
||||
@ -75,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,
|
||||
@ -91,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
|
||||
|
@ -1,10 +1,14 @@
|
||||
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 = [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:
|
||||
@ -12,7 +16,6 @@ def snafu2number(number: str) -> int:
|
||||
value += coeffs[c]
|
||||
return value
|
||||
|
||||
|
||||
def number2snafu(number: int) -> str:
|
||||
values = ["0", "1", "2", "=", "-"]
|
||||
res = ""
|
||||
@ -22,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)))
|
||||
|
@ -1,23 +1,28 @@
|
||||
import string
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = [line.strip() for line in sys.stdin.readlines()]
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
|
||||
# extract content of each part
|
||||
parts = [(set(line[: len(line) // 2]), set(line[len(line) // 2 :])) for line in lines]
|
||||
parts = [
|
||||
(set(line[: len(line) // 2]), set(line[len(line) // 2 :])) for line in lines
|
||||
]
|
||||
|
||||
# priorities
|
||||
priorities = {c: i + 1 for i, c in enumerate(string.ascii_letters)}
|
||||
|
||||
# part 1
|
||||
part1 = sum(priorities[c] for p1, p2 in parts for c in p1.intersection(p2))
|
||||
print(f"answer 1 is {part1}")
|
||||
yield sum(priorities[c] for p1, p2 in parts for c in p1.intersection(p2))
|
||||
|
||||
# part 2
|
||||
n_per_group = 3
|
||||
part2 = sum(
|
||||
yield sum(
|
||||
priorities[c]
|
||||
for i in range(0, len(lines), n_per_group)
|
||||
for c in set(lines[i]).intersection(*lines[i + 1 : i + n_per_group])
|
||||
)
|
||||
print(f"answer 2 is {part2}")
|
||||
|
@ -1,6 +1,6 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
lines = [line.strip() for line in sys.stdin.readlines()]
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def make_range(value: str) -> set[int]:
|
||||
@ -8,10 +8,13 @@ def make_range(value: str) -> set[int]:
|
||||
return set(range(int(parts[0]), int(parts[1]) + 1))
|
||||
|
||||
|
||||
sections = [tuple(make_range(part) for part in line.split(",")) for line in lines]
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
|
||||
answer_1 = sum(s1.issubset(s2) or s2.issubset(s1) for s1, s2 in sections)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
sections = [
|
||||
tuple(make_range(part) for part in line.split(",")) for line in lines
|
||||
]
|
||||
|
||||
answer_2 = sum(bool(s1.intersection(s2)) for s1, s2 in sections)
|
||||
print(f"answer 1 is {answer_2}")
|
||||
yield sum(s1.issubset(s2) or s2.issubset(s1) for s1, s2 in sections)
|
||||
yield sum(bool(s1.intersection(s2)) for s1, s2 in sections)
|
||||
|
@ -1,7 +1,12 @@
|
||||
import copy
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
blocks_s, moves_s = (part.splitlines() for part in sys.stdin.read().split("\n\n"))
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
blocks_s, moves_s = (part.splitlines() for part in input.split("\n\n"))
|
||||
|
||||
blocks: dict[str, list[str]] = {stack: [] for stack in blocks_s[-1].split()}
|
||||
|
||||
@ -34,8 +39,5 @@ for move in moves_s:
|
||||
blocks_2[to_].extend(blocks_2[from_][-count:])
|
||||
del blocks_2[from_][-count:]
|
||||
|
||||
answer_1 = "".join(s[-1] for s in blocks_1.values())
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
answer_2 = "".join(s[-1] for s in blocks_2.values())
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield "".join(s[-1] for s in blocks_1.values())
|
||||
yield "".join(s[-1] for s in blocks_2.values())
|
||||
|
@ -1,4 +1,6 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def index_of_first_n_differents(data: str, n: int) -> int:
|
||||
@ -8,8 +10,7 @@ def index_of_first_n_differents(data: str, n: int) -> int:
|
||||
return -1
|
||||
|
||||
|
||||
data = sys.stdin.read().strip()
|
||||
|
||||
|
||||
print(f"answer 1 is {index_of_first_n_differents(data, 4)}")
|
||||
print(f"answer 2 is {index_of_first_n_differents(data, 14)}")
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
yield index_of_first_n_differents(input, 4)
|
||||
yield index_of_first_n_differents(input, 14)
|
||||
|
@ -1,7 +1,12 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
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 = [line.strip() for line in input.splitlines()]
|
||||
|
||||
# we are going to use Path to create path and go up/down in the file tree since it
|
||||
# implements everything we need
|
||||
@ -53,7 +58,6 @@ for line in lines[1:]:
|
||||
trees[cur_path].append(path)
|
||||
sizes[path] = size
|
||||
|
||||
|
||||
def compute_size(path: Path) -> int:
|
||||
size = sizes[path]
|
||||
|
||||
@ -62,12 +66,10 @@ def compute_size(path: Path) -> int:
|
||||
|
||||
return sum(compute_size(sub) for sub in trees[path])
|
||||
|
||||
|
||||
acc_sizes = {path: compute_size(path) for path in trees}
|
||||
|
||||
# part 1
|
||||
answer_1 = sum(size for size in acc_sizes.values() if size <= 100_000)
|
||||
print(f"answer 1 is {answer_1}")
|
||||
yield sum(size for size in acc_sizes.values() if size <= 100_000)
|
||||
|
||||
# part 2
|
||||
total_space = 70_000_000
|
||||
@ -76,5 +78,4 @@ free_space = total_space - acc_sizes[base_path]
|
||||
|
||||
to_free_space = update_space - free_space
|
||||
|
||||
answer_2 = min(size for size in acc_sizes.values() if size >= to_free_space)
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield min(size for size in acc_sizes.values() if size >= to_free_space)
|
||||
|
@ -1,9 +1,14 @@
|
||||
import sys
|
||||
from typing import Any, Iterator
|
||||
|
||||
import numpy as np
|
||||
from numpy.typing import NDArray
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
|
||||
trees = np.array([[int(x) for x in row] for row in lines])
|
||||
|
||||
@ -22,9 +27,7 @@ highest_trees[1:-1, 1:-1] = [
|
||||
for i in range(1, trees.shape[0] - 1)
|
||||
]
|
||||
|
||||
answer_1 = (highest_trees.min(axis=2) < trees).sum()
|
||||
print(f"answer 1 is {answer_1}")
|
||||
|
||||
yield (highest_trees.min(axis=2) < trees).sum()
|
||||
|
||||
def viewing_distance(row_of_trees: NDArray[np.int_], value: int) -> int:
|
||||
w = np.where(row_of_trees >= value)[0]
|
||||
@ -34,7 +37,6 @@ def viewing_distance(row_of_trees: NDArray[np.int_], value: int) -> int:
|
||||
|
||||
return w[0] + 1
|
||||
|
||||
|
||||
# answer 2
|
||||
v_distances = np.zeros(trees.shape + (4,), dtype=int)
|
||||
v_distances[1:-1, 1:-1, :] = [
|
||||
@ -49,5 +51,4 @@ v_distances[1:-1, 1:-1, :] = [
|
||||
]
|
||||
for i in range(1, trees.shape[0] - 1)
|
||||
]
|
||||
answer_2 = np.prod(v_distances, axis=2).max()
|
||||
print(f"answer 2 is {answer_2}")
|
||||
yield np.prod(v_distances, axis=2).max()
|
||||
|
@ -1,7 +1,10 @@
|
||||
import sys
|
||||
import itertools as it
|
||||
from typing import Any, Iterator
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
|
||||
def move(head: tuple[int, int], command: str) -> tuple[int, int]:
|
||||
h_col, h_row = head
|
||||
@ -43,17 +46,14 @@ def run(commands: list[str], n_blocks: int) -> list[tuple[int, int]]:
|
||||
return visited
|
||||
|
||||
|
||||
lines = sys.stdin.read().splitlines()
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
lines = [line.strip() for line in input.splitlines()]
|
||||
|
||||
# flatten the commands
|
||||
commands: list[str] = []
|
||||
for line in lines:
|
||||
d, c = line.split()
|
||||
commands.extend(d * int(c))
|
||||
commands = list(
|
||||
it.chain(*(p[0] * int(p[1]) for line in lines if (p := line.split())))
|
||||
)
|
||||
|
||||
|
||||
visited_1 = run(commands, n_blocks=2)
|
||||
print(f"answer 1 is {len(set(visited_1))}")
|
||||
|
||||
visited_2 = run(commands, n_blocks=10)
|
||||
print(f"answer 2 is {len(set(visited_2))}")
|
||||
yield len(set(run(commands, n_blocks=2)))
|
||||
yield len(set(run(commands, n_blocks=10)))
|
||||
|
@ -1,3 +1,5 @@
|
||||
# pyright: reportUnknownMemberType=false
|
||||
|
||||
from typing import Any, Iterator
|
||||
|
||||
import networkx as nx
|
||||
|
@ -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}")
|
||||
|
Loading…
Reference in New Issue
Block a user