Compare commits

...

15 Commits

Author SHA1 Message Date
288a0809ff Solve 2025 day 7 2026-02-16 12:23:56 -05:00
42e0c68d93 Implement 2025 day 6 2026-02-16 11:51:21 -05:00
867a7ed2df Solve 2025 day 5 2026-01-11 12:12:55 -05:00
df0682e989 Solve 2025 day 3 and 4 2026-01-11 10:33:21 -05:00
e5421854a9 Do day 3 and 4 for 2025 2025-12-26 20:18:44 -05:00
183f909508 Solve 2025 day 1 and 2 2025-12-14 10:36:32 -05:00
e655580b3f Setup for 2025 2025-12-13 20:32:11 -05:00
c9145e6106 Rename 2024 scripts for nicer ordering 2025-12-13 20:19:48 -05:00
7013c5b0a0 Solve 2024 day 21 to finish the year 2024-12-30 23:11:54 -05:00
376a2eac09 2024 d21 wip 2024-12-30 22:28:06 -05:00
2ee16eb1ba Solve 2024 day 24 2024-12-25 12:14:04 -05:00
8cc199105c Solve 2024 day 25 2024-12-25 10:42:33 -05:00
3dd208c088 Solve 2024 day 23 2024-12-24 11:33:35 -05:00
265829715a Solve 2024 day 22 2024-12-22 00:31:27 -05:00
3979a60fa4 2024 d21 wip that is not going anywhere 2024-12-21 22:37:40 -05:00
27 changed files with 928 additions and 425 deletions

2
.gitignore vendored
View File

@@ -4,6 +4,8 @@
__pycache__/
*.py[cod]
*$py.class
.python-version
uv.lock
# C extensions
*.so

View File

@@ -1,189 +1,87 @@
import heapq
from lib import get_data
from collections import deque
from functools import cache
data = get_data(__file__)
# data = """029A
# 980A
# 179A
# 456A
# 379A"""
def new_dir(current, move):
if current == "<":
if move == ">": return "v"
else: return None
elif current == "^":
if move == ">": return "A"
elif move == "v": return "v"
else: return None
elif current == "v":
if move == "<": return "<"
elif move == "^": return "^"
elif move == ">": return ">"
else: return None
elif current == ">":
if move == "<": return "v"
elif move == "^": return "A"
else: return None
elif current == "A":
if move == "<": return "^"
elif move == "v": return ">"
else: return None
else:
assert False
return {
"<": {">": "v"},
"^": {">": "A", "v": "v"},
"v": {"<": "<", ">": ">", "^": "^"},
">": {"^": "A", "<": "v"},
"A": {"<": "^", "v": ">"},
}[current].get(move, None)
def new_num(current, move):
if current == "7":
if move == ">": return "8"
elif move == "v": return "4"
else: return None
elif current == "8":
if move == "<": return "7"
elif move == ">": return "9"
elif move == "v": return "5"
else: return None
elif current == "9":
if move == "<": return "8"
elif move == "v": return "6"
else: return None
elif current == "5":
if move == "^": return "8"
elif move == "<": return "4"
elif move == ">": return "6"
elif move == "v": return "2"
else: assert False
elif current == "4":
if move == "^": return "7"
elif move == ">": return "5"
elif move == "v": return "1"
else: return None
elif current == "6":
if move == "^": return "9"
elif move == "<": return "5"
elif move == "v": return "3"
else: return None
elif current == "1":
if move == "^": return "4"
elif move == ">": return "2"
else: return None
elif current == "2":
if move == "^": return "5"
elif move == "<": return "1"
elif move == ">": return "3"
elif move == "v": return "0"
else: assert False
elif current == "3":
if move == "^": return "6"
elif move == "<": return "2"
elif move == "v": return "A"
else: return None
elif current == "0":
if move == "^": return "2"
elif move == ">": return "A"
else: return None
elif current == "A":
if move == "^": return "3"
elif move == "<": return "0"
else: return None
else:
assert False
return {
"7": {">": "8", "v": "4", },
"8": {"<": "7", ">": "9", "v": "5"},
"9": {"<": "8", "v": "6"},
"4": {"^": "7", ">": "5", "v": "1"},
"5": {"^": "8", ">": "6", "<": "4", "v": "2"},
"6": {"^": "9", "<": "5", "v": "3"},
"1": {"^": "4", ">": "2"},
"2": {"^": "5", ">": "3", "<": "1", "v": "0"},
"3": {"^": "6", "<": "2", "v": "A"},
"0": {"^": "2", ">": "A"},
"A": {"^": "3", "<": "0"},
}[current].get(move, None)
@cache
def new_state(press, node):
cost, r, n, path = node
r = list(r)
for i in range(len(r)):
if press == "A":
press = r[i]
else:
r[i] = new_dir(r[i], press)
if r[i] is None:
return None
press = None
break
npath = str(path)
if press is not None:
if press == "A":
npath += n
else:
n = new_num(n, press)
if n is None:
return None
nnode = cost + 1, tuple(r), n, npath
return nnode
if press == "A":
if r[0] == "A":
if r[1] == "A":
path += n
else:
n = new_num(n, r[1])
else:
r[1] = new_dir(r[1], r[0])
else:
r[0] = new_dir(r[0], press)
if r[0] is None or r[1] is None or n is None:
return None
npath = str(path)
return cost + 1, tuple(r), n, npath
def sequence(line):
def dist(n1, n2):
"""cost from node to node"""
return 1
def h(node):
"""heuristic function (never overestimate)"""
return len(line) - len(node[3])
def is_goal(node):
return node[3] == line
def neighbors(node):
nbs = []
for move in '<^v>A':
nb = new_state(move, node)
if nb is None:
def find(code, depth, lookup):
if depth == 0:
return len(code)
code = list(code)
t = 0
current = "A"
while code:
solutions = []
target = code.pop(0)
start = (current, "")
seen = set()
queue = deque([start])
while queue:
node = queue.popleft()
if node in seen:
continue
if not line.startswith(node[3]):
seen.add(node)
if node[0] == target:
solution = node[1] + "A"
if len(solutions) == 0:
solutions.append(solution)
elif len(solutions[-1]) == len(solution):
solutions.append(solution)
else:
break
continue
nbs.append(nb)
return nbs
key_pads = tuple(['A' for _ in range(5)])
start = (0, key_pads, 'A', '')
starts = [start]
open_set = []
g_score = {}
cost = None
for c in "<>v^":
next = lookup(node[0], c)
if next is not None:
queue.append((next, node[1] + c))
current = target
t += min(find(code, depth - 1, new_dir) for code in solutions)
return t
for start in starts:
heapq.heappush(open_set, (h(start), start))
g_score[start] = dist(0, start)
while open_set:
current_f_score, current = heapq.heappop(open_set)
if is_goal(current):
assert current_f_score == g_score[current]
cost = g_score[current]
break
for neighbor in neighbors(current):
tentative_g_score = g_score[current] + dist(current, neighbor)
if neighbor not in g_score or tentative_g_score < g_score[neighbor]:
g_score[neighbor] = tentative_g_score
f_score = g_score[neighbor] + h(neighbor)
heapq.heappush(open_set, (f_score, neighbor))
return cost
t = 0
p1, p2 = 0, 0
for line in data.splitlines():
s = sequence(line)
i = int("".join([c for c in line if ord('0') <= ord(c) <= ord('9')]))
print(s, i)
t += (s - 1) * i
print(t)
i = int("".join([c for c in line if c.isdigit()]))
s1 = find(line, 3, new_num)
s2 = find(line, 26, new_num)
p1 += s1 * i
p2 += s2 * i
print(p1)
print(p2)

56
2024/d22.py Normal file
View File

@@ -0,0 +1,56 @@
from lib import get_data
from lib import ints
from collections import defaultdict
data = get_data(__file__)
def secret(x):
x ^= x * 64
x %= 16777216
x ^= x // 32
x %= 16777216
x ^= x * 2048
x %= 16777216
return x
def slice(xs, n):
return [slice for slice in zip(*[xs[i:] for i in range(n)])]
codes = defaultdict(int)
def int_seq(x):
xs = []
for _ in range(2000):
xs.append(x % 10)
x = secret(x)
ds = [a - b for a, b in zip(xs[1:], xs)]
seen = set()
for s in slice(list(zip(xs[1:], ds)), 4):
code = tuple([e[1] for e in s])
v = s[-1][0]
if not code in seen:
codes[code] += v
seen.add(code)
for line in data.splitlines():
(x,) = ints(line)
s = 0
for line in data.splitlines():
(x,) = ints(line)
int_seq(x)
for _ in range(2000):
x = secret(x)
s += x
print(s)
print(max(codes.values()))

43
2024/d23.py Normal file
View File

@@ -0,0 +1,43 @@
from lib import get_data
from collections import defaultdict
data = get_data(__file__)
g = defaultdict(set)
for line in data.splitlines():
a, b = line.split("-")
g[a].add(b)
g[b].add(a)
cs = set()
for a, bs in g.items():
for b in bs:
for c in g[b]:
if c in bs:
cs.add(tuple(sorted([a, b, c])))
cs = [t for t in cs if any(x.startswith("t") for x in t)]
print(len(cs))
def find_largest_clique(graph):
def bron_kerbosch(r, p, x, max_clique):
if not p and not x:
if len(r) > len(max_clique[0]):
max_clique[0] = r.copy()
return
for v in p.copy():
neighbors = graph[v]
bron_kerbosch(r | {v}, p & neighbors, x & neighbors, max_clique)
p.remove(v)
x.add(v)
max_clique = [set()]
bron_kerbosch(set(), set(graph.keys()), set(), max_clique)
return max_clique[0]
cs = find_largest_clique(g)
print(",".join(sorted(cs)))

143
2024/d24.py Normal file
View File

@@ -0,0 +1,143 @@
from lib import get_data
from lib import ints
LHS, OP, RHS, _, OUT = 0, 1, 2, 3, 4
def run(states, gates):
change = True
while change:
change = False
for gate in gates:
i1, op, i2, _, out = gate
if i1 in states and i2 in states and out not in states:
change = True
outv = None
match op:
case "OR":
outv = states[i1] | states[i2]
case "XOR":
outv = states[i1] ^ states[i2]
case "AND":
outv = states[i1] & states[i2]
assert outv is not None
states[out] = outv
i, t = 0, 0
while True:
si = f"z{i:02}"
if not si in states:
break
if states[si]:
t += 2**i
i += 1
return t
data = get_data(__file__)
s1, s2 = data.split("\n\n")
states = {}
for line in s1.splitlines():
gate, value = line.split(": ")
states[gate] = int(value)
gates = []
out_to_index = {}
xors = {}
zz = []
for i, line in enumerate(s2.splitlines()):
o1, op, o2, _, out = line.split()
o1, o2 = sorted([o1, o2])
gate = [o1, op, o2, "->", out]
gates.append(gate)
out_to_index[out] = i
if out.startswith("z"):
zz.append(out)
if op == "XOR" and o1.startswith("x"):
(index,) = ints(o1)
xors[index] = gate
p1 = run(states, gates)
print(p1)
NUMBER_Z_GATES = ints(sorted(zz)[-1])[0] + 1
# print(NUMBER_Z_GATES)
# pprint(xors)
xx = [f"x{i:02}" for i in range(NUMBER_Z_GATES - 1)]
yy = [f"y{i:02}" for i in range(NUMBER_Z_GATES - 1)]
# def resolve(x):
# if x in xx or x in yy:
# return f"{x}"
# # return x
# a, op, b = gate_to_in[x]
# aa = resolve(a)
# bb = resolve(b)
# # print(aa, bb)
# aa, bb = sorted([aa, bb], reverse=True)
# op = "^" if op == "XOR" else ("|" if op == "OR" else "&")
# return f"({aa} {op} {bb})"
# # return [aa, op, bb]
def find_xor(index):
xor = xors[index][OUT]
for gate in gates:
if gate[OP] == "XOR" and (gate[LHS] == xor or gate[RHS] == xor):
return gate
return None
corrected = []
for i in range(NUMBER_Z_GATES):
sx, sy, sz = [f"{c}{i:02}" for c in "xyz"]
gate = gates[out_to_index[sz]]
if i == 0:
assert gate == xors[i]
elif i == NUMBER_Z_GATES - 1:
pass # last gate needs special check
else:
fixed = False
xor = xors[i]
if gate[OP] != "XOR":
cgate = find_xor(i)
assert cgate is not None
gate[OUT], cgate[OUT] = cgate[OUT], gate[OUT]
corrected.append(gate[OUT])
corrected.append(cgate[OUT])
out_to_index[gate[OUT]], out_to_index[cgate[OUT]] = (
out_to_index[cgate[OUT]],
out_to_index[gate[OUT]],
)
fixed = True
# print(f"[swap] {gate} <-> {cgate}")
gate = gates[out_to_index[sz]]
assert gate[OP] == "XOR"
# print(gate)
i_lhs, i_rhs = out_to_index[gate[LHS]], out_to_index[gate[RHS]]
a, b = gates[i_lhs], gates[i_rhs]
if a == xor:
# print(f"[ok] {sz} ({fixed})")
pass
elif b == xor:
# print(f"[ok] {sz} ({fixed})")
gate[0], gate[2] = gate[2], gate[0]
else:
if a[OP] == "AND":
a[OUT], xor[OUT] = xor[OUT], a[OUT]
corrected.append(a[OUT])
corrected.append(xor[OUT])
# print(f"[swap] {a} <-> {xor}")
out_to_index[a[OUT]], out_to_index[xor[OUT]] = (
out_to_index[xor[OUT]],
out_to_index[a[OUT]],
)
# print(f"[ok] {sz} (manual fix)")
else:
print(f"[nok] {sz} (don't know how to fix)")
print(",".join(sorted(corrected)))

25
2024/d25.py Normal file
View File

@@ -0,0 +1,25 @@
from lib import get_data
from lib import Grid2D
data = get_data(__file__)
locks = []
keys = []
for o in data.split("\n\n"):
g = Grid2D(o)
if all(g[(0, c)] == "#" for c in range(g.n_cols)):
keys.append(g)
elif all(g[g.n_rows - 1, c] == "#" for c in range(g.n_cols)):
locks.append(g)
else:
assert False
t = 0
for key in keys:
for lock in locks:
k = key.find("#")
l = lock.find("#")
if len(k) + len(l) == len(set(k) | set(l)):
t += 1
print(t)

22
2025/d01.py Normal file
View File

@@ -0,0 +1,22 @@
from lib import get_data
NUM = 100
data = get_data(__file__)
pos = 50
r1, r2 = 0, 0
for line in data.splitlines():
d = line[0]
n = int(line[1:])
dir = 1 if d == "R" else -1
for _ in range(n):
pos = (pos + dir) % NUM
if pos == 0:
r2 += 1
if pos == 0:
r1 += 1
print(r1)
print(r2)

34
2025/d02.py Normal file
View File

@@ -0,0 +1,34 @@
from lib import get_data
data = get_data(__file__)
def is_invalid(xs: str) -> bool:
for seq_len in range(1, len(xs) // 2 + 1):
if xs[:seq_len] == xs[seq_len:]:
return True
return False
def is_invalid2(xs: str) -> bool:
for seq_len in range(1, len(xs)):
if len(xs) % seq_len != 0:
continue
for repeat in range(len(xs) // seq_len):
i = seq_len * repeat
if xs[:seq_len] != xs[i:i + seq_len]:
break
else:
return True
return False
r1, r2 = 0, 0
for id_range in data.split(","):
lo, up = list(map(int, id_range.split("-")))
for id in range(lo, up + 1):
if is_invalid(str(id)):
r1 += id
if is_invalid2(str(id)):
r2 += id
print(r1)
print(r2)

34
2025/d03.py Normal file
View File

@@ -0,0 +1,34 @@
from lib import get_data
data = get_data(__file__)
def max_joltage(xs: str, target_len: int) -> int:
def dynamic(xs: str) -> dict[int, str]:
if len(xs) == 0:
return {0: ""}
rs = dynamic(xs[1:])
for i in range(target_len, 0, -1):
if not (i - 1) in rs:
continue
nv = int(xs[0] + rs[i - 1])
if not i in rs:
rs[i] = str(nv)
elif nv > int(rs[i]):
rs[i] = str(nv)
return rs
rs = dynamic(xs)
return int(rs[target_len])
r1 = 0
r2 = 0
for line in data.splitlines():
r1 += max_joltage(line, 2)
r2 += max_joltage(line, 12)
print(r1)
print(r2)

32
2025/d04.py Normal file
View File

@@ -0,0 +1,32 @@
from lib import get_data, Grid2D
data = get_data(__file__)
g = Grid2D(data)
r = 0
adjs = []
for pos in g.all_coords():
if g[pos] != "@":
continue
nr_of_rolls_adj = len([nb for nb in g.neighbors_adj(pos) if g[nb] == "@"])
if nr_of_rolls_adj < 4:
r += 1
adjs.append(pos)
print(r)
r = 0
while True:
adjs = []
for pos in g.all_coords():
if g[pos] != "@":
continue
nr_of_rolls_adj = len([nb for nb in g.neighbors_adj(pos) if g[nb] == "@"])
if nr_of_rolls_adj < 4:
adjs.append(pos)
if adjs:
r += len(adjs)
for a in adjs:
g[a] = "."
else:
break
print(r)

45
2025/d05.py Normal file
View File

@@ -0,0 +1,45 @@
from lib import get_data
data = get_data(__file__)
ranges, ids = data.split("\n\n")
ids = tuple(map(int, ids.splitlines()))
ranges = [list(map(int, line.split("-"))) for line in ranges.splitlines()]
updated = True
while updated:
updated = False
sorted_ranges = []
for lo, up in ranges:
assert lo <= up
for i in range(len(sorted_ranges)):
slo, sup = sorted_ranges[i]
if up + 1 < slo:
sorted_ranges.insert(i, (lo, up))
elif lo < slo:
sorted_ranges[i] = (lo, max(up, sup))
updated = True
elif lo <= sup:
sorted_ranges[i] = (slo, max(up, sup))
updated = True
else:
continue
break
else:
sorted_ranges.append((lo, up))
ranges = sorted_ranges
result_part_1 = 0
for id_to_check in ids:
for lo, up in ranges:
if lo <= id_to_check <= up:
result_part_1 += 1
break
print(result_part_1)
result_part_2 = 0
for lo, up in ranges:
result_part_2 += up - lo + 1
print(result_part_2)
assert result_part_2 == 350780324308385

60
2025/d06.py Normal file
View File

@@ -0,0 +1,60 @@
from lib import get_data
from typing import TypeVar
T = TypeVar('T')
data = """123 328 51 64
45 64 387 23
6 98 215 314
* + * + """
data = get_data(__file__)
def mul(xs):
r = 1
for x in xs:
r *= x
return r
lines = zip(*[line.split() for line in data.splitlines()])
r = 0
for xs in lines:
xs, op = xs[:-1], xs[-1]
xs = list(map(int, xs))
if op == "+":
r += sum(xs)
elif op == "*":
r += mul(xs)
else:
assert False, "Unexpected op"
print(r)
def split(xs: list[T], delim: T) -> list[list[T]]:
res = [[]]
for x in xs:
if x == delim:
res.append([])
else:
res[-1].append(x)
return res
chars_trans: list[tuple[str]] = list(zip(*[line for line in data.splitlines()]))
lines_trans: list[str] = list(map(lambda line: "".join(line).strip(), chars_trans))
cols: list[list[str]] = split(lines_trans, '')
r2 = 0
for xs in cols:
x0 = xs[0]
acc, op = int(x0[:-1]), x0[-1]
for x in xs[1:]:
x = int(x)
if op == "+":
acc += x
elif op == "*":
acc *= x
else:
assert False, "Unexpected op"
r2 += acc
print(r2)

59
2025/d07.py Normal file
View File

@@ -0,0 +1,59 @@
from lib import get_data, Grid2D
from collections import defaultdict
data = """.......S.......
...............
.......^.......
...............
......^.^......
...............
.....^.^.^.....
...............
....^.^...^....
...............
...^.^...^.^...
...............
..^...^.....^..
...............
.^.^.^.^.^...^.
..............."""
data = get_data(__file__)
g = Grid2D(data)
beams = [g.find('S')[0][1]]
splits = 0
for row in range(1, g.n_rows - 1):
new_beams = set()
for beam in beams:
field = g[(row, beam)]
if field == "^":
splits += 1
new_beams.add(beam - 1)
new_beams.add(beam + 1)
elif field == ".":
new_beams.add(beam)
else:
assert False, "unexpected field"
beams = list(new_beams)
# print(beams)
print(splits)
beams = {g.find('S')[0][1]: 1}
for row in range(1, g.n_rows - 1):
new_beams = defaultdict(int)
for beam, count in beams.items():
field = g[(row, beam)]
if field == "^":
new_beams[beam - 1] += count
new_beams[beam + 1] += count
elif field == ".":
new_beams[beam] += count
else:
assert False, "unexpected field"
beams = new_beams
print(sum(beams.values()))

1
2025/lib.py Symbolic link
View File

@@ -0,0 +1 @@
../lib.py

547
README.md
View File

@@ -2,244 +2,60 @@
Solutions and utility script for Advent of Code challenges in Python.
## AoC 2015
## AoC 2025
- Day 1: 3:10
- Day 2: 5:24 :/
- Day 3: 7:26 ... :/
- Day 4: 5:11 ...
- Day 5: 14:05 o.O
- Day 6: 15:00
- Day 7: 17:00
- Day 8: 14:55
- Day 9: 13:48
- Day 10: 70:00 ... slow, but fun
- Day 11: 12:30
- Day 12: 6:03
- Day 13: 7:06 (would have been first by a minute, probably 70ish in 2023)
- Day 14: 27:45 ... that was weak, logic error for part 2 :/
- Day 15: 16:00 I should probably stop brute forcing these optimization problems
- Day 16: 19:00
- Day 17: 9:05
- Day 18: 10:39
- Day 19: Many days... yeah this one took me way too long to figure out
- Day 20: 10:54
- Day 21: 25:52 cute bug where I didn't consider that no armor is an option
- Day 22: That was bad. Did not know how to choose between dfs/bfs and logic errors.
- Day 23: 10:00
- Day 24: 20:00 ugly - recursive solution would be more elegant
- Day 25: 9:34
Only twelve problems and no leaderboard this year. That means life will be less
stressful and this will actually be more fun. Thank you Eric Wastl and let's go!
## AoC 2016
- Day 1: Maybe the hardest day 1 part 2 ever for me? I struggled to calculate
the number of ticks on 0 directly and ended up just iterating over all the
ticks naively. Maybe I should revisit this.
- Day 2: The simple direct approach with iterating over all the IDs in the
ranges just worked okay. Not pretty but acceptable.
- Day 3: Fun dynamic programming problem. Part 2 took me a little too long
overall but it was fun.
- Day 4: One of the easier grid based puzzles. Less than five minutes with
existing grid library.
- Day 5: A problem with ranges; I decided to implement proper range merging this
time because I always kind of avoid that. I could probably decrease the
complexity from O(n^2) to O(n log(n)) but I am happy that I've implemented
merging at all.
- Day 6: Transposing some rows and cols. Fun and good to keep the brain fit
but not really hard. I would have been way too slow for the leaderboard
in pre-AI years.
- Day 7: Grid puzzle that required non-naiv implementation for part 2. Took
me a second to realize that I could not save the coordinates by list but
had to use a count to be efficient.
- Day 8:
- Day 1: 29:00 That was emberassingly slow. Out of my rhythm?
- Day 2: 13:24 Getting back into it but still slow af, obviously.
- Day 3: 11:20 Ugly and slow.
- Day 4: 21:05 -__-
- Day 5: 29:35 -___-
- Day 6: 4:20 okay
- Day 7: 31:20 hmm
- Day 8: 14:50 meh
- Day 9: 26:00 okay
- Day 10: 23:07 okay
- Day 11: 75:00 -__-
- Day 12: 10:05 okay
- Day 13: 9:43 okayish
- Day 14: 120:00 struggled with this one (example incorrect?)
- Day 15: Trial and error. Should use CRT instead.
- Day 16: 14:11
- Day 17: 45:00 didn't follow instructions... focus!
- Day 18: 9:43
- Day 19: 90:00 that wasn't easy for me
- Day 20: 67:00
- Day 21: 32:33
- Day 22: 90:00
- Day 23: 60:00
- Day 24: 48:00
- Day 25: 6:45
## AoC 2017
## AoC 2024
- Day 1: 7:30
- Day 2: 6:15
- Day 3: 75:00 hmm should have been way quicker
- Day 4: 3:02
- Day 5: 6:13
- Day 6: 8:37
- Day 7: 19:22
- Day 8: 8:15
- Day 9: 6:10
- Day 10: 55:00
- Day 11: 66:06
- Day 12: 6:44
- Day 13: 120:00
- Day 14: 17:48
- Day 15: 11:40
- Day 16: 13:16
- Day 17: 55:00
- Day 18: 23:00
- Day 19: 45:00
- Day 20: 9:55 (would have been 10th)
- Day 21: 90:00
- Day 22: 25:00
- Day 23: Multiple days... but super fun.
- Day 24: 15:45 (48th)
- Day 25: 41:00
## AoC 2018
- Day 1: 1:49 (2nd)
- Day 2: 10:53
- Day 3: 6:16 (24th)
- Day 4: 25:16
- Day 5: 17:03
- Day 6: 1:10:29
- Day 7: 20:15
- Day 8: 18:35
- Day 9: 1:17:52
- Day 10: 19:14
- Day 11: 22:52
- Day 12: 180:00
- Day 13: 73:09
- Day 14: 20:48
- Day 15: 185:11
- Day 16: 36:10
- Day 17: 180:00
- Day 18: 24:04
- Day 19: days (super fun, but hard for me)
- Day 20: weeks (a cache was all it took - weak)
- Day 21: 28:40 (16th - brute force but still not so bad)
- Day 22: 185:00 (should not have been so slow but was fun)
- Day 23: days
- Day 24: days
- Day 25: 29:20 (slow)
## AoC 2019
- Day 1: 4:25 (copy and paste error, no leaderboard)
- Day 2: 7:07 (19th)
- Day 3: 10:46 (37th)
- Day 4: 8:48 (just too slow, no leaderboard)
- Day 5: 34:24 (that wasn't hard at all)
- Day 6: 23:17 (so slow, no leaderboard)
- Day 7: 72:17 (I found that one challenging, 30:33 would have been required for leaderboard)
- Day 8: 08:55 (54th)
- Day 9: 115:00 (Try to read next time.)
- Day 10: 76:00 (This wasn't easy for me. Fun, though.)
- Day 11: 21:04 (Too slow, but fun.)
- Day 12: days (Took me a while to get the right idea.)
- Day 13: >120:00 (Just struggling so much for some reason.)
- Day 14: >120:00 (Hmm, wasn't that hard either.)
- Day 15: >120:00 (I am really weak at the moment.)
- Day 16: days (Wow. Just too hard for me to solve quickly?)
- Day 17: days (Fun but too tricky for me to be fast.)
- Day 18: days (Slow and slow algorithm.)
- Day 19: 40:00 (Way too slow! Oversight error. Come on.)
- Day 20: days (Not actually that hard but I struggled for no reason.)
- Day 21: days (But it was super fun!)
- Day 22: days (Needed some help...)
- Day 23: 23:13 (Still too slow even though my int computer was in good shape...)
- Day 24: 53:00 (Can I ever even get points at all?)
- Day 25: 70:00 (Well, done at least. Super super fun!)
## AoC 2020
- Day 1: 2:48 (people weren't able to submit because of a website outage)
- Day 2: 4:47 (no leaderboard, you can tell it's getting faster)
- Day 3: 7:06 (way too slow, lol; time to take it seriously)
- Day 4: 14:30 (yo, I am just too slow)
- Day 5: 11:53 (not competitive)
- Day 6: 4:11 (75th, finally on the leaderboard)
- Day 7: 24:39 (bad)
- Day 8: 6:26 (43th, tied George Hotz :)
- Day 9: 7:37 (choked bad)
- Day 10: 34:27 (so weak)
- Day 11: 21:05 (hmmm, I rally have to analyze why I am so slow)
- Day 12: 21:52 (just slow again for an easy problem)
- Day 13: 18:00 (I don't really understand the CRT to be honest)
- Day 14: 40:26 (Made a bunch of mistakes even misunderstanding Python)
- Day 15: 17:57 (Too slow for an easy one like this)
- Day 16: 33:00 (Not too unhappy really.)
- Day 17: 10:00 (40th)
- Day 18: 80:00 (I am struggling with stuff where parsing is involved)
- Day 19: 78:00 (Should have been faster)
- Day 20: 95:00 (Lot's of code)
- Day 21: 23:02 (Simply to slow)
- Day 22: 45:49 (Simple and too slow)
- Day 23: 105:00 (Sad)
- Day 24: 15:38 (Close to leaderboard)
- Day 25: 14:40 (Way too slow)
## AoC 2021
- Day 1: 4:01 (Haha. Why am I so bad?!?!)
- Day 2: 6:36 (Okay. I might as well stop doing these.)
- Day 3: 23:46 (How long can I take when I try to take long?)
- Day 4: 22:18 (No way. Such stupid bugs.)
- Day 5: 13:30 (Decent, but obviously too slow.)
- Day 6: 16:33 (Right idea quickly but then struggled to implement.)
- Day 7: 6:12 (Decent, but too slow again, obviously. Maybe fast enough for part 1.)
- Day 8: 72:00 (Yeah, that was too much of a struggle.)
- Day 9: 8:07 (37th okay, okay)
- Day 10: 15:50 (I was pretty happy with that but double the 100th place.)
- Day 11: 11:43 (Could have been much faster here.)
- Day 12: 19:11 (Okayish, but of course too slow for leaderboard.)
- Day 13: 16:48 (Should have been way faster.)
- Day 14: 25:52 (Not hard but just too slow.)
- Day 15: 19:17 (Not that bad. Multiplying the thing threw me off.)
- Day 16: 50:01 (Way too slow. Was non-trivial but fun. Much better was feasible.)
- Day 17: 21:59 (Not tricky again but struggling for no reason.)
- Day 18: 162:00 (I couldn't figure out how to solve it as a tree so I did the basic way.)
- Day 19: days (Super hard for me but super fun ultimately once I had the right approach.)
- Day 20: 105:00 (That wasn't easy but was able to solve in one go.)
- Day 21: 37:45 (Wasn't hard but I was just too slow.)
- Day 22: 142:00 (Wonderful problem and hard for me but learned something new for sure.)
- Day 23: 81:38 (Fun but obviously not competitive.)
- Day 24: 232:00 (Super hard for me but I am proud of it.)
- Day 25: 15:43 (That could have been much much faster.)
## AoC 2022
Done with this. Overall everything is solvable. It's more about consistency
and focus. Of course, learning more algorithms and techniques helps.
**Times:**
- Day 1: 7:52 ... so slow brah :/ top 100 required 2:05...
- Day 2: 22:30 ... I mistyped the first and second was just bad top 100 would
have been 6:16 (doable?)
- Day 3: 13:08 actually decent but top 100 required 5:24
- Day 4: 7:08 but top 100 required 3:33 still okay
- Day 5: 11:56 but 7:58 for top 100... getting better?
- Day 6: 3:50 but 2:25 for leaderboard :D
- Day 7: 27:55 and 14:47 for leaderboard; okay, I would say
- Day 8: 61:00 and 10:00 for leaderboard; I need template code for searching
coordinate systems
- Day 9: 58:00 and 7:32 for leaderboard; I need code for 2D stuff
- Day 10: 25:20 and 12:17 for leaderboard; okay, okay
- Day 11: 45:00 and 18:00 for leaderboard; arg, it was doable man
- Day 12: 39:45 and 9:46 for leaderboard; the people are ready for their
searches :D
- Day 13: 44:00 and 12:56 for leaderboard; these people are just good,
seriously
- Day 14: 35:00 and 14:54; I just have to get that much quicker... 2D code!
- Day 15: 150:00 and 27:00; I didn't use Manhattan dist initially, lot's of
debugging
- Day 16: 52:00 and 64:00; ARE YOU SAYING I WOULD HAVE MADE THE
LEADERBOARD?!?!?!?!?!?!?!
- Day 17: Second one was fun with having to detect the repetition.
- Day 18: 12:00 and 32:00; really straightforward and of course way too slow.
- Day 19: Slow. (2024-09-13 improved with help from hyper neutrino.)
- Day 20: Struggled way too much.
- Day 21: Straightforward and relatively fast.
- Day 22: Very hard and wasn't able to do hands free. Even the best guys took
over an hour.
- Day 23: Super straightforward, but took me way longer than it should have
because I loose focus and make silly errors. Like back in Middleschool.
- Day 24: Easy. Should have been faster, but no crazy mistakes. Still way too
slow for something easy like this.
- Day 25: Quickly solved via constraint solver. Actual solution much simpler. Don't know
if I would have been able to figure it out.
- Day 1: `00:01:30 124 0 00:02:49 141 0`
- Day 2: `00:05:34 686 0 00:08:21 531 0`
- Day 3: `00:01:48 165 0 00:02:40 56 45`
- Day 4: `00:07:47 1101 0 00:24:07 2410 0`
- Day 5: `00:07:07 679 0 00:23:02 1998 0`
- Day 6: `00:09:43 1082 0 00:16:29 464 0`
- Day 7: `00:12:29 2058 0 00:13:08 1170 0`
- Day 8: `00:15:59 1742 0 00:26:56 2190 0`
- Day 9: `00:48:23 6055 0 01:09:38 3159 0`
- Day 10: `00:19:47 2976 0 00:20:47 2244 0`
- Day 11: `00:05:13 642 0 00:33:07 2673 0`
- Day 12: `00:30:30 3540 0 11:41:33 16212 0`
- Day 13: `00:05:35 225 0 00:55:48 2544 0`
- Day 14: `00:20:41 2136 0 00:35:14 1048 0`
- Day 15: ` >24h 32248 0 >24h 23671 0`
- Day 16: ` >24h 24941 0 >24h 20575 0`
- Day 17: `17:34:16 23722 0 >24h 17778 0`
- Day 18: `14:35:01 20398 0 14:37:18 19550 0`
- Day 19: `00:14:37 2001 0 00:19:43 1584 0`
- Day 20: `01:08:53 3637 0 01:53:01 2837 0`
- Day 21: `00:48:40 215 0 >24h 16427 0`
- Day 22: `00:13:04 1930 0 00:28:29 739 0`
- Day 23: ` >24h 20096 0 >24h 17620 0`
- Day 24: `15:57:01 17307 0 >24h 11326 0`
- Day 25: `10:41:54 14140 0 >24h 13631 0`
## AoC 2023
@@ -289,26 +105,249 @@ and focus. Of course, learning more algorithms and techniques helps.
Manually plotting requires matplotlib and networkx packages.
## AoC 2024
## AoC 2022
- Day 1: `00:01:30 124 0 00:02:49 141 0`
- Day 2: `00:05:34 686 0 00:08:21 531 0`
- Day 3: `00:01:48 165 0 00:02:40 56 45`
- Day 4: `00:07:47 1101 0 00:24:07 2410 0`
- Day 5: `00:07:07 679 0 00:23:02 1998 0`
- Day 6: `00:09:43 1082 0 00:16:29 464 0`
- Day 7: `00:12:29 2058 0 00:13:08 1170 0`
- Day 8: `00:15:59 1742 0 00:26:56 2190 0`
- Day 9: `00:48:23 6055 0 01:09:38 3159 0`
- Day 10: `00:19:47 2976 0 00:20:47 2244 0`
- Day 11: `00:05:13 642 0 00:33:07 2673 0`
- Day 12: `00:30:30 3540 0 11:41:33 16212 0`
- Day 13: `00:05:35 225 0 00:55:48 2544 0`
- Day 14: `00:20:41 2136 0 00:35:14 1048 0`
- Day 15: ` >24h 32248 0 >24h 23671 0`
- Day 16: ` >24h 24941 0 >24h 20575 0`
- Day 17: `17:34:16 23722 0 >24h 17778 0`
- Day 18: `14:35:01 20398 0 14:37:18 19550 0`
- Day 19: `00:14:37 2001 0 00:19:43 1584 0`
- Day 20: `01:08:53 3637 0 01:53:01 2837 0`
Done with this. Overall everything is solvable. It's more about consistency
and focus. Of course, learning more algorithms and techniques helps.
**Times:**
- Day 1: 7:52 ... so slow brah :/ top 100 required 2:05...
- Day 2: 22:30 ... I mistyped the first and second was just bad top 100 would
have been 6:16 (doable?)
- Day 3: 13:08 actually decent but top 100 required 5:24
- Day 4: 7:08 but top 100 required 3:33 still okay
- Day 5: 11:56 but 7:58 for top 100... getting better?
- Day 6: 3:50 but 2:25 for leaderboard :D
- Day 7: 27:55 and 14:47 for leaderboard; okay, I would say
- Day 8: 61:00 and 10:00 for leaderboard; I need template code for searching
coordinate systems
- Day 9: 58:00 and 7:32 for leaderboard; I need code for 2D stuff
- Day 10: 25:20 and 12:17 for leaderboard; okay, okay
- Day 11: 45:00 and 18:00 for leaderboard; arg, it was doable man
- Day 12: 39:45 and 9:46 for leaderboard; the people are ready for their
searches :D
- Day 13: 44:00 and 12:56 for leaderboard; these people are just good,
seriously
- Day 14: 35:00 and 14:54; I just have to get that much quicker... 2D code!
- Day 15: 150:00 and 27:00; I didn't use Manhattan dist initially, lot's of
debugging
- Day 16: 52:00 and 64:00; ARE YOU SAYING I WOULD HAVE MADE THE
LEADERBOARD?!?!?!?!?!?!?!
- Day 17: Second one was fun with having to detect the repetition.
- Day 18: 12:00 and 32:00; really straightforward and of course way too slow.
- Day 19: Slow. (2024-09-13 improved with help from hyper neutrino.)
- Day 20: Struggled way too much.
- Day 21: Straightforward and relatively fast.
- Day 22: Very hard and wasn't able to do hands free. Even the best guys took
over an hour.
- Day 23: Super straightforward, but took me way longer than it should have
because I loose focus and make silly errors. Like back in Middleschool.
- Day 24: Easy. Should have been faster, but no crazy mistakes. Still way too
slow for something easy like this.
- Day 25: Quickly solved via constraint solver. Actual solution much simpler. Don't know
if I would have been able to figure it out.
## AoC 2021
- Day 1: 4:01 (Haha. Why am I so bad?!?!)
- Day 2: 6:36 (Okay. I might as well stop doing these.)
- Day 3: 23:46 (How long can I take when I try to take long?)
- Day 4: 22:18 (No way. Such stupid bugs.)
- Day 5: 13:30 (Decent, but obviously too slow.)
- Day 6: 16:33 (Right idea quickly but then struggled to implement.)
- Day 7: 6:12 (Decent, but too slow again, obviously. Maybe fast enough for part 1.)
- Day 8: 72:00 (Yeah, that was too much of a struggle.)
- Day 9: 8:07 (37th okay, okay)
- Day 10: 15:50 (I was pretty happy with that but double the 100th place.)
- Day 11: 11:43 (Could have been much faster here.)
- Day 12: 19:11 (Okayish, but of course too slow for leaderboard.)
- Day 13: 16:48 (Should have been way faster.)
- Day 14: 25:52 (Not hard but just too slow.)
- Day 15: 19:17 (Not that bad. Multiplying the thing threw me off.)
- Day 16: 50:01 (Way too slow. Was non-trivial but fun. Much better was feasible.)
- Day 17: 21:59 (Not tricky again but struggling for no reason.)
- Day 18: 162:00 (I couldn't figure out how to solve it as a tree so I did the basic way.)
- Day 19: days (Super hard for me but super fun ultimately once I had the right approach.)
- Day 20: 105:00 (That wasn't easy but was able to solve in one go.)
- Day 21: 37:45 (Wasn't hard but I was just too slow.)
- Day 22: 142:00 (Wonderful problem and hard for me but learned something new for sure.)
- Day 23: 81:38 (Fun but obviously not competitive.)
- Day 24: 232:00 (Super hard for me but I am proud of it.)
- Day 25: 15:43 (That could have been much much faster.)
## AoC 2020
- Day 1: 2:48 (people weren't able to submit because of a website outage)
- Day 2: 4:47 (no leaderboard, you can tell it's getting faster)
- Day 3: 7:06 (way too slow, lol; time to take it seriously)
- Day 4: 14:30 (yo, I am just too slow)
- Day 5: 11:53 (not competitive)
- Day 6: 4:11 (75th, finally on the leaderboard)
- Day 7: 24:39 (bad)
- Day 8: 6:26 (43th, tied George Hotz :)
- Day 9: 7:37 (choked bad)
- Day 10: 34:27 (so weak)
- Day 11: 21:05 (hmmm, I rally have to analyze why I am so slow)
- Day 12: 21:52 (just slow again for an easy problem)
- Day 13: 18:00 (I don't really understand the CRT to be honest)
- Day 14: 40:26 (Made a bunch of mistakes even misunderstanding Python)
- Day 15: 17:57 (Too slow for an easy one like this)
- Day 16: 33:00 (Not too unhappy really.)
- Day 17: 10:00 (40th)
- Day 18: 80:00 (I am struggling with stuff where parsing is involved)
- Day 19: 78:00 (Should have been faster)
- Day 20: 95:00 (Lot's of code)
- Day 21: 23:02 (Simply to slow)
- Day 22: 45:49 (Simple and too slow)
- Day 23: 105:00 (Sad)
- Day 24: 15:38 (Close to leaderboard)
- Day 25: 14:40 (Way too slow)
## AoC 2019
- Day 1: 4:25 (copy and paste error, no leaderboard)
- Day 2: 7:07 (19th)
- Day 3: 10:46 (37th)
- Day 4: 8:48 (just too slow, no leaderboard)
- Day 5: 34:24 (that wasn't hard at all)
- Day 6: 23:17 (so slow, no leaderboard)
- Day 7: 72:17 (I found that one challenging, 30:33 would have been required for leaderboard)
- Day 8: 08:55 (54th)
- Day 9: 115:00 (Try to read next time.)
- Day 10: 76:00 (This wasn't easy for me. Fun, though.)
- Day 11: 21:04 (Too slow, but fun.)
- Day 12: days (Took me a while to get the right idea.)
- Day 13: >120:00 (Just struggling so much for some reason.)
- Day 14: >120:00 (Hmm, wasn't that hard either.)
- Day 15: >120:00 (I am really weak at the moment.)
- Day 16: days (Wow. Just too hard for me to solve quickly?)
- Day 17: days (Fun but too tricky for me to be fast.)
- Day 18: days (Slow and slow algorithm.)
- Day 19: 40:00 (Way too slow! Oversight error. Come on.)
- Day 20: days (Not actually that hard but I struggled for no reason.)
- Day 21: days (But it was super fun!)
- Day 22: days (Needed some help...)
- Day 23: 23:13 (Still too slow even though my int computer was in good shape...)
- Day 24: 53:00 (Can I ever even get points at all?)
- Day 25: 70:00 (Well, done at least. Super super fun!)
## AoC 2018
- Day 1: 1:49 (2nd)
- Day 2: 10:53
- Day 3: 6:16 (24th)
- Day 4: 25:16
- Day 5: 17:03
- Day 6: 1:10:29
- Day 7: 20:15
- Day 8: 18:35
- Day 9: 1:17:52
- Day 10: 19:14
- Day 11: 22:52
- Day 12: 180:00
- Day 13: 73:09
- Day 14: 20:48
- Day 15: 185:11
- Day 16: 36:10
- Day 17: 180:00
- Day 18: 24:04
- Day 19: days (super fun, but hard for me)
- Day 20: weeks (a cache was all it took - weak)
- Day 21: 28:40 (16th - brute force but still not so bad)
- Day 22: 185:00 (should not have been so slow but was fun)
- Day 23: days
- Day 24: days
- Day 25: 29:20 (slow)
## AoC 2017
- Day 1: 7:30
- Day 2: 6:15
- Day 3: 75:00 hmm should have been way quicker
- Day 4: 3:02
- Day 5: 6:13
- Day 6: 8:37
- Day 7: 19:22
- Day 8: 8:15
- Day 9: 6:10
- Day 10: 55:00
- Day 11: 66:06
- Day 12: 6:44
- Day 13: 120:00
- Day 14: 17:48
- Day 15: 11:40
- Day 16: 13:16
- Day 17: 55:00
- Day 18: 23:00
- Day 19: 45:00
- Day 20: 9:55 (would have been 10th)
- Day 21: 90:00
- Day 22: 25:00
- Day 23: Multiple days... but super fun.
- Day 24: 15:45 (48th)
- Day 25: 41:00
## AoC 2016
- Day 1: 29:00 That was emberassingly slow. Out of my rhythm?
- Day 2: 13:24 Getting back into it but still slow af, obviously.
- Day 3: 11:20 Ugly and slow.
- Day 4: 21:05 -__-
- Day 5: 29:35 -___-
- Day 6: 4:20 okay
- Day 7: 31:20 hmm
- Day 8: 14:50 meh
- Day 9: 26:00 okay
- Day 10: 23:07 okay
- Day 11: 75:00 -__-
- Day 12: 10:05 okay
- Day 13: 9:43 okayish
- Day 14: 120:00 struggled with this one (example incorrect?)
- Day 15: Trial and error. Should use CRT instead.
- Day 16: 14:11
- Day 17: 45:00 didn't follow instructions... focus!
- Day 18: 9:43
- Day 19: 90:00 that wasn't easy for me
- Day 20: 67:00
- Day 21: 32:33
- Day 22: 90:00
- Day 23: 60:00
- Day 24: 48:00
- Day 25: 6:45
## AoC 2015
- Day 1: 3:10
- Day 2: 5:24 :/
- Day 3: 7:26 ... :/
- Day 4: 5:11 ...
- Day 5: 14:05 o.O
- Day 6: 15:00
- Day 7: 17:00
- Day 8: 14:55
- Day 9: 13:48
- Day 10: 70:00 ... slow, but fun
- Day 11: 12:30
- Day 12: 6:03
- Day 13: 7:06 (would have been first by a minute, probably 70ish in 2023)
- Day 14: 27:45 ... that was weak, logic error for part 2 :/
- Day 15: 16:00 I should probably stop brute forcing these optimization problems
- Day 16: 19:00
- Day 17: 9:05
- Day 18: 10:39
- Day 19: Many days... yeah this one took me way too long to figure out
- Day 20: 10:54
- Day 21: 25:52 cute bug where I didn't consider that no armor is an option
- Day 22: That was bad. Did not know how to choose between dfs/bfs and logic errors.
- Day 23: 10:00
- Day 24: 20:00 ugly - recursive solution would be more elegant
- Day 25: 9:34

2
lib.py
View File

@@ -276,7 +276,7 @@ def extract_year_and_date(scriptname) -> tuple[str, str]:
def get_data(filename):
path, file = os.path.split(filename)
year = path[-4:]
day = file.replace("d", "").replace(".py", "")
day = file.replace("d0", "").replace("d", "").replace(".py", "")
txt_file = f"d{day}.txt"
if os.path.isfile(txt_file):

10
pyproject.toml Normal file
View File

@@ -0,0 +1,10 @@
[project]
name = "aocpy"
version = "0.1.0"
description = "Make ruff and ty available via uv"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"ruff>=0.14.11",
"ty>=0.0.11",
]