22 Commits

Author SHA1 Message Date
Mikaël Capelle
3732e70ef7 Ugly day 22. 2022-12-22 23:00:36 +01:00
Mikaël Capelle
b0cc6b4a46 Update day 22. 2022-12-22 18:46:59 +01:00
Mikael CAPELLE
8c24b9f9e2 Update day 22. 2022-12-22 17:22:56 +01:00
Mikael CAPELLE
dca6f6a08f Update day 22. 2022-12-22 17:00:09 +01:00
Mikael CAPELLE
8d7a20f575 Day 22 part 1. 2022-12-22 14:10:30 +01:00
Mikael CAPELLE
3934dfd152 Start day 22. 2022-12-22 08:20:09 +01:00
Mikael CAPELLE
b656e8929e Remove tqdm from day 19. 2022-12-21 17:39:43 +01:00
Mikael CAPELLE
c9c69f479b Day 21. 2022-12-21 09:44:05 +01:00
Mikaël Capelle
72ebcfff1f Clean day 20. 2022-12-20 23:39:26 +01:00
Mikaël Capelle
dd72bb3238 Add empty files for following days. 2022-12-20 21:51:38 +01:00
Mikael CAPELLE
c1dd74c57d Faster day 20. 2022-12-20 18:27:09 +01:00
Mikael CAPELLE
1bf2de62c7 Day 20, slow. 2022-12-20 13:39:36 +01:00
Mikael CAPELLE
df808bc98a Start day 20. 2022-12-20 12:35:01 +01:00
Mikaël Capelle
f46e190e98 Add all tests from previous days. 2022-12-19 22:32:15 +01:00
Mikaël Capelle
7f4a34b2d7 Day 19. 2022-12-19 22:09:20 +01:00
Mikael CAPELLE
ddebd26db2 Tmp 2022-12-19 18:46:16 +01:00
Mikael CAPELLE
01300e23b2 Start working on day 19. 2022-12-19 13:51:32 +01:00
Mikaël Capelle
b8e2faa8c9 Clean day 17. 2022-12-18 16:46:00 +01:00
Mikaël Capelle
ea5b757180 Day 18. 2022-12-18 09:57:35 +01:00
Mikaël Capelle
89a71c175f Day 17. 2022-12-17 12:27:05 +01:00
Mikaël Capelle
9ffb332dea Day 17. 2022-12-17 12:25:48 +01:00
Mikaël Capelle
8167ab34c7 Less BFS for day 16. 2022-12-16 22:56:34 +01:00
45 changed files with 10375 additions and 10 deletions

View File

@@ -33,25 +33,28 @@ class Pipe(NamedTuple):
return self.name return self.name
def breadth_first_search(pipes: dict[str, Pipe], pipe_1: Pipe, pipe_2: Pipe) -> int: 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)] queue = [(0, pipe_1)]
visited = set() visited = set()
distances: dict[Pipe, int] = {}
while queue: while len(distances) < len(pipes):
distance, current = heapq.heappop(queue) distance, current = heapq.heappop(queue)
if current in visited: if current in visited:
continue continue
visited.add(current) visited.add(current)
distances[current] = distance
if current == pipe_2:
return distance
for tunnel in current.tunnels: for tunnel in current.tunnels:
heapq.heappush(queue, (distance + 1, pipes[tunnel])) heapq.heappush(queue, (distance + 1, pipes[tunnel]))
return -1 return distances
def update_with_better( def update_with_better(
@@ -141,8 +144,12 @@ for line in lines:
# compute distances from one valve to any other # compute distances from one valve to any other
distances: dict[tuple[Pipe, Pipe], int] = {} distances: dict[tuple[Pipe, Pipe], int] = {}
for pipe_1 in pipes.values(): for pipe_1 in pipes.values():
for pipe_2 in pipes.values(): distances.update(
distances[pipe_1, pipe_2] = breadth_first_search(pipes, pipe_1, pipe_2) {
(pipe_1, pipe_2): distance
for pipe_2, distance in breadth_first_search(pipes, pipe_1).items()
}
)
# valves with flow # valves with flow
relevant_pipes = frozenset(pipe for pipe in pipes.values() if pipe.flow > 0) relevant_pipes = frozenset(pipe for pipe in pipes.values() if pipe.flow > 0)

125
2022/day17.py Normal file
View File

@@ -0,0 +1,125 @@
# -*- encoding: utf-8 -*-
import sys
from typing import Sequence, TypeVar
import numpy as np
T = TypeVar("T")
def print_tower(tower: np.ndarray, out: str = "#"):
print("-" * (tower.shape[1] + 2))
non_empty = False
for row in reversed(range(1, tower.shape[0])):
if not non_empty and not tower[row, :].any():
continue
non_empty = True
print("|" + "".join(out if c else "." for c in tower[row, :]) + "|")
print("+" + "-" * tower.shape[1] + "+")
def tower_height(tower: np.ndarray) -> int:
return int(tower.shape[0] - tower[::-1, :].argmax(axis=0).min() - 1)
def next_cycle(sequence: Sequence[T], index: int) -> tuple[T, int]:
t = sequence[index]
index = (index + 1) % len(sequence)
return t, index
ROCKS = [
np.array([(0, 0), (0, 1), (0, 2), (0, 3)]),
np.array([(0, 1), (1, 0), (1, 1), (1, 2), (2, 1)]),
np.array([(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)]),
np.array([(0, 0), (1, 0), (2, 0), (3, 0)]),
np.array([(0, 0), (0, 1), (1, 0), (1, 1)]),
]
WIDTH = 7
START_X = 2
EMPTY_BLOCKS = np.zeros((10, WIDTH), dtype=bool)
def build_tower(
n_rocks: int,
jets: str,
early_stop: bool = False,
init: np.ndarray = np.ones(WIDTH, dtype=bool),
) -> tuple[np.ndarray, int, int, dict[int, int]]:
tower = EMPTY_BLOCKS.copy()
tower[0, :] = init
done_at: dict[tuple[int, int], int] = {}
heights: dict[int, int] = {}
i_jet, i_rock = 0, 0
rock_count = 0
for rock_count in range(n_rocks):
if early_stop:
if i_rock == 0 and (i_rock, i_jet) in done_at:
break
done_at[i_rock, i_jet] = rock_count
y_start = tower.shape[0] - tower[::-1, :].argmax(axis=0).min() + 3
rock, i_rock = next_cycle(ROCKS, i_rock)
rock_y = rock[:, 0] + y_start
rock_x = rock[:, 1] + START_X
if rock_y.max() >= tower.shape[0]:
tower = np.concatenate([tower, EMPTY_BLOCKS], axis=0)
while True:
jet, i_jet = next_cycle(jets, i_jet)
dx = 0
if jet == ">" and rock_x.max() < WIDTH - 1:
dx = 1
elif jet == "<" and rock_x.min() > 0:
dx = -1
if dx != 0 and not tower[rock_y, rock_x + dx].any():
rock_x = rock_x + dx
# move down
rock_y -= 1
if tower[rock_y, rock_x].any():
rock_y += 1
break
heights[rock_count] = tower_height(tower)
tower[rock_y, rock_x] = True
return tower, rock_count, done_at.get((i_rock, i_jet), -1), heights
line = sys.stdin.read().strip()
tower, *_ = build_tower(2022, line)
answer_1 = tower_height(tower)
print(f"answer 1 is {answer_1}")
TOTAL_ROCKS = 1_000_000_000_000
tower_1, n_rocks_1, prev_1, heights_1 = build_tower(TOTAL_ROCKS, line, True)
assert prev_1 > 0
# 2767 1513
remaining_rocks = TOTAL_ROCKS - n_rocks_1
n_repeat_rocks = n_rocks_1 - prev_1
n_repeat_towers = remaining_rocks // n_repeat_rocks
base_height = heights_1[prev_1]
repeat_height = heights_1[prev_1 + n_repeat_rocks - 1] - heights_1[prev_1]
remaining_height = (
heights_1[prev_1 + remaining_rocks % n_repeat_rocks] - heights_1[prev_1]
)
answer_2 = base_height + (n_repeat_towers + 1) * repeat_height + remaining_height
print(f"answer 2 is {answer_2}")

53
2022/day18.py Normal file
View File

@@ -0,0 +1,53 @@
# -*- encoding: utf-8 -*-
import sys
from typing import FrozenSet
import numpy as np
xyz = np.asarray(
[
tuple(int(x) for x in row.split(",")) # type: ignore
for row in sys.stdin.read().splitlines()
]
)
xyz = xyz - xyz.min(axis=0) + 1
cubes = np.zeros(xyz.max(axis=0) + 3, dtype=bool)
cubes[xyz[:, 0], xyz[:, 1], xyz[:, 2]] = True
n_dims = len(cubes.shape)
faces = [(-1, 0, 0), (1, 0, 0), (0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1)]
answer_1 = sum(
1 for x, y, z in xyz for dx, dy, dz in faces if not cubes[x + dx, y + dy, z + dz]
)
print(f"answer 1 is {answer_1}")
visited = np.zeros_like(cubes, dtype=bool)
queue = [(0, 0, 0)]
n_faces = 0
while queue:
x, y, z = queue.pop(0)
if visited[x, y, z]:
continue
visited[x, y, z] = True
for dx, dy, dz in faces:
nx, ny, nz = x + dx, y + dy, z + dz
if not all(n >= 0 and n < cubes.shape[i] for i, n in enumerate((nx, ny, nz))):
continue
if visited[nx, ny, nz]:
continue
if cubes[nx, ny, nz]:
n_faces += 1
else:
queue.append((nx, ny, nz))
print(f"answer 2 is {n_faces}")

186
2022/day19.py Normal file
View File

@@ -0,0 +1,186 @@
# -*- encoding: utf-8 -*-
import sys
from typing import Literal
import numpy as np
import parse
from tqdm import tqdm
Reagent = Literal["ore", "clay", "obsidian", "geode"]
REAGENTS: tuple[Reagent, ...] = (
"ore",
"clay",
"obsidian",
"geode",
)
IntOfReagent = dict[Reagent, int]
class State:
robots: IntOfReagent
reagents: IntOfReagent
def __init__(
self,
robots: IntOfReagent | None = None,
reagents: IntOfReagent | None = None,
):
if robots is None:
assert reagents is None
self.reagents = {reagent: 0 for reagent in REAGENTS}
self.robots = {reagent: 0 for reagent in REAGENTS}
self.robots["ore"] = 1
else:
assert robots is not None and reagents is not None
self.robots = robots
self.reagents = reagents
def __eq__(self, other) -> bool:
return (
isinstance(other, State)
and self.robots == other.robots
and self.reagents == other.reagents
)
def __hash__(self) -> int:
return hash(tuple((self.robots[r], self.reagents[r]) for r in REAGENTS))
def __str__(self) -> str:
return "State({}, {})".format(
"/".join(str(self.robots[k]) for k in REAGENTS),
"/".join(str(self.reagents[k]) for k in REAGENTS),
)
def __repr__(self) -> str:
return str(self)
def dominates(lhs: State, rhs: State):
return all(
lhs.robots[r] >= rhs.robots[r] and lhs.reagents[r] >= rhs.reagents[r]
for r in REAGENTS
)
lines = sys.stdin.read().splitlines()
blueprints: list[dict[Reagent, IntOfReagent]] = []
for line in lines:
r = parse.parse(
"Blueprint {}: "
"Each ore robot costs {:d} ore. "
"Each clay robot costs {:d} ore. "
"Each obsidian robot costs {:d} ore and {:d} clay. "
"Each geode robot costs {:d} ore and {:d} obsidian.",
line,
)
blueprints.append(
{
"ore": {"ore": r[1]},
"clay": {"ore": r[2]},
"obsidian": {"ore": r[3], "clay": r[4]},
"geode": {"ore": r[5], "obsidian": r[6]},
}
)
def run(blueprint: dict[Reagent, dict[Reagent, int]], max_time: int) -> int:
# since we can only build one robot per time, we do not need more than X robots
# of type K where X is the maximum number of K required among all robots, e.g.,
# in the first toy blueprint, we need at most 4 ore robots, 14 clay ones and 7
# obsidian ones
maximums = {
name: max(blueprint[r].get(name, 0) for r in REAGENTS) for name in REAGENTS
}
state_after_t: dict[int, set[State]] = {0: [State()]}
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
states_for_t: set[State] = set()
for state in state_after_t[t - 1]:
robots_that_can_be_built = [
robot
for robot in REAGENTS
if all(
state.reagents[reagent] >= blueprint[robot].get(reagent, 0)
for reagent in REAGENTS
)
]
states_for_t.add(
State(
robots=state.robots,
reagents={
reagent: state.reagents[reagent] + state.robots[reagent]
for reagent in REAGENTS
},
)
)
if "geode" in robots_that_can_be_built:
robots_that_can_be_built = ["geode"]
else:
robots_that_can_be_built = [
robot
for robot in robots_that_can_be_built
if state.robots[robot] < maximums[robot]
]
for robot in robots_that_can_be_built:
robots = state.robots.copy()
robots[robot] += 1
reagents = {
reagent: state.reagents[reagent]
+ state.robots[reagent]
- blueprint[robot].get(reagent, 0)
for reagent in REAGENTS
}
states_for_t.add(State(robots=robots, reagents=reagents))
# use numpy to switch computation of dominated states -> store each state
# as a 8 array and use numpy broadcasting to find dominated states
states_after = np.asarray(list(states_for_t))
np_states = np.array(
[
[state.robots[r] for r in REAGENTS]
+ [state.reagents[r] for r in REAGENTS]
for state in states_after
]
)
to_keep = []
while len(np_states) > 0:
first_dom = (np_states[1:] >= np_states[0]).all(axis=1).any()
if first_dom:
np_states = np_states[1:]
else:
to_keep.append(np_states[0])
np_states = np_states[1:][~(np_states[1:] <= np_states[0]).all(axis=1)]
state_after_t[t] = {
State(
robots=dict(zip(REAGENTS, row[:4])),
reagents=dict(zip(REAGENTS, row[4:])),
)
for row in to_keep
}
return max(state.reagents["geode"] for state in state_after_t[max_time])
answer_1 = sum(
(i_blueprint + 1) * run(blueprint, 24)
for i_blueprint, blueprint in enumerate(blueprints)
)
print(f"answer 1 is {answer_1}")
answer_2 = run(blueprints[0], 32) * run(blueprints[1], 32) * run(blueprints[2], 32)
print(f"answer 2 is {answer_2}")

77
2022/day20.py Normal file
View File

@@ -0,0 +1,77 @@
# -*- encoding: utf-8 -*-
from __future__ import annotations
import sys
class Number:
current: int
value: int
def __init__(self, value: int):
self.current = 0
self.value = value
def __str__(self):
return str(self.value)
def __repr__(self):
return str(self)
def decrypt(numbers: list[Number], key: int, rounds: int) -> int:
numbers = numbers.copy()
original = numbers.copy()
for index, number in enumerate(numbers):
number.current = index
for _ in range(rounds):
for number in original:
index = number.current
offset = (number.value * key) % (len(numbers) - 1)
target = index + offset
# need to wrap
if target >= len(numbers):
target = offset - (len(numbers) - index) + 1
for number_2 in numbers[target:index]:
number_2.current += 1
numbers = (
numbers[:target]
+ [number]
+ numbers[target:index]
+ numbers[index + 1 :]
)
else:
for number_2 in numbers[index : target + 1]:
number_2.current -= 1
numbers = (
numbers[:index]
+ numbers[index + 1 : target + 1]
+ [number]
+ numbers[target + 1 :]
)
number.current = target
index_of_0 = next(
filter(lambda index: numbers[index].value == 0, range(len(numbers)))
)
return sum(
numbers[(index_of_0 + offset) % len(numbers)].value * key
for offset in (1000, 2000, 3000)
)
numbers = [Number(int(x)) for i, x in enumerate(sys.stdin.readlines())]
answer_1 = decrypt(numbers, 1, 1)
print(f"answer 1 is {answer_1}")
answer_2 = decrypt(numbers, 811589153, 10)
print(f"answer 2 is {answer_2}")

109
2022/day21.py Normal file
View File

@@ -0,0 +1,109 @@
# -*- encoding: utf-8 -*-
import operator
import sys
from typing import Callable
def compute(monkeys: dict[str, int | tuple[str, str, str]], monkey: str) -> int:
value = monkeys[monkey]
if isinstance(value, int):
return value
else:
op: dict[str, Callable[[int, int], int]] = {
"+": operator.add,
"-": operator.sub,
"*": operator.mul,
"/": operator.floordiv,
}
value = op[value[1]](compute(monkeys, value[0]), compute(monkeys, value[2]))
monkeys[monkey] = value
return value
def invert(
monkeys: dict[str, int | tuple[str, str, str]], monkey: str, target: int
) -> dict[str, int | tuple[str, str, str]]:
"""
Revert the given mapping from monkey name to value or operation such that
the value from 'monkey' is computable by inverting operation until the root is
found.
Args:
monkeys: Dictionary of monkeys, that will be updated and returned.
monkey: Name of the monkey to start from.
target: Target value to set for the monkey that depends on root.
Returns:
The given dictionary of monkeys.
"""
monkeys = monkeys.copy()
depends: dict[str, str] = {}
for m, v in monkeys.items():
if isinstance(v, int):
continue
op1, _, op2 = v
assert op1 not in depends
assert op2 not in depends
depends[op1] = m
depends[op2] = m
invert_op = {"+": "-", "-": "+", "*": "/", "/": "*"}
current = monkey
while True:
dep = depends[current]
if dep == "root":
monkeys[current] = target
break
val = monkeys[dep]
assert not isinstance(val, int)
op1, ope, op2 = val
if op1 == current:
monkeys[current] = (dep, invert_op[ope], op2)
elif ope in ("+", "*"):
monkeys[current] = (dep, invert_op[ope], op1)
else:
monkeys[current] = (op1, ope, dep)
current = dep
return monkeys
lines = sys.stdin.read().splitlines()
monkeys: dict[str, int | tuple[str, str, str]] = {}
op_monkeys: set[str] = set()
for line in lines:
parts = line.split(":")
name = parts[0].strip()
try:
value = int(parts[1].strip())
monkeys[name] = value
except ValueError:
op1, ope, op2 = parts[1].strip().split()
monkeys[name] = (op1, ope, op2)
op_monkeys.add(name)
answer_1 = compute(monkeys.copy(), "root")
print(f"answer 1 is {answer_1}")
# assume the second operand of 'root' can be computed, and the first one depends on
# humn, which is the case is my input and the test input
p1, _, p2 = monkeys["root"] # type: ignore
answer_2 = compute(invert(monkeys, "humn", compute(monkeys.copy(), p2)), "humn")
print(f"answer 2 is {answer_2}")

225
2022/day22.py Normal file
View File

@@ -0,0 +1,225 @@
# -*- encoding: utf-8 -*-
import re
import sys
from typing import Callable
import numpy as np
VOID, EMPTY, WALL = 0, 1, 2
TILE_FROM_CHAR = {" ": VOID, ".": EMPTY, "#": WALL}
SCORES = {"E": 0, "S": 1, "W": 2, "N": 3}
board_map_s, direction_s = sys.stdin.read().split("\n\n")
# board
board_lines = board_map_s.splitlines()
max_line = max(len(line) for line in board_lines)
board = np.array(
[
[TILE_FROM_CHAR[c] for c in row] + [VOID] * (max_line - len(row))
for row in board_map_s.splitlines()
]
)
directions = [
int(p1) if p2 else p1 for p1, p2 in re.findall(R"(([0-9])+|L|R)", direction_s)
]
# find on each row and column the first and last non-void
row_first_non_void = np.argmax(board != VOID, axis=1)
row_last_non_void = board.shape[1] - np.argmax(board[:, ::-1] != VOID, axis=1) - 1
col_first_non_void = np.argmax(board != VOID, axis=0)
col_last_non_void = board.shape[0] - np.argmax(board[::-1, :] != VOID, axis=0) - 1
faces = np.zeros_like(board)
size = np.gcd(board.shape[0], board.shape[1])
for row in range(0, board.shape[0], size):
for col in range(row_first_non_void[row], row_last_non_void[row], size):
faces[row : row + size, col : col + size] = faces.max() + 1
SIZE = np.gcd(*board.shape)
# TODO: deduce this from the actual cube...
faces_wrap: dict[int, dict[str, Callable[[int, int], tuple[int, int, str]]]]
if board.shape == (12, 16): # example
faces_wrap = {
1: {
"W": lambda y, x: (4, 4 + y, "S"), # 3N
"N": lambda y, x: (4, 11 - x, "S"), # 2N
"E": lambda y, x: (11 - y, 15, "W"), # 6E
},
2: {
"W": lambda y, x: (11, 19 - y, "N"), # 6S
"N": lambda y, x: (0, 11 - y, "S"), # 1N
"S": lambda y, x: (11, 11 - x, "N"), # 5S
},
3: {
"N": lambda y, x: (x - 4, 8, "E"), # 1W
"S": lambda y, x: (15 - x, 8, "E"), # 5W
},
4: {"E": lambda y, x: (8, 19 - y, "S")}, # 6N
5: {
"W": lambda y, x: (7, 15 - y, "N"), # 3S
"S": lambda y, x: (7, 11 - x, "N"), # 2S
},
6: {
"N": lambda y, x: (19 - x, 11, "W"), # 4E
"E": lambda y, x: (11 - y, 11, "W"), # 1E
"S": lambda y, x: (19 - x, 0, "E"), # 2W
},
}
else:
faces_wrap = {
1: {
"W": lambda y, x: (3 * SIZE - y - 1, 0, "E"), # 4W
"N": lambda y, x: (2 * SIZE + x, 0, "E"), # 6W
},
2: {
"N": lambda y, x: (4 * SIZE - 1, x - 2 * SIZE, "N"), # 6S
"E": lambda y, x: (3 * SIZE - y - 1, 2 * SIZE - 1, "W"), # 5E
"S": lambda y, x: (x - SIZE, 2 * SIZE - 1, "W"), # 3E
},
3: {
"W": lambda y, x: (2 * SIZE, y - SIZE, "S"), # 4N
"E": lambda y, x: (SIZE - 1, SIZE + y, "N"), # 2S
},
4: {
"W": lambda y, x: (3 * SIZE - y - 1, SIZE, "E"), # 1W
"N": lambda y, x: (SIZE + x, SIZE, "E"), # 3W
},
5: {
"E": lambda y, x: (3 * SIZE - y - 1, 3 * SIZE - 1, "W"), # 2E
"S": lambda y, x: (2 * SIZE + x, SIZE - 1, "W"), # 6E
},
6: {
"W": lambda y, x: (0, y - 2 * SIZE, "S"), # 1N
"E": lambda y, x: (3 * SIZE - 1, y - 2 * SIZE, "N"), # 5S
"S": lambda y, x: (0, x + 2 * SIZE, "S"), # 2N
},
}
def wrap_part_1(y0: int, x0: int, r0: str) -> tuple[int, int, str]:
if r0 == "E":
return y0, row_first_non_void[y0], r0
elif r0 == "S":
return col_first_non_void[x0], x0, r0
elif r0 == "W":
return y0, row_last_non_void[y0], r0
elif r0 == "N":
return col_last_non_void[x0], x0, r0
assert False
def wrap_part_2(y0: int, x0: int, r0: str) -> tuple[int, int, str]:
cube = faces[y0, x0]
assert r0 in faces_wrap[cube]
return faces_wrap[cube][r0](y0, x0)
def run(wrap: Callable[[int, int, str], tuple[int, int, str]]) -> tuple[int, int, str]:
y0 = 0
x0 = np.where(board[0] == EMPTY)[0][0]
r0 = "E"
for direction in directions:
if isinstance(direction, int):
while direction > 0:
if r0 == "E":
xi = np.where(board[y0, x0 + 1 : x0 + direction + 1] == WALL)[0]
if len(xi):
x0 = x0 + xi[0]
direction = 0
elif (
x0 + direction < board.shape[1]
and board[y0, x0 + direction] == EMPTY
):
x0 = x0 + direction
direction = 0
else:
y0_t, x0_t, r0_t = wrap(y0, x0, r0)
if board[y0_t, x0_t] == WALL:
x0 = row_last_non_void[y0]
direction = 0
else:
direction = direction - (row_last_non_void[y0] - x0) - 1
y0, x0, r0 = y0_t, x0_t, r0_t
elif r0 == "S":
yi = np.where(board[y0 + 1 : y0 + direction + 1, x0] == WALL)[0]
if len(yi):
y0 = y0 + yi[0]
direction = 0
elif (
y0 + direction < board.shape[0]
and board[y0 + direction, x0] == EMPTY
):
y0 = y0 + direction
direction = 0
else:
y0_t, x0_t, r0_t = wrap(y0, x0, r0)
if board[y0_t, x0_t] == WALL:
y0 = col_last_non_void[x0]
direction = 0
else:
direction = direction - (col_last_non_void[x0] - y0) - 1
y0, x0, r0 = y0_t, x0_t, r0_t
elif r0 == "W":
left = max(x0 - direction - 1, 0)
xi = np.where(board[y0, left:x0] == WALL)[0]
if len(xi):
x0 = left + xi[-1] + 1
direction = 0
elif x0 - direction >= 0 and board[y0, x0 - direction] == EMPTY:
x0 = x0 - direction
direction = 0
else:
y0_t, x0_t, r0_t = wrap(y0, x0, r0)
if board[y0_t, x0_t] == WALL:
x0 = row_first_non_void[y0]
direction = 0
else:
direction = direction - (x0 - row_first_non_void[y0]) - 1
y0, x0, r0 = y0_t, x0_t, r0_t
elif r0 == "N":
top = max(y0 - direction - 1, 0)
yi = np.where(board[top:y0, x0] == WALL)[0]
if len(yi):
y0 = top + yi[-1] + 1
direction = 0
elif y0 - direction >= 0 and board[y0 - direction, x0] == EMPTY:
y0 = y0 - direction
direction = 0
else:
y0_t, x0_t, r0_t = wrap(y0, x0, r0)
if board[y0_t, x0_t] == WALL:
y0 = col_first_non_void[x0]
direction = 0
else:
direction = direction - (y0 - col_first_non_void[x0]) - 1
y0, x0, r0 = y0_t, x0_t, r0_t
else:
r0 = {
"E": {"L": "N", "R": "S"},
"N": {"L": "W", "R": "E"},
"W": {"L": "S", "R": "N"},
"S": {"L": "E", "R": "W"},
}[r0][direction]
return y0, x0, r0
y1, x1, r1 = run(wrap_part_1)
answer_1 = 1000 * (1 + y1) + 4 * (1 + x1) + SCORES[r1]
print(f"answer 1 is {answer_1}")
y2, x2, r2 = run(wrap_part_2)
answer_2 = 1000 * (1 + y2) + 4 * (1 + x2) + SCORES[r2]
print(f"answer 2 is {answer_2}")

3
2022/day23.py Normal file
View File

@@ -0,0 +1,3 @@
# -*- encoding: utf-8 -*-
import sys

3
2022/day24.py Normal file
View File

@@ -0,0 +1,3 @@
# -*- encoding: utf-8 -*-
import sys

3
2022/day25.py Normal file
View File

@@ -0,0 +1,3 @@
# -*- encoding: utf-8 -*-
import sys

1
2022/inputs/day17.txt Normal file

File diff suppressed because one or more lines are too long

2150
2022/inputs/day18.txt Normal file

File diff suppressed because it is too large Load Diff

30
2022/inputs/day19.txt Normal file
View File

@@ -0,0 +1,30 @@
Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 12 clay. Each geode robot costs 4 ore and 19 obsidian.
Blueprint 2: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 11 clay. Each geode robot costs 2 ore and 7 obsidian.
Blueprint 3: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 13 clay. Each geode robot costs 3 ore and 12 obsidian.
Blueprint 4: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 18 clay. Each geode robot costs 2 ore and 19 obsidian.
Blueprint 5: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 19 clay. Each geode robot costs 4 ore and 13 obsidian.
Blueprint 6: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 7 clay. Each geode robot costs 4 ore and 11 obsidian.
Blueprint 7: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 15 clay. Each geode robot costs 4 ore and 17 obsidian.
Blueprint 8: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 13 clay. Each geode robot costs 3 ore and 7 obsidian.
Blueprint 9: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 12 clay. Each geode robot costs 3 ore and 15 obsidian.
Blueprint 10: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 4 ore and 18 clay. Each geode robot costs 4 ore and 11 obsidian.
Blueprint 11: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 8 clay. Each geode robot costs 2 ore and 15 obsidian.
Blueprint 12: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 4 ore and 8 clay. Each geode robot costs 3 ore and 7 obsidian.
Blueprint 13: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 10 clay. Each geode robot costs 2 ore and 10 obsidian.
Blueprint 14: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 13 clay. Each geode robot costs 2 ore and 20 obsidian.
Blueprint 15: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 19 clay. Each geode robot costs 3 ore and 8 obsidian.
Blueprint 16: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 16 clay. Each geode robot costs 2 ore and 18 obsidian.
Blueprint 17: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 9 clay. Each geode robot costs 3 ore and 19 obsidian.
Blueprint 18: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 11 clay. Each geode robot costs 4 ore and 8 obsidian.
Blueprint 19: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 12 clay. Each geode robot costs 3 ore and 17 obsidian.
Blueprint 20: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 14 clay. Each geode robot costs 3 ore and 17 obsidian.
Blueprint 21: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 15 clay. Each geode robot costs 3 ore and 16 obsidian.
Blueprint 22: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 16 clay. Each geode robot costs 4 ore and 16 obsidian.
Blueprint 23: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 19 clay. Each geode robot costs 4 ore and 11 obsidian.
Blueprint 24: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 18 clay. Each geode robot costs 4 ore and 9 obsidian.
Blueprint 25: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 17 clay. Each geode robot costs 3 ore and 16 obsidian.
Blueprint 26: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 20 clay. Each geode robot costs 4 ore and 7 obsidian.
Blueprint 27: Each ore robot costs 2 ore. Each clay robot costs 2 ore. Each obsidian robot costs 2 ore and 8 clay. Each geode robot costs 2 ore and 14 obsidian.
Blueprint 28: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 20 clay. Each geode robot costs 3 ore and 14 obsidian.
Blueprint 29: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 4 ore and 20 clay. Each geode robot costs 4 ore and 8 obsidian.
Blueprint 30: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 18 clay. Each geode robot costs 3 ore and 13 obsidian.

5000
2022/inputs/day20.txt Normal file

File diff suppressed because it is too large Load Diff

1835
2022/inputs/day21.txt Normal file

File diff suppressed because it is too large Load Diff

202
2022/inputs/day22.txt Normal file

File diff suppressed because one or more lines are too long

0
2022/inputs/day23.txt Normal file
View File

0
2022/inputs/day24.txt Normal file
View File

0
2022/inputs/day25.txt Normal file
View File

14
2022/tests/day1.txt Normal file
View File

@@ -0,0 +1,14 @@
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000

146
2022/tests/day10.txt Normal file
View File

@@ -0,0 +1,146 @@
addx 15
addx -11
addx 6
addx -3
addx 5
addx -1
addx -8
addx 13
addx 4
noop
addx -1
addx 5
addx -1
addx 5
addx -1
addx 5
addx -1
addx 5
addx -1
addx -35
addx 1
addx 24
addx -19
addx 1
addx 16
addx -11
noop
noop
addx 21
addx -15
noop
noop
addx -3
addx 9
addx 1
addx -3
addx 8
addx 1
addx 5
noop
noop
noop
noop
noop
addx -36
noop
addx 1
addx 7
noop
noop
noop
addx 2
addx 6
noop
noop
noop
noop
noop
addx 1
noop
noop
addx 7
addx 1
noop
addx -13
addx 13
addx 7
noop
addx 1
addx -33
noop
noop
noop
addx 2
noop
noop
noop
addx 8
noop
addx -1
addx 2
addx 1
noop
addx 17
addx -9
addx 1
addx 1
addx -3
addx 11
noop
noop
addx 1
noop
addx 1
noop
noop
addx -13
addx -19
addx 1
addx 3
addx 26
addx -30
addx 12
addx -1
addx 3
addx 1
noop
noop
noop
addx -9
addx 18
addx 1
addx 2
noop
noop
addx 9
noop
noop
noop
addx -1
addx 2
addx -37
addx 1
addx 3
noop
addx 15
addx -21
addx 22
addx -6
addx 1
noop
addx 2
addx 1
noop
addx -10
noop
noop
addx 20
addx 1
addx 2
addx 2
addx -6
addx -11
noop
noop
noop

27
2022/tests/day11.txt Normal file
View File

@@ -0,0 +1,27 @@
Monkey 0:
Starting items: 79, 98
Operation: new = old * 19
Test: divisible by 23
If true: throw to monkey 2
If false: throw to monkey 3
Monkey 1:
Starting items: 54, 65, 75, 74
Operation: new = old + 6
Test: divisible by 19
If true: throw to monkey 2
If false: throw to monkey 0
Monkey 2:
Starting items: 79, 60, 97
Operation: new = old * old
Test: divisible by 13
If true: throw to monkey 1
If false: throw to monkey 3
Monkey 3:
Starting items: 74
Operation: new = old + 3
Test: divisible by 17
If true: throw to monkey 0
If false: throw to monkey 1

5
2022/tests/day12.txt Normal file
View File

@@ -0,0 +1,5 @@
Sabqponm
abcryxxl
accszExk
acctuvwj
abdefghi

23
2022/tests/day13.txt Normal file
View File

@@ -0,0 +1,23 @@
[1,1,3,1,1]
[1,1,5,1,1]
[[1],[2,3,4]]
[[1],4]
[9]
[[8,7,6]]
[[4,4],4,4]
[[4,4],4,4,4]
[7,7,7,7]
[7,7,7]
[]
[3]
[[[]]]
[[]]
[1,[2,[3,[4,[5,6,7]]]],8,9]
[1,[2,[3,[4,[5,6,0]]]],8,9]

2
2022/tests/day14.txt Normal file
View File

@@ -0,0 +1,2 @@
498,4 -> 498,6 -> 496,6
503,4 -> 502,4 -> 502,9 -> 494,9

14
2022/tests/day15.txt Normal file
View File

@@ -0,0 +1,14 @@
Sensor at x=2, y=18: closest beacon is at x=-2, y=15
Sensor at x=9, y=16: closest beacon is at x=10, y=16
Sensor at x=13, y=2: closest beacon is at x=15, y=3
Sensor at x=12, y=14: closest beacon is at x=10, y=16
Sensor at x=10, y=20: closest beacon is at x=10, y=16
Sensor at x=14, y=17: closest beacon is at x=10, y=16
Sensor at x=8, y=7: closest beacon is at x=2, y=10
Sensor at x=2, y=0: closest beacon is at x=2, y=10
Sensor at x=0, y=11: closest beacon is at x=2, y=10
Sensor at x=20, y=14: closest beacon is at x=25, y=17
Sensor at x=17, y=20: closest beacon is at x=21, y=22
Sensor at x=16, y=7: closest beacon is at x=15, y=3
Sensor at x=14, y=3: closest beacon is at x=15, y=3
Sensor at x=20, y=1: closest beacon is at x=15, y=3

10
2022/tests/day16.txt Normal file
View File

@@ -0,0 +1,10 @@
Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
Valve BB has flow rate=13; tunnels lead to valves CC, AA
Valve CC has flow rate=2; tunnels lead to valves DD, BB
Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
Valve EE has flow rate=3; tunnels lead to valves FF, DD
Valve FF has flow rate=0; tunnels lead to valves EE, GG
Valve GG has flow rate=0; tunnels lead to valves FF, HH
Valve HH has flow rate=22; tunnel leads to valve GG
Valve II has flow rate=0; tunnels lead to valves AA, JJ
Valve JJ has flow rate=21; tunnel leads to valve II

1
2022/tests/day17.txt Normal file
View File

@@ -0,0 +1 @@
>>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>

13
2022/tests/day18.txt Normal file
View File

@@ -0,0 +1,13 @@
2,2,2
1,2,2
3,2,2
2,1,2
2,3,2
2,2,1
2,2,3
2,2,4
2,2,6
1,2,5
3,2,5
2,1,5
2,3,5

2
2022/tests/day19.txt Normal file
View File

@@ -0,0 +1,2 @@
Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian.
Blueprint 2: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 8 clay. Each geode robot costs 3 ore and 12 obsidian.

3
2022/tests/day2.txt Normal file
View File

@@ -0,0 +1,3 @@
A Y
B X
C Z

7
2022/tests/day20.txt Normal file
View File

@@ -0,0 +1,7 @@
1
2
-3
3
-2
0
4

15
2022/tests/day21.txt Normal file
View File

@@ -0,0 +1,15 @@
root: pppw + sjmn
dbpl: 5
cczh: sllz + lgvd
zczc: 2
ptdq: humn - dvpt
dvpt: 3
lfqf: 4
humn: 5
ljgn: 2
sjmn: drzm * dbpl
sllz: 4
pppw: cczh / lfqf
lgvd: ljgn * ptdq
drzm: hmdt - zczc
hmdt: 32

14
2022/tests/day22.txt Normal file
View File

@@ -0,0 +1,14 @@
...#
.#..
#...
....
...#.......#
........#...
..#....#....
..........#.
...#....
.....#..
.#......
......#.
10R5L5R10L4R5L5

0
2022/tests/day23.txt Normal file
View File

0
2022/tests/day24.txt Normal file
View File

0
2022/tests/day25.txt Normal file
View File

6
2022/tests/day3.txt Normal file
View File

@@ -0,0 +1,6 @@
vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw

6
2022/tests/day4.txt Normal file
View File

@@ -0,0 +1,6 @@
2-4,6-8
2-3,4-5
5-7,7-9
2-8,3-7
6-6,4-6
2-6,4-8

9
2022/tests/day5.txt Normal file
View File

@@ -0,0 +1,9 @@
[D]
[N] [C]
[Z] [M] [P]
1 2 3
move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2

1
2022/tests/day6.txt Normal file
View File

@@ -0,0 +1 @@
mjqjpqmgbljsphdztnvjfqwrcgsmlb

23
2022/tests/day7.txt Normal file
View File

@@ -0,0 +1,23 @@
$ cd /
$ ls
dir a
14848514 b.txt
8504156 c.dat
dir d
$ cd a
$ ls
dir e
29116 f
2557 g
62596 h.lst
$ cd e
$ ls
584 i
$ cd ..
$ cd ..
$ cd d
$ ls
4060174 j
8033020 d.log
5626152 d.ext
7214296 k

5
2022/tests/day8.txt Normal file
View File

@@ -0,0 +1,5 @@
30373
25512
65332
33549
35390

8
2022/tests/day9.txt Normal file
View File

@@ -0,0 +1,8 @@
R 4
U 4
L 3
D 1
R 4
D 1
L 5
R 2

View File

@@ -1,3 +1,5 @@
param ($day) param([switch]$Test, $day)
Get-Content ".\2022\inputs\day$day.txt" | python ".\2022\day$day.py" $folder = $Test ? "tests" : "inputs"
Get-Content ".\2022\$folder\day$day.txt" | python ".\2022\day$day.py"