AoC 2023 Advent of Code (Python)
By telleropnul, December 31, 2023
Advent of Code (AoC) is an Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like. Go check it out: Advent of Code
Input files: adventofcode2023inputs.zip
2023 Day 25
python -m pip install -U networkx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| from math import prod
import networkx as nx
G = nx.Graph()
with open("input") as f:
for line in f:
v, adj = line.split(": ")
for a in adj.strip().split(" "):
G.add_edge(v, a)
G.remove_edges_from(nx.minimum_edge_cut(G))
print(prod([len(c) for c in nx.connected_components(G)])) |
from math import prod
import networkx as nx
G = nx.Graph()
with open("input") as f:
for line in f:
v, adj = line.split(": ")
for a in adj.strip().split(" "):
G.add_edge(v, a)
G.remove_edges_from(nx.minimum_edge_cut(G))
print(prod([len(c) for c in nx.connected_components(G)]))
2023 Day 24 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| import sympy as sp
X, Y, Z = 0, 1, 2
axr,ayr,azr = sp.symbols('axr,ayr,azr')
bzr,byr,bxr= sp.symbols('bzr,byr,bxr', positive=True, integer=True)
def expand_system(pos, speed, system):
system.append(sp.Eq((pos[X] - bxr) * (ayr - speed[Y]), (pos[Y] - byr) * (axr-speed[X])))
system.append(sp.Eq((pos[X] - bxr) * (azr - speed[Z]), (pos[Z] - bzr) * (axr-speed[X])))
linear_system = []
for line in open("input"):
pos, speed = line.strip().split('@')
expand_system(list(map(int, pos.split(','))), list(map(int, speed.split(','))), linear_system)
ans = sp.solve(linear_system, [azr,bzr,ayr,byr,axr,bxr])
print(ans)
print(ans[0][1]+ ans[0][3]+ans[0][5]) |
import sympy as sp
X, Y, Z = 0, 1, 2
axr,ayr,azr = sp.symbols('axr,ayr,azr')
bzr,byr,bxr= sp.symbols('bzr,byr,bxr', positive=True, integer=True)
def expand_system(pos, speed, system):
system.append(sp.Eq((pos[X] - bxr) * (ayr - speed[Y]), (pos[Y] - byr) * (axr-speed[X])))
system.append(sp.Eq((pos[X] - bxr) * (azr - speed[Z]), (pos[Z] - bzr) * (axr-speed[X])))
linear_system = []
for line in open("input"):
pos, speed = line.strip().split('@')
expand_system(list(map(int, pos.split(','))), list(map(int, speed.split(','))), linear_system)
ans = sp.solve(linear_system, [azr,bzr,ayr,byr,axr,bxr])
print(ans)
print(ans[0][1]+ ans[0][3]+ans[0][5])
2023 Day 24 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| import sympy as sp
X, Y, Z = 0, 1, 2
UPPER_LIMIT = 400000000000000
LOWER_LIMIT = 200000000000000
Y_PER_X, Y_AT_X0, SPEED_Y, POS_Y = 0, 1, 2, 3
# process input
data = []
for line in open("input"):
pos, speed = line.strip().split('@')
pos = [int(pos) for pos in pos.split(',')]
speed = [int(speed) for speed in speed.split(',')]
data.append((pos, speed))
#process data
lines = []
for pos, speed in data:
y_per_x = speed[Y] / speed[X]
y_at_x0 = pos[Y] - y_per_x * pos[X]
t_per_x = 1 / speed[X]
t_at_x0 = 0 - t_per_x * pos[X]
lines.append((y_per_x, y_at_x0, speed[Y], pos[Y]))
#intersection
def intersect(A, B):
if (B[Y_PER_X] - A[Y_PER_X]) == 0:
return False
x_val = (A[Y_AT_X0]-B[Y_AT_X0]) / (B[Y_PER_X] - A[Y_PER_X])
y_val = x_val * A[Y_PER_X] + A[Y_AT_X0]
if LOWER_LIMIT <= x_val <= UPPER_LIMIT and LOWER_LIMIT <= y_val <= UPPER_LIMIT:
if ((A[SPEED_Y] > 0 and y_val > A[POS_Y]) or (A[SPEED_Y] < 0 and y_val < A[POS_Y])) \
and ((B[SPEED_Y] > 0 and y_val > B[POS_Y]) or (B[SPEED_Y] < 0 and y_val < B[POS_Y])):
return True
return False
# run solution
counter = 0
for index, line1 in enumerate(lines):
for line2 in lines[index+1:]:
if intersect(line1, line2):
counter += 1
print(counter) |
import sympy as sp
X, Y, Z = 0, 1, 2
UPPER_LIMIT = 400000000000000
LOWER_LIMIT = 200000000000000
Y_PER_X, Y_AT_X0, SPEED_Y, POS_Y = 0, 1, 2, 3
# process input
data = []
for line in open("input"):
pos, speed = line.strip().split('@')
pos = [int(pos) for pos in pos.split(',')]
speed = [int(speed) for speed in speed.split(',')]
data.append((pos, speed))
#process data
lines = []
for pos, speed in data:
y_per_x = speed[Y] / speed[X]
y_at_x0 = pos[Y] - y_per_x * pos[X]
t_per_x = 1 / speed[X]
t_at_x0 = 0 - t_per_x * pos[X]
lines.append((y_per_x, y_at_x0, speed[Y], pos[Y]))
#intersection
def intersect(A, B):
if (B[Y_PER_X] - A[Y_PER_X]) == 0:
return False
x_val = (A[Y_AT_X0]-B[Y_AT_X0]) / (B[Y_PER_X] - A[Y_PER_X])
y_val = x_val * A[Y_PER_X] + A[Y_AT_X0]
if LOWER_LIMIT <= x_val <= UPPER_LIMIT and LOWER_LIMIT <= y_val <= UPPER_LIMIT:
if ((A[SPEED_Y] > 0 and y_val > A[POS_Y]) or (A[SPEED_Y] < 0 and y_val < A[POS_Y])) \
and ((B[SPEED_Y] > 0 and y_val > B[POS_Y]) or (B[SPEED_Y] < 0 and y_val < B[POS_Y])):
return True
return False
# run solution
counter = 0
for index, line1 in enumerate(lines):
for line2 in lines[index+1:]:
if intersect(line1, line2):
counter += 1
print(counter)
2023 Day 23 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| import sys
sys.setrecursionlimit(2**31 - 1)
def neighbors(i, j):
yield i - 1, j
yield i + 1, j
yield i, j - 1
yield i, j + 1
data = open('input').read().splitlines()
N, M = len(data), len(data[0])
grid = {(i, j) for i, l in enumerate(data) for j, x in enumerate(l) if x != "#"}
adj = {p: {q: 1 for q in neighbors(*p) if q in grid} for p in grid}
while True:
for p, qs in adj.items():
if len(qs) != 2:
continue
q1, q2 = adj[p]
adj[q1][q2] = adj[q2][q1] = adj[q1][p] + adj[p][q2]
del adj[p], adj[q1][p], adj[q2][p]
break
else:
break
visited = set()
def dfs(p):
if p == (N - 1, M - 2):
return 0
visited.add(p)
ans = float("-inf")
for n, dist in adj[p].items():
if n in visited:
continue
ans = max(ans, dist + dfs(n))
visited.remove(p)
return ans
print(dfs((0, 1))) |
import sys
sys.setrecursionlimit(2**31 - 1)
def neighbors(i, j):
yield i - 1, j
yield i + 1, j
yield i, j - 1
yield i, j + 1
data = open('input').read().splitlines()
N, M = len(data), len(data[0])
grid = {(i, j) for i, l in enumerate(data) for j, x in enumerate(l) if x != "#"}
adj = {p: {q: 1 for q in neighbors(*p) if q in grid} for p in grid}
while True:
for p, qs in adj.items():
if len(qs) != 2:
continue
q1, q2 = adj[p]
adj[q1][q2] = adj[q2][q1] = adj[q1][p] + adj[p][q2]
del adj[p], adj[q1][p], adj[q2][p]
break
else:
break
visited = set()
def dfs(p):
if p == (N - 1, M - 2):
return 0
visited.add(p)
ans = float("-inf")
for n, dist in adj[p].items():
if n in visited:
continue
ans = max(ans, dist + dfs(n))
visited.remove(p)
return ans
print(dfs((0, 1)))
2023 Day 23 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| import sys
sys.setrecursionlimit(2**31 - 1)
def neighbors(i, j):
yield i - 1, j
yield i + 1, j
yield i, j - 1
yield i, j + 1
data = open('input').read().splitlines()
N, M = len(data), len(data[0])
grid = {(i, j): x for i, l in enumerate(data) for j, x in enumerate(l) if x != "#"}
visited = set()
def dfs(p):
if p == (N - 1, M - 2):
return 0
visited.add(p)
ans = float("-inf")
ns = list(neighbors(*p))
if grid[p] in "^v<>":
ns = [ns["^v<>".index(grid[p])]]
for n in ns:
if n not in grid or n in visited:
continue
ans = max(ans, 1 + dfs(n))
visited.remove(p)
return ans
print(dfs((0, 1))) |
import sys
sys.setrecursionlimit(2**31 - 1)
def neighbors(i, j):
yield i - 1, j
yield i + 1, j
yield i, j - 1
yield i, j + 1
data = open('input').read().splitlines()
N, M = len(data), len(data[0])
grid = {(i, j): x for i, l in enumerate(data) for j, x in enumerate(l) if x != "#"}
visited = set()
def dfs(p):
if p == (N - 1, M - 2):
return 0
visited.add(p)
ans = float("-inf")
ns = list(neighbors(*p))
if grid[p] in "^v<>":
ns = [ns["^v<>".index(grid[p])]]
for n in ns:
if n not in grid or n in visited:
continue
ans = max(ans, 1 + dfs(n))
visited.remove(p)
return ans
print(dfs((0, 1)))
2023 Day 22 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
| from itertools import zip_longest
def irange(start, stop):
if start <= stop:
return range(start, stop + 1)
else:
return range(start, stop - 1, -1)
def draw(a, b):
i1, j1, k1 = a
i2, j2, k2 = b
result = zip_longest(irange(i1, i2), irange(j1, j2), irange(k1, k2))
return set((a or i1, b or j1, c or k1) for a, b, c in result)
def fall(tiles, brick):
while True:
down = {(x, y, z - 1) for x, y, z in brick}
if any(z == 0 for _, _, z in down) or down & tiles:
return brick
brick = down
tiles = set()
bricks = []
data = open('input').read().splitlines()
for line in data:
a, b = [[int(x) for x in r.split(",")] for r in line.split("~")]
brick = draw(a, b)
bricks.append(brick)
tiles |= brick
bricks.sort(key=lambda p: min(z for _, _, z in p))
for i, brick in enumerate(bricks):
tiles -= brick
bricks[i] = fall(tiles, brick)
tiles |= bricks[i]
ans = 0
for brick in bricks:
without = tiles - brick
for other in bricks:
if other == brick:
continue
without -= other
if fall(without, other) != other:
ans += 1
else:
without |= other
print(ans) |
from itertools import zip_longest
def irange(start, stop):
if start <= stop:
return range(start, stop + 1)
else:
return range(start, stop - 1, -1)
def draw(a, b):
i1, j1, k1 = a
i2, j2, k2 = b
result = zip_longest(irange(i1, i2), irange(j1, j2), irange(k1, k2))
return set((a or i1, b or j1, c or k1) for a, b, c in result)
def fall(tiles, brick):
while True:
down = {(x, y, z - 1) for x, y, z in brick}
if any(z == 0 for _, _, z in down) or down & tiles:
return brick
brick = down
tiles = set()
bricks = []
data = open('input').read().splitlines()
for line in data:
a, b = [[int(x) for x in r.split(",")] for r in line.split("~")]
brick = draw(a, b)
bricks.append(brick)
tiles |= brick
bricks.sort(key=lambda p: min(z for _, _, z in p))
for i, brick in enumerate(bricks):
tiles -= brick
bricks[i] = fall(tiles, brick)
tiles |= bricks[i]
ans = 0
for brick in bricks:
without = tiles - brick
for other in bricks:
if other == brick:
continue
without -= other
if fall(without, other) != other:
ans += 1
else:
without |= other
print(ans)
2023 Day 22 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| from itertools import zip_longest
def irange(start, stop):
if start <= stop:
return range(start, stop + 1)
else:
return range(start, stop - 1, -1)
def draw(a, b):
i1, j1, k1 = a
i2, j2, k2 = b
result = zip_longest(irange(i1, i2), irange(j1, j2), irange(k1, k2))
return set((a or i1, b or j1, c or k1) for a, b, c in result)
def fall(tiles, brick):
while True:
down = {(x, y, z - 1) for x, y, z in brick}
if any(z == 0 for _, _, z in down) or down & tiles:
return brick
brick = down
tiles = set()
bricks = []
data = open('input').read().splitlines()
for line in data:
a, b = [[int(x) for x in r.split(",")] for r in line.split("~")]
brick = draw(a, b)
bricks.append(brick)
tiles |= brick
bricks.sort(key=lambda p: min(z for _, _, z in p))
for i, brick in enumerate(bricks):
tiles -= brick
bricks[i] = fall(tiles, brick)
tiles |= bricks[i]
ans = 0
for brick in bricks:
without = tiles - brick
for other in bricks:
if other == brick:
continue
without -= other
if fall(without, other) != other:
break
without |= other
else:
ans += 1
print(ans) |
from itertools import zip_longest
def irange(start, stop):
if start <= stop:
return range(start, stop + 1)
else:
return range(start, stop - 1, -1)
def draw(a, b):
i1, j1, k1 = a
i2, j2, k2 = b
result = zip_longest(irange(i1, i2), irange(j1, j2), irange(k1, k2))
return set((a or i1, b or j1, c or k1) for a, b, c in result)
def fall(tiles, brick):
while True:
down = {(x, y, z - 1) for x, y, z in brick}
if any(z == 0 for _, _, z in down) or down & tiles:
return brick
brick = down
tiles = set()
bricks = []
data = open('input').read().splitlines()
for line in data:
a, b = [[int(x) for x in r.split(",")] for r in line.split("~")]
brick = draw(a, b)
bricks.append(brick)
tiles |= brick
bricks.sort(key=lambda p: min(z for _, _, z in p))
for i, brick in enumerate(bricks):
tiles -= brick
bricks[i] = fall(tiles, brick)
tiles |= bricks[i]
ans = 0
for brick in bricks:
without = tiles - brick
for other in bricks:
if other == brick:
continue
without -= other
if fall(without, other) != other:
break
without |= other
else:
ans += 1
print(ans)
2023 Day 21 Part 01 + 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
| from collections import deque
import numpy as np
def zxcv(fname, n):
dat1 = open('input').read().splitlines()
if n > 64:
data = []
for i in range(5):
for line in dat1:
data.append(5 * line.replace("S", "."))
else:
data = dat1
width = len(data[0])
height = len(data)
q = deque()
sx, sy = width // 2, height // 2
q.append((sx, sy, 0))
s64 = set()
visited = set()
while q:
x, y, steps = q.popleft()
if (x, y, steps) in visited:
continue
visited.add((x, y, steps))
if steps == n:
s64.add((x, y))
else:
if x >= 0:
if data[y][x - 1] != "#":
q.append((x - 1, y, steps + 1))
if x < width - 1:
if data[y][x + 1] != "#":
q.append((x + 1, y, steps + 1))
if y >= 0:
if data[y - 1][x] != "#":
q.append((x, y - 1, steps + 1))
if y < height - 1:
if data[y + 1][x] != "#":
q.append((x, y + 1, steps + 1))
if n == 64:
for y, line in enumerate(data):
ll = list(line)
for sx, sy in s64:
if sy == y:
ll[sx] = "O"
print("".join(ll))
return len(s64)
zxcv("input", 2)
print("part 1:", zxcv("input", 64))
# polynomial extrapolation
a0 = zxcv("input", 65)
a1 = zxcv("input", 65 + 131)
a2 = zxcv("input", 65 + 2 * 131)
vandermonde = np.matrix([[0, 0, 1], [1, 1, 1], [4, 2, 1]])
b = np.array([a0, a1, a2])
x = np.linalg.solve(vandermonde, b).astype(np.int64)
# note that 26501365 = 202300 * 131 + 65 where 131 is the dimension of the grid
n = 202300
print("part 2:", x[0] * n * n + x[1] * n + x[2]) |
from collections import deque
import numpy as np
def zxcv(fname, n):
dat1 = open('input').read().splitlines()
if n > 64:
data = []
for i in range(5):
for line in dat1:
data.append(5 * line.replace("S", "."))
else:
data = dat1
width = len(data[0])
height = len(data)
q = deque()
sx, sy = width // 2, height // 2
q.append((sx, sy, 0))
s64 = set()
visited = set()
while q:
x, y, steps = q.popleft()
if (x, y, steps) in visited:
continue
visited.add((x, y, steps))
if steps == n:
s64.add((x, y))
else:
if x >= 0:
if data[y][x - 1] != "#":
q.append((x - 1, y, steps + 1))
if x < width - 1:
if data[y][x + 1] != "#":
q.append((x + 1, y, steps + 1))
if y >= 0:
if data[y - 1][x] != "#":
q.append((x, y - 1, steps + 1))
if y < height - 1:
if data[y + 1][x] != "#":
q.append((x, y + 1, steps + 1))
if n == 64:
for y, line in enumerate(data):
ll = list(line)
for sx, sy in s64:
if sy == y:
ll[sx] = "O"
print("".join(ll))
return len(s64)
zxcv("input", 2)
print("part 1:", zxcv("input", 64))
# polynomial extrapolation
a0 = zxcv("input", 65)
a1 = zxcv("input", 65 + 131)
a2 = zxcv("input", 65 + 2 * 131)
vandermonde = np.matrix([[0, 0, 1], [1, 1, 1], [4, 2, 1]])
b = np.array([a0, a1, a2])
x = np.linalg.solve(vandermonde, b).astype(np.int64)
# note that 26501365 = 202300 * 131 + 65 where 131 is the dimension of the grid
n = 202300
print("part 2:", x[0] * n * n + x[1] * n + x[2])
2023 Day 21 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| def do(get, start, t):
bfs = set([start])
for _ in range(t):
new_bfs = set()
for i, j in bfs:
new_bfs.add((i, j - 1))
new_bfs.add((i, j + 1))
new_bfs.add((i - 1, j))
new_bfs.add((i + 1, j))
new_bfs = {(i, j) for i, j in new_bfs if get(i, j) == "."}
bfs = new_bfs
return len(bfs)
data = open('input').read().splitlines()
grid = {(i, j): x for i, line in enumerate(data) for j, x in enumerate(line.strip())}
start = next(p for p, x in grid.items() if x == "S")
grid[start] = "."
print(do(lambda i, j: grid.get((i, j)), start, 64)) |
def do(get, start, t):
bfs = set([start])
for _ in range(t):
new_bfs = set()
for i, j in bfs:
new_bfs.add((i, j - 1))
new_bfs.add((i, j + 1))
new_bfs.add((i - 1, j))
new_bfs.add((i + 1, j))
new_bfs = {(i, j) for i, j in new_bfs if get(i, j) == "."}
bfs = new_bfs
return len(bfs)
data = open('input').read().splitlines()
grid = {(i, j): x for i, line in enumerate(data) for j, x in enumerate(line.strip())}
start = next(p for p, x in grid.items() if x == "S")
grid[start] = "."
print(do(lambda i, j: grid.get((i, j)), start, 64))
2023 Day 20 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
| import math
from collections import deque
class Module:
def __init__(self, name, type, outputs):
self.name = name
self.type = type
self.outputs = outputs
if type == "%":
self.memory = "off"
else:
self.memory = {}
def __repr__(self):
return self.name + "{type=" + self.type + ",outputs=" + ",".join(self.outputs) + ",memory=" + str(self.memory) + "}"
modules = {}
broadcast_targets = []
data = open('input').read().splitlines()
for line in data:
left, right = line.strip().split(" -> ")
outputs = right.split(", ")
if left == "broadcaster":
broadcast_targets = outputs
else:
type = left[0]
name = left[1:]
modules[name] = Module(name, type, outputs)
for name, module in modules.items():
for output in module.outputs:
if output in modules and modules[output].type == "&":
modules[output].memory[name] = "lo"
(feed,) = [name for name, module in modules.items() if "rx" in module.outputs]
cycle_lengths = {}
seen = {name: 0 for name, module in modules.items() if feed in module.outputs}
presses = 0
while True:
presses += 1
q = deque([("broadcaster", x, "lo") for x in broadcast_targets])
while q:
origin, target, pulse = q.popleft()
if target not in modules:
continue
module = modules[target]
if module.name == feed and pulse == "hi":
seen[origin] += 1
if origin not in cycle_lengths:
cycle_lengths[origin] = presses
else:
assert presses == seen[origin] * cycle_lengths[origin]
if all(seen.values()):
x = 1
for cycle_length in cycle_lengths.values():
x = x * cycle_length // math.gcd(x, cycle_length)
print(x)
exit(0)
if module.type == "%":
if pulse == "lo":
module.memory = "on" if module.memory == "off" else "off"
outgoing = "hi" if module.memory == "on" else "lo"
for x in module.outputs:
q.append((module.name, x, outgoing))
else:
module.memory[origin] = pulse
outgoing = "lo" if all(x == "hi" for x in module.memory.values()) else "hi"
for x in module.outputs:
q.append((module.name, x, outgoing)) |
import math
from collections import deque
class Module:
def __init__(self, name, type, outputs):
self.name = name
self.type = type
self.outputs = outputs
if type == "%":
self.memory = "off"
else:
self.memory = {}
def __repr__(self):
return self.name + "{type=" + self.type + ",outputs=" + ",".join(self.outputs) + ",memory=" + str(self.memory) + "}"
modules = {}
broadcast_targets = []
data = open('input').read().splitlines()
for line in data:
left, right = line.strip().split(" -> ")
outputs = right.split(", ")
if left == "broadcaster":
broadcast_targets = outputs
else:
type = left[0]
name = left[1:]
modules[name] = Module(name, type, outputs)
for name, module in modules.items():
for output in module.outputs:
if output in modules and modules[output].type == "&":
modules[output].memory[name] = "lo"
(feed,) = [name for name, module in modules.items() if "rx" in module.outputs]
cycle_lengths = {}
seen = {name: 0 for name, module in modules.items() if feed in module.outputs}
presses = 0
while True:
presses += 1
q = deque([("broadcaster", x, "lo") for x in broadcast_targets])
while q:
origin, target, pulse = q.popleft()
if target not in modules:
continue
module = modules[target]
if module.name == feed and pulse == "hi":
seen[origin] += 1
if origin not in cycle_lengths:
cycle_lengths[origin] = presses
else:
assert presses == seen[origin] * cycle_lengths[origin]
if all(seen.values()):
x = 1
for cycle_length in cycle_lengths.values():
x = x * cycle_length // math.gcd(x, cycle_length)
print(x)
exit(0)
if module.type == "%":
if pulse == "lo":
module.memory = "on" if module.memory == "off" else "off"
outgoing = "hi" if module.memory == "on" else "lo"
for x in module.outputs:
q.append((module.name, x, outgoing))
else:
module.memory[origin] = pulse
outgoing = "lo" if all(x == "hi" for x in module.memory.values()) else "hi"
for x in module.outputs:
q.append((module.name, x, outgoing))
2023 Day 20 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
| from collections import deque
class Module:
def __init__(self, name, type, outputs):
self.name = name
self.type = type
self.outputs = outputs
if type == "%":
self.memory = "off"
else:
self.memory = {}
def __repr__(self):
return self.name + "{type=" + self.type + ",outputs=" + ",".join(self.outputs) + ",memory=" + str(self.memory) + "}"
modules = {}
broadcast_targets = []
data = open('input').read().splitlines()
for line in data:
left, right = line.strip().split(" -> ")
outputs = right.split(", ")
if left == "broadcaster":
broadcast_targets = outputs
else:
type = left[0]
name = left[1:]
modules[name] = Module(name, type, outputs)
for name, module in modules.items():
for output in module.outputs:
if output in modules and modules[output].type == "&":
modules[output].memory[name] = "lo"
lo = hi = 0
for _ in range(1000):
lo += 1
q = deque([("broadcaster", x, "lo") for x in broadcast_targets])
while q:
origin, target, pulse = q.popleft()
if pulse == "lo":
lo += 1
else:
hi += 1
if target not in modules:
continue
module = modules[target]
if module.type == "%":
if pulse == "lo":
module.memory = "on" if module.memory == "off" else "off"
outgoing = "hi" if module.memory == "on" else "lo"
for x in module.outputs:
q.append((module.name, x, outgoing))
else:
module.memory[origin] = pulse
outgoing = "lo" if all(x == "hi" for x in module.memory.values()) else "hi"
for x in module.outputs:
q.append((module.name, x, outgoing))
print(lo * hi) |
from collections import deque
class Module:
def __init__(self, name, type, outputs):
self.name = name
self.type = type
self.outputs = outputs
if type == "%":
self.memory = "off"
else:
self.memory = {}
def __repr__(self):
return self.name + "{type=" + self.type + ",outputs=" + ",".join(self.outputs) + ",memory=" + str(self.memory) + "}"
modules = {}
broadcast_targets = []
data = open('input').read().splitlines()
for line in data:
left, right = line.strip().split(" -> ")
outputs = right.split(", ")
if left == "broadcaster":
broadcast_targets = outputs
else:
type = left[0]
name = left[1:]
modules[name] = Module(name, type, outputs)
for name, module in modules.items():
for output in module.outputs:
if output in modules and modules[output].type == "&":
modules[output].memory[name] = "lo"
lo = hi = 0
for _ in range(1000):
lo += 1
q = deque([("broadcaster", x, "lo") for x in broadcast_targets])
while q:
origin, target, pulse = q.popleft()
if pulse == "lo":
lo += 1
else:
hi += 1
if target not in modules:
continue
module = modules[target]
if module.type == "%":
if pulse == "lo":
module.memory = "on" if module.memory == "off" else "off"
outgoing = "hi" if module.memory == "on" else "lo"
for x in module.outputs:
q.append((module.name, x, outgoing))
else:
module.memory[origin] = pulse
outgoing = "lo" if all(x == "hi" for x in module.memory.values()) else "hi"
for x in module.outputs:
q.append((module.name, x, outgoing))
print(lo * hi)
2023 Day 19 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| from collections import deque
class Thing:
def __init__(self, name, range=None):
self.name = name
self.range = range
def __lt__(self, other):
return Thing(self.name, range(1, other))
def __gt__(self, other):
return Thing(self.name, range(other + 1, 4001))
def split_range(a, b):
sect = range(max(a.start, b.start), min(a.stop, b.stop))
left = range(a.start, sect.start)
right = range(sect.stop, a.stop)
return left, sect, right
data = open('input').read().split("\n\n")
workflows, _ = data
names = {"A": "A", "R": "R"}
for line in workflows.splitlines():
name, conds = line.split("{")
names[name] = name
maps = {}
for line in workflows.splitlines():
name, conds = line.split("{")
x, m, a, s = Thing("x"), Thing("m"), Thing("a"), Thing("s")
conds = "{" + ",None:".join(conds.rsplit(",", 1))
maps[name] = eval(conds, globals(), names)
q = deque()
q.append(("in", *[range(1, 4001) for _ in range(4)]))
ans = 0
while q:
at, x, m, a, s = q.popleft()
if at == "R":
continue
if at == "A":
ans += len(x) * len(m) * len(a) * len(s)
continue
for k, v in maps[at].items():
if k is None:
q.append((v, x, m, a, s))
continue
left, sect, right = split_range(eval(k.name), k.range)
assert not (left and right)
exec(f"global {k.name}; {k.name} = sect")
q.append((v, x, m, a, s))
exec(f"global {k.name}; {k.name} = left or right")
print(ans) |
from collections import deque
class Thing:
def __init__(self, name, range=None):
self.name = name
self.range = range
def __lt__(self, other):
return Thing(self.name, range(1, other))
def __gt__(self, other):
return Thing(self.name, range(other + 1, 4001))
def split_range(a, b):
sect = range(max(a.start, b.start), min(a.stop, b.stop))
left = range(a.start, sect.start)
right = range(sect.stop, a.stop)
return left, sect, right
data = open('input').read().split("\n\n")
workflows, _ = data
names = {"A": "A", "R": "R"}
for line in workflows.splitlines():
name, conds = line.split("{")
names[name] = name
maps = {}
for line in workflows.splitlines():
name, conds = line.split("{")
x, m, a, s = Thing("x"), Thing("m"), Thing("a"), Thing("s")
conds = "{" + ",None:".join(conds.rsplit(",", 1))
maps[name] = eval(conds, globals(), names)
q = deque()
q.append(("in", *[range(1, 4001) for _ in range(4)]))
ans = 0
while q:
at, x, m, a, s = q.popleft()
if at == "R":
continue
if at == "A":
ans += len(x) * len(m) * len(a) * len(s)
continue
for k, v in maps[at].items():
if k is None:
q.append((v, x, m, a, s))
continue
left, sect, right = split_range(eval(k.name), k.range)
assert not (left and right)
exec(f"global {k.name}; {k.name} = sect")
q.append((v, x, m, a, s))
exec(f"global {k.name}; {k.name} = left or right")
print(ans)
2023 Day 19 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| from collections import deque
data = open('input').read().split("\n\n")
workflows, instructions = data
names = {"A": "A", "R": "R"}
maps = {}
for line in workflows.splitlines():
name, conds = line[:-1].split("{")
conds = conds.replace(":", " and ").replace(",", " or ")
maps[name] = conds
names[name] = name
ans = 0
for inst in instructions.splitlines():
x, m, a, s = [int(x[2:]) for x in inst[1:-1].split(",")]
at = "in"
while at not in "AR":
at = eval(maps[at], names, locals())
if at == "A":
ans += x + m + a + s
print(ans) |
from collections import deque
data = open('input').read().split("\n\n")
workflows, instructions = data
names = {"A": "A", "R": "R"}
maps = {}
for line in workflows.splitlines():
name, conds = line[:-1].split("{")
conds = conds.replace(":", " and ").replace(",", " or ")
maps[name] = conds
names[name] = name
ans = 0
for inst in instructions.splitlines():
x, m, a, s = [int(x[2:]) for x in inst[1:-1].split(",")]
at = "in"
while at not in "AR":
at = eval(maps[at], names, locals())
if at == "A":
ans += x + m + a + s
print(ans)
2023 Day 18 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| class _(tuple):
def __add__(self, other):
return _(x + y for x, y in zip(self, other))
def __mul__(self, other):
return _(x * other for x in self)
NORTH = _((-1, 0))
SOUTH = _((1, 0))
EAST = _((0, 1))
WEST = _((0, -1))
DIRECTIONS = {
"U": NORTH,
"D": SOUTH,
"R": EAST,
"L": WEST,
}
pos = _((0, 0))
vertices = [pos]
boundary = 0
data = open('input').read().splitlines()
for line in data:
dir, num, hex = line.split()
dir = "RDLU"[int(hex[-2])]
num = int(hex[2:-2], 16)
vertices.append(vertices[-1] + DIRECTIONS[dir] * num)
boundary += num
xs, ys = zip(*vertices)
left = sum(a * b for a, b in zip(xs, ys[1:]))
right = sum(a * b for a, b in zip(ys, xs[1:]))
print (abs(left - right) // 2 + boundary // 2 + 1) |
class _(tuple):
def __add__(self, other):
return _(x + y for x, y in zip(self, other))
def __mul__(self, other):
return _(x * other for x in self)
NORTH = _((-1, 0))
SOUTH = _((1, 0))
EAST = _((0, 1))
WEST = _((0, -1))
DIRECTIONS = {
"U": NORTH,
"D": SOUTH,
"R": EAST,
"L": WEST,
}
pos = _((0, 0))
vertices = [pos]
boundary = 0
data = open('input').read().splitlines()
for line in data:
dir, num, hex = line.split()
dir = "RDLU"[int(hex[-2])]
num = int(hex[2:-2], 16)
vertices.append(vertices[-1] + DIRECTIONS[dir] * num)
boundary += num
xs, ys = zip(*vertices)
left = sum(a * b for a, b in zip(xs, ys[1:]))
right = sum(a * b for a, b in zip(ys, xs[1:]))
print (abs(left - right) // 2 + boundary // 2 + 1)
2023 Day 18 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| class _(tuple):
def __add__(self, other):
return _(x + y for x, y in zip(self, other))
def __mul__(self, other):
return _(x * other for x in self)
NORTH = _((-1, 0))
SOUTH = _((1, 0))
EAST = _((0, 1))
WEST = _((0, -1))
DIRECTIONS = {
"U": NORTH,
"D": SOUTH,
"R": EAST,
"L": WEST,
}
pos = _((0, 0))
vertices = [pos]
boundary = 0
data = open('input').read().splitlines()
for line in data:
dir, num, hex = line.split()
num = int(num)
vertices.append(vertices[-1] + DIRECTIONS[dir] * num)
boundary += num
xs, ys = zip(*vertices)
left = sum(a * b for a, b in zip(xs, ys[1:]))
right = sum(a * b for a, b in zip(ys, xs[1:]))
print (abs(left - right) // 2 + boundary // 2 + 1) |
class _(tuple):
def __add__(self, other):
return _(x + y for x, y in zip(self, other))
def __mul__(self, other):
return _(x * other for x in self)
NORTH = _((-1, 0))
SOUTH = _((1, 0))
EAST = _((0, 1))
WEST = _((0, -1))
DIRECTIONS = {
"U": NORTH,
"D": SOUTH,
"R": EAST,
"L": WEST,
}
pos = _((0, 0))
vertices = [pos]
boundary = 0
data = open('input').read().splitlines()
for line in data:
dir, num, hex = line.split()
num = int(num)
vertices.append(vertices[-1] + DIRECTIONS[dir] * num)
boundary += num
xs, ys = zip(*vertices)
left = sum(a * b for a, b in zip(xs, ys[1:]))
right = sum(a * b for a, b in zip(ys, xs[1:]))
print (abs(left - right) // 2 + boundary // 2 + 1)
2023 Day 17 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
| from collections import defaultdict
from heapq import heappop, heappush
def dijkstra(grid, s, e, neighbors, max_run):
start = s, (0, 0)
dist = defaultdict(lambda: float("inf"), {start: 0})
pq = [(0, start)]
while pq:
_, u = heappop(pq)
if not all(-max_run <= n <= max_run for n in u[1]):
continue
if u[0] == e:
return dist[u]
for v in neighbors(*u):
if v[0] not in grid:
continue
alt = dist[u] + grid[v[0]]
if alt < dist[v]:
dist[v] = alt
heappush(pq, (alt, v))
def neighbors1(p, run):
r, c = p
nr, nc = run
if nr == 0:
yield (r - 1, c), (-1, 0)
yield (r + 1, c), (1, 0)
if nc == 0:
yield (r, c - 1), (0, -1)
yield (r, c + 1), (0, 1)
if nr > 0:
yield (r + 1, c), (nr + 1, 0)
if nr < 0:
yield (r - 1, c), (nr - 1, 0)
if nc > 0:
yield (r, c + 1), (0, nc + 1)
if nc < 0:
yield (r, c - 1), (0, nc - 1)
def neighbors2(p, run):
r, c = p
nr, nc = run
if 0 < nr < 4:
yield (r + 1, c), (nr + 1, 0)
elif -4 < nr < 0:
yield (r - 1, c), (nr - 1, 0)
elif 0 < nc < 4:
yield (r, c + 1), (0, nc + 1)
elif -4 < nc < 0:
yield (r, c - 1), (0, nc - 1)
else:
yield from neighbors1(p, run)
data = open('input').read().splitlines()
grid = {(i, j): int(x) for i, line in enumerate(data) for j, x in enumerate(line)}
n, m = len(data), len(data[0])
print (dijkstra(grid, (0, 0), (n - 1, m - 1), neighbors2, 10)) |
from collections import defaultdict
from heapq import heappop, heappush
def dijkstra(grid, s, e, neighbors, max_run):
start = s, (0, 0)
dist = defaultdict(lambda: float("inf"), {start: 0})
pq = [(0, start)]
while pq:
_, u = heappop(pq)
if not all(-max_run <= n <= max_run for n in u[1]):
continue
if u[0] == e:
return dist[u]
for v in neighbors(*u):
if v[0] not in grid:
continue
alt = dist[u] + grid[v[0]]
if alt < dist[v]:
dist[v] = alt
heappush(pq, (alt, v))
def neighbors1(p, run):
r, c = p
nr, nc = run
if nr == 0:
yield (r - 1, c), (-1, 0)
yield (r + 1, c), (1, 0)
if nc == 0:
yield (r, c - 1), (0, -1)
yield (r, c + 1), (0, 1)
if nr > 0:
yield (r + 1, c), (nr + 1, 0)
if nr < 0:
yield (r - 1, c), (nr - 1, 0)
if nc > 0:
yield (r, c + 1), (0, nc + 1)
if nc < 0:
yield (r, c - 1), (0, nc - 1)
def neighbors2(p, run):
r, c = p
nr, nc = run
if 0 < nr < 4:
yield (r + 1, c), (nr + 1, 0)
elif -4 < nr < 0:
yield (r - 1, c), (nr - 1, 0)
elif 0 < nc < 4:
yield (r, c + 1), (0, nc + 1)
elif -4 < nc < 0:
yield (r, c - 1), (0, nc - 1)
else:
yield from neighbors1(p, run)
data = open('input').read().splitlines()
grid = {(i, j): int(x) for i, line in enumerate(data) for j, x in enumerate(line)}
n, m = len(data), len(data[0])
print (dijkstra(grid, (0, 0), (n - 1, m - 1), neighbors2, 10))
2023 Day 17 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| from collections import defaultdict
from heapq import heappop, heappush
def dijkstra(grid, s, e, neighbors, max_run):
start = s, (0, 0)
dist = defaultdict(lambda: float("inf"), {start: 0})
pq = [(0, start)]
while pq:
_, u = heappop(pq)
if not all(-max_run <= n <= max_run for n in u[1]):
continue
if u[0] == e:
return dist[u]
for v in neighbors(*u):
if v[0] not in grid:
continue
alt = dist[u] + grid[v[0]]
if alt < dist[v]:
dist[v] = alt
heappush(pq, (alt, v))
def neighbors1(p, run):
r, c = p
nr, nc = run
if nr == 0:
yield (r - 1, c), (-1, 0)
yield (r + 1, c), (1, 0)
if nc == 0:
yield (r, c - 1), (0, -1)
yield (r, c + 1), (0, 1)
if nr > 0:
yield (r + 1, c), (nr + 1, 0)
if nr < 0:
yield (r - 1, c), (nr - 1, 0)
if nc > 0:
yield (r, c + 1), (0, nc + 1)
if nc < 0:
yield (r, c - 1), (0, nc - 1)
data = open('input').read().splitlines()
grid = {(i, j): int(x) for i, line in enumerate(data) for j, x in enumerate(line)}
n, m = len(data), len(data[0])
print(dijkstra(grid, (0, 0), (n - 1, m - 1), neighbors1, 3)) |
from collections import defaultdict
from heapq import heappop, heappush
def dijkstra(grid, s, e, neighbors, max_run):
start = s, (0, 0)
dist = defaultdict(lambda: float("inf"), {start: 0})
pq = [(0, start)]
while pq:
_, u = heappop(pq)
if not all(-max_run <= n <= max_run for n in u[1]):
continue
if u[0] == e:
return dist[u]
for v in neighbors(*u):
if v[0] not in grid:
continue
alt = dist[u] + grid[v[0]]
if alt < dist[v]:
dist[v] = alt
heappush(pq, (alt, v))
def neighbors1(p, run):
r, c = p
nr, nc = run
if nr == 0:
yield (r - 1, c), (-1, 0)
yield (r + 1, c), (1, 0)
if nc == 0:
yield (r, c - 1), (0, -1)
yield (r, c + 1), (0, 1)
if nr > 0:
yield (r + 1, c), (nr + 1, 0)
if nr < 0:
yield (r - 1, c), (nr - 1, 0)
if nc > 0:
yield (r, c + 1), (0, nc + 1)
if nc < 0:
yield (r, c - 1), (0, nc - 1)
data = open('input').read().splitlines()
grid = {(i, j): int(x) for i, line in enumerate(data) for j, x in enumerate(line)}
n, m = len(data), len(data[0])
print(dijkstra(grid, (0, 0), (n - 1, m - 1), neighbors1, 3))
2023 Day 16 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
| from collections import deque
class _(tuple):
def __add__(self, other):
return _(x + y for x, y in zip(self, other))
NORTH = _((-1, 0))
SOUTH = _((1, 0))
EAST = _((0, 1))
WEST = _((0, -1))
def do(grid, start, dstart):
q = deque([(_(start), _(dstart))])
visited = set()
while q:
i, di = q.popleft()
if i not in grid or (i, di) in visited:
continue
visited.add((i, di))
match grid[i]:
case "/":
di = -di[1], -di[0]
q.append((i + di, di))
case "\\":
di = di[1], di[0]
q.append((i + di, di))
case "|" if di[0] == 0:
q.append((i + NORTH, NORTH))
q.append((i + SOUTH, SOUTH))
case "-" if di[1] == 0:
q.append((i + EAST, EAST))
q.append((i + WEST, WEST))
case _:
q.append((i + di, di))
return len(set(p for p, _ in visited))
data = open('input').read().splitlines()
grid = {(i, j): x for i, row in enumerate(data) for j, x in enumerate(row)}
n, m = len(data), len(data[0])
ans = 0
for i in range(n):
ans = max(ans, do(grid, (0, i), SOUTH))
ans = max(ans, do(grid, (n - 1, i), NORTH))
for i in range(m):
ans = max(ans, do(grid, (i, 0), EAST))
ans = max(ans, do(grid, (i, m - 1), WEST))
print(ans) |
from collections import deque
class _(tuple):
def __add__(self, other):
return _(x + y for x, y in zip(self, other))
NORTH = _((-1, 0))
SOUTH = _((1, 0))
EAST = _((0, 1))
WEST = _((0, -1))
def do(grid, start, dstart):
q = deque([(_(start), _(dstart))])
visited = set()
while q:
i, di = q.popleft()
if i not in grid or (i, di) in visited:
continue
visited.add((i, di))
match grid[i]:
case "/":
di = -di[1], -di[0]
q.append((i + di, di))
case "\\":
di = di[1], di[0]
q.append((i + di, di))
case "|" if di[0] == 0:
q.append((i + NORTH, NORTH))
q.append((i + SOUTH, SOUTH))
case "-" if di[1] == 0:
q.append((i + EAST, EAST))
q.append((i + WEST, WEST))
case _:
q.append((i + di, di))
return len(set(p for p, _ in visited))
data = open('input').read().splitlines()
grid = {(i, j): x for i, row in enumerate(data) for j, x in enumerate(row)}
n, m = len(data), len(data[0])
ans = 0
for i in range(n):
ans = max(ans, do(grid, (0, i), SOUTH))
ans = max(ans, do(grid, (n - 1, i), NORTH))
for i in range(m):
ans = max(ans, do(grid, (i, 0), EAST))
ans = max(ans, do(grid, (i, m - 1), WEST))
print(ans)
2023 Day 16 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| from collections import deque
class _(tuple):
def __add__(self, other):
return _(x + y for x, y in zip(self, other))
NORTH = _((-1, 0))
SOUTH = _((1, 0))
EAST = _((0, 1))
WEST = _((0, -1))
def do(grid, start, dstart):
q = deque([(_(start), _(dstart))])
visited = set()
while q:
i, di = q.popleft()
if i not in grid or (i, di) in visited:
continue
visited.add((i, di))
match grid[i]:
case "/":
di = -di[1], -di[0]
q.append((i + di, di))
case "\\":
di = di[1], di[0]
q.append((i + di, di))
case "|" if di[0] == 0:
q.append((i + NORTH, NORTH))
q.append((i + SOUTH, SOUTH))
case "-" if di[1] == 0:
q.append((i + EAST, EAST))
q.append((i + WEST, WEST))
case _:
q.append((i + di, di))
return len(set(p for p, _ in visited))
data = open('input').read().splitlines()
grid = {(i, j): x for i, row in enumerate(data) for j, x in enumerate(row.strip())}
print(do(grid, (0, 0), EAST)) |
from collections import deque
class _(tuple):
def __add__(self, other):
return _(x + y for x, y in zip(self, other))
NORTH = _((-1, 0))
SOUTH = _((1, 0))
EAST = _((0, 1))
WEST = _((0, -1))
def do(grid, start, dstart):
q = deque([(_(start), _(dstart))])
visited = set()
while q:
i, di = q.popleft()
if i not in grid or (i, di) in visited:
continue
visited.add((i, di))
match grid[i]:
case "/":
di = -di[1], -di[0]
q.append((i + di, di))
case "\\":
di = di[1], di[0]
q.append((i + di, di))
case "|" if di[0] == 0:
q.append((i + NORTH, NORTH))
q.append((i + SOUTH, SOUTH))
case "-" if di[1] == 0:
q.append((i + EAST, EAST))
q.append((i + WEST, WEST))
case _:
q.append((i + di, di))
return len(set(p for p, _ in visited))
data = open('input').read().splitlines()
grid = {(i, j): x for i, row in enumerate(data) for j, x in enumerate(row.strip())}
print(do(grid, (0, 0), EAST))
2023 Day 15 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| from collections import defaultdict
def hash(x):
val = 0
for c in x:
val += ord(c)
val *= 17
val %= 256
return val
hashmap = defaultdict(dict)
data = open('input').read().strip().split(",")
for block in data:
if "=" in block:
label, block = block.split("=")
hashmap[hash(label)][label] = int(block)
elif "-" in block:
label = block[:-1]
hashmap[hash(label)].pop(label, None)
print(sum(
(1 + i) * (1 + j) * val
for i, box in hashmap.items()
for j, val in enumerate(box.values())
)) |
from collections import defaultdict
def hash(x):
val = 0
for c in x:
val += ord(c)
val *= 17
val %= 256
return val
hashmap = defaultdict(dict)
data = open('input').read().strip().split(",")
for block in data:
if "=" in block:
label, block = block.split("=")
hashmap[hash(label)][label] = int(block)
elif "-" in block:
label = block[:-1]
hashmap[hash(label)].pop(label, None)
print(sum(
(1 + i) * (1 + j) * val
for i, box in hashmap.items()
for j, val in enumerate(box.values())
))
2023 Day 15 Part 01
1
2
3
4
5
6
7
8
9
10
| def hash(x):
val = 0
for c in x:
val += ord(c)
val *= 17
val %= 256
return val
data = open('input').read().strip().split(",")
print(sum(map(hash, data))) |
def hash(x):
val = 0
for c in x:
val += ord(c)
val *= 17
val %= 256
return val
data = open('input').read().strip().split(",")
print(sum(map(hash, data)))
2023 Day 14 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| grid = tuple(open('input').read().splitlines())
def cycle():
global grid
for _ in range(4):
grid = tuple(map("".join, zip(*grid)))
grid = tuple("#".join(["".join(sorted(tuple(group), reverse=True)) for group in row.split("#")]) for row in grid)
grid = tuple(row[::-1] for row in grid)
seen = {grid}
array = [grid]
iter = 0
while True:
iter += 1
cycle()
if grid in seen:
break
seen.add(grid)
array.append(grid)
first = array.index(grid)
grid = array[(1000000000 - first) % (iter - first) + first]
print(sum(row.count("O") * (len(grid) - r) for r, row in enumerate(grid))) |
grid = tuple(open('input').read().splitlines())
def cycle():
global grid
for _ in range(4):
grid = tuple(map("".join, zip(*grid)))
grid = tuple("#".join(["".join(sorted(tuple(group), reverse=True)) for group in row.split("#")]) for row in grid)
grid = tuple(row[::-1] for row in grid)
seen = {grid}
array = [grid]
iter = 0
while True:
iter += 1
cycle()
if grid in seen:
break
seen.add(grid)
array.append(grid)
first = array.index(grid)
grid = array[(1000000000 - first) % (iter - first) + first]
print(sum(row.count("O") * (len(grid) - r) for r, row in enumerate(grid)))
2023 Day 14 Part 01
1
2
3
4
5
| grid = open('input').read().splitlines()
grid = list(map("".join, zip(*grid)))
grid = ["#".join(["".join(sorted(list(group), reverse=True)) for group in row.split("#")]) for row in grid]
grid = list(map("".join, zip(*grid)))
print(sum(row.count("O") * (len(grid) - r) for r, row in enumerate(grid))) |
grid = open('input').read().splitlines()
grid = list(map("".join, zip(*grid)))
grid = ["#".join(["".join(sorted(list(group), reverse=True)) for group in row.split("#")]) for row in grid]
grid = list(map("".join, zip(*grid)))
print(sum(row.count("O") * (len(grid) - r) for r, row in enumerate(grid)))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| data = open('input').read().splitlines()
grid = list ([[x for x in line] for line in data])
while True:
modified = False
for y in range (0,len(grid[0])):
for x in range (0,len(grid)-1):
if grid[x][y] == "." and grid[x+1][y] =="O":
# a,b = b,a
grid[x][y], grid[x+1][y] = grid[x+1][y], grid[x][y]
modified = True
if modified == False:
break
ans = 0
for x in range (0,len(grid)):
count = 0
for y in range (0,len(grid[0])):
if grid[x][y] == 'O':
count += 1
ans += (len(grid) - x) * count
print(ans) |
data = open('input').read().splitlines()
grid = list ([[x for x in line] for line in data])
while True:
modified = False
for y in range (0,len(grid[0])):
for x in range (0,len(grid)-1):
if grid[x][y] == "." and grid[x+1][y] =="O":
# a,b = b,a
grid[x][y], grid[x+1][y] = grid[x+1][y], grid[x][y]
modified = True
if modified == False:
break
ans = 0
for x in range (0,len(grid)):
count = 0
for y in range (0,len(grid[0])):
if grid[x][y] == 'O':
count += 1
ans += (len(grid) - x) * count
print(ans)
2023 Day 13 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| def find(lines):
for i in range(1, len(lines)):
differs = sum(
ax != bx
for a, b in zip(lines[:i][::-1], lines[i:])
for ax, bx in zip(a, b)
)
if differs == 1:
return i
data = open('input').read().split("\n\n")
ans = 0
for block in data:
block = block.splitlines()
if v := find([*zip(*block)]):
ans += v
if h := find(block):
ans += h * 100
print(ans) |
def find(lines):
for i in range(1, len(lines)):
differs = sum(
ax != bx
for a, b in zip(lines[:i][::-1], lines[i:])
for ax, bx in zip(a, b)
)
if differs == 1:
return i
data = open('input').read().split("\n\n")
ans = 0
for block in data:
block = block.splitlines()
if v := find([*zip(*block)]):
ans += v
if h := find(block):
ans += h * 100
print(ans)
2023 Day 13 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| def find(lines):
for i in range(1, len(lines)):
if all(a == b for a, b in zip(lines[:i][::-1], lines[i:])):
return i
data = open('input').read().split("\n\n")
ans = 0
for block in data:
block = block.splitlines()
if v := find([*zip(*block)]):
ans += v
if h := find(block):
ans += h * 100
print(ans) |
def find(lines):
for i in range(1, len(lines)):
if all(a == b for a, b in zip(lines[:i][::-1], lines[i:])):
return i
data = open('input').read().split("\n\n")
ans = 0
for block in data:
block = block.splitlines()
if v := find([*zip(*block)]):
ans += v
if h := find(block):
ans += h * 100
print(ans)
2023 Day 12 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| from functools import cache
from itertools import combinations
@cache
def dp(chars, nums, *, curr=None):
if len(nums) == 0 and curr is None:
return "#" not in chars
match chars[:1], curr:
case "", None | 0:
return len(nums) == 0
case "", _:
return 0
case "?", None:
return dp(chars[1:], nums) + dp(chars, nums[1:], curr=nums[0])
case "?", 0:
return dp(chars[1:], nums)
case "?", _:
return dp(chars[1:], nums, curr=curr - 1)
case "#", None:
return dp(chars, nums[1:], curr=nums[0])
case "#", 0:
return 0
case "#", _:
return dp(chars[1:], nums, curr=curr - 1)
case ".", None | 0:
return dp(chars[1:], nums)
case ".", _:
return 0
data = open('input').read().splitlines()
ans = 0
for line in data:
chars, nums = line.split()
chars = "?".join([chars] * 5)
nums = tuple(int(x) for x in nums.split(",")) * 5
ans += dp(chars, nums)
print(ans) |
from functools import cache
from itertools import combinations
@cache
def dp(chars, nums, *, curr=None):
if len(nums) == 0 and curr is None:
return "#" not in chars
match chars[:1], curr:
case "", None | 0:
return len(nums) == 0
case "", _:
return 0
case "?", None:
return dp(chars[1:], nums) + dp(chars, nums[1:], curr=nums[0])
case "?", 0:
return dp(chars[1:], nums)
case "?", _:
return dp(chars[1:], nums, curr=curr - 1)
case "#", None:
return dp(chars, nums[1:], curr=nums[0])
case "#", 0:
return 0
case "#", _:
return dp(chars[1:], nums, curr=curr - 1)
case ".", None | 0:
return dp(chars[1:], nums)
case ".", _:
return 0
data = open('input').read().splitlines()
ans = 0
for line in data:
chars, nums = line.split()
chars = "?".join([chars] * 5)
nums = tuple(int(x) for x in nums.split(",")) * 5
ans += dp(chars, nums)
print(ans)
2023 Day 12 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| def count(cfg, nums):
if cfg == "":
return 1 if nums == () else 0
if nums == ():
return 0 if "#" in cfg else 1
result = 0
if cfg[0] in ".?":
result += count(cfg[1:], nums)
if cfg[0] in "#?":
if nums[0] <= len(cfg) and "." not in cfg[:nums[0]] and (nums[0] == len(cfg) or cfg[nums[0]] != "#"):
result += count(cfg[nums[0] + 1:], nums[1:])
return result
data = open('input').read().splitlines()
total = 0
for line in data:
cfg, nums = line.split()
nums = tuple(map(int, nums.split(",")))
total += count(cfg, nums)
print(total) |
def count(cfg, nums):
if cfg == "":
return 1 if nums == () else 0
if nums == ():
return 0 if "#" in cfg else 1
result = 0
if cfg[0] in ".?":
result += count(cfg[1:], nums)
if cfg[0] in "#?":
if nums[0] <= len(cfg) and "." not in cfg[:nums[0]] and (nums[0] == len(cfg) or cfg[nums[0]] != "#"):
result += count(cfg[nums[0] + 1:], nums[1:])
return result
data = open('input').read().splitlines()
total = 0
for line in data:
cfg, nums = line.split()
nums = tuple(map(int, nums.split(",")))
total += count(cfg, nums)
print(total)
2023 Day 11 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| from collections import deque
grid = open('input').read().splitlines()
empty_rows = [r for r, row in enumerate(grid) if all(ch == "." for ch in row)]
empty_cols = [c for c, col in enumerate(zip(*grid)) if all(ch == "." for ch in col)]
points = [(r, c) for r, row in enumerate(grid) for c, ch in enumerate(row) if ch == "#"]
total = 0
scale = 1000000
for i, (r1, c1) in enumerate(points):
for (r2, c2) in points[:i]:
for r in range(min(r1, r2), max(r1, r2)):
total += scale if r in empty_rows else 1
for c in range(min(c1, c2), max(c1, c2)):
total += scale if c in empty_cols else 1
print(total) |
from collections import deque
grid = open('input').read().splitlines()
empty_rows = [r for r, row in enumerate(grid) if all(ch == "." for ch in row)]
empty_cols = [c for c, col in enumerate(zip(*grid)) if all(ch == "." for ch in col)]
points = [(r, c) for r, row in enumerate(grid) for c, ch in enumerate(row) if ch == "#"]
total = 0
scale = 1000000
for i, (r1, c1) in enumerate(points):
for (r2, c2) in points[:i]:
for r in range(min(r1, r2), max(r1, r2)):
total += scale if r in empty_rows else 1
for c in range(min(c1, c2), max(c1, c2)):
total += scale if c in empty_cols else 1
print(total)
2023 Day 11 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| from collections import deque
grid = open('input').read().splitlines()
empty_rows = [r for r, row in enumerate(grid) if all(ch == "." for ch in row)]
empty_cols = [c for c, col in enumerate(zip(*grid)) if all(ch == "." for ch in col)]
points = [(r, c) for r, row in enumerate(grid) for c, ch in enumerate(row) if ch == "#"]
total = 0
scale = 2
for i, (r1, c1) in enumerate(points):
for (r2, c2) in points[:i]:
for r in range(min(r1, r2), max(r1, r2)):
total += scale if r in empty_rows else 1
for c in range(min(c1, c2), max(c1, c2)):
total += scale if c in empty_cols else 1
print(total) |
from collections import deque
grid = open('input').read().splitlines()
empty_rows = [r for r, row in enumerate(grid) if all(ch == "." for ch in row)]
empty_cols = [c for c, col in enumerate(zip(*grid)) if all(ch == "." for ch in col)]
points = [(r, c) for r, row in enumerate(grid) for c, ch in enumerate(row) if ch == "#"]
total = 0
scale = 2
for i, (r1, c1) in enumerate(points):
for (r2, c2) in points[:i]:
for r in range(min(r1, r2), max(r1, r2)):
total += scale if r in empty_rows else 1
for c in range(min(c1, c2), max(c1, c2)):
total += scale if c in empty_cols else 1
print(total)
2023 Day 10 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
| from collections import deque
grid = open('input').read().splitlines()
for r, row in enumerate(grid):
for c, ch in enumerate(row):
if ch == "S":
sr = r
sc = c
break
else:
continue
break
loop = {(sr, sc)}
q = deque([(sr, sc)])
maybe_s = {"|", "-", "J", "L", "7", "F"}
while q:
r, c = q.popleft()
ch = grid[r][c]
if r > 0 and ch in "S|JL" and grid[r - 1][c] in "|7F" and (r - 1, c) not in loop:
loop.add((r - 1, c))
q.append((r - 1, c))
if ch == "S":
maybe_s &= {"|", "J", "L"}
if r < len(grid) - 1 and ch in "S|7F" and grid[r + 1][c] in "|JL" and (r + 1, c) not in loop:
loop.add((r + 1, c))
q.append((r + 1, c))
if ch == "S":
maybe_s &= {"|", "7", "F"}
if c > 0 and ch in "S-J7" and grid[r][c - 1] in "-LF" and (r, c - 1) not in loop:
loop.add((r, c - 1))
q.append((r, c - 1))
if ch == "S":
maybe_s &= {"-", "J", "7"}
if c < len(grid[r]) - 1 and ch in "S-LF" and grid[r][c + 1] in "-J7" and (r, c + 1) not in loop:
loop.add((r, c + 1))
q.append((r, c + 1))
if ch == "S":
maybe_s &= {"-", "L", "F"}
assert len(maybe_s) == 1
(S,) = maybe_s
grid = [row.replace("S", S) for row in grid]
grid = ["".join(ch if (r, c) in loop else "." for c, ch in enumerate(row)) for r, row in enumerate(grid)]
outside = set()
for r, row in enumerate(grid):
within = False
up = None
for c, ch in enumerate(row):
if ch == "|":
assert up is None
within = not within
elif ch == "-":
assert up is not None
elif ch in "LF":
assert up is None
up = ch == "L"
elif ch in "7J":
assert up is not None
if ch != ("J" if up else "7"):
within = not within
up = None
elif ch == ".":
pass
else:
raise RuntimeError(f"unexpected character (horizontal): {ch}")
if not within:
outside.add((r, c))
print(len(grid) * len(grid[0]) - len(outside | loop)) |
from collections import deque
grid = open('input').read().splitlines()
for r, row in enumerate(grid):
for c, ch in enumerate(row):
if ch == "S":
sr = r
sc = c
break
else:
continue
break
loop = {(sr, sc)}
q = deque([(sr, sc)])
maybe_s = {"|", "-", "J", "L", "7", "F"}
while q:
r, c = q.popleft()
ch = grid[r][c]
if r > 0 and ch in "S|JL" and grid[r - 1][c] in "|7F" and (r - 1, c) not in loop:
loop.add((r - 1, c))
q.append((r - 1, c))
if ch == "S":
maybe_s &= {"|", "J", "L"}
if r < len(grid) - 1 and ch in "S|7F" and grid[r + 1][c] in "|JL" and (r + 1, c) not in loop:
loop.add((r + 1, c))
q.append((r + 1, c))
if ch == "S":
maybe_s &= {"|", "7", "F"}
if c > 0 and ch in "S-J7" and grid[r][c - 1] in "-LF" and (r, c - 1) not in loop:
loop.add((r, c - 1))
q.append((r, c - 1))
if ch == "S":
maybe_s &= {"-", "J", "7"}
if c < len(grid[r]) - 1 and ch in "S-LF" and grid[r][c + 1] in "-J7" and (r, c + 1) not in loop:
loop.add((r, c + 1))
q.append((r, c + 1))
if ch == "S":
maybe_s &= {"-", "L", "F"}
assert len(maybe_s) == 1
(S,) = maybe_s
grid = [row.replace("S", S) for row in grid]
grid = ["".join(ch if (r, c) in loop else "." for c, ch in enumerate(row)) for r, row in enumerate(grid)]
outside = set()
for r, row in enumerate(grid):
within = False
up = None
for c, ch in enumerate(row):
if ch == "|":
assert up is None
within = not within
elif ch == "-":
assert up is not None
elif ch in "LF":
assert up is None
up = ch == "L"
elif ch in "7J":
assert up is not None
if ch != ("J" if up else "7"):
within = not within
up = None
elif ch == ".":
pass
else:
raise RuntimeError(f"unexpected character (horizontal): {ch}")
if not within:
outside.add((r, c))
print(len(grid) * len(grid[0]) - len(outside | loop))
2023 Day 10 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| from collections import deque
grid = open('input').read().splitlines()
for r, row in enumerate(grid):
for c, ch in enumerate(row):
if ch == "S":
sr = r
sc = c
break
else:
continue
break
loop = {(sr, sc)}
q = deque([(sr, sc)])
while q:
r, c = q.popleft()
ch = grid[r][c]
if r > 0 and ch in "S|JL" and grid[r - 1][c] in "|7F" and (r - 1, c) not in loop:
loop.add((r - 1, c))
q.append((r - 1, c))
if r < len(grid) - 1 and ch in "S|7F" and grid[r + 1][c] in "|JL" and (r + 1, c) not in loop:
loop.add((r + 1, c))
q.append((r + 1, c))
if c > 0 and ch in "S-J7" and grid[r][c - 1] in "-LF" and (r, c - 1) not in loop:
loop.add((r, c - 1))
q.append((r, c - 1))
if c < len(grid[r]) - 1 and ch in "S-LF" and grid[r][c + 1] in "-J7" and (r, c + 1) not in loop:
loop.add((r, c + 1))
q.append((r, c + 1))
print(len(loop) // 2) |
from collections import deque
grid = open('input').read().splitlines()
for r, row in enumerate(grid):
for c, ch in enumerate(row):
if ch == "S":
sr = r
sc = c
break
else:
continue
break
loop = {(sr, sc)}
q = deque([(sr, sc)])
while q:
r, c = q.popleft()
ch = grid[r][c]
if r > 0 and ch in "S|JL" and grid[r - 1][c] in "|7F" and (r - 1, c) not in loop:
loop.add((r - 1, c))
q.append((r - 1, c))
if r < len(grid) - 1 and ch in "S|7F" and grid[r + 1][c] in "|JL" and (r + 1, c) not in loop:
loop.add((r + 1, c))
q.append((r + 1, c))
if c > 0 and ch in "S-J7" and grid[r][c - 1] in "-LF" and (r, c - 1) not in loop:
loop.add((r, c - 1))
q.append((r, c - 1))
if c < len(grid[r]) - 1 and ch in "S-LF" and grid[r][c + 1] in "-J7" and (r, c + 1) not in loop:
loop.add((r, c + 1))
q.append((r, c + 1))
print(len(loop) // 2)
2023 Day 09 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
| from itertools import pairwise
def seq(ints):
if all(ints == 0 for ints in ints):
return 0
diffs = [b - a for a, b in pairwise(ints)]
return ints[-1] + seq(diffs)
data = open('input').read().splitlines()
ans = 0
for d in data:
ans += seq([int(x) for x in d.split()[::-1]])
print(ans) |
from itertools import pairwise
def seq(ints):
if all(ints == 0 for ints in ints):
return 0
diffs = [b - a for a, b in pairwise(ints)]
return ints[-1] + seq(diffs)
data = open('input').read().splitlines()
ans = 0
for d in data:
ans += seq([int(x) for x in d.split()[::-1]])
print(ans)
2023 Day 09 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
| from itertools import pairwise
def seq(ints):
if all(ints == 0 for ints in ints):
return 0
diffs = [b - a for a, b in pairwise(ints)]
return ints[-1] + seq(diffs)
data = open('input').read().splitlines()
ans = 0
for d in data:
ans += seq([int(x) for x in d.split()])
print(ans) |
from itertools import pairwise
def seq(ints):
if all(ints == 0 for ints in ints):
return 0
diffs = [b - a for a, b in pairwise(ints)]
return ints[-1] + seq(diffs)
data = open('input').read().splitlines()
ans = 0
for d in data:
ans += seq([int(x) for x in d.split()])
print(ans)
2023 Day 08 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| import math
from itertools import cycle
instr, data = open('input').read().split("\n\n")
data = [x.split(" = ") for x in data.splitlines()]
data = {a: b[1:-1].split(", ") for a, b in data}
ans = []
for curr in data:
if not curr.endswith("A"):
continue
visited = set()
for count, (idx, d) in enumerate(cycle(enumerate(instr)), start=1):
prev, curr = curr, data[curr][d == "R"]
visited.add((curr, idx))
if prev.endswith("Z") and (curr, idx) in visited:
ans.append(count - 1)
break
print(math.lcm(*ans)) |
import math
from itertools import cycle
instr, data = open('input').read().split("\n\n")
data = [x.split(" = ") for x in data.splitlines()]
data = {a: b[1:-1].split(", ") for a, b in data}
ans = []
for curr in data:
if not curr.endswith("A"):
continue
visited = set()
for count, (idx, d) in enumerate(cycle(enumerate(instr)), start=1):
prev, curr = curr, data[curr][d == "R"]
visited.add((curr, idx))
if prev.endswith("Z") and (curr, idx) in visited:
ans.append(count - 1)
break
print(math.lcm(*ans))
The least common multiple is the smallest number divisable by all numbers. For example, the lcm of 15 and 25 is 75.
2023 Day 08 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
| from itertools import cycle
instr, data = open('input').read().split("\n\n")
data = [x.split(" = ") for x in data.splitlines()]
data = {a: b[1:-1].split(", ") for a, b in data}
curr = "AAA"
for count, d in enumerate(cycle(instr), start=1):
curr = data[curr][d == "R"]
if curr == "ZZZ":
print(count)
break |
from itertools import cycle
instr, data = open('input').read().split("\n\n")
data = [x.split(" = ") for x in data.splitlines()]
data = {a: b[1:-1].split(", ") for a, b in data}
curr = "AAA"
for count, d in enumerate(cycle(instr), start=1):
curr = data[curr][d == "R"]
if curr == "ZZZ":
print(count)
break
2023 Day 07 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| from collections import Counter
from itertools import product
def get_score(x):
match [count for _, count in Counter(x).most_common()]:
case 5, *_:
return 1
case 4, *_:
return 2
case 3, 2, *_:
return 3
case 3, *_:
return 4
case 2, 2, *_:
return 5
case 2, *_:
return 6
case _:
return 7
order = "AKQT98765432J"
vals = []
data = open('input').read().splitlines()
for line in data:
cards, pts = line.split()
score = get_score(cards)
# A joker can represent any of these cards: AKQT98765432
# For each substitution we calculate the score.
# We keep the highest score (= lowest value).
# Note there can be multiple jokers in a hand.
# Example: 3 jokers -> AAA, AAK, AAQ, etc.
for subs in product(order[:-1], repeat=cards.count("J")):
score = min(score, get_score(cards.replace("J", "") + "".join(subs)))
vals.append((score, [order.index(x) for x in cards], int(pts)))
vals.sort(reverse=True)
print(sum((i + 1) * v[-1] for i, v in enumerate(vals))) |
from collections import Counter
from itertools import product
def get_score(x):
match [count for _, count in Counter(x).most_common()]:
case 5, *_:
return 1
case 4, *_:
return 2
case 3, 2, *_:
return 3
case 3, *_:
return 4
case 2, 2, *_:
return 5
case 2, *_:
return 6
case _:
return 7
order = "AKQT98765432J"
vals = []
data = open('input').read().splitlines()
for line in data:
cards, pts = line.split()
score = get_score(cards)
# A joker can represent any of these cards: AKQT98765432
# For each substitution we calculate the score.
# We keep the highest score (= lowest value).
# Note there can be multiple jokers in a hand.
# Example: 3 jokers -> AAA, AAK, AAQ, etc.
for subs in product(order[:-1], repeat=cards.count("J")):
score = min(score, get_score(cards.replace("J", "") + "".join(subs)))
vals.append((score, [order.index(x) for x in cards], int(pts)))
vals.sort(reverse=True)
print(sum((i + 1) * v[-1] for i, v in enumerate(vals)))
2023 Day 07 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| from collections import Counter
def get_score(x):
match [count for _, count in Counter(x).most_common()]:
case 5, *_: # Five of a kind
return 1
case 4, *_: # Four of a kind
return 2
case 3, 2, *_: # Full House
return 3
case 3, *_: # Three of a kind
return 4
case 2, 2, *_: # Two pairs
return 5
case 2, *_: # Two of a kind
return 6
case _: # Unique
return 7
order = "AKQJT98765432"
vals = []
data = open('input').read().splitlines()
for line in data:
cards, pts = line.split()
vals.append((get_score(cards), [order.index(x) for x in cards], int(pts)))
vals.sort(reverse=True)
print(sum((i + 1) * v[-1] for i, v in enumerate(vals))) |
from collections import Counter
def get_score(x):
match [count for _, count in Counter(x).most_common()]:
case 5, *_: # Five of a kind
return 1
case 4, *_: # Four of a kind
return 2
case 3, 2, *_: # Full House
return 3
case 3, *_: # Three of a kind
return 4
case 2, 2, *_: # Two pairs
return 5
case 2, *_: # Two of a kind
return 6
case _: # Unique
return 7
order = "AKQJT98765432"
vals = []
data = open('input').read().splitlines()
for line in data:
cards, pts = line.split()
vals.append((get_score(cards), [order.index(x) for x in cards], int(pts)))
vals.sort(reverse=True)
print(sum((i + 1) * v[-1] for i, v in enumerate(vals)))
2023 Day 06 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| data = open('input').read().splitlines()
data = [x.split() for x in data]
races = []
for i in range(1,len(data[0])):
races.append([int(data[0][i]),int(data[1][i])])
wins = 0
ans = 1
for r in races:
for btn in range (0,r[0]):
dist = (r[0] - btn) * btn
if dist > r[1]:
wins += 1
ans *= wins
wins = 0
print(ans) |
data = open('input').read().splitlines()
data = [x.split() for x in data]
races = []
for i in range(1,len(data[0])):
races.append([int(data[0][i]),int(data[1][i])])
wins = 0
ans = 1
for r in races:
for btn in range (0,r[0]):
dist = (r[0] - btn) * btn
if dist > r[1]:
wins += 1
ans *= wins
wins = 0
print(ans)
2023 Day 05 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| def pairs(l):
it = iter(l)
return zip(it, it)
seeds, *mappings = open('input').read().split('\n\n')
seeds = seeds.split(": ")[1]
seeds = [int(x) for x in seeds.split()]
seeds = [range(a, a + b) for a, b in pairs(seeds)]
for m in mappings:
_, *ranges = m.splitlines()
ranges = [[int(x) for x in r.split()] for r in ranges]
ranges = [(range(a, a + c), range(b, b + c)) for a, b, c in ranges]
new_seeds = []
for r in seeds:
for tr, fr in ranges:
offset = tr.start - fr.start
if r.stop <= fr.start or fr.stop <= r.start:
continue
ir = range(max(r.start, fr.start), min(r.stop, fr.stop))
lr = range(r.start, ir.start)
rr = range(ir.stop, r.stop)
if lr:
seeds.append(lr)
if rr:
seeds.append(rr)
new_seeds.append(range(ir.start + offset, ir.stop + offset))
break
else:
new_seeds.append(r)
seeds = new_seeds
print(min(x.start for x in seeds)) |
def pairs(l):
it = iter(l)
return zip(it, it)
seeds, *mappings = open('input').read().split('\n\n')
seeds = seeds.split(": ")[1]
seeds = [int(x) for x in seeds.split()]
seeds = [range(a, a + b) for a, b in pairs(seeds)]
for m in mappings:
_, *ranges = m.splitlines()
ranges = [[int(x) for x in r.split()] for r in ranges]
ranges = [(range(a, a + c), range(b, b + c)) for a, b, c in ranges]
new_seeds = []
for r in seeds:
for tr, fr in ranges:
offset = tr.start - fr.start
if r.stop <= fr.start or fr.stop <= r.start:
continue
ir = range(max(r.start, fr.start), min(r.stop, fr.stop))
lr = range(r.start, ir.start)
rr = range(ir.stop, r.stop)
if lr:
seeds.append(lr)
if rr:
seeds.append(rr)
new_seeds.append(range(ir.start + offset, ir.stop + offset))
break
else:
new_seeds.append(r)
seeds = new_seeds
print(min(x.start for x in seeds))
2023 Day 05 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| seeds, *mappings = open('input').read().split('\n\n')
seeds = seeds.split(": ")[1]
seeds = [int(x) for x in seeds.split()]
for m in mappings:
_, *ranges = m.splitlines()
ranges = [[int(x) for x in r.split()] for r in ranges]
ranges = [(range(a, a + c), range(b, b + c)) for a, b, c in ranges]
def translate(x):
for a, b in ranges:
if x in b:
return a.start + x - b.start
return x
seeds = [translate(x) for x in seeds]
print(min(seeds)) |
seeds, *mappings = open('input').read().split('\n\n')
seeds = seeds.split(": ")[1]
seeds = [int(x) for x in seeds.split()]
for m in mappings:
_, *ranges = m.splitlines()
ranges = [[int(x) for x in r.split()] for r in ranges]
ranges = [(range(a, a + c), range(b, b + c)) for a, b, c in ranges]
def translate(x):
for a, b in ranges:
if x in b:
return a.start + x - b.start
return x
seeds = [translate(x) for x in seeds]
print(min(seeds))
2023 Day 04 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| data = open("input").read().splitlines()
data = [x.split('|') for x in data]
win = [x[0][10:] for x in data]
win = [x.strip().split() for x in win]
elf = [x[1] for x in data]
elf = [x.strip().split() for x in elf]
dups = []
for _ in elf:
dups.append(1)
for i,card in enumerate(elf):
hitcount=0
for num in card:
if num in win[i]:
hitcount+=1
dups[i+hitcount]+=dups[i]
print(sum(dups)) |
data = open("input").read().splitlines()
data = [x.split('|') for x in data]
win = [x[0][10:] for x in data]
win = [x.strip().split() for x in win]
elf = [x[1] for x in data]
elf = [x.strip().split() for x in elf]
dups = []
for _ in elf:
dups.append(1)
for i,card in enumerate(elf):
hitcount=0
for num in card:
if num in win[i]:
hitcount+=1
dups[i+hitcount]+=dups[i]
print(sum(dups))
2023 Day 04 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| data = open("input").read().splitlines()
data = [x.split('|') for x in data]
win = [x[0][10:] for x in data]
win = [x.strip().split() for x in win]
elf = [x[1] for x in data]
elf = [x.strip().split() for x in elf]
points = 0
ans = 0
firstwin = True
for i,card in enumerate(elf):
for num in card:
if num in win[i]:
if firstwin:
points += 1
firstwin=False
else:
points *= 2
ans += points
points = 0
firstwin = True
print(ans) |
data = open("input").read().splitlines()
data = [x.split('|') for x in data]
win = [x[0][10:] for x in data]
win = [x.strip().split() for x in win]
elf = [x[1] for x in data]
elf = [x.strip().split() for x in elf]
points = 0
ans = 0
firstwin = True
for i,card in enumerate(elf):
for num in card:
if num in win[i]:
if firstwin:
points += 1
firstwin=False
else:
points *= 2
ans += points
points = 0
firstwin = True
print(ans)
2023 Day 03 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| import re
from collections import defaultdict
lines = open("input").read().splitlines()
adj = defaultdict(list)
ans = 0
for i, line in enumerate(lines):
for m in re.finditer(r"\d+", line):
idxs = [(i, m.start() - 1), (i, m.end())] # left, right
idxs += [(i - 1, j) for j in range(m.start() - 1, m.end() + 1)] # row above
idxs += [(i + 1, j) for j in range(m.start() - 1, m.end() + 1)] # row below
for a, b in idxs:
if 0 <= a < len(lines) and 0 <= b < len(lines[a]) and lines[a][b] == "*": # asterisk nearby within bounds
adj[a, b].append(m.group())
for v in adj.values():
if len(v) == 2: # two numbers near an asterisk
ans += int(v[0]) * int(v[1])
print (ans) |
import re
from collections import defaultdict
lines = open("input").read().splitlines()
adj = defaultdict(list)
ans = 0
for i, line in enumerate(lines):
for m in re.finditer(r"\d+", line):
idxs = [(i, m.start() - 1), (i, m.end())] # left, right
idxs += [(i - 1, j) for j in range(m.start() - 1, m.end() + 1)] # row above
idxs += [(i + 1, j) for j in range(m.start() - 1, m.end() + 1)] # row below
for a, b in idxs:
if 0 <= a < len(lines) and 0 <= b < len(lines[a]) and lines[a][b] == "*": # asterisk nearby within bounds
adj[a, b].append(m.group())
for v in adj.values():
if len(v) == 2: # two numbers near an asterisk
ans += int(v[0]) * int(v[1])
print (ans)
2023 Day 03 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| import re
lines = open("input").read().splitlines()
ans = 0
for i, line in enumerate(lines):
for m in re.finditer(r"\d+", line):
# idx contains neighbours
idxs = [(i, m.start() - 1), (i, m.end())] # left, right
idxs += [(i - 1, j) for j in range(m.start() - 1, m.end() + 1)] # row above
idxs += [(i + 1, j) for j in range(m.start() - 1, m.end() + 1)] # row below
count = sum(0 <= a < len(lines) and 0 <= b < len(lines[a]) and lines[a][b] != "." for a, b in idxs) # special char nearby within bounds
if count > 0:
ans += int(m.group())
print(ans) |
import re
lines = open("input").read().splitlines()
ans = 0
for i, line in enumerate(lines):
for m in re.finditer(r"\d+", line):
# idx contains neighbours
idxs = [(i, m.start() - 1), (i, m.end())] # left, right
idxs += [(i - 1, j) for j in range(m.start() - 1, m.end() + 1)] # row above
idxs += [(i + 1, j) for j in range(m.start() - 1, m.end() + 1)] # row below
count = sum(0 <= a < len(lines) and 0 <= b < len(lines[a]) and lines[a][b] != "." for a, b in idxs) # special char nearby within bounds
if count > 0:
ans += int(m.group())
print(ans)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| import re
lines = open("input").read().splitlines()
ans = 0
for i, line in enumerate(lines):
for m in re.finditer(r"\d+", line):
# idx contains neighbours
idxs = [(i, m.start() - 1), (i, m.end())] # left, right
idxs += [(i - 1, j) for j in range(m.start() - 1, m.end() + 1)] # row above
idxs += [(i + 1, j) for j in range(m.start() - 1, m.end() + 1)] # row below
count = 0
for a, b in idxs:
if 0 <= a < len(lines) and 0 <= b < len(lines[a]) and lines[a][b] != ".": # special char nearby within bounds
count+=1
if count > 0:
ans += int(m.group())
print(ans) |
import re
lines = open("input").read().splitlines()
ans = 0
for i, line in enumerate(lines):
for m in re.finditer(r"\d+", line):
# idx contains neighbours
idxs = [(i, m.start() - 1), (i, m.end())] # left, right
idxs += [(i - 1, j) for j in range(m.start() - 1, m.end() + 1)] # row above
idxs += [(i + 1, j) for j in range(m.start() - 1, m.end() + 1)] # row below
count = 0
for a, b in idxs:
if 0 <= a < len(lines) and 0 <= b < len(lines[a]) and lines[a][b] != ".": # special char nearby within bounds
count+=1
if count > 0:
ans += int(m.group())
print(ans)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| def neighbours(grid, r, c):
vals = sum((row[c -(c>0): c+2] for row in grid[r -(r>0):r+2]), [])
vals.remove(grid[r][c]) # remove itself.
return vals
data = open("input").read().splitlines()
grid = list ([[x for x in line] for line in data])
ans=0
numstr=""
adj=[]
symbol=False
for x in range(0,len(grid)):
for y in range(0,len(grid[0])):
if grid[x][y].isnumeric():
numstr += grid[x][y]
# test if symbol nearby
adj=neighbours(grid,x,y)
for a in adj:
if a in ("!","@","#","$","%","^","&","*","(",")","-","+","=","/"):
symbol=True
elif numstr is not "":
if symbol==True:
ans+=int(numstr)
symbol=False
numstr=""
print(ans) |
def neighbours(grid, r, c):
vals = sum((row[c -(c>0): c+2] for row in grid[r -(r>0):r+2]), [])
vals.remove(grid[r][c]) # remove itself.
return vals
data = open("input").read().splitlines()
grid = list ([[x for x in line] for line in data])
ans=0
numstr=""
adj=[]
symbol=False
for x in range(0,len(grid)):
for y in range(0,len(grid[0])):
if grid[x][y].isnumeric():
numstr += grid[x][y]
# test if symbol nearby
adj=neighbours(grid,x,y)
for a in adj:
if a in ("!","@","#","$","%","^","&","*","(",")","-","+","=","/"):
symbol=True
elif numstr is not "":
if symbol==True:
ans+=int(numstr)
symbol=False
numstr=""
print(ans)
2023 Day 02 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| data = open("input").read().splitlines()
data = [x.replace(',','') for x in data]
data = [x.split(';') for x in data]
ans=0
for d in data:
rmax,gmax,bmax=0,0,0
for e in d:
r,g,b=0,0,0
prev=0
ff = e.split(' ')
for f in ff:
match f:
case 'red':
r+=int(prev)
case 'green':
g+=int(prev)
case 'blue':
b+=int(prev)
prev = f
if r>rmax:
rmax = r
if g>gmax:
gmax = g
if b>bmax:
bmax = b
ans+=rmax*gmax*bmax
print(ans) |
data = open("input").read().splitlines()
data = [x.replace(',','') for x in data]
data = [x.split(';') for x in data]
ans=0
for d in data:
rmax,gmax,bmax=0,0,0
for e in d:
r,g,b=0,0,0
prev=0
ff = e.split(' ')
for f in ff:
match f:
case 'red':
r+=int(prev)
case 'green':
g+=int(prev)
case 'blue':
b+=int(prev)
prev = f
if r>rmax:
rmax = r
if g>gmax:
gmax = g
if b>bmax:
bmax = b
ans+=rmax*gmax*bmax
print(ans)
2023 Day 02 Part 01
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| data = open("input").read().splitlines()
data = [x.replace(',','') for x in data]
data = [x.split(';') for x in data]
ans=0
round=1
for d in data:
for e in d:
r,g,b=0,0,0
prev=0
possible=True
ff = e.split(' ')
for f in ff:
match f:
case 'red':
r+=int(prev)
case 'green':
g+=int(prev)
case 'blue':
b+=int(prev)
prev = f
if r>12 or g>13 or b>14:
possible=False
break
if possible == True:
ans+=round
round+=1
print(ans) |
data = open("input").read().splitlines()
data = [x.replace(',','') for x in data]
data = [x.split(';') for x in data]
ans=0
round=1
for d in data:
for e in d:
r,g,b=0,0,0
prev=0
possible=True
ff = e.split(' ')
for f in ff:
match f:
case 'red':
r+=int(prev)
case 'green':
g+=int(prev)
case 'blue':
b+=int(prev)
prev = f
if r>12 or g>13 or b>14:
possible=False
break
if possible == True:
ans+=round
round+=1
print(ans)
2023 Day 01 Part 02
1
2
3
4
5
6
7
8
9
10
11
12
| lines = open("input").read().splitlines()
p = 0
for line in lines:
digits = []
for i,c in enumerate(line):
if c.isdigit():
digits.append(c)
for d,val in enumerate(['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']):
if line[i:].startswith(val):
digits.append(str(d+1))
p += int(digits[0]+digits[-1])
print(p) |
lines = open("input").read().splitlines()
p = 0
for line in lines:
digits = []
for i,c in enumerate(line):
if c.isdigit():
digits.append(c)
for d,val in enumerate(['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']):
if line[i:].startswith(val):
digits.append(str(d+1))
p += int(digits[0]+digits[-1])
print(p)
2023 Day 01 Part 01
1
2
3
4
5
6
| lines = open("input").read().splitlines()
p = 0
for line in lines:
digits = [c for c in line if c.isdigit()]
p += int(digits[0] + digits[-1])
print(p) |
lines = open("input").read().splitlines()
p = 0
for line in lines:
digits = [c for c in line if c.isdigit()]
p += int(digits[0] + digits[-1])
print(p)
1
2
3
4
5
6
7
8
9
| lines = open("input").read().splitlines()
p = 0
for line in lines:
digits = []
for c in line:
if c.isdigit():
digits.append(c)
p += int(digits[0]+digits[-1])
print(p) |
lines = open("input").read().splitlines()
p = 0
for line in lines:
digits = []
for c in line:
if c.isdigit():
digits.append(c)
p += int(digits[0]+digits[-1])
print(p)