Compare commits
1 Commits
master
...
2022/day16
Author | SHA1 | Date | |
---|---|---|---|
|
2fb65387f7 |
12
.drone.yml
12
.drone.yml
@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: default
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: tests
|
|
||||||
image: python:3.10-slim
|
|
||||||
commands:
|
|
||||||
- pip install poetry
|
|
||||||
- poetry install
|
|
||||||
- poetry run poe lint
|
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,6 +1 @@
|
|||||||
# python / VS Code
|
|
||||||
venv
|
venv
|
||||||
__pycache__
|
|
||||||
.ruff_cache
|
|
||||||
.vscode
|
|
||||||
build
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
@ -31,6 +34,7 @@ counts_1 = np.zeros((y_max + 1, x_max + 1), dtype=int)
|
|||||||
counts_2 = counts_1.copy()
|
counts_2 = counts_1.copy()
|
||||||
|
|
||||||
for (x1, y1), (x2, y2) in sections:
|
for (x1, y1), (x2, y2) in sections:
|
||||||
|
|
||||||
x_rng = range(x1, x2 + 1, 1) if x2 >= x1 else range(x1, x2 - 1, -1)
|
x_rng = range(x1, x2 + 1, 1) if x2 >= x1 else range(x1, x2 - 1, -1)
|
||||||
y_rng = range(y1, y2 + 1, 1) if y2 >= y1 else range(y1, y2 - 1, -1)
|
y_rng = range(y1, y2 + 1, 1) if y2 >= y1 else range(y1, y2 - 1, -1)
|
||||||
|
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
blocks = sys.stdin.read().split("\n\n")
|
blocks = sys.stdin.read().split("\n\n")
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import sys
|
import sys
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
@ -5,6 +7,7 @@ from typing import Callable, Final, Mapping, Sequence
|
|||||||
|
|
||||||
|
|
||||||
class Monkey:
|
class Monkey:
|
||||||
|
|
||||||
id: Final[int]
|
id: Final[int]
|
||||||
items: Final[Sequence[int]]
|
items: Final[Sequence[int]]
|
||||||
worry_fn: Final[Callable[[int], int]]
|
worry_fn: Final[Callable[[int], int]]
|
||||||
@ -94,7 +97,8 @@ def run(
|
|||||||
# number of inspects
|
# number of inspects
|
||||||
inspects = {monkey: 0 for monkey in monkeys}
|
inspects = {monkey: 0 for monkey in monkeys}
|
||||||
|
|
||||||
for _ in range(n_rounds):
|
for round in range(n_rounds):
|
||||||
|
|
||||||
for monkey in monkeys:
|
for monkey in monkeys:
|
||||||
for item in items[monkey]:
|
for item in items[monkey]:
|
||||||
inspects[monkey] += 1
|
inspects[monkey] += 1
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import heapq
|
import heapq
|
||||||
import sys
|
import sys
|
||||||
from typing import Callable, Iterator, TypeVar
|
from typing import Callable, Iterator, TypeVar
|
||||||
@ -42,6 +44,7 @@ def dijkstra(
|
|||||||
visited.add(current)
|
visited.add(current)
|
||||||
|
|
||||||
for neighbor in neighbors(current):
|
for neighbor in neighbors(current):
|
||||||
|
|
||||||
if neighbor in visited:
|
if neighbor in visited:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -57,6 +60,7 @@ def dijkstra(
|
|||||||
|
|
||||||
|
|
||||||
def make_path(parents: dict[Node, Node], start: Node, end: Node) -> list[Node] | None:
|
def make_path(parents: dict[Node, Node], start: Node, end: Node) -> list[Node] | None:
|
||||||
|
|
||||||
if end not in parents:
|
if end not in parents:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -105,6 +109,7 @@ def neighbors(
|
|||||||
(c_row, c_col - 1),
|
(c_row, c_col - 1),
|
||||||
(c_row, c_col + 1),
|
(c_row, c_col + 1),
|
||||||
):
|
):
|
||||||
|
|
||||||
if not (n_row >= 0 and n_row < n_rows and n_col >= 0 and n_col < n_cols):
|
if not (n_row >= 0 and n_row < n_rows and n_col >= 0 and n_col < n_cols):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -122,8 +127,8 @@ lines = sys.stdin.read().splitlines()
|
|||||||
|
|
||||||
grid = [[ord(cell) - ord("a") for cell in line] for line in lines]
|
grid = [[ord(cell) - ord("a") for cell in line] for line in lines]
|
||||||
|
|
||||||
start: tuple[int, int] | None = None
|
start: tuple[int, int]
|
||||||
end: tuple[int, int] | None = None
|
end: tuple[int, int]
|
||||||
|
|
||||||
# for part 2
|
# for part 2
|
||||||
start_s: list[tuple[int, int]] = []
|
start_s: list[tuple[int, int]] = []
|
||||||
@ -138,9 +143,6 @@ for i_row, row in enumerate(grid):
|
|||||||
elif col == 0:
|
elif col == 0:
|
||||||
start_s.append((i_row, i_col))
|
start_s.append((i_row, i_col))
|
||||||
|
|
||||||
assert start is not None
|
|
||||||
assert end is not None
|
|
||||||
|
|
||||||
# fix values
|
# fix values
|
||||||
grid[start[0]][start[1]] = 0
|
grid[start[0]][start[1]] = 0
|
||||||
grid[end[0]][end[1]] = ord("z") - ord("a")
|
grid[end[0]][end[1]] = ord("z") - ord("a")
|
@ -1,27 +1,27 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
from functools import cmp_to_key
|
from functools import cmp_to_key
|
||||||
from typing import TypeAlias, cast
|
|
||||||
|
|
||||||
blocks = sys.stdin.read().strip().split("\n\n")
|
blocks = sys.stdin.read().strip().split("\n\n")
|
||||||
|
|
||||||
pairs = [tuple(json.loads(p) for p in block.split("\n")) for block in blocks]
|
pairs = [tuple(json.loads(p) for p in block.split("\n")) for block in blocks]
|
||||||
|
|
||||||
Packet: TypeAlias = list[int | list["Packet"]]
|
|
||||||
|
|
||||||
|
def compare(lhs: list[int | list], rhs: list[int | list]) -> int:
|
||||||
|
|
||||||
def compare(lhs: Packet, rhs: Packet) -> int:
|
|
||||||
for lhs_a, rhs_a in zip(lhs, rhs):
|
for lhs_a, rhs_a in zip(lhs, rhs):
|
||||||
if isinstance(lhs_a, int) and isinstance(rhs_a, int):
|
if isinstance(lhs_a, int) and isinstance(rhs_a, int):
|
||||||
if lhs_a != rhs_a:
|
if lhs_a != rhs_a:
|
||||||
return rhs_a - lhs_a
|
return rhs_a - lhs_a
|
||||||
else:
|
else:
|
||||||
if not isinstance(lhs_a, list):
|
if not isinstance(lhs_a, list):
|
||||||
lhs_a = [lhs_a] # type: ignore
|
lhs_a = [lhs_a]
|
||||||
elif not isinstance(rhs_a, list):
|
elif not isinstance(rhs_a, list):
|
||||||
rhs_a = [rhs_a] # type: ignore
|
rhs_a = [rhs_a]
|
||||||
assert isinstance(rhs_a, list) and isinstance(lhs_a, list)
|
assert isinstance(rhs_a, list) and isinstance(lhs_a, list)
|
||||||
r = compare(cast(Packet, lhs_a), cast(Packet, rhs_a))
|
r = compare(lhs_a, rhs_a)
|
||||||
if r != 0:
|
if r != 0:
|
||||||
return r
|
return r
|
||||||
|
|
@ -1,4 +1,7 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from typing import Callable, cast
|
from typing import Callable, cast
|
||||||
|
|
||||||
@ -20,10 +23,10 @@ def print_blocks(blocks: dict[tuple[int, int], Cell]):
|
|||||||
blocks: Set of blocks to print.
|
blocks: Set of blocks to print.
|
||||||
"""
|
"""
|
||||||
x_min, y_min, x_max, y_max = (
|
x_min, y_min, x_max, y_max = (
|
||||||
min(x for x, _ in blocks),
|
min(x for x, y in blocks),
|
||||||
0,
|
0,
|
||||||
max(x for x, _ in blocks),
|
max(x for x, y in blocks),
|
||||||
max(y for _, y in blocks),
|
max(y for x, y in blocks),
|
||||||
)
|
)
|
||||||
|
|
||||||
for y in range(y_min, y_max + 1):
|
for y in range(y_min, y_max + 1):
|
||||||
@ -53,12 +56,13 @@ def flow(
|
|||||||
The input blocks.
|
The input blocks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
y_max = max(y for _, y in blocks)
|
y_max = max(y for x, y in blocks)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
x, y = 500, 0
|
x, y = 500, 0
|
||||||
|
|
||||||
while y <= y_max:
|
while y <= y_max:
|
||||||
|
|
||||||
moved = False
|
moved = False
|
||||||
for cx, cy in ((x, y + 1), (x - 1, y + 1), (x + 1, y + 1)):
|
for cx, cy in ((x, y + 1), (x - 1, y + 1), (x + 1, y + 1)):
|
||||||
if (cx, cy) not in blocks and fill_fn(cx, cy) == Cell.AIR:
|
if (cx, cy) not in blocks and fill_fn(cx, cy) == Cell.AIR:
|
||||||
@ -113,10 +117,10 @@ print_blocks(blocks)
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
x_min, y_min, x_max, y_max = (
|
x_min, y_min, x_max, y_max = (
|
||||||
min(x for x, _ in blocks),
|
min(x for x, y in blocks),
|
||||||
0,
|
0,
|
||||||
max(x for x, _ in blocks),
|
max(x for x, y in blocks),
|
||||||
max(y for _, y in blocks),
|
max(y for x, y in blocks),
|
||||||
)
|
)
|
||||||
|
|
||||||
# === part 1 ===
|
# === part 1 ===
|
@ -1,22 +1,23 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import parse # type: ignore
|
import parse
|
||||||
from numpy.typing import NDArray
|
|
||||||
|
|
||||||
|
|
||||||
def part1(sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], row: int) -> int:
|
def part1(sensor_to_beacon: dict[tuple[int, int], tuple[int, int]], row: int) -> int:
|
||||||
no_beacons_row_l: list[NDArray[np.floating[Any]]] = []
|
|
||||||
|
no_beacons_row_l: list[np.ndarray] = []
|
||||||
|
|
||||||
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
||||||
d = abs(sx - bx) + abs(sy - by) # closest
|
d = abs(sx - bx) + abs(sy - by) # closest
|
||||||
|
|
||||||
no_beacons_row_l.append(sx - np.arange(0, d - abs(sy - row) + 1)) # type: ignore
|
no_beacons_row_l.append(sx - np.arange(0, d - abs(sy - row) + 1))
|
||||||
no_beacons_row_l.append(sx + np.arange(0, d - abs(sy - row) + 1)) # type: ignore
|
no_beacons_row_l.append(sx + np.arange(0, d - abs(sy - row) + 1))
|
||||||
|
|
||||||
beacons_at_row = set(bx for (bx, by) in sensor_to_beacon.values() if by == row)
|
beacons_at_row = set(bx for (bx, by) in sensor_to_beacon.values() if by == row)
|
||||||
no_beacons_row = set(np.concatenate(no_beacons_row_l)).difference(beacons_at_row) # type: ignore
|
no_beacons_row = set(np.concatenate(no_beacons_row_l)).difference(beacons_at_row)
|
||||||
|
|
||||||
return len(no_beacons_row)
|
return len(no_beacons_row)
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ def part2_intervals(
|
|||||||
its.append((max(0, sx - dx), min(sx + dx, xy_max)))
|
its.append((max(0, sx - dx), min(sx + dx, xy_max)))
|
||||||
|
|
||||||
its = sorted(its)
|
its = sorted(its)
|
||||||
_, e = its[0]
|
s, e = its[0]
|
||||||
|
|
||||||
for si, ei in its[1:]:
|
for si, ei in its[1:]:
|
||||||
if si > e + 1:
|
if si > e + 1:
|
||||||
@ -58,12 +59,11 @@ def part2_cplex(
|
|||||||
|
|
||||||
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
for (sx, sy), (bx, by) in sensor_to_beacon.items():
|
||||||
d = abs(sx - bx) + abs(sy - by)
|
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.add_constraint(m.abs(x - sx) + m.abs(y - sy) >= d + 1, ctname=f"ct_{sx}_{sy}")
|
||||||
|
|
||||||
m.set_objective("min", x + y)
|
m.set_objective("min", x + y)
|
||||||
|
|
||||||
s = m.solve()
|
s = m.solve()
|
||||||
assert s is not None
|
|
||||||
|
|
||||||
vx = int(s.get_value(x))
|
vx = int(s.get_value(x))
|
||||||
vy = int(s.get_value(y))
|
vy = int(s.get_value(y))
|
||||||
@ -75,7 +75,7 @@ lines = sys.stdin.read().splitlines()
|
|||||||
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]] = {}
|
sensor_to_beacon: dict[tuple[int, int], tuple[int, int]] = {}
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
r: dict[str, str] = parse.parse( # type: ignore
|
r = parse.parse(
|
||||||
"Sensor at x={sx}, y={sy}: closest beacon is at x={bx}, y={by}", line
|
"Sensor at x={sx}, y={sy}: closest beacon is at x={bx}, y={by}", line
|
||||||
)
|
)
|
||||||
sensor_to_beacon[int(r["sx"]), int(r["sy"])] = (int(r["bx"]), int(r["by"]))
|
sensor_to_beacon[int(r["sx"]), int(r["sy"])] = (int(r["bx"]), int(r["by"]))
|
270
2022/day16.py
Normal file
270
2022/day16.py
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import heapq
|
||||||
|
import itertools
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time as time_p
|
||||||
|
from collections import defaultdict
|
||||||
|
from typing import FrozenSet, NamedTuple
|
||||||
|
|
||||||
|
from tqdm import tqdm, trange
|
||||||
|
|
||||||
|
|
||||||
|
class Pipe(NamedTuple):
|
||||||
|
name: str
|
||||||
|
flow: int
|
||||||
|
tunnels: list[str]
|
||||||
|
|
||||||
|
def __lt__(self, other: object) -> bool:
|
||||||
|
return isinstance(other, Pipe) and other.name < self.name
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
return isinstance(other, Pipe) and other.name == self.name
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash(self.name)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
distances: dict[Pipe, int] = {}
|
||||||
|
|
||||||
|
while len(distances) < len(pipes):
|
||||||
|
distance, current = heapq.heappop(queue)
|
||||||
|
|
||||||
|
if current in visited:
|
||||||
|
continue
|
||||||
|
|
||||||
|
visited.add(current)
|
||||||
|
distances[current] = distance
|
||||||
|
|
||||||
|
for tunnel in current.tunnels:
|
||||||
|
heapq.heappush(queue, (distance + 1, pipes[tunnel]))
|
||||||
|
|
||||||
|
return distances
|
||||||
|
|
||||||
|
|
||||||
|
def update_with_better(
|
||||||
|
node_at_times: dict[FrozenSet[Pipe], int], flow: int, flowing: FrozenSet[Pipe]
|
||||||
|
) -> None:
|
||||||
|
node_at_times[flowing] = max(node_at_times[flowing], flow)
|
||||||
|
|
||||||
|
|
||||||
|
def part_1(
|
||||||
|
start_pipe: Pipe,
|
||||||
|
max_time: int,
|
||||||
|
distances: dict[tuple[Pipe, Pipe], int],
|
||||||
|
relevant_pipes: FrozenSet[Pipe],
|
||||||
|
):
|
||||||
|
|
||||||
|
node_at_times: dict[int, dict[Pipe, dict[FrozenSet[Pipe], int]]] = defaultdict(
|
||||||
|
lambda: defaultdict(lambda: defaultdict(lambda: 0))
|
||||||
|
)
|
||||||
|
node_at_times[0] = {start_pipe: {frozenset(): 0}}
|
||||||
|
|
||||||
|
for time in range(max_time):
|
||||||
|
for c_pipe, nodes in node_at_times[time].items():
|
||||||
|
for flowing, flow in nodes.items():
|
||||||
|
for target in relevant_pipes:
|
||||||
|
|
||||||
|
distance = distances[c_pipe, target] + 1
|
||||||
|
if time + distance >= max_time or target in flowing:
|
||||||
|
continue
|
||||||
|
|
||||||
|
update_with_better(
|
||||||
|
node_at_times[time + distance][target],
|
||||||
|
flow + sum(pipe.flow for pipe in flowing) * distance,
|
||||||
|
flowing | {target},
|
||||||
|
)
|
||||||
|
|
||||||
|
update_with_better(
|
||||||
|
node_at_times[max_time][c_pipe],
|
||||||
|
flow + sum(pipe.flow for pipe in flowing) * (max_time - time),
|
||||||
|
flowing,
|
||||||
|
)
|
||||||
|
|
||||||
|
return max(
|
||||||
|
flow
|
||||||
|
for nodes_of_pipe in node_at_times[max_time].values()
|
||||||
|
for flow in nodes_of_pipe.values()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def part_2(
|
||||||
|
start_pipe: Pipe,
|
||||||
|
max_time: int,
|
||||||
|
pipes: dict[str, Pipe],
|
||||||
|
relevant_pipes: FrozenSet[Pipe],
|
||||||
|
distances: dict[tuple[Pipe, Pipe], int],
|
||||||
|
):
|
||||||
|
|
||||||
|
node_at_times: dict[
|
||||||
|
int, dict[tuple[Pipe, Pipe], dict[FrozenSet[Pipe], int]]
|
||||||
|
] = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 0)))
|
||||||
|
node_at_times[0] = {(start_pipe, start_pipe): {frozenset(): 0}}
|
||||||
|
|
||||||
|
# map node + distance to
|
||||||
|
d1, d2, d3, d4 = 0, 0, 0, 0
|
||||||
|
best_flow = 0
|
||||||
|
|
||||||
|
for time in range(max_time):
|
||||||
|
print(
|
||||||
|
f"{time + 1:2d}/{max_time} - {best_flow:4d} - "
|
||||||
|
f"{sum(map(len, node_at_times[time].values())):7d} - "
|
||||||
|
f"{d1:.3f} {d2:.3f} {d3:.3f} {d4:.3f}"
|
||||||
|
)
|
||||||
|
|
||||||
|
d1, d2, d3, d4 = 0, 0, 0, 0
|
||||||
|
for (c_pipe, e_pipe), nodes in node_at_times[time].items():
|
||||||
|
for flowing, flow in nodes.items():
|
||||||
|
|
||||||
|
t1 = time_p.time()
|
||||||
|
|
||||||
|
c_best_flow = (
|
||||||
|
flow
|
||||||
|
+ sum(pipe.flow for pipe in flowing) * (max_time - time)
|
||||||
|
+ sum(
|
||||||
|
(
|
||||||
|
pipe.flow
|
||||||
|
* (
|
||||||
|
max_time
|
||||||
|
- time
|
||||||
|
- 1
|
||||||
|
- min(distances[c_pipe, pipe], distances[e_pipe, pipe])
|
||||||
|
)
|
||||||
|
for pipe in relevant_pipes
|
||||||
|
if pipe not in flowing
|
||||||
|
),
|
||||||
|
start=0,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
d1 += time_p.time() - t1
|
||||||
|
|
||||||
|
if c_best_flow < best_flow:
|
||||||
|
continue
|
||||||
|
|
||||||
|
best_flow = max(
|
||||||
|
best_flow,
|
||||||
|
flow + sum(pipe.flow for pipe in flowing) * (max_time - time),
|
||||||
|
)
|
||||||
|
|
||||||
|
t1 = time_p.time()
|
||||||
|
|
||||||
|
if flowing != relevant_pipes:
|
||||||
|
for c_next_s, e_next_s in itertools.product(
|
||||||
|
c_pipe.tunnels, e_pipe.tunnels
|
||||||
|
):
|
||||||
|
|
||||||
|
c_next = pipes[c_next_s]
|
||||||
|
e_next = pipes[e_next_s]
|
||||||
|
update_with_better(
|
||||||
|
node_at_times[time + 1][c_next, e_next],
|
||||||
|
flow + sum(pipe.flow for pipe in flowing),
|
||||||
|
flowing,
|
||||||
|
)
|
||||||
|
|
||||||
|
d2 += time_p.time() - t1
|
||||||
|
|
||||||
|
t1 = time_p.time()
|
||||||
|
|
||||||
|
if c_pipe in relevant_pipes and c_pipe not in flowing:
|
||||||
|
for e_next_s in e_pipe.tunnels:
|
||||||
|
|
||||||
|
e_next = pipes[e_next_s]
|
||||||
|
|
||||||
|
update_with_better(
|
||||||
|
node_at_times[time + 1][c_pipe, e_next],
|
||||||
|
flow + sum(pipe.flow for pipe in flowing),
|
||||||
|
flowing | {c_pipe},
|
||||||
|
)
|
||||||
|
|
||||||
|
if e_pipe in relevant_pipes and e_pipe not in flowing:
|
||||||
|
for c_next_s in c_pipe.tunnels:
|
||||||
|
|
||||||
|
c_next = pipes[c_next_s]
|
||||||
|
|
||||||
|
update_with_better(
|
||||||
|
node_at_times[time + 1][c_next, e_pipe],
|
||||||
|
flow + sum(pipe.flow for pipe in flowing),
|
||||||
|
flowing | {e_pipe},
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
e_pipe in relevant_pipes
|
||||||
|
and c_pipe in relevant_pipes
|
||||||
|
and e_pipe not in flowing
|
||||||
|
and c_pipe not in flowing
|
||||||
|
):
|
||||||
|
update_with_better(
|
||||||
|
node_at_times[time + 1][c_pipe, e_pipe],
|
||||||
|
flow + sum(pipe.flow for pipe in flowing),
|
||||||
|
flowing | {c_pipe, e_pipe},
|
||||||
|
)
|
||||||
|
|
||||||
|
update_with_better(
|
||||||
|
node_at_times[max_time][c_pipe, e_pipe],
|
||||||
|
flow + sum(pipe.flow for pipe in flowing) * (max_time - time),
|
||||||
|
flowing,
|
||||||
|
)
|
||||||
|
|
||||||
|
d3 += time_p.time() - t1
|
||||||
|
|
||||||
|
return max(
|
||||||
|
flow
|
||||||
|
for nodes_of_pipe in node_at_times[max_time].values()
|
||||||
|
for flow in nodes_of_pipe.values()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# === MAIN ===
|
||||||
|
|
||||||
|
|
||||||
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
||||||
|
|
||||||
|
pipes: dict[str, Pipe] = {}
|
||||||
|
for line in lines:
|
||||||
|
r = re.match(
|
||||||
|
R"Valve ([A-Z]+) has flow rate=([0-9]+); tunnels? leads? to valves? (.+)",
|
||||||
|
line,
|
||||||
|
)
|
||||||
|
assert r
|
||||||
|
|
||||||
|
g = r.groups()
|
||||||
|
|
||||||
|
pipes[g[0]] = Pipe(g[0], int(g[1]), g[2].split(", "))
|
||||||
|
|
||||||
|
# compute distances from one valve to any other
|
||||||
|
distances: dict[tuple[Pipe, Pipe], int] = {}
|
||||||
|
for pipe_1 in pipes.values():
|
||||||
|
distances.update(
|
||||||
|
{
|
||||||
|
(pipe_1, pipe_2): distance
|
||||||
|
for pipe_2, distance in breadth_first_search(pipes, pipe_1).items()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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))
|
||||||
|
|
||||||
|
# 1707, 2223
|
||||||
|
print(part_2(pipes["AA"], 26, pipes, relevant_pipes, distances))
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from typing import Sequence, TypeVar
|
from typing import Sequence, TypeVar
|
||||||
|
|
||||||
@ -47,6 +49,7 @@ def build_tower(
|
|||||||
early_stop: bool = False,
|
early_stop: bool = False,
|
||||||
init: np.ndarray = np.ones(WIDTH, dtype=bool),
|
init: np.ndarray = np.ones(WIDTH, dtype=bool),
|
||||||
) -> tuple[np.ndarray, int, int, dict[int, int]]:
|
) -> tuple[np.ndarray, int, int, dict[int, int]]:
|
||||||
|
|
||||||
tower = EMPTY_BLOCKS.copy()
|
tower = EMPTY_BLOCKS.copy()
|
||||||
tower[0, :] = init
|
tower[0, :] = init
|
||||||
|
|
||||||
@ -56,6 +59,7 @@ def build_tower(
|
|||||||
rock_count = 0
|
rock_count = 0
|
||||||
|
|
||||||
for rock_count in range(n_rocks):
|
for rock_count in range(n_rocks):
|
||||||
|
|
||||||
if early_stop:
|
if early_stop:
|
||||||
if i_rock == 0 and (i_rock, i_jet) in done_at:
|
if i_rock == 0 and (i_rock, i_jet) in done_at:
|
||||||
break
|
break
|
||||||
@ -71,6 +75,7 @@ def build_tower(
|
|||||||
tower = np.concatenate([tower, EMPTY_BLOCKS], axis=0)
|
tower = np.concatenate([tower, EMPTY_BLOCKS], axis=0)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
jet, i_jet = next_cycle(jets, i_jet)
|
jet, i_jet = next_cycle(jets, i_jet)
|
||||||
|
|
||||||
dx = 0
|
dx = 0
|
@ -1,4 +1,7 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
from typing import FrozenSet
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
@ -1,9 +1,11 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, Literal
|
from typing import Literal
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import parse # pyright: ignore[reportMissingTypeStubs]
|
import parse
|
||||||
from numpy.typing import NDArray
|
from tqdm import tqdm
|
||||||
|
|
||||||
Reagent = Literal["ore", "clay", "obsidian", "geode"]
|
Reagent = Literal["ore", "clay", "obsidian", "geode"]
|
||||||
REAGENTS: tuple[Reagent, ...] = (
|
REAGENTS: tuple[Reagent, ...] = (
|
||||||
@ -35,7 +37,7 @@ class State:
|
|||||||
self.robots = robots
|
self.robots = robots
|
||||||
self.reagents = reagents
|
self.reagents = reagents
|
||||||
|
|
||||||
def __eq__(self, other: object) -> bool:
|
def __eq__(self, other) -> bool:
|
||||||
return (
|
return (
|
||||||
isinstance(other, State)
|
isinstance(other, State)
|
||||||
and self.robots == other.robots
|
and self.robots == other.robots
|
||||||
@ -66,7 +68,7 @@ lines = sys.stdin.read().splitlines()
|
|||||||
|
|
||||||
blueprints: list[dict[Reagent, IntOfReagent]] = []
|
blueprints: list[dict[Reagent, IntOfReagent]] = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
r: list[int] = parse.parse( # type: ignore
|
r = parse.parse(
|
||||||
"Blueprint {}: "
|
"Blueprint {}: "
|
||||||
"Each ore robot costs {:d} ore. "
|
"Each ore robot costs {:d} ore. "
|
||||||
"Each clay robot costs {:d} ore. "
|
"Each clay robot costs {:d} ore. "
|
||||||
@ -86,6 +88,7 @@ for line in lines:
|
|||||||
|
|
||||||
|
|
||||||
def run(blueprint: dict[Reagent, dict[Reagent, int]], max_time: int) -> int:
|
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
|
# 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.,
|
# of type K where X is the maximum number of K required among all robots, e.g.,
|
||||||
# in the first toy blueprint, we need at most 4 ore robots, 14 clay ones and 7
|
# in the first toy blueprint, we need at most 4 ore robots, 14 clay ones and 7
|
||||||
@ -94,12 +97,12 @@ def run(blueprint: dict[Reagent, dict[Reagent, int]], max_time: int) -> int:
|
|||||||
name: max(blueprint[r].get(name, 0) for r in REAGENTS) for name in REAGENTS
|
name: max(blueprint[r].get(name, 0) for r in REAGENTS) for name in REAGENTS
|
||||||
}
|
}
|
||||||
|
|
||||||
state_after_t: dict[int, set[State]] = {0: {State()}}
|
state_after_t: dict[int, set[State]] = {0: [State()]}
|
||||||
|
|
||||||
for t in range(1, max_time + 1):
|
for t in range(1, max_time + 1):
|
||||||
|
|
||||||
# list of new states at the end of step t that we are going to prune later
|
# list of new states at the end of step t that we are going to prune later
|
||||||
states_for_t: set[State] = set()
|
states_for_t: set[State] = set()
|
||||||
robots_that_can_be_built: list[Reagent]
|
|
||||||
|
|
||||||
for state in state_after_t[t - 1]:
|
for state in state_after_t[t - 1]:
|
||||||
robots_that_can_be_built = [
|
robots_that_can_be_built = [
|
||||||
@ -133,7 +136,7 @@ def run(blueprint: dict[Reagent, dict[Reagent, int]], max_time: int) -> int:
|
|||||||
for robot in robots_that_can_be_built:
|
for robot in robots_that_can_be_built:
|
||||||
robots = state.robots.copy()
|
robots = state.robots.copy()
|
||||||
robots[robot] += 1
|
robots[robot] += 1
|
||||||
reagents: IntOfReagent = {
|
reagents = {
|
||||||
reagent: state.reagents[reagent]
|
reagent: state.reagents[reagent]
|
||||||
+ state.robots[reagent]
|
+ state.robots[reagent]
|
||||||
- blueprint[robot].get(reagent, 0)
|
- blueprint[robot].get(reagent, 0)
|
||||||
@ -152,7 +155,7 @@ def run(blueprint: dict[Reagent, dict[Reagent, int]], max_time: int) -> int:
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
to_keep: list[NDArray[np.integer[Any]]] = []
|
to_keep = []
|
||||||
while len(np_states) > 0:
|
while len(np_states) > 0:
|
||||||
first_dom = (np_states[1:] >= np_states[0]).all(axis=1).any()
|
first_dom = (np_states[1:] >= np_states[0]).all(axis=1).any()
|
||||||
|
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
@ -47,7 +49,7 @@ lines = sys.stdin.readlines()
|
|||||||
values = [(ord(row[0]) - ord("A"), ord(row[2]) - ord("X")) for row in lines]
|
values = [(ord(row[0]) - ord("A"), ord(row[2]) - ord("X")) for row in lines]
|
||||||
|
|
||||||
# part 1 - 13526
|
# part 1 - 13526
|
||||||
print(f"answer 1 is {sum(score_1(*v) for v in values)}")
|
print(f"score 1 is {sum(score_1(*v) for v in values)}")
|
||||||
|
|
||||||
# part 2 - 14204
|
# part 2 - 14204
|
||||||
print(f"answer 2 is {sum(score_2(*v) for v in values)}")
|
print(f"score 2 is {sum(score_2(*v) for v in values)}")
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@ -19,6 +21,7 @@ class Number:
|
|||||||
|
|
||||||
|
|
||||||
def decrypt(numbers: list[Number], key: int, rounds: int) -> int:
|
def decrypt(numbers: list[Number], key: int, rounds: int) -> int:
|
||||||
|
|
||||||
numbers = numbers.copy()
|
numbers = numbers.copy()
|
||||||
original = numbers.copy()
|
original = numbers.copy()
|
||||||
|
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import operator
|
import operator
|
||||||
import sys
|
import sys
|
||||||
from typing import Callable
|
from typing import Callable
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
@ -124,6 +126,7 @@ def wrap_part_2(y0: int, x0: int, r0: str) -> tuple[int, int, str]:
|
|||||||
|
|
||||||
|
|
||||||
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
|
y0 = 0
|
||||||
x0 = np.where(board[0] == EMPTY)[0][0]
|
x0 = np.where(board[0] == EMPTY)[0][0]
|
||||||
r0 = "E"
|
r0 = "E"
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
import sys
|
import sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
@ -39,7 +41,7 @@ def round(
|
|||||||
directions: Directions,
|
directions: Directions,
|
||||||
):
|
):
|
||||||
to_move: dict[tuple[int, int], list[tuple[int, int]]] = defaultdict(lambda: [])
|
to_move: dict[tuple[int, int], list[tuple[int, int]]] = defaultdict(lambda: [])
|
||||||
for y, x in positions:
|
for (y, x) in positions:
|
||||||
elves = {
|
elves = {
|
||||||
(dy, dx): (y + dy, x + dx) in positions
|
(dy, dx): (y + dy, x + dx) in positions
|
||||||
for dy, dx in itertools.product((-1, 0, 1), (-1, 0, 1))
|
for dy, dx in itertools.product((-1, 0, 1), (-1, 0, 1))
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import heapq
|
import heapq
|
||||||
import math
|
import math
|
||||||
import sys
|
import sys
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import string
|
import string
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@ -11,7 +13,7 @@ priorities = {c: i + 1 for i, c in enumerate(string.ascii_letters)}
|
|||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
part1 = sum(priorities[c] for p1, p2 in parts for c in p1.intersection(p2))
|
part1 = sum(priorities[c] for p1, p2 in parts for c in p1.intersection(p2))
|
||||||
print(f"answer 1 is {part1}")
|
print(f"score 1 is {part1}")
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
n_per_group = 3
|
n_per_group = 3
|
||||||
@ -20,4 +22,4 @@ part2 = sum(
|
|||||||
for i in range(0, len(lines), n_per_group)
|
for i in range(0, len(lines), n_per_group)
|
||||||
for c in set(lines[i]).intersection(*lines[i + 1 : i + n_per_group])
|
for c in set(lines[i]).intersection(*lines[i + 1 : i + n_per_group])
|
||||||
)
|
)
|
||||||
print(f"answer 2 is {part2}")
|
print(f"score 2 is {part2}")
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
lines = [line.strip() for line in sys.stdin.readlines()]
|
lines = [line.strip() for line in sys.stdin.readlines()]
|
||||||
@ -10,8 +12,8 @@ def make_range(value: str) -> set[int]:
|
|||||||
|
|
||||||
sections = [tuple(make_range(part) for part in line.split(",")) for line in lines]
|
sections = [tuple(make_range(part) for part in line.split(",")) for line in lines]
|
||||||
|
|
||||||
answer_1 = sum(s1.issubset(s2) or s2.issubset(s1) for s1, s2 in sections)
|
score_1 = sum(s1.issubset(s2) or s2.issubset(s1) for s1, s2 in sections)
|
||||||
print(f"answer 1 is {answer_1}")
|
print(f"score 1 is {score_1}")
|
||||||
|
|
||||||
answer_2 = sum(bool(s1.intersection(s2)) for s1, s2 in sections)
|
score_2 = sum(bool(s1.intersection(s2)) for s1, s2 in sections)
|
||||||
print(f"answer 1 is {answer_2}")
|
print(f"score 1 is {score_2}")
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
@ -1,7 +1,8 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.typing import NDArray
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ answer_1 = (highest_trees.min(axis=2) < trees).sum()
|
|||||||
print(f"answer 1 is {answer_1}")
|
print(f"answer 1 is {answer_1}")
|
||||||
|
|
||||||
|
|
||||||
def viewing_distance(row_of_trees: NDArray[np.int_], value: int) -> int:
|
def viewing_distance(row_of_trees: np.ndarray, value: int) -> int:
|
||||||
w = np.where(row_of_trees >= value)[0]
|
w = np.where(row_of_trees >= value)[0]
|
||||||
|
|
||||||
if not w.size:
|
if not w.size:
|
@ -1,9 +1,12 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
def move(head: tuple[int, int], command: str) -> tuple[int, int]:
|
def move(head: tuple[int, int], command: str) -> tuple[int, int]:
|
||||||
|
|
||||||
h_col, h_row = head
|
h_col, h_row = head
|
||||||
|
|
||||||
if command == "L":
|
if command == "L":
|
||||||
@ -19,6 +22,7 @@ def move(head: tuple[int, int], command: str) -> tuple[int, int]:
|
|||||||
|
|
||||||
|
|
||||||
def follow(head: tuple[int, int], tail: tuple[int, int]) -> tuple[int, int]:
|
def follow(head: tuple[int, int], tail: tuple[int, int]) -> tuple[int, int]:
|
||||||
|
|
||||||
h_col, h_row = head
|
h_col, h_row = head
|
||||||
t_col, t_row = tail
|
t_col, t_row = tail
|
||||||
|
|
||||||
@ -29,7 +33,8 @@ def follow(head: tuple[int, int], tail: tuple[int, int]) -> tuple[int, int]:
|
|||||||
|
|
||||||
|
|
||||||
def run(commands: list[str], n_blocks: int) -> list[tuple[int, int]]:
|
def run(commands: list[str], n_blocks: int) -> list[tuple[int, int]]:
|
||||||
blocks: list[tuple[int, int]] = [(0, 0) for _ in range(n_blocks)]
|
|
||||||
|
blocks = [(0, 0) for _ in range(n_blocks)]
|
||||||
visited = [blocks[-1]]
|
visited = [blocks[-1]]
|
||||||
|
|
||||||
for command in commands:
|
for command in commands:
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,5 +1,6 @@
|
|||||||
import math
|
import operator
|
||||||
import sys
|
import sys
|
||||||
|
from functools import reduce
|
||||||
from typing import Literal, TypeAlias, cast
|
from typing import Literal, TypeAlias, cast
|
||||||
|
|
||||||
CubeType: TypeAlias = Literal["red", "blue", "green"]
|
CubeType: TypeAlias = Literal["red", "blue", "green"]
|
||||||
@ -35,8 +36,9 @@ print(f"answer 1 is {answer_1}")
|
|||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
answer_2 = sum(
|
answer_2 = sum(
|
||||||
math.prod(
|
reduce(
|
||||||
max(cube_set.get(cube, 0) for cube_set in set_of_cubes) for cube in MAX_CUBES
|
operator.mul,
|
||||||
|
(max(cube_set.get(cube, 0) for cube_set in set_of_cubes) for cube in MAX_CUBES),
|
||||||
)
|
)
|
||||||
for set_of_cubes in games.values()
|
for set_of_cubes in games.values()
|
||||||
)
|
)
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
@ -1,4 +1,6 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user