Compare commits
No commits in common. "master" and "day22" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1 @@
|
|||||||
venv
|
venv
|
||||||
__pycache__
|
|
||||||
|
@ -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)
|
||||||
|
|
20
2022/day1.py
Normal file
20
2022/day1.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
lines = sys.stdin.readlines()
|
||||||
|
|
||||||
|
# we store the list of calories for each elf in values, and we use the last element
|
||||||
|
# of values to accumulate
|
||||||
|
values: list[int] = [0]
|
||||||
|
for line in lines:
|
||||||
|
if not line.strip():
|
||||||
|
values = values + [0]
|
||||||
|
else:
|
||||||
|
values[-1] += int(line.strip())
|
||||||
|
|
||||||
|
# part 1
|
||||||
|
print(f"answer 1 is {max(values)}")
|
||||||
|
|
||||||
|
# part 2
|
||||||
|
print(f"answer 2 is {sum(sorted(values)[-3:])}")
|
@ -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
|
||||||
|
|
@ -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,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@ -5,6 +7,7 @@ import parse
|
|||||||
|
|
||||||
|
|
||||||
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[np.ndarray] = []
|
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():
|
||||||
@ -34,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:
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import heapq
|
import heapq
|
||||||
@ -67,6 +69,7 @@ def part_1(
|
|||||||
distances: dict[tuple[Pipe, Pipe], int],
|
distances: dict[tuple[Pipe, Pipe], int],
|
||||||
relevant_pipes: FrozenSet[Pipe],
|
relevant_pipes: FrozenSet[Pipe],
|
||||||
):
|
):
|
||||||
|
|
||||||
node_at_times: dict[int, dict[Pipe, dict[FrozenSet[Pipe], int]]] = defaultdict(
|
node_at_times: dict[int, dict[Pipe, dict[FrozenSet[Pipe], int]]] = defaultdict(
|
||||||
lambda: defaultdict(lambda: defaultdict(lambda: 0))
|
lambda: defaultdict(lambda: defaultdict(lambda: 0))
|
||||||
)
|
)
|
||||||
@ -76,6 +79,7 @@ def part_1(
|
|||||||
for c_pipe, nodes in node_at_times[time].items():
|
for c_pipe, nodes in node_at_times[time].items():
|
||||||
for flowing, flow in nodes.items():
|
for flowing, flow in nodes.items():
|
||||||
for target in relevant_pipes:
|
for target in relevant_pipes:
|
||||||
|
|
||||||
distance = distances[c_pipe, target] + 1
|
distance = distances[c_pipe, target] + 1
|
||||||
if time + distance >= max_time or target in flowing:
|
if time + distance >= max_time or target in flowing:
|
||||||
continue
|
continue
|
@ -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,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from typing import FrozenSet
|
from typing import FrozenSet
|
||||||
|
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
@ -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
|
||||||
@ -97,6 +100,7 @@ def run(blueprint: dict[Reagent, dict[Reagent, int]], max_time: int) -> int:
|
|||||||
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()
|
||||||
|
|
@ -1,5 +1,20 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
lines = sys.stdin.readlines()
|
||||||
|
|
||||||
|
# the solution relies on replacing rock / paper / scissor by values 0 / 1 / 2 and using
|
||||||
|
# modulo-3 arithmetic
|
||||||
|
#
|
||||||
|
# in modulo-3 arithmetic, the winning move is 1 + the opponent move (e.g., winning move
|
||||||
|
# if opponent plays 0 is 1, or 0 if opponent plays 2 (0 = (2 + 1 % 3)))
|
||||||
|
#
|
||||||
|
|
||||||
|
# we read the lines in a Nx2 in array with value 0/1/2 instead of A/B/C or X/Y/Z for
|
||||||
|
# easier manipulation
|
||||||
|
values = [(ord(row[0]) - ord("A"), ord(row[2]) - ord("X")) for row in lines]
|
||||||
|
|
||||||
|
|
||||||
def score_1(ux: int, vx: int) -> int:
|
def score_1(ux: int, vx: int) -> int:
|
||||||
# here ux and vx are both moves: 0 = rock, 1 = paper, 2 = scissor
|
# here ux and vx are both moves: 0 = rock, 1 = paper, 2 = scissor
|
||||||
@ -33,21 +48,8 @@ def score_2(ux: int, vx: int) -> int:
|
|||||||
return (ux + vx - 1) % 3 + 1 + vx * 3
|
return (ux + vx - 1) % 3 + 1 + vx * 3
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.readlines()
|
|
||||||
|
|
||||||
# the solution relies on replacing rock / paper / scissor by values 0 / 1 / 2 and using
|
|
||||||
# modulo-3 arithmetic
|
|
||||||
#
|
|
||||||
# in modulo-3 arithmetic, the winning move is 1 + the opponent move (e.g., winning move
|
|
||||||
# if opponent plays 0 is 1, or 0 if opponent plays 2 (0 = (2 + 1 % 3)))
|
|
||||||
#
|
|
||||||
|
|
||||||
# we read the lines in a Nx2 in array with value 0/1/2 instead of A/B/C or X/Y/Z for
|
|
||||||
# easier manipulation
|
|
||||||
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
|
||||||
@ -44,7 +46,6 @@ SIZE = np.gcd(*board.shape)
|
|||||||
|
|
||||||
# TODO: deduce this from the actual cube...
|
# TODO: deduce this from the actual cube...
|
||||||
faces_wrap: dict[int, dict[str, Callable[[int, int], tuple[int, int, str]]]]
|
faces_wrap: dict[int, dict[str, Callable[[int, int], tuple[int, int, str]]]]
|
||||||
|
|
||||||
if board.shape == (12, 16): # example
|
if board.shape == (12, 16): # example
|
||||||
faces_wrap = {
|
faces_wrap = {
|
||||||
1: {
|
1: {
|
||||||
@ -124,6 +125,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"
|
3
2022/day23.py
Normal file
3
2022/day23.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import sys
|
3
2022/day24.py
Normal file
3
2022/day24.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import sys
|
3
2022/day25.py
Normal file
3
2022/day25.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import sys
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import string
|
import string
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@ -11,13 +13,13 @@ 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
|
||||||
part2 = sum(
|
part2 = sum(
|
||||||
priorities[c]
|
priorities[c]
|
||||||
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.intersection(*map(set, (lines[i : 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,5 +1,9 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
data = sys.stdin.read().strip()
|
||||||
|
|
||||||
|
|
||||||
def index_of_first_n_differents(data: str, n: int) -> int:
|
def index_of_first_n_differents(data: str, n: int) -> int:
|
||||||
for i in range(len(data)):
|
for i in range(len(data)):
|
||||||
@ -8,8 +12,5 @@ def index_of_first_n_differents(data: str, n: int) -> int:
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
|
||||||
data = sys.stdin.read().strip()
|
|
||||||
|
|
||||||
|
|
||||||
print(f"answer 1 is {index_of_first_n_differents(data, 4)}")
|
print(f"answer 1 is {index_of_first_n_differents(data, 4)}")
|
||||||
print(f"answer 2 is {index_of_first_n_differents(data, 14)}")
|
print(f"answer 2 is {index_of_first_n_differents(data, 14)}")
|
@ -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:
|
35
README.md
35
README.md
@ -1,36 +1,7 @@
|
|||||||
# Holt59 - Advent Of Code
|
# Advent Of Code
|
||||||
|
|
||||||
Installation (with [`poetry`](https://python-poetry.org/)):
|
To run any script, you need to pipe the input:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
poetry install
|
cat 2022/inputs/day2.txt | python 2022/day2.py
|
||||||
```
|
|
||||||
|
|
||||||
To run any day:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
holt59-aoc $day
|
|
||||||
```
|
|
||||||
|
|
||||||
You can use `-v` / `--verbose` for extra outputs in some case, `-t` / `--test` to run
|
|
||||||
the code on the test data (one of the test data if multiple are present) or even
|
|
||||||
`-u XXX` / `--user XXX` to run the code on a specific input after putting the input
|
|
||||||
file under `src/holt59/aoc/inputs/XXX/$year/$day`.
|
|
||||||
|
|
||||||
Full usage:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
usage: Holt59 Advent-Of-Code Runner [-h] [-v] [-t] [-u USER] [-i INPUT] [-y YEAR] day
|
|
||||||
|
|
||||||
positional arguments:
|
|
||||||
day day to run
|
|
||||||
|
|
||||||
options:
|
|
||||||
-h, --help show this help message and exit
|
|
||||||
-v, --verbose verbose mode
|
|
||||||
-t, --test test mode
|
|
||||||
-u USER, --user USER user input to use
|
|
||||||
-i INPUT, --input INPUT
|
|
||||||
input to use (override user and test)
|
|
||||||
-y YEAR, --year YEAR year to run
|
|
||||||
```
|
```
|
||||||
|
1395
poetry.lock
generated
1395
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,52 +0,0 @@
|
|||||||
[tool.poetry]
|
|
||||||
name = "holt59-advent-of-code"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = ""
|
|
||||||
authors = ["Mikael CAPELLE <capelle.mikael@gmail.com>"]
|
|
||||||
license = "MIT"
|
|
||||||
readme = "README.md"
|
|
||||||
packages = [{ include = "holt59", from = "src" }]
|
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
|
||||||
python = "^3.10"
|
|
||||||
numpy = "^1.26.2"
|
|
||||||
tqdm = "^4.66.1"
|
|
||||||
parse = "^1.20.0"
|
|
||||||
scipy = "^1.11.4"
|
|
||||||
ortools = "^9.8.3296"
|
|
||||||
sympy = "^1.12"
|
|
||||||
networkx = "^3.2.1"
|
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
|
||||||
holt59-aoc = "holt59.aoc.__main__:main"
|
|
||||||
|
|
||||||
[tool.poe.tasks]
|
|
||||||
lint-black = "black --check --diff src tests typings"
|
|
||||||
lint-isort = "isort -c src tests typings"
|
|
||||||
lint-ruff = "ruff src tests typings"
|
|
||||||
lint-flake8 = "flake8 src tests typings"
|
|
||||||
lint-pyright = "pyright src tests"
|
|
||||||
lint-all.sequence = [
|
|
||||||
"lint-black",
|
|
||||||
"lint-isort",
|
|
||||||
"lint-flake8",
|
|
||||||
"lint-ruff",
|
|
||||||
"lint-pyright",
|
|
||||||
]
|
|
||||||
lint-all.ignore_fail = "return_non_zero"
|
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
|
||||||
flake8 = "^6.1.0"
|
|
||||||
flake8-black = "^0.3.6"
|
|
||||||
black = "^23.12.0"
|
|
||||||
pyright = "^1.1.341"
|
|
||||||
mypy = "^1.7.1"
|
|
||||||
isort = "^5.13.2"
|
|
||||||
ruff = "^0.1.8"
|
|
||||||
poethepoet = "^0.24.4"
|
|
||||||
ipykernel = "^6.27.1"
|
|
||||||
networkx-stubs = "^0.0.1"
|
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["poetry-core"]
|
|
||||||
build-backend = "poetry.core.masonry.api"
|
|
11
run.ps1
11
run.ps1
@ -1,12 +1,5 @@
|
|||||||
param(
|
param([switch]$Test, $day)
|
||||||
[switch]$Test,
|
|
||||||
[PSDefaultValue()]
|
|
||||||
[Parameter(Mandatory = $false)]
|
|
||||||
$Year = 2023,
|
|
||||||
[Parameter(Mandatory = $true, Position = 0)]
|
|
||||||
$Day)
|
|
||||||
|
|
||||||
$folder = $Test ? "tests" : "inputs"
|
$folder = $Test ? "tests" : "inputs"
|
||||||
|
|
||||||
$env:AOC_VERBOSE = $VerbosePreference -eq "Continue"
|
Get-Content ".\2022\$folder\day$day.txt" | python ".\2022\day$day.py"
|
||||||
Get-Content ".\$Year\$folder\day$Day.txt" | python ".\$Year\day$Day.py"
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
|
||||||
|
|
||||||
floor = 0
|
|
||||||
floors = [(floor := floor + (1 if c == "(" else -1)) for c in line]
|
|
||||||
|
|
||||||
|
|
||||||
print(f"answer 1 is {floors[-1]}")
|
|
||||||
print(f"answer 2 is {floors.index(-1)}")
|
|
@ -1,148 +0,0 @@
|
|||||||
import itertools
|
|
||||||
import sys
|
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
|
||||||
|
|
||||||
# see http://www.se16.info/js/lands2.htm for the explanation of 'atoms' (or elements)
|
|
||||||
#
|
|
||||||
# see also https://www.youtube.com/watch?v=ea7lJkEhytA (video link from AOC) and this
|
|
||||||
# CodeGolf answer https://codegolf.stackexchange.com/a/8479/42148
|
|
||||||
|
|
||||||
# fmt: off
|
|
||||||
atoms = [
|
|
||||||
("22", (0, )), # 0
|
|
||||||
("13112221133211322112211213322112", (71, 90, 0, 19, 2, )), # 1
|
|
||||||
("312211322212221121123222112", (1, )), # 2
|
|
||||||
("111312211312113221133211322112211213322112", (31, 19, 2, )), # 3
|
|
||||||
("1321132122211322212221121123222112", (3, )), # 4
|
|
||||||
("3113112211322112211213322112", (4, )), # 5
|
|
||||||
("111312212221121123222112", (5, )), # 6
|
|
||||||
("132112211213322112", (6, )), # 7
|
|
||||||
("31121123222112", (7, )), # 8
|
|
||||||
("111213322112", (8, )), # 9
|
|
||||||
("123222112", (9, )), # 10
|
|
||||||
("3113322112", (60, 10, )), # 11
|
|
||||||
("1113222112", (11, )), # 12
|
|
||||||
("1322112", (12, )), # 13
|
|
||||||
("311311222112", (66, 13, )), # 14
|
|
||||||
("1113122112", (14, )), # 15
|
|
||||||
("132112", (15, )), # 16
|
|
||||||
("3112", (16, )), # 17
|
|
||||||
("1112", (17, )), # 18
|
|
||||||
("12", (18, )), # 19
|
|
||||||
("3113112221133112", (66, 90, 0, 19, 26, )), # 20
|
|
||||||
("11131221131112", (20, )), # 21
|
|
||||||
("13211312", (21, )), # 22
|
|
||||||
("31132", (22, )), # 23
|
|
||||||
("111311222112", (23, 13, )), # 24
|
|
||||||
("13122112", (24, )), # 25
|
|
||||||
("32112", (25, )), # 26
|
|
||||||
("11133112", (29, 26, )), # 27
|
|
||||||
("131112", (27, )), # 28
|
|
||||||
("312", (28, )), # 29
|
|
||||||
("13221133122211332", (62, 19, 88, 0, 19, 29, )), # 30
|
|
||||||
("31131122211311122113222", (66, 30, )), # 31
|
|
||||||
("11131221131211322113322112", (31, 10, )), # 32
|
|
||||||
("13211321222113222112", (32, )), # 33
|
|
||||||
("3113112211322112", (33, )), # 34
|
|
||||||
("11131221222112", (34, )), # 35
|
|
||||||
("1321122112", (35, )), # 36
|
|
||||||
("3112112", (36, )), # 37
|
|
||||||
("1112133", (37, 91, )), # 38
|
|
||||||
("12322211331222113112211", (38, 0, 19, 42, )), # 39
|
|
||||||
("1113122113322113111221131221", (67, 39, )), # 40
|
|
||||||
("13211322211312113211", (40, )), # 41
|
|
||||||
("311322113212221", (41, )), # 42
|
|
||||||
("132211331222113112211", (62, 19, 42, )), # 43
|
|
||||||
("311311222113111221131221", (66, 43, )), # 44
|
|
||||||
("111312211312113211", (44, )), # 45
|
|
||||||
("132113212221", (45, )), # 46
|
|
||||||
("3113112211", (46, )), # 47
|
|
||||||
("11131221", (47, )), # 48
|
|
||||||
("13211", (48, )), # 49
|
|
||||||
("3112221", (60, 49, )), # 50
|
|
||||||
("1322113312211", (62, 19, 50, )), # 51
|
|
||||||
("311311222113111221", (66, 51, )), # 52
|
|
||||||
("11131221131211", (52, )), # 53
|
|
||||||
("13211321", (53, )), # 54
|
|
||||||
("311311", (54, )), # 55
|
|
||||||
("11131", (55, )), # 56
|
|
||||||
("1321133112", (56, 0, 19, 26, )), # 57
|
|
||||||
("31131112", (57, )), # 58
|
|
||||||
("111312", (58, )), # 59
|
|
||||||
("132", (59, )), # 60
|
|
||||||
("311332", (60, 19, 29, )), # 61
|
|
||||||
("1113222", (61, )), # 62
|
|
||||||
("13221133112", (62, 19, 26, )), # 63
|
|
||||||
("3113112221131112", (66, 63, )), # 64
|
|
||||||
("111312211312", (64, )), # 65
|
|
||||||
("1321132", (65, )), # 66
|
|
||||||
("311311222", (66, 60, )), # 67
|
|
||||||
("11131221133112", (67, 19, 26, )), # 68
|
|
||||||
("1321131112", (68, )), # 69
|
|
||||||
("311312", (69, )), # 70
|
|
||||||
("11132", (70, )), # 71
|
|
||||||
("13112221133211322112211213322113", (71, 90, 0, 19, 73, )), # 72
|
|
||||||
("312211322212221121123222113", (72, )), # 73
|
|
||||||
("111312211312113221133211322112211213322113", (31, 19, 73, )), # 74
|
|
||||||
("1321132122211322212221121123222113", (74, )), # 75
|
|
||||||
("3113112211322112211213322113", (75, )), # 76
|
|
||||||
("111312212221121123222113", (76, )), # 77
|
|
||||||
("132112211213322113", (77, )), # 78
|
|
||||||
("31121123222113", (78, )), # 79
|
|
||||||
("111213322113", (79, )), # 80
|
|
||||||
("123222113", (80, )), # 81
|
|
||||||
("3113322113", (60, 81, )), # 82
|
|
||||||
("1113222113", (82, )), # 83
|
|
||||||
("1322113", (83, )), # 84
|
|
||||||
("311311222113", (66, 84, )), # 85
|
|
||||||
("1113122113", (85, )), # 86
|
|
||||||
("132113", (86, )), # 87
|
|
||||||
("3113", (87, )), # 88
|
|
||||||
("1113", (88, )), # 89
|
|
||||||
("13", (89, )), # 90
|
|
||||||
("3", (90, )), # 91
|
|
||||||
]
|
|
||||||
# fmt: on
|
|
||||||
|
|
||||||
starters = [
|
|
||||||
"1",
|
|
||||||
"11",
|
|
||||||
"21",
|
|
||||||
"1211",
|
|
||||||
"111221",
|
|
||||||
"312211",
|
|
||||||
"13112221",
|
|
||||||
"1113213211",
|
|
||||||
"31131211131221",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def look_and_say_length(s: str, n: int) -> int:
|
|
||||||
if n == 0:
|
|
||||||
return len(s)
|
|
||||||
|
|
||||||
if s in starters:
|
|
||||||
return look_and_say_length(
|
|
||||||
"".join(f"{len(list(g))}{k}" for k, g in itertools.groupby(s)), n - 1
|
|
||||||
)
|
|
||||||
|
|
||||||
counts = {i: 0 for i in range(len(atoms))}
|
|
||||||
idx = next(i for i, (a, _) in enumerate(atoms) if s == a)
|
|
||||||
counts[idx] = 1
|
|
||||||
|
|
||||||
for _ in range(n):
|
|
||||||
c2 = {i: 0 for i in range(len(atoms))}
|
|
||||||
for i in counts:
|
|
||||||
for j in atoms[i][1]:
|
|
||||||
c2[j] += counts[i]
|
|
||||||
counts = c2
|
|
||||||
|
|
||||||
return sum(counts[i] * len(a[0]) for i, a in enumerate(atoms))
|
|
||||||
|
|
||||||
|
|
||||||
answer_1 = look_and_say_length(line, 40)
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = look_and_say_length(line, 50)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,49 +0,0 @@
|
|||||||
import itertools
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def is_valid(p: str) -> bool:
|
|
||||||
if any(c in "iol" for c in p):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not any(
|
|
||||||
ord(a) + 1 == ord(b) and ord(b) + 1 == ord(c)
|
|
||||||
for a, b, c in zip(p, p[1:], p[2:])
|
|
||||||
):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if sum(len(list(g)) >= 2 for _, g in itertools.groupby(p)) < 2:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
assert not is_valid("hijklmmn")
|
|
||||||
assert not is_valid("abbceffg")
|
|
||||||
assert not is_valid("abbcegjk")
|
|
||||||
assert is_valid("abcdffaa")
|
|
||||||
assert is_valid("ghjaabcc")
|
|
||||||
|
|
||||||
|
|
||||||
def increment(p: str) -> str:
|
|
||||||
if p[-1] == "z":
|
|
||||||
return increment(p[:-1]) + "a"
|
|
||||||
elif p[-1] in "iol":
|
|
||||||
return p[:-1] + chr(ord(p[-1]) + 2)
|
|
||||||
else:
|
|
||||||
return p[:-1] + chr(ord(p[-1]) + 1)
|
|
||||||
|
|
||||||
|
|
||||||
def find_next_password(p: str) -> str:
|
|
||||||
while not is_valid(p):
|
|
||||||
p = increment(p)
|
|
||||||
return p
|
|
||||||
|
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
|
||||||
|
|
||||||
answer_1 = find_next_password(line)
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = find_next_password(increment(answer_1))
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,27 +0,0 @@
|
|||||||
import json
|
|
||||||
import sys
|
|
||||||
from typing import TypeAlias
|
|
||||||
|
|
||||||
JsonObject: TypeAlias = dict[str, "JsonObject"] | list["JsonObject"] | int | str
|
|
||||||
|
|
||||||
|
|
||||||
def json_sum(value: JsonObject, ignore: str | None = None) -> int:
|
|
||||||
if isinstance(value, str):
|
|
||||||
return 0
|
|
||||||
elif isinstance(value, int):
|
|
||||||
return value
|
|
||||||
elif isinstance(value, list):
|
|
||||||
return sum(json_sum(v, ignore=ignore) for v in value)
|
|
||||||
elif ignore not in value.values():
|
|
||||||
return sum(json_sum(v, ignore=ignore) for v in value.values())
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
data: JsonObject = json.load(sys.stdin)
|
|
||||||
|
|
||||||
answer_1 = json_sum(data)
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = json_sum(data, "red")
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,41 +0,0 @@
|
|||||||
import itertools
|
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
|
||||||
from typing import Literal, cast
|
|
||||||
|
|
||||||
import parse # type: ignore
|
|
||||||
|
|
||||||
|
|
||||||
def max_change_in_happiness(happiness: dict[str, dict[str, int]]) -> int:
|
|
||||||
guests = list(happiness)
|
|
||||||
return max(
|
|
||||||
sum(
|
|
||||||
happiness[o][d] + happiness[d][o]
|
|
||||||
for o, d in zip((guests[0],) + order, order + (guests[0],))
|
|
||||||
)
|
|
||||||
for order in map(tuple, itertools.permutations(guests[1:]))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
|
||||||
happiness: dict[str, dict[str, int]] = defaultdict(dict)
|
|
||||||
for line in lines:
|
|
||||||
u1, gain_or_loose, hap, u2 = cast(
|
|
||||||
tuple[str, Literal["gain", "lose"], int, str],
|
|
||||||
parse.parse( # type: ignore
|
|
||||||
"{} would {} {:d} happiness units by sitting next to {}.", line
|
|
||||||
),
|
|
||||||
)
|
|
||||||
happiness[u1][u2] = hap if gain_or_loose == "gain" else -hap
|
|
||||||
|
|
||||||
|
|
||||||
answer_1 = max_change_in_happiness(happiness)
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
for guest in list(happiness):
|
|
||||||
happiness["me"][guest] = 0
|
|
||||||
happiness[guest]["me"] = 0
|
|
||||||
|
|
||||||
answer_2 = max_change_in_happiness(happiness)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,62 +0,0 @@
|
|||||||
import sys
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from typing import Literal, cast
|
|
||||||
|
|
||||||
import parse # type: ignore
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class Reindeer:
|
|
||||||
name: str
|
|
||||||
speed: int
|
|
||||||
fly_time: int
|
|
||||||
rest_time: int
|
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
|
||||||
reindeers: list[Reindeer] = []
|
|
||||||
for line in lines:
|
|
||||||
reindeer, speed, speed_time, rest_time = cast(
|
|
||||||
tuple[str, int, int, int],
|
|
||||||
parse.parse( # type: ignore
|
|
||||||
"{} can fly {:d} km/s for {:d} seconds, "
|
|
||||||
"but then must rest for {:d} seconds.",
|
|
||||||
line,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
reindeers.append(
|
|
||||||
Reindeer(name=reindeer, speed=speed, fly_time=speed_time, rest_time=rest_time)
|
|
||||||
)
|
|
||||||
|
|
||||||
target = 1000 if len(reindeers) <= 2 else 2503
|
|
||||||
|
|
||||||
states: dict[Reindeer, tuple[Literal["resting", "flying"], int]] = {
|
|
||||||
reindeer: ("resting", 0) for reindeer in reindeers
|
|
||||||
}
|
|
||||||
distances: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
|
||||||
points: dict[Reindeer, int] = {reindeer: 0 for reindeer in reindeers}
|
|
||||||
|
|
||||||
for time in range(target):
|
|
||||||
for reindeer in reindeers:
|
|
||||||
if states[reindeer][0] == "flying":
|
|
||||||
distances[reindeer] += reindeer.speed
|
|
||||||
|
|
||||||
top_distance = max(distances.values())
|
|
||||||
for reindeer in reindeers:
|
|
||||||
if distances[reindeer] == top_distance:
|
|
||||||
points[reindeer] += 1
|
|
||||||
|
|
||||||
for reindeer in reindeers:
|
|
||||||
if states[reindeer][1] == time:
|
|
||||||
if states[reindeer][0] == "resting":
|
|
||||||
states[reindeer] = ("flying", time + reindeer.fly_time)
|
|
||||||
else:
|
|
||||||
states[reindeer] = ("resting", time + reindeer.rest_time)
|
|
||||||
|
|
||||||
|
|
||||||
answer_1 = max(distances.values())
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = max(points.values()) - 1
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,57 +0,0 @@
|
|||||||
import math
|
|
||||||
import sys
|
|
||||||
from typing import Sequence, cast
|
|
||||||
|
|
||||||
import parse # type: ignore
|
|
||||||
|
|
||||||
|
|
||||||
def score(ingredients: list[list[int]], teaspoons: Sequence[int]) -> int:
|
|
||||||
return math.prod(
|
|
||||||
max(
|
|
||||||
0,
|
|
||||||
sum(
|
|
||||||
ingredient[prop] * teaspoon
|
|
||||||
for ingredient, teaspoon in zip(ingredients, teaspoons)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
for prop in range(len(ingredients[0]) - 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
|
||||||
ingredients: list[list[int]] = []
|
|
||||||
for line in lines:
|
|
||||||
_, *scores = cast(
|
|
||||||
tuple[str, int, int, int, int, int],
|
|
||||||
parse.parse( # type: ignore
|
|
||||||
"{}: capacity {:d}, durability {:d}, flavor {:d}, "
|
|
||||||
"texture {:d}, calories {:d}",
|
|
||||||
line,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
ingredients.append(scores)
|
|
||||||
|
|
||||||
total_teaspoons = 100
|
|
||||||
calories: list[int] = []
|
|
||||||
scores: list[int] = []
|
|
||||||
|
|
||||||
for a in range(total_teaspoons + 1):
|
|
||||||
for b in range(total_teaspoons + 1 - a):
|
|
||||||
for c in range(total_teaspoons + 1 - a - b):
|
|
||||||
teaspoons = (a, b, c, total_teaspoons - a - b - c)
|
|
||||||
|
|
||||||
scores.append(score(ingredients, teaspoons))
|
|
||||||
calories.append(
|
|
||||||
sum(
|
|
||||||
ingredient[-1] * teaspoon
|
|
||||||
for ingredient, teaspoon in zip(ingredients, teaspoons)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
answer_1 = max(scores)
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = max(score for score, calory in zip(scores, calories) if calory == 500)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,51 +0,0 @@
|
|||||||
import operator as op
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from collections import defaultdict
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
MFCSAM: dict[str, int] = {
|
|
||||||
"children": 3,
|
|
||||||
"cats": 7,
|
|
||||||
"samoyeds": 2,
|
|
||||||
"pomeranians": 3,
|
|
||||||
"akitas": 0,
|
|
||||||
"vizslas": 0,
|
|
||||||
"goldfish": 5,
|
|
||||||
"trees": 3,
|
|
||||||
"cars": 2,
|
|
||||||
"perfumes": 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
lines = sys.stdin.readlines()
|
|
||||||
|
|
||||||
aunts: list[dict[str, int]] = [
|
|
||||||
{
|
|
||||||
match[1]: int(match[2])
|
|
||||||
for match in re.findall(R"((?P<compound>[^:, ]+): (?P<quantity>\d+))", line)
|
|
||||||
}
|
|
||||||
for line in lines
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def match(operators: dict[str, Callable[[int, int], bool]]) -> int:
|
|
||||||
return next(
|
|
||||||
i
|
|
||||||
for i, aunt in enumerate(aunts, start=1)
|
|
||||||
if all(operators[k](aunt[k], MFCSAM[k]) for k in aunt)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
answer_1 = match(defaultdict(lambda: op.eq))
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = match(
|
|
||||||
defaultdict(
|
|
||||||
lambda: op.eq,
|
|
||||||
trees=op.gt,
|
|
||||||
cats=op.gt,
|
|
||||||
pomeranians=op.lt,
|
|
||||||
goldfish=op.lt,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,30 +0,0 @@
|
|||||||
import sys
|
|
||||||
from typing import Iterator
|
|
||||||
|
|
||||||
|
|
||||||
def iter_combinations(value: int, containers: list[int]) -> Iterator[tuple[int, ...]]:
|
|
||||||
if value < 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
if value == 0:
|
|
||||||
yield ()
|
|
||||||
|
|
||||||
for i in range(len(containers)):
|
|
||||||
for combination in iter_combinations(
|
|
||||||
value - containers[i], containers[i + 1 :]
|
|
||||||
):
|
|
||||||
yield (containers[i],) + combination
|
|
||||||
|
|
||||||
|
|
||||||
containers = [int(c) for c in sys.stdin.read().split()]
|
|
||||||
total = 25 if len(containers) <= 5 else 150
|
|
||||||
|
|
||||||
combinations = [combination for combination in iter_combinations(total, containers)]
|
|
||||||
|
|
||||||
answer_1 = len(combinations)
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
min_containers = min(len(combination) for combination in combinations)
|
|
||||||
|
|
||||||
answer_2 = sum(1 for combination in combinations if len(combination) == min_containers)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,66 +0,0 @@
|
|||||||
import itertools
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
from numpy.typing import NDArray
|
|
||||||
|
|
||||||
grid0 = np.array([[c == "#" for c in line] for line in sys.stdin.read().splitlines()])
|
|
||||||
|
|
||||||
# add an always off circle around
|
|
||||||
grid0 = np.concatenate(
|
|
||||||
[
|
|
||||||
np.zeros((grid0.shape[0] + 2, 1), dtype=bool),
|
|
||||||
np.concatenate(
|
|
||||||
[
|
|
||||||
np.zeros((1, grid0.shape[1]), dtype=bool),
|
|
||||||
grid0,
|
|
||||||
np.zeros((1, grid0.shape[1]), dtype=bool),
|
|
||||||
]
|
|
||||||
),
|
|
||||||
np.zeros((grid0.shape[0] + 2, 1), dtype=bool),
|
|
||||||
],
|
|
||||||
axis=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
moves = list(itertools.product([-1, 0, 1], repeat=2))
|
|
||||||
moves.remove((0, 0))
|
|
||||||
|
|
||||||
jjs, iis = np.meshgrid(
|
|
||||||
np.arange(1, grid0.shape[0] - 1, dtype=int),
|
|
||||||
np.arange(1, grid0.shape[1] - 1, dtype=int),
|
|
||||||
)
|
|
||||||
iis, jjs = iis.flatten(), jjs.flatten()
|
|
||||||
|
|
||||||
ins = iis[:, None] + np.array(moves)[:, 0]
|
|
||||||
jns = jjs[:, None] + np.array(moves)[:, 1]
|
|
||||||
|
|
||||||
|
|
||||||
def game_of_life(grid: NDArray[np.bool_]) -> NDArray[np.bool_]:
|
|
||||||
neighbors_on = grid[ins, jns].sum(axis=1)
|
|
||||||
cells_on = grid[iis, jjs]
|
|
||||||
|
|
||||||
grid = np.zeros_like(grid)
|
|
||||||
grid[iis, jjs] = (neighbors_on == 3) | (cells_on & (neighbors_on == 2))
|
|
||||||
|
|
||||||
return grid
|
|
||||||
|
|
||||||
|
|
||||||
grid = grid0
|
|
||||||
n_steps = 4 if len(grid) < 10 else 100
|
|
||||||
for _ in range(n_steps):
|
|
||||||
grid = game_of_life(grid)
|
|
||||||
|
|
||||||
answer_1 = grid.sum()
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
|
|
||||||
n_steps = 5 if len(grid) < 10 else 100
|
|
||||||
grid = grid0
|
|
||||||
for _ in range(n_steps):
|
|
||||||
grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True
|
|
||||||
grid = game_of_life(grid)
|
|
||||||
|
|
||||||
grid[[1, 1, -2, -2], [1, -2, 1, -2]] = True
|
|
||||||
|
|
||||||
answer_2 = sum(cell for line in grid for cell in line)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,56 +0,0 @@
|
|||||||
import sys
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
replacements_s, molecule = sys.stdin.read().split("\n\n")
|
|
||||||
|
|
||||||
REPLACEMENTS: dict[str, list[str]] = defaultdict(list)
|
|
||||||
for replacement_s in replacements_s.splitlines():
|
|
||||||
p = replacement_s.split(" => ")
|
|
||||||
REPLACEMENTS[p[0]].append(p[1])
|
|
||||||
molecule = molecule.strip()
|
|
||||||
|
|
||||||
generated = [
|
|
||||||
molecule[:i] + replacement + molecule[i + len(symbol) :]
|
|
||||||
for symbol, replacements in REPLACEMENTS.items()
|
|
||||||
for replacement in replacements
|
|
||||||
for i in range(len(molecule))
|
|
||||||
if molecule[i:].startswith(symbol)
|
|
||||||
]
|
|
||||||
|
|
||||||
answer_1 = len(set(generated))
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
inversion: dict[str, str] = {
|
|
||||||
replacement: symbol
|
|
||||||
for symbol, replacements in REPLACEMENTS.items()
|
|
||||||
for replacement in replacements
|
|
||||||
}
|
|
||||||
|
|
||||||
# there is actually only one way to create the molecule, and we can greedily replace
|
|
||||||
# tokens with their replacements, e.g., if H => OH then we can replace OH by H directly
|
|
||||||
# without thinking
|
|
||||||
|
|
||||||
count = 0
|
|
||||||
while molecule != "e":
|
|
||||||
i = 0
|
|
||||||
m2 = ""
|
|
||||||
while i < len(molecule):
|
|
||||||
found = False
|
|
||||||
for replacement in inversion:
|
|
||||||
if molecule[i:].startswith(replacement):
|
|
||||||
m2 += inversion[replacement]
|
|
||||||
i += len(replacement)
|
|
||||||
count += 1
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if not found:
|
|
||||||
m2 += molecule[i]
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
# print(m2)
|
|
||||||
molecule = m2
|
|
||||||
|
|
||||||
|
|
||||||
answer_2 = count
|
|
||||||
print(f"answer 2 is {count}")
|
|
@ -1,20 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
|
||||||
length, width, height = np.array(
|
|
||||||
[[int(c) for c in line.split("x")] for line in lines]
|
|
||||||
).T
|
|
||||||
|
|
||||||
lw, wh, hl = (length * width, width * height, height * length)
|
|
||||||
|
|
||||||
answer_1 = np.sum(2 * (lw + wh + hl) + np.min(np.stack([lw, wh, hl]), axis=0))
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = np.sum(
|
|
||||||
length * width * height
|
|
||||||
+ 2 * np.min(np.stack([length + width, length + height, height + width]), axis=0)
|
|
||||||
)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,28 +0,0 @@
|
|||||||
import itertools
|
|
||||||
import sys
|
|
||||||
|
|
||||||
target = int(sys.stdin.read())
|
|
||||||
|
|
||||||
|
|
||||||
def presents(n: int, elf: int, max: int = target) -> int:
|
|
||||||
count = 0
|
|
||||||
k = 1
|
|
||||||
while k * k < n:
|
|
||||||
if n % k == 0:
|
|
||||||
if n // k <= max:
|
|
||||||
count += elf * k
|
|
||||||
if k <= max:
|
|
||||||
count += elf * (n // k)
|
|
||||||
k += 1
|
|
||||||
|
|
||||||
if k * k == n and k <= max:
|
|
||||||
count += elf * k
|
|
||||||
|
|
||||||
return count
|
|
||||||
|
|
||||||
|
|
||||||
answer_1 = next(n for n in itertools.count(1) if presents(n, 10) >= target)
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = next(n for n in itertools.count(1) if presents(n, 11, 50) >= target)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,66 +0,0 @@
|
|||||||
import itertools
|
|
||||||
import sys
|
|
||||||
from math import ceil
|
|
||||||
from typing import TypeAlias
|
|
||||||
|
|
||||||
Modifier: TypeAlias = tuple[str, int, int, int]
|
|
||||||
|
|
||||||
WEAPONS: list[Modifier] = [
|
|
||||||
("Dagger", 8, 4, 0),
|
|
||||||
("Shortsword", 10, 5, 0),
|
|
||||||
("Warhammer", 25, 6, 0),
|
|
||||||
("Longsword", 40, 7, 0),
|
|
||||||
("Greataxe", 74, 8, 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
ARMORS: list[Modifier] = [
|
|
||||||
("", 0, 0, 0),
|
|
||||||
("Leather", 13, 0, 1),
|
|
||||||
("Chainmail", 31, 0, 2),
|
|
||||||
("Splintmail", 53, 0, 3),
|
|
||||||
("Bandedmail", 75, 0, 4),
|
|
||||||
("Platemail", 102, 0, 5),
|
|
||||||
]
|
|
||||||
|
|
||||||
RINGS: list[Modifier] = [
|
|
||||||
("", 0, 0, 0),
|
|
||||||
("Damage +1", 25, 1, 0),
|
|
||||||
("Damage +2", 50, 2, 0),
|
|
||||||
("Damage +3", 100, 3, 0),
|
|
||||||
("Defense +1", 20, 0, 1),
|
|
||||||
("Defense +2", 40, 0, 2),
|
|
||||||
("Defense +3", 80, 0, 3),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
|
||||||
player_hp = 100
|
|
||||||
|
|
||||||
boss_attack = int(lines[1].split(":")[1].strip())
|
|
||||||
boss_armor = int(lines[2].split(":")[1].strip())
|
|
||||||
boss_hp = int(lines[0].split(":")[1].strip())
|
|
||||||
|
|
||||||
|
|
||||||
min_cost, max_cost = 1_000_000, 0
|
|
||||||
for equipments in itertools.product(WEAPONS, ARMORS, RINGS, RINGS):
|
|
||||||
if equipments[-1][0] != "" and equipments[-2] == equipments[-1]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
cost, player_attack, player_armor = (
|
|
||||||
sum(equipment[1:][k] for equipment in equipments) for k in range(3)
|
|
||||||
)
|
|
||||||
|
|
||||||
if ceil(boss_hp / max(1, player_attack - boss_armor)) <= ceil(
|
|
||||||
player_hp / max(1, boss_attack - player_armor)
|
|
||||||
):
|
|
||||||
min_cost = min(cost, min_cost)
|
|
||||||
else:
|
|
||||||
max_cost = max(cost, max_cost)
|
|
||||||
|
|
||||||
|
|
||||||
answer_1 = min_cost
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = max_cost
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,34 +0,0 @@
|
|||||||
import sys
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
|
||||||
|
|
||||||
|
|
||||||
def process(directions: str) -> dict[tuple[int, int], int]:
|
|
||||||
counts: dict[tuple[int, int], int] = defaultdict(lambda: 0)
|
|
||||||
counts[0, 0] = 1
|
|
||||||
x, y = (0, 0)
|
|
||||||
|
|
||||||
for c in directions:
|
|
||||||
match c:
|
|
||||||
case ">":
|
|
||||||
x += 1
|
|
||||||
case "<":
|
|
||||||
x -= 1
|
|
||||||
case "^":
|
|
||||||
y -= 1
|
|
||||||
case "v":
|
|
||||||
y += 1
|
|
||||||
case _:
|
|
||||||
raise ValueError()
|
|
||||||
|
|
||||||
counts[x, y] += 1
|
|
||||||
|
|
||||||
return counts
|
|
||||||
|
|
||||||
|
|
||||||
answer_1 = len(process(line))
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = len(process(line[::2]) | process(line[1::2]))
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,16 +0,0 @@
|
|||||||
import hashlib
|
|
||||||
import itertools
|
|
||||||
import sys
|
|
||||||
|
|
||||||
line = sys.stdin.read().strip()
|
|
||||||
|
|
||||||
it = iter(itertools.count(1))
|
|
||||||
answer_1 = next(
|
|
||||||
i for i in it if hashlib.md5(f"{line}{i}".encode()).hexdigest().startswith("00000")
|
|
||||||
)
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = next(
|
|
||||||
i for i in it if hashlib.md5(f"{line}{i}".encode()).hexdigest().startswith("000000")
|
|
||||||
)
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
@ -1,36 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
VOWELS = "aeiou"
|
|
||||||
FORBIDDEN = {"ab", "cd", "pq", "xy"}
|
|
||||||
|
|
||||||
|
|
||||||
def is_nice_1(s: str) -> bool:
|
|
||||||
if sum(c in VOWELS for c in s) < 3:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not any(a == b for a, b in zip(s[:-1:], s[1::])):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if any(s.find(f) >= 0 for f in FORBIDDEN):
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def is_nice_2(s: str) -> bool:
|
|
||||||
if not any(s.find(s[i : i + 2], i + 2) >= 0 for i in range(len(s))):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not any(a == b for a, b in zip(s[:-1:], s[2::])):
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
|
|
||||||
answer_1 = sum(map(is_nice_1, lines))
|
|
||||||
print(f"answer 1 is {answer_1}")
|
|
||||||
|
|
||||||
answer_2 = sum(map(is_nice_2, lines))
|
|
||||||
print(f"answer 2 is {answer_2}")
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user