Compare commits
23 Commits
9485fbae1d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ceb67a7fd | |||
| 40ac031eb4 | |||
| 0d1eff4f00 | |||
| cd00f46b77 | |||
| 217e770a25 | |||
| f132355ad5 | |||
| ee96c1336f | |||
| 66e2d48eaa | |||
| 78f9fb6e0b | |||
| 9d2cd556b2 | |||
| 65d1a85912 | |||
| 9fcc902703 | |||
| c5813e168b | |||
| 0d1b3fbc2c | |||
| 476fb277a4 | |||
| aa4730cad7 | |||
| 053d07dd27 | |||
| 30a8489313 | |||
| 5626205b2f | |||
| 1b3e58c307 | |||
| 87e2568b12 | |||
| d41e4737cb | |||
| 437498a745 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
|||||||
|
Pipfile
|
||||||
__pycache__
|
__pycache__
|
||||||
|
*.txt
|
||||||
|
|||||||
48
README.md
48
README.md
@@ -1,11 +1,53 @@
|
|||||||
|
My solutions to the Advent of Code 2023 programming challenges.
|
||||||
|
|
||||||
|
Thanks to Eric Wastl for creating this enjoyable event.
|
||||||
|
|
||||||
|
- Requires `lib.py` from [aocpy](https://git.felixm.de/felixm/aocpy) repository.
|
||||||
|
- Requires `sympy` for day 24.
|
||||||
|
- Requires `matplotlib` and `networkx` for hands-on day 25.
|
||||||
|
|
||||||
# Times
|
# Times
|
||||||
|
|
||||||
- Day 1: 40:00 (I don't know what I am doing.)
|
- Day 1: 40:00 (I don't know what I am doing.)
|
||||||
- Day 2: 14:15 (Okay, but far way from leaderboard.)
|
- Day 2: 14:15 (Okay, but far way from leaderboard.)
|
||||||
- Day 3: 1st 20:00, 2nd 70:00... (I had a logic error that took me a while to find.)
|
- Day 3: 1st 20:00, 2nd 70:00... (I had a logic error that took me a while to
|
||||||
|
find.)
|
||||||
- Day 4: 1st 9:06, 2nd 22:31; it wasn't hard but I didn't think quick enough :/
|
- Day 4: 1st 9:06, 2nd 22:31; it wasn't hard but I didn't think quick enough :/
|
||||||
- Day 5: 1st 25:00, 2nd 1:55:00; Required patience and accuracy
|
- Day 5: 1st 25:00, 2nd 1:55:00; Required patience and accuracy
|
||||||
- Day 6: 13:54; I was slow because I thought it is much harder?
|
- Day 6: 13:54; I was slow because I thought it is much harder?
|
||||||
- Day 7: 75:00; leaderboard 16:00... that was just bad; no excuse
|
- Day 7: 75:00; leaderboard 16:00... that was just bad; no excuse
|
||||||
- Day 8: 57:00; my input parse function did not consider negative values...
|
- Day 8: 25:00; I was doing pretty decent here.
|
||||||
- Day 9:
|
- Day 9: 57:00; my input parse function did not consider negative values...
|
||||||
|
- Day 10: 180:00; this one was hard for me.
|
||||||
|
- Day 11: 68:00; okay but not elegant and way too slow ofc; x-ray solution
|
||||||
|
would have been neat
|
||||||
|
- Day 12: 52:00 and 22:00 for leaderboard; had the right idea and I am good at
|
||||||
|
this type of problem
|
||||||
|
- Day 13: 90:00; pretty straightforward but way too slow
|
||||||
|
- Day 14: 5:55 for first and then 48:00; straightforward but slow, ofc
|
||||||
|
- Day 15: 4:30 and 31:20; more reading comprehension than programming
|
||||||
|
- Day 16: 00:27:30 745; best placement so far, of course still horribly slow
|
||||||
|
- Day 17: a couple of hours; I realized that I need A* after a while; reused
|
||||||
|
implementation from Project Euler but improved with heapq which was super fun
|
||||||
|
- Day 18: a couple of hours; I realized that I need shoelace algo for part two
|
||||||
|
but didn't realize that I have to compute the outer edges for a while and
|
||||||
|
after I did, I still got clockwise/counter-clockwise issues. They could have
|
||||||
|
made it meaner by using different clock directions for example and input.
|
||||||
|
- Day 19: This one was pretty straightforward and required the interval
|
||||||
|
technique we applied earlier.
|
||||||
|
- Day 20: Part 2 was tough. I had the right idea of printing out the periods of
|
||||||
|
the input conjunction gate pretty early, but then messed up the
|
||||||
|
implementation and thought it wasn't gonna work. Spent a half day thinking up
|
||||||
|
something else before returning to the idea and it worked flawlessly.
|
||||||
|
- Day 21: Part 1 was straightforward, but part 2 maybe the hardest problem this
|
||||||
|
year.
|
||||||
|
- Day 22: Not too hard, but definitely way too slow for leaderboard.
|
||||||
|
- Day 23: I found this fun because it required some creativity for part 2. Slow
|
||||||
|
af, of course.
|
||||||
|
- Day 24: Solve problem with sympy. I first used numpy to solve part 1 and it
|
||||||
|
was much faster than using sympy, but I lost that solution when switching to
|
||||||
|
sympy. Takes about three minutes to run for part 1 and then part 2 is under a
|
||||||
|
second.
|
||||||
|
- Day 25: I cheeky solved this by plotting the graph and manually removing the
|
||||||
|
nodes. I should probably try to write an algorith that does that, but meh.
|
||||||
|
Manually plotting requires matplotlib and networkx packages.
|
||||||
|
|||||||
218
d10.py
Normal file
218
d10.py
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
import lib
|
||||||
|
|
||||||
|
EXAMPLE = """
|
||||||
|
..F7.
|
||||||
|
.FJ|.
|
||||||
|
SJ.L7
|
||||||
|
|F--J
|
||||||
|
LJ...
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLE2 = """
|
||||||
|
...........
|
||||||
|
.S-------7.
|
||||||
|
.|F-----7|.
|
||||||
|
.||.....||.
|
||||||
|
.||.....||.
|
||||||
|
.|L-7.F-J|.
|
||||||
|
.|..|.|..|.
|
||||||
|
.L--J.L--J.
|
||||||
|
...........
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLE3 = """
|
||||||
|
..........
|
||||||
|
.S------7.
|
||||||
|
.|F----7|.
|
||||||
|
.||OOOO||.
|
||||||
|
.||OOOO||.
|
||||||
|
.|L-7F-J|.
|
||||||
|
.|II||II|.
|
||||||
|
.L--JL--J.
|
||||||
|
..........
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLE4 = """
|
||||||
|
FF7FSF7F7F7F7F7F---7
|
||||||
|
L|LJ||||||||||||F--J
|
||||||
|
FL-7LJLJ||||||LJL-77
|
||||||
|
F--JF--7||LJLJIF7FJ-
|
||||||
|
L---JF-JLJIIIIFJLJJ7
|
||||||
|
|F|F-JF---7IIIL7L|7|
|
||||||
|
|FFJF7L7F-JF7IIL---7
|
||||||
|
7-L-JL7||F7|L7F-7F7|
|
||||||
|
L.L7LFJ|||||FJL7||LJ
|
||||||
|
L7JLJL-JLJLJL--JLJ.L
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# | is a vertical pipe connecting north and south.
|
||||||
|
# - is a horizontal pipe connecting east and west.
|
||||||
|
# L is a 90-degree bend connecting north and east.
|
||||||
|
# J is a 90-degree bend connecting north and west.
|
||||||
|
# 7 is a 90-degree bend connecting south and west.
|
||||||
|
# F is a 90-degree bend connecting south and east.
|
||||||
|
# . is ground; there is no pipe in this tile.
|
||||||
|
# S is the starting position of the animal; there is a pipe on this tile, but your sketch doesn't show what shape the pipe has.
|
||||||
|
|
||||||
|
# Mapping of in-direction to out-direction for each piece.
|
||||||
|
DIRS = {
|
||||||
|
'|': {
|
||||||
|
(1, 0): (1, 0),
|
||||||
|
(-1, 0): (-1, 0),
|
||||||
|
},
|
||||||
|
'-': {
|
||||||
|
(0, -1): (0, -1),
|
||||||
|
(0, 1): (0, 1),
|
||||||
|
},
|
||||||
|
'L': {
|
||||||
|
(1, 0): (0, 1),
|
||||||
|
(0, -1): (-1, 0),
|
||||||
|
},
|
||||||
|
'J': {
|
||||||
|
(0, 1): (-1, 0),
|
||||||
|
(1, 0): (0, -1),
|
||||||
|
},
|
||||||
|
'7': {
|
||||||
|
(0, 1): (1, 0),
|
||||||
|
(-1, 0): (0, -1),
|
||||||
|
},
|
||||||
|
'F': {
|
||||||
|
(-1, 0): (0, 1),
|
||||||
|
(0, -1): (1, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RIGHTS = {
|
||||||
|
'|': {
|
||||||
|
(1, 0): [(0, -1)],
|
||||||
|
(-1, 0): [(0, 1)],
|
||||||
|
},
|
||||||
|
'-': {
|
||||||
|
(0, -1): [(-1, 0)],
|
||||||
|
(0, 1): [(1, 0)],
|
||||||
|
},
|
||||||
|
'L': {
|
||||||
|
(1, 0): [(0, -1), (1, 0)],
|
||||||
|
(0, -1): [(-1, 1)],
|
||||||
|
},
|
||||||
|
'J': {
|
||||||
|
(0, 1): [(1, 0), (0, 1)],
|
||||||
|
(1, 0): [(-1, -1)],
|
||||||
|
},
|
||||||
|
'7': {
|
||||||
|
(0, 1): [(1, -1)],
|
||||||
|
(-1, 0): [(0, 1), (-1, 0)],
|
||||||
|
},
|
||||||
|
'F': {
|
||||||
|
(-1, 0): [(1, 1)],
|
||||||
|
(0, -1): [(-1, 0), (0, -1)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def solve(lines: list[str], return_path=False):
|
||||||
|
res = 0
|
||||||
|
maze = []
|
||||||
|
start = ()
|
||||||
|
max_path, max_right = [], []
|
||||||
|
maze = list(map(list, lines))
|
||||||
|
for i, row in enumerate(maze):
|
||||||
|
for j, col in enumerate(row):
|
||||||
|
if col == "S":
|
||||||
|
start = (i , j)
|
||||||
|
row_max, col_max = len(maze), len(maze[0])
|
||||||
|
|
||||||
|
for current_dir in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
|
||||||
|
current_field = start
|
||||||
|
steps = 0
|
||||||
|
path = []
|
||||||
|
right = []
|
||||||
|
# print(f"START: {start} {current_dir=}")
|
||||||
|
while True:
|
||||||
|
path.append(current_field)
|
||||||
|
next_field = (current_field[0] + current_dir[0], current_field[1] + current_dir[1])
|
||||||
|
if next_field == start:
|
||||||
|
break
|
||||||
|
|
||||||
|
if next_field[0] < 0 or next_field[1] < 0 or next_field[0] >= row_max or next_field[1] >= col_max:
|
||||||
|
# print(f"BREAK: Cannot go to {next_field=}!")
|
||||||
|
break
|
||||||
|
|
||||||
|
prev_dir = current_dir
|
||||||
|
next_char = maze[next_field[0]][next_field[1]]
|
||||||
|
try:
|
||||||
|
current_dir = DIRS[next_char][current_dir]
|
||||||
|
except KeyError:
|
||||||
|
# print(f"BREAK: Cannot go from {current_field=} with {current_dir=} to {next_field=} ({next_char})!")
|
||||||
|
break
|
||||||
|
|
||||||
|
for rights in RIGHTS[next_char][prev_dir]:
|
||||||
|
rf = (next_field[0] + rights[0], next_field[1] + rights[1])
|
||||||
|
right.append(rf)
|
||||||
|
|
||||||
|
current_field = next_field
|
||||||
|
steps += 1
|
||||||
|
res = max(res, (steps + 1) // 2)
|
||||||
|
if len(path) > len(max_path):
|
||||||
|
max_path = path
|
||||||
|
max_right = right
|
||||||
|
|
||||||
|
if return_path:
|
||||||
|
return res, max_path, max_right
|
||||||
|
else:
|
||||||
|
return res
|
||||||
|
|
||||||
|
def solve2(lines: list[str]):
|
||||||
|
row_max = len(lines)
|
||||||
|
col_max = len(lines[0])
|
||||||
|
length, path, right = solve(lines, True)
|
||||||
|
|
||||||
|
def clean(fields):
|
||||||
|
fields = list(set(fields))
|
||||||
|
cleaned = []
|
||||||
|
for f in fields:
|
||||||
|
if f in path:
|
||||||
|
continue
|
||||||
|
if f[0] < 0 or f[1] < 0 or f[0] >= row_max or f[1] >= col_max:
|
||||||
|
continue
|
||||||
|
cleaned.append(f)
|
||||||
|
return cleaned
|
||||||
|
|
||||||
|
right = clean(right)
|
||||||
|
visited = set()
|
||||||
|
to_visit = list(right)
|
||||||
|
while to_visit:
|
||||||
|
current = to_visit.pop()
|
||||||
|
for nb in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
|
||||||
|
f = (current[0] + nb[0], current[1] + nb[1])
|
||||||
|
if f[0] < 0 or f[1] < 0 or f[0] >= row_max or f[1] >= col_max:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if f not in visited and f not in path:
|
||||||
|
right.append(f)
|
||||||
|
to_visit.append(f)
|
||||||
|
visited.add(f)
|
||||||
|
|
||||||
|
right = clean(right)
|
||||||
|
|
||||||
|
n_all = row_max * col_max
|
||||||
|
n_right = len(right)
|
||||||
|
n_left = n_all - n_right - len(path)
|
||||||
|
|
||||||
|
return min(n_left, n_right)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
lines = lib.str_to_lines_no_empty(EXAMPLE)
|
||||||
|
print("Example 1:", solve(lines))
|
||||||
|
|
||||||
|
lines = lib.str_to_lines_no_empty(open("i10.txt").read())
|
||||||
|
print("Solution 1:", solve(lines))
|
||||||
|
|
||||||
|
lines = lib.str_to_lines_no_empty(EXAMPLE4)
|
||||||
|
print("Example 2:", solve2(lines))
|
||||||
|
|
||||||
|
lines = lib.str_to_lines_no_empty(open("i10.txt").read())
|
||||||
|
print("Solution 2:", solve2(lines))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
111
d11.py
Normal file
111
d11.py
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import lib
|
||||||
|
|
||||||
|
EXAMPLE = """
|
||||||
|
...#......
|
||||||
|
.......#..
|
||||||
|
#.........
|
||||||
|
..........
|
||||||
|
......#...
|
||||||
|
.#........
|
||||||
|
.........#
|
||||||
|
..........
|
||||||
|
.......#..
|
||||||
|
#...#.....
|
||||||
|
"""
|
||||||
|
|
||||||
|
def mdist(a, b):
|
||||||
|
return abs(a[0] - b[0]) + abs(a[1] - b[1])
|
||||||
|
|
||||||
|
def solve(lines: list[str]):
|
||||||
|
res = 0
|
||||||
|
|
||||||
|
g = list(map(list, lines))
|
||||||
|
|
||||||
|
er = []
|
||||||
|
for (i, r) in enumerate(g):
|
||||||
|
if "#" not in r:
|
||||||
|
er.append(i)
|
||||||
|
|
||||||
|
ec = []
|
||||||
|
for j in range(len(g[0])):
|
||||||
|
for row in g:
|
||||||
|
if "#" == row[j]:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
ec.append(j)
|
||||||
|
|
||||||
|
for r in reversed(er):
|
||||||
|
g.insert(r, ["." for _ in range(len(g[0]))])
|
||||||
|
|
||||||
|
for row in g:
|
||||||
|
for c in reversed(ec):
|
||||||
|
row.insert(c, ".")
|
||||||
|
|
||||||
|
# for row in g:
|
||||||
|
# print("".join(row))
|
||||||
|
|
||||||
|
gxs = []
|
||||||
|
for (row, line) in enumerate(g):
|
||||||
|
for (col, c) in enumerate(line):
|
||||||
|
if c == '#':
|
||||||
|
gxs.append((row, col))
|
||||||
|
|
||||||
|
for i in range(len(gxs)):
|
||||||
|
for j in range(i, len(gxs)):
|
||||||
|
a, b = gxs[i], gxs[j]
|
||||||
|
d = mdist(a, b)
|
||||||
|
# print(a, b, d)
|
||||||
|
res += d
|
||||||
|
# 16:00
|
||||||
|
return res
|
||||||
|
|
||||||
|
def solve2(lines: list[str], factor):
|
||||||
|
res = 0
|
||||||
|
g = list(map(list, lines))
|
||||||
|
gxs = []
|
||||||
|
for (row, line) in enumerate(g):
|
||||||
|
for (col, c) in enumerate(line):
|
||||||
|
if c == '#':
|
||||||
|
gxs.append((row, col, row, col))
|
||||||
|
|
||||||
|
for (row_i, row) in enumerate(g):
|
||||||
|
if "#" not in row:
|
||||||
|
for i in range(len(gxs)):
|
||||||
|
row, col, orig_row, orig_col = gxs[i]
|
||||||
|
if orig_row > row_i:
|
||||||
|
gxs[i] = (row + factor, col, orig_row, orig_col)
|
||||||
|
|
||||||
|
for col_j in range(len(g[0])):
|
||||||
|
for row in g:
|
||||||
|
if "#" == row[col_j]:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
for i in range(len(gxs)):
|
||||||
|
row, col, orig_row, orig_col = gxs[i]
|
||||||
|
if orig_col > col_j:
|
||||||
|
gxs[i] = (row, col + factor, orig_row, orig_col)
|
||||||
|
|
||||||
|
for i in range(len(gxs)):
|
||||||
|
for j in range(i, len(gxs)):
|
||||||
|
a, b = gxs[i], gxs[j]
|
||||||
|
d = mdist(a, b)
|
||||||
|
# print(a, b, d)
|
||||||
|
res += d
|
||||||
|
# 16:00
|
||||||
|
return res
|
||||||
|
|
||||||
|
def main():
|
||||||
|
lines = lib.str_to_lines_no_empty(EXAMPLE)
|
||||||
|
print("Example 1:", solve(lines))
|
||||||
|
|
||||||
|
lines = lib.str_to_lines_no_empty(open("i11.txt").read())
|
||||||
|
print("Solution 1:", solve(lines))
|
||||||
|
|
||||||
|
lines = lib.str_to_lines_no_empty(EXAMPLE)
|
||||||
|
print("Example 2:", solve2(lines, 99))
|
||||||
|
|
||||||
|
lines = lib.str_to_lines_no_empty(open("i11.txt").read())
|
||||||
|
print("Solution 2:", solve2(lines, 10**6 - 1))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
69
d12.py
Normal file
69
d12.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import lib
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
EXAMPLE = """
|
||||||
|
???.### 1,1,3
|
||||||
|
.??..??...?##. 1,1,3
|
||||||
|
?#?#?#?#?#?#?#? 1,3,1,6
|
||||||
|
????.#...#... 4,1,1
|
||||||
|
????.######..#####. 1,6,5
|
||||||
|
?###???????? 3,2,1
|
||||||
|
"""
|
||||||
|
|
||||||
|
@lru_cache
|
||||||
|
def count(elems, groups):
|
||||||
|
if not elems and not groups:
|
||||||
|
return 1
|
||||||
|
elif elems and not groups:
|
||||||
|
if all(e in ['.', '?'] for e in elems):
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
elif not elems and groups:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
r = 0
|
||||||
|
if elems[0] in ['?', '.']:
|
||||||
|
r += count(elems[1:], groups)
|
||||||
|
|
||||||
|
g = groups[0]
|
||||||
|
if len(elems) < g:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
es, rest = elems[:g], elems[g:]
|
||||||
|
if all(e in ['?', '#'] for e in es) and (len(rest) == 0 or rest[0] in ['.', '?']):
|
||||||
|
r += count(rest[1:], groups[1:])
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def solve(lines: list[str], repeat=1):
|
||||||
|
all = []
|
||||||
|
for (_, line) in enumerate(lines):
|
||||||
|
springs, numbers = line.split()
|
||||||
|
numbers = tuple(lib.str_to_ints(numbers))
|
||||||
|
ns = "?".join([springs for _ in range(repeat)])
|
||||||
|
all.append((tuple(ns), numbers * repeat))
|
||||||
|
|
||||||
|
res = 0
|
||||||
|
for a in all:
|
||||||
|
# print(a, count(*a))
|
||||||
|
res += count(*a)
|
||||||
|
# 28:00
|
||||||
|
# 50:00 total
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
lines = lib.str_to_lines_no_empty(EXAMPLE)
|
||||||
|
print("Example 1:", solve(lines))
|
||||||
|
|
||||||
|
lines = lib.str_to_lines_no_empty(open("i12.txt").read())
|
||||||
|
print("Solution 1:", solve(lines))
|
||||||
|
|
||||||
|
lines = lib.str_to_lines_no_empty(EXAMPLE)
|
||||||
|
print("Example 2:", solve(lines, 5))
|
||||||
|
|
||||||
|
lines = lib.str_to_lines_no_empty(open("i12.txt").read())
|
||||||
|
print("Solution 2:", solve(lines, 5))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
92
d13.py
Normal file
92
d13.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
from lib import *
|
||||||
|
|
||||||
|
EXAMPLE = """
|
||||||
|
#.##..##.
|
||||||
|
..#.##.#.
|
||||||
|
##......#
|
||||||
|
##......#
|
||||||
|
..#.##.#.
|
||||||
|
..##..##.
|
||||||
|
#.#.##.#.
|
||||||
|
|
||||||
|
#...##..#
|
||||||
|
#....#..#
|
||||||
|
..##..###
|
||||||
|
#####.##.
|
||||||
|
#####.##.
|
||||||
|
..##..###
|
||||||
|
#....#..#
|
||||||
|
""".replace("\n", "", count=1)
|
||||||
|
|
||||||
|
|
||||||
|
def solve(i: Input, second=False):
|
||||||
|
res = 0
|
||||||
|
ps = i.paras()
|
||||||
|
|
||||||
|
for p in ps:
|
||||||
|
g = Input(p).grid2()
|
||||||
|
|
||||||
|
row_res, col_res = [], []
|
||||||
|
rows = g.rows()
|
||||||
|
cols = g.cols()
|
||||||
|
for i_row in range(len(rows) - 1):
|
||||||
|
for i in range(min(i_row + 1, len(rows) - i_row - 1)):
|
||||||
|
if rows[i_row - i] != rows[i_row + i + 1]:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if not second:
|
||||||
|
res += (i_row + 1) * 100
|
||||||
|
row_res.append(i_row)
|
||||||
|
|
||||||
|
for i_col in range(len(cols) - 1):
|
||||||
|
for i in range(min(i_col + 1, len(cols) - i_col - 1)):
|
||||||
|
if cols[i_col - i] != cols[i_col + i + 1]:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if not second:
|
||||||
|
res += (i_col + 1)
|
||||||
|
col_res.append(i_col)
|
||||||
|
|
||||||
|
if not second:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for c in g.all_coords():
|
||||||
|
g[c] = '.' if g[c] == '#' else '#'
|
||||||
|
rows = g.rows()
|
||||||
|
cols = g.cols()
|
||||||
|
for i_row in range(len(rows) - 1):
|
||||||
|
for i in range(min(i_row + 1, len(rows) - i_row - 1)):
|
||||||
|
if rows[i_row - i] != rows[i_row + i + 1]:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if not i_row in row_res:
|
||||||
|
res += (i_row + 1) * 100
|
||||||
|
row_res.append(i_row)
|
||||||
|
|
||||||
|
for i_col in range(len(cols) - 1):
|
||||||
|
for i in range(min(i_col + 1, len(cols) - i_col - 1)):
|
||||||
|
if cols[i_col - i] != cols[i_col + i + 1]:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if not i_col in col_res:
|
||||||
|
res += (i_col + 1)
|
||||||
|
col_res.append(i_col)
|
||||||
|
g[c] = '.' if g[c] == '#' else '#'
|
||||||
|
if len(row_res + col_res) != 2:
|
||||||
|
raise Exception("Unexpected amount of mirrors!!")
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DAY_INPUT = "i13.txt"
|
||||||
|
|
||||||
|
print("Example 1:", solve(Input(EXAMPLE)))
|
||||||
|
print("Solution 1:", solve(Input(DAY_INPUT)))
|
||||||
|
assert 29846 == solve(Input(DAY_INPUT))
|
||||||
|
|
||||||
|
print("Example 2:", solve(Input(EXAMPLE), True))
|
||||||
|
print("Solution 2:", solve(Input(DAY_INPUT), True))
|
||||||
|
assert 25401 == solve(Input(DAY_INPUT), True)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
88
d14.py
Normal file
88
d14.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
from lib import *
|
||||||
|
|
||||||
|
EXAMPLE = """
|
||||||
|
O....#....
|
||||||
|
O.OO#....#
|
||||||
|
.....##...
|
||||||
|
OO.#O....O
|
||||||
|
.O.....O#.
|
||||||
|
O.#..O.#.#
|
||||||
|
..O..#O..O
|
||||||
|
.......O..
|
||||||
|
#....###..
|
||||||
|
#OO..#....
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_nth_elem(xs, n):
|
||||||
|
""" Find the nth element of a repeating sequence. """
|
||||||
|
found = set()
|
||||||
|
x = None
|
||||||
|
for (i, x) in enumerate(xs):
|
||||||
|
if x in found:
|
||||||
|
break
|
||||||
|
found.add(x)
|
||||||
|
if i is None:
|
||||||
|
raise Exception("No repeating sequence.")
|
||||||
|
first_repeat = i
|
||||||
|
initial_sequence_length = xs.index(x)
|
||||||
|
repeating_sequence_length = first_repeat - xs.index(x)
|
||||||
|
|
||||||
|
adjusted_n = n - initial_sequence_length
|
||||||
|
position_in_repeating_sequence = adjusted_n % repeating_sequence_length
|
||||||
|
element = xs[initial_sequence_length + position_in_repeating_sequence]
|
||||||
|
return element
|
||||||
|
|
||||||
|
def solve(i: Input, second=False):
|
||||||
|
res = 0
|
||||||
|
g = i.grid2()
|
||||||
|
if second:
|
||||||
|
dirs = [Grid2D.N, Grid2D.W, Grid2D.S, Grid2D.E]
|
||||||
|
cycles = 1000000000
|
||||||
|
else:
|
||||||
|
dirs = [Grid2D.N]
|
||||||
|
cycles = 1
|
||||||
|
|
||||||
|
fs = {}
|
||||||
|
v = []
|
||||||
|
for i in range(cycles):
|
||||||
|
h = hash(tuple(g.find('O')))
|
||||||
|
fs[h] = tuple(g.find('O'))
|
||||||
|
v.append(h)
|
||||||
|
if v.count(h) > 1:
|
||||||
|
break
|
||||||
|
for d in dirs:
|
||||||
|
moved = True
|
||||||
|
while moved:
|
||||||
|
moved = False
|
||||||
|
for r in g.find('O'):
|
||||||
|
rn = add2(r, d)
|
||||||
|
if rn in g and g[rn] == '.':
|
||||||
|
g[r] = '.'
|
||||||
|
g[rn] = 'O'
|
||||||
|
moved = True
|
||||||
|
|
||||||
|
if second:
|
||||||
|
h = get_nth_elem(v, 1000000000)
|
||||||
|
os = fs[h]
|
||||||
|
for r, c in os:
|
||||||
|
s = g.n_rows - r
|
||||||
|
res += s
|
||||||
|
else:
|
||||||
|
for r, c in g.find('O'):
|
||||||
|
s = g.n_rows - r
|
||||||
|
res += s
|
||||||
|
return res
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DAY_INPUT = "i14.txt"
|
||||||
|
|
||||||
|
print("Example 1:", solve(Input(EXAMPLE)))
|
||||||
|
print("Solution 1:", solve(Input(DAY_INPUT)))
|
||||||
|
# 5:55
|
||||||
|
|
||||||
|
print("Example 2:", solve(Input(EXAMPLE), True))
|
||||||
|
print("Solution 2:", solve(Input(DAY_INPUT), True))
|
||||||
|
# 48:00
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
71
d15.py
Normal file
71
d15.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
from lib import *
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
EXAMPLE = "rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7"
|
||||||
|
|
||||||
|
def hash(word):
|
||||||
|
# Init to 0
|
||||||
|
# Determine the ASCII code for the current character of the string.
|
||||||
|
# Increase the current value by the ASCII code you just determined. Set
|
||||||
|
# the current value to itself multiplied by 17. Set the current value to
|
||||||
|
# the remainder of dividing itself by 256.
|
||||||
|
h = 0
|
||||||
|
for c in word:
|
||||||
|
v = ord(c)
|
||||||
|
h += v
|
||||||
|
h *= 17
|
||||||
|
h %= 256
|
||||||
|
return h
|
||||||
|
|
||||||
|
def solve(input: Input, second=False):
|
||||||
|
res = 0
|
||||||
|
line = input.lines()[0]
|
||||||
|
for word in line.split(","):
|
||||||
|
h = hash(word)
|
||||||
|
res += h
|
||||||
|
|
||||||
|
if not second:
|
||||||
|
return res
|
||||||
|
|
||||||
|
boxes = [OrderedDict() for _ in range(256)]
|
||||||
|
for word in line.split(","):
|
||||||
|
h = hash(word)
|
||||||
|
if '=' in word:
|
||||||
|
label, focal = word.split('=')
|
||||||
|
box = boxes[hash(label)]
|
||||||
|
if label in box:
|
||||||
|
box[label] = (label, focal)
|
||||||
|
else:
|
||||||
|
box[label] = (label, focal)
|
||||||
|
elif '-' in word:
|
||||||
|
label = word.replace('-', '')
|
||||||
|
box = boxes[hash(label)]
|
||||||
|
if label in box:
|
||||||
|
del box[label]
|
||||||
|
else:
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
res = 0
|
||||||
|
for i, b in enumerate(boxes):
|
||||||
|
for j, label in enumerate(b):
|
||||||
|
label, focal = b[label]
|
||||||
|
r = (i + 1) * (j + 1) * int(focal)
|
||||||
|
res += r
|
||||||
|
return res
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DAY_INPUT = "i15.txt"
|
||||||
|
|
||||||
|
print("Example 1:", solve(Input(EXAMPLE)))
|
||||||
|
print("Solution 1:", solve(Input(DAY_INPUT)))
|
||||||
|
assert solve(Input(DAY_INPUT)) == 511343
|
||||||
|
# 4:30
|
||||||
|
|
||||||
|
print("Example 2:", solve(Input(EXAMPLE), True))
|
||||||
|
print("Solution 2:", solve(Input(DAY_INPUT), True))
|
||||||
|
assert solve(Input(DAY_INPUT), True) == 294474
|
||||||
|
# 31:20
|
||||||
|
return
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
109
d16.py
Normal file
109
d16.py
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import lib
|
||||||
|
|
||||||
|
EXAMPLE = r"""
|
||||||
|
.|...\....
|
||||||
|
|.-.\.....
|
||||||
|
.....|-...
|
||||||
|
........|.
|
||||||
|
..........
|
||||||
|
.........\
|
||||||
|
..../.\\..
|
||||||
|
.-.-/..|..
|
||||||
|
.|....-|.\
|
||||||
|
..//.|....
|
||||||
|
"""
|
||||||
|
|
||||||
|
NEW = {
|
||||||
|
'.': {
|
||||||
|
'w': (0, -1, 'w'),
|
||||||
|
'e': (0, 1, 'e'),
|
||||||
|
's': (1, 0, 's'),
|
||||||
|
'n': (-1, 0, 'n'),
|
||||||
|
},
|
||||||
|
'|': {
|
||||||
|
'w': [(1, 0, 's'), (-1, 0, 'n')],
|
||||||
|
'e': [(1, 0, 's'), (-1, 0, 'n')],
|
||||||
|
's': (1, 0, 's'),
|
||||||
|
'n': (-1, 0, 'n'),
|
||||||
|
},
|
||||||
|
'-': {
|
||||||
|
's': [(0, -1, 'w'), (0, 1, 'e')],
|
||||||
|
'n': [(0, -1, 'w'), (0, 1, 'e')],
|
||||||
|
'e': (0, 1, 'e'),
|
||||||
|
'w': (0, -1, 'w'),
|
||||||
|
},
|
||||||
|
'/': {
|
||||||
|
's': (0, -1, 'w'),
|
||||||
|
'n': (0, 1, 'e'),
|
||||||
|
'e': (-1, 0, 'n'),
|
||||||
|
'w': (1, 0, 's'),
|
||||||
|
},
|
||||||
|
'\\': {
|
||||||
|
's': (0, 1, 'e'),
|
||||||
|
'n': (0, -1, 'w'),
|
||||||
|
'e': (1, 0, 's'),
|
||||||
|
'w': (-1, 0, 'n'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def solve(lines: list[str], second=False):
|
||||||
|
res = 0
|
||||||
|
|
||||||
|
g = list(map(list, lines))
|
||||||
|
|
||||||
|
rows = len(g)
|
||||||
|
cols = len(g[0])
|
||||||
|
|
||||||
|
if not second:
|
||||||
|
starts = [(0, 0, 'e')]
|
||||||
|
else:
|
||||||
|
starts = [(r, 0, 'e') for r in range(rows)]
|
||||||
|
starts += [(r, cols - 1, 'w') for r in range(rows)]
|
||||||
|
starts += [(0, c, 's') for c in range(cols)]
|
||||||
|
starts += [(rows - 1, c, 'n') for c in range(cols)]
|
||||||
|
|
||||||
|
for start in starts:
|
||||||
|
visited = set()
|
||||||
|
beams = [start]
|
||||||
|
while beams:
|
||||||
|
b = beams.pop()
|
||||||
|
row, col, d = b
|
||||||
|
visited.add((row, col, d))
|
||||||
|
|
||||||
|
f = g[row][col]
|
||||||
|
new = NEW[f][d]
|
||||||
|
if isinstance(new, tuple):
|
||||||
|
r, c, nd = new
|
||||||
|
nr = row + r
|
||||||
|
nc = col + c
|
||||||
|
if nr >= 0 and nc >= 0 and nr < rows and nc < cols:
|
||||||
|
if not (nr, nc, nd) in visited:
|
||||||
|
beams.append((nr, nc, nd))
|
||||||
|
else:
|
||||||
|
for r, c, nd in new:
|
||||||
|
nr = row + r
|
||||||
|
nc = col + c
|
||||||
|
if nr >= 0 and nc >= 0 and nr < rows and nc < cols:
|
||||||
|
if not (nr, nc, nd) in visited:
|
||||||
|
beams.append((nr, nc, nd))
|
||||||
|
|
||||||
|
v = set([(r, c) for (r, c, _) in list(visited)])
|
||||||
|
res = max(res, len(v))
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
lines = lib.str_to_lines_no_empty(EXAMPLE)
|
||||||
|
print("Example 1:", solve(lines))
|
||||||
|
|
||||||
|
lines = lib.str_to_lines_no_empty(open("i16.txt").read())
|
||||||
|
print("Solution 1:", solve(lines))
|
||||||
|
|
||||||
|
lines = lib.str_to_lines_no_empty(EXAMPLE)
|
||||||
|
print("Example 2:", solve(lines, True))
|
||||||
|
|
||||||
|
lines = lib.str_to_lines_no_empty(open("i16.txt").read())
|
||||||
|
print("Solution 2:", solve(lines, True))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
71
d17.py
Normal file
71
d17.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
from lib import *
|
||||||
|
|
||||||
|
EXAMPLE = """
|
||||||
|
2413432311323
|
||||||
|
3215453535623
|
||||||
|
3255245654254
|
||||||
|
3446585845452
|
||||||
|
4546657867536
|
||||||
|
1438598798454
|
||||||
|
4457876987766
|
||||||
|
3637877979653
|
||||||
|
4654967986887
|
||||||
|
4564679986453
|
||||||
|
1224686865563
|
||||||
|
2546548887735
|
||||||
|
4322674655533
|
||||||
|
"""
|
||||||
|
|
||||||
|
def solve(i: Input, second=False):
|
||||||
|
g = i.grid2()
|
||||||
|
starts = [((0, 0), (0, None))]
|
||||||
|
|
||||||
|
def is_goal(node):
|
||||||
|
pos, _ = node
|
||||||
|
return pos == (g.n_cols - 1, g.n_rows - 1)
|
||||||
|
|
||||||
|
def neighbors(node):
|
||||||
|
pos, dirs = node
|
||||||
|
repeats, prev_dir = dirs
|
||||||
|
nbs = []
|
||||||
|
for dir in g.dirs_ort():
|
||||||
|
if second:
|
||||||
|
if repeats < 4 and prev_dir is not None and prev_dir != dir:
|
||||||
|
continue
|
||||||
|
if repeats == 10 and prev_dir == dir:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if repeats == 3 and prev_dir == dir:
|
||||||
|
continue
|
||||||
|
if prev_dir == g.flip_ort(dir):
|
||||||
|
continue
|
||||||
|
|
||||||
|
nb = add2(pos, dir)
|
||||||
|
if nb not in g:
|
||||||
|
continue
|
||||||
|
nbs.append((nb, (repeats + 1 if dir == prev_dir else 1, dir)))
|
||||||
|
return nbs
|
||||||
|
|
||||||
|
def h(node):
|
||||||
|
pos, _ = node
|
||||||
|
return abs(g.n_rows - 1 - pos[0]) + abs(g.n_cols - 1 - pos[1])
|
||||||
|
|
||||||
|
def d(_, b):
|
||||||
|
pos, _ = b
|
||||||
|
if pos == (0, 0):
|
||||||
|
return 0
|
||||||
|
return int(g[pos])
|
||||||
|
|
||||||
|
a = A_Star(starts, is_goal, h, d, neighbors)
|
||||||
|
return a.cost
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DAY_INPUT = "i17.txt"
|
||||||
|
print("Example 1:", solve(Input(EXAMPLE)))
|
||||||
|
print("Solution 1:", solve(Input(DAY_INPUT)))
|
||||||
|
print("Example 2:", solve(Input(EXAMPLE), True))
|
||||||
|
print("Solution 2:", solve(Input(DAY_INPUT), True))
|
||||||
|
return
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
143
d18.py
Normal file
143
d18.py
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
from lib import *
|
||||||
|
|
||||||
|
EXAMPLE = """
|
||||||
|
R 6 (#70c710)
|
||||||
|
D 5 (#0dc571)
|
||||||
|
L 2 (#5713f0)
|
||||||
|
D 2 (#d2c081)
|
||||||
|
R 2 (#59c680)
|
||||||
|
D 2 (#411b91)
|
||||||
|
L 5 (#8ceee2)
|
||||||
|
U 2 (#caa173)
|
||||||
|
L 1 (#1b58a2)
|
||||||
|
U 2 (#caa171)
|
||||||
|
R 2 (#7807d2)
|
||||||
|
U 3 (#a77fa3)
|
||||||
|
L 2 (#015232)
|
||||||
|
U 2 (#7a21e3)
|
||||||
|
"""
|
||||||
|
|
||||||
|
m = {
|
||||||
|
"R": Grid2D.E,
|
||||||
|
"D": Grid2D.S,
|
||||||
|
"L": Grid2D.W,
|
||||||
|
"U": Grid2D.N,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def solve(i: Input, second=False):
|
||||||
|
lines = i.lines()
|
||||||
|
ins = []
|
||||||
|
for l in lines:
|
||||||
|
if not l:
|
||||||
|
continue
|
||||||
|
d, l, c = l.split()
|
||||||
|
ins.append((int(l), m[d]))
|
||||||
|
|
||||||
|
c = (0, 0)
|
||||||
|
coords: list[tuple[int, int]] = [c]
|
||||||
|
for count, d in ins:
|
||||||
|
for _ in range(count):
|
||||||
|
c = add2(c, d)
|
||||||
|
coords.append(c)
|
||||||
|
|
||||||
|
to_visit: list[tuple[int, int]] = [(1, 1)]
|
||||||
|
while to_visit:
|
||||||
|
c = to_visit.pop()
|
||||||
|
for n in [Grid2D.S, Grid2D.E, Grid2D.W, Grid2D.N]:
|
||||||
|
nc = add2(c, n)
|
||||||
|
if nc not in coords:
|
||||||
|
coords.append(nc)
|
||||||
|
to_visit.append(nc)
|
||||||
|
return len(set(coords))
|
||||||
|
|
||||||
|
def area(ins):
|
||||||
|
c = (0, 0)
|
||||||
|
corners = []
|
||||||
|
for i, (count, d) in enumerate(ins):
|
||||||
|
c = (c[0] + count * d[0], c[1] + count * d[1])
|
||||||
|
nd = ins[(i + 1) % len(ins)][1]
|
||||||
|
|
||||||
|
if d == nd:
|
||||||
|
raise Exception("Dirs should not be equal!")
|
||||||
|
|
||||||
|
# XXX: This mapping only works when going clockwise!
|
||||||
|
match d, nd:
|
||||||
|
case Grid2D.W, Grid2D.N:
|
||||||
|
c_log = (c[0] + 1, c[1])
|
||||||
|
case Grid2D.W, Grid2D.S:
|
||||||
|
c_log = (c[0] + 1, c[1] + 1)
|
||||||
|
case Grid2D.E, Grid2D.N:
|
||||||
|
c_log = (c[0], c[1])
|
||||||
|
case Grid2D.E, Grid2D.S:
|
||||||
|
c_log = (c[0], c[1] + 1)
|
||||||
|
case Grid2D.N, Grid2D.E:
|
||||||
|
c_log = (c[0], c[1])
|
||||||
|
case Grid2D.N, Grid2D.W:
|
||||||
|
c_log = (c[0] + 1, c[1])
|
||||||
|
case Grid2D.S, Grid2D.E:
|
||||||
|
c_log = (c[0], c[1] + 1)
|
||||||
|
case Grid2D.S, Grid2D.W:
|
||||||
|
c_log = (c[0] + 1, c[1] + 1)
|
||||||
|
case d, nd:
|
||||||
|
raise Exception(f"Uncoverred {d=} -> {nd=}")
|
||||||
|
corners.append(c_log)
|
||||||
|
return int(shoelace_area(corners))
|
||||||
|
|
||||||
|
|
||||||
|
def area2(ins):
|
||||||
|
# Solution based on Shoelace area and the idea that the outside area is
|
||||||
|
# perimate * 1 // 2, and then +1 (for four quarter corners). All other
|
||||||
|
# corners cancel out to zero.
|
||||||
|
c = (0, 0)
|
||||||
|
corners = []
|
||||||
|
perimeter = 0
|
||||||
|
for i, (count, d) in enumerate(ins):
|
||||||
|
c = (c[0] + count * d[0], c[1] + count * d[1])
|
||||||
|
corners.append(c)
|
||||||
|
perimeter += count
|
||||||
|
return int(shoelace_area(corners)) + perimeter // 2 + 1
|
||||||
|
|
||||||
|
|
||||||
|
def solve2(i: Input, second=False):
|
||||||
|
lines = i.lines()
|
||||||
|
ins = []
|
||||||
|
for line in lines:
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
_, _, c = line.split()
|
||||||
|
c = c.replace("(#", "").replace(")", "")
|
||||||
|
d = int(c[:5], 16)
|
||||||
|
m = {"0": Grid2D.E, "1": Grid2D.S, "2": Grid2D.W, "3": Grid2D.N}[c[5]]
|
||||||
|
ins.append((d, m))
|
||||||
|
assert area(ins) == area2(ins)
|
||||||
|
return area(ins)
|
||||||
|
|
||||||
|
|
||||||
|
def debug():
|
||||||
|
ins = [
|
||||||
|
( 3, Grid2D.S,),
|
||||||
|
( 2, Grid2D.E,),
|
||||||
|
( 1, Grid2D.N,),
|
||||||
|
( 1, Grid2D.E,),
|
||||||
|
( 1, Grid2D.N,),
|
||||||
|
( 1, Grid2D.W,),
|
||||||
|
( 1, Grid2D.N,),
|
||||||
|
( 2, Grid2D.W,),
|
||||||
|
]
|
||||||
|
# Should be 14 but is 2 because it's going counter-clockwise, but mapping
|
||||||
|
# only works for clockwise.
|
||||||
|
print(area(ins))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DAY_INPUT = "i18.txt"
|
||||||
|
print("Example 1:", solve(Input(EXAMPLE)))
|
||||||
|
print("Solution 1:", solve(Input(DAY_INPUT)))
|
||||||
|
print("Example 2:", solve2(Input(EXAMPLE), True))
|
||||||
|
print("Solution 2:", solve2(Input(DAY_INPUT), True))
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
148
d19.py
Normal file
148
d19.py
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
from lib import *
|
||||||
|
|
||||||
|
EXAMPLE = """px{a<2006:qkq,m>2090:A,rfg}
|
||||||
|
pv{a>1716:R,A}
|
||||||
|
lnx{m>1548:A,A}
|
||||||
|
rfg{s<537:gd,x>2440:R,A}
|
||||||
|
qs{s>3448:A,lnx}
|
||||||
|
qkq{x<1416:A,crn}
|
||||||
|
crn{x>2662:A,R}
|
||||||
|
in{s<1351:px,qqz}
|
||||||
|
qqz{s>2770:qs,m<1801:hdj,R}
|
||||||
|
gd{a>3333:R,R}
|
||||||
|
hdj{m>838:A,pv}
|
||||||
|
|
||||||
|
{x=787,m=2655,a=1222,s=2876}
|
||||||
|
{x=1679,m=44,a=2067,s=496}
|
||||||
|
{x=2036,m=264,a=79,s=2244}
|
||||||
|
{x=2461,m=1339,a=466,s=291}
|
||||||
|
{x=2127,m=1623,a=2188,s=1013}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def solve(i: Input, second=False):
|
||||||
|
res = 0
|
||||||
|
ps = i.paras()
|
||||||
|
wfs, parts = ps
|
||||||
|
wfs = wfs.splitlines()
|
||||||
|
parts = parts.splitlines()
|
||||||
|
|
||||||
|
workflows = {}
|
||||||
|
for w in wfs:
|
||||||
|
name, ins = w.split("{")
|
||||||
|
ins = ins.replace("}", "")
|
||||||
|
conds = ins.split(",")
|
||||||
|
wf = []
|
||||||
|
for cond in conds[:-1]:
|
||||||
|
cmp, n = cond.split(":")
|
||||||
|
if '<' in cmp:
|
||||||
|
a, b = cmp.split('<')
|
||||||
|
wf.append((a, 'smaller', int(b), n))
|
||||||
|
elif '>' in cmp:
|
||||||
|
a, b = cmp.split('>')
|
||||||
|
wf.append((a, 'greater', int(b), n))
|
||||||
|
else:
|
||||||
|
raise Exception()
|
||||||
|
wf.append(conds[-1:])
|
||||||
|
workflows[name] = wf
|
||||||
|
|
||||||
|
def parse_part(part):
|
||||||
|
d = {}
|
||||||
|
p = part.replace("{", "").replace("}", "")
|
||||||
|
for pair in p.split(","):
|
||||||
|
a, b = pair.split("=")
|
||||||
|
d[a] = int(b)
|
||||||
|
return d
|
||||||
|
|
||||||
|
parts = list(map(parse_part, parts))
|
||||||
|
if not second:
|
||||||
|
for p in parts:
|
||||||
|
current = 'in'
|
||||||
|
while current not in ['A', 'R']:
|
||||||
|
for inst in workflows[current]:
|
||||||
|
if len(inst) == 4:
|
||||||
|
letter = inst[0]
|
||||||
|
value = inst[2]
|
||||||
|
next = inst[3]
|
||||||
|
if inst[1] == 'smaller':
|
||||||
|
if p[letter] < value:
|
||||||
|
current = next
|
||||||
|
break
|
||||||
|
elif inst[1] == 'greater':
|
||||||
|
if p[letter] > value:
|
||||||
|
current = next
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise Exception()
|
||||||
|
elif len(inst) == 1:
|
||||||
|
current = inst[0]
|
||||||
|
else:
|
||||||
|
raise Exception()
|
||||||
|
if current == 'A':
|
||||||
|
r = sum(p.values())
|
||||||
|
res += r
|
||||||
|
return res
|
||||||
|
|
||||||
|
ranges = [{c: (1, 4000) for c in 'xmas'}]
|
||||||
|
ranges[0]['cur'] = 'in'
|
||||||
|
ranges[0]['idx'] = 0
|
||||||
|
accepted = []
|
||||||
|
while ranges:
|
||||||
|
r = ranges.pop()
|
||||||
|
cur, idx = r['cur'], r['idx']
|
||||||
|
|
||||||
|
if cur == 'A':
|
||||||
|
accepted.append(r)
|
||||||
|
continue
|
||||||
|
elif cur == 'R':
|
||||||
|
continue
|
||||||
|
|
||||||
|
inst = workflows[cur][idx]
|
||||||
|
if len(inst) == 4:
|
||||||
|
letter = inst[0]
|
||||||
|
value = inst[2]
|
||||||
|
nxt = inst[3]
|
||||||
|
ro = r[letter]
|
||||||
|
r1, r2 = dict(r), dict(r)
|
||||||
|
if inst[1] == 'smaller':
|
||||||
|
r1[letter] = (ro[0], value - 1)
|
||||||
|
r1['idx'] = 0
|
||||||
|
r1['cur'] = nxt
|
||||||
|
r2[letter] = (value, ro[1])
|
||||||
|
r2['idx'] += 1
|
||||||
|
elif inst[1] == 'greater':
|
||||||
|
r1[letter] = (ro[0], value)
|
||||||
|
r1['idx'] += 1
|
||||||
|
r2[letter] = (value + 1, ro[1])
|
||||||
|
r2['idx'] = 0
|
||||||
|
r2['cur'] = nxt
|
||||||
|
if r1[letter][1] >= r1[letter][0]:
|
||||||
|
ranges.append(r1)
|
||||||
|
if r2[letter][1] >= r2[letter][0]:
|
||||||
|
ranges.append(r2)
|
||||||
|
elif len(inst) == 1:
|
||||||
|
r['cur'] = inst[0]
|
||||||
|
r['idx'] = 0
|
||||||
|
ranges.append(r)
|
||||||
|
res = 0
|
||||||
|
for a in accepted:
|
||||||
|
r = 1
|
||||||
|
for c in 'xmas':
|
||||||
|
l, u = a[c]
|
||||||
|
r *= (u + 1 - l)
|
||||||
|
res += r
|
||||||
|
return res
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DAY_INPUT = "i19.txt"
|
||||||
|
|
||||||
|
print("Example 1:", solve(Input(EXAMPLE)))
|
||||||
|
print("Solution 1:", solve(Input(DAY_INPUT)))
|
||||||
|
# 25:00
|
||||||
|
|
||||||
|
print("Example 2:", solve(Input(EXAMPLE), True))
|
||||||
|
assert solve(Input(EXAMPLE), True) == 167409079868000
|
||||||
|
print("Solution 2:", solve(Input(DAY_INPUT), True))
|
||||||
|
# 120:00
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
109
d20.py
Normal file
109
d20.py
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
from lib import *
|
||||||
|
from math import lcm
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
EXAMPLE = """
|
||||||
|
broadcaster -> a, b, c
|
||||||
|
%a -> b
|
||||||
|
%b -> c
|
||||||
|
%c -> inv
|
||||||
|
&inv -> a
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLE2 = """
|
||||||
|
broadcaster -> a
|
||||||
|
%a -> inv, con
|
||||||
|
&inv -> b
|
||||||
|
%b -> con
|
||||||
|
&con -> output
|
||||||
|
"""
|
||||||
|
|
||||||
|
def visualize(modules):
|
||||||
|
with open("g20.dot", 'w') as f:
|
||||||
|
f.write("digraph G {\n")
|
||||||
|
for m in modules.values():
|
||||||
|
for cm in m[3]:
|
||||||
|
f.write(" " + m[1] + ' -> ' + cm + "\n")
|
||||||
|
f.write("}")
|
||||||
|
|
||||||
|
def solve(input: Input, second=False):
|
||||||
|
modules = {}
|
||||||
|
for line in input.lines():
|
||||||
|
if not line: continue
|
||||||
|
src, dsts = line.split(" -> ")
|
||||||
|
dsts = dsts.split(", ")
|
||||||
|
if src.startswith("%"): # flip-flop
|
||||||
|
modules[src[1:]] = [src[0], src[1:], 0, dsts]
|
||||||
|
elif src.startswith("&"): # conjunction
|
||||||
|
modules[src[1:]] = [src[0], src[1:], {}, dsts]
|
||||||
|
elif src.startswith("broadcaster"):
|
||||||
|
modules[src] = ["b", src, 0, dsts]
|
||||||
|
else:
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
for m in modules.values():
|
||||||
|
for d in m[3]:
|
||||||
|
if d in modules and modules[d][0] == "&":
|
||||||
|
modules[d][2][m[1]] = 0
|
||||||
|
|
||||||
|
if second:
|
||||||
|
# visualize(modules)
|
||||||
|
(feed,) = [m[1] for m in modules.values() if "rx" in m[3]]
|
||||||
|
periods = {d: [] for d in modules[feed][2].keys()}
|
||||||
|
BUTTON_PUSHES = 10000
|
||||||
|
else:
|
||||||
|
BUTTON_PUSHES = 1000
|
||||||
|
|
||||||
|
lo, hi = 0, 0
|
||||||
|
for i in range(BUTTON_PUSHES):
|
||||||
|
qs = deque([("button module", "broadcaster", 0)])
|
||||||
|
while qs:
|
||||||
|
src, dst, sig = qs.popleft()
|
||||||
|
|
||||||
|
if sig == 0:
|
||||||
|
lo += 1
|
||||||
|
else:
|
||||||
|
hi += 1
|
||||||
|
|
||||||
|
if not dst in modules:
|
||||||
|
continue
|
||||||
|
|
||||||
|
m = modules[dst]
|
||||||
|
new_pulse = None
|
||||||
|
if m[0] == "b":
|
||||||
|
new_pulse = 0
|
||||||
|
elif m[0] == "%":
|
||||||
|
if sig == 0:
|
||||||
|
m[2] = (m[2] + 1) % 2
|
||||||
|
new_pulse = m[2]
|
||||||
|
elif m[0] == "&":
|
||||||
|
m[2][src] = sig
|
||||||
|
if all(s == 1 for s in m[2].values()):
|
||||||
|
new_pulse = 0
|
||||||
|
else:
|
||||||
|
new_pulse = 1
|
||||||
|
else:
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
if new_pulse is not None:
|
||||||
|
for nxtdst in m[3]:
|
||||||
|
if second and nxtdst == feed and new_pulse == 1:
|
||||||
|
# print(f"{i:>4}: {dst[:4]:>4} -{new_pulse}> {nxtdst}")
|
||||||
|
periods[dst].append(i)
|
||||||
|
qs.append((dst, nxtdst, new_pulse))
|
||||||
|
if second:
|
||||||
|
# print(periods)
|
||||||
|
# Yeah, we got a bit lucky that this works, I guess, but it does.
|
||||||
|
return lcm(*[v[1] - v[0] for v in periods.values()])
|
||||||
|
return lo * hi
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DAY_INPUT = "i20.txt"
|
||||||
|
print("Example 1:", solve(Input(EXAMPLE)))
|
||||||
|
print("Example 2:", solve(Input(EXAMPLE2)))
|
||||||
|
print("Solution 1:", solve(Input(DAY_INPUT)))
|
||||||
|
print("Solution 2:", solve(Input(DAY_INPUT), True))
|
||||||
|
assert solve(Input(DAY_INPUT), True) == 244178746156661
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
213
d21.py
Normal file
213
d21.py
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
from lib import *
|
||||||
|
import os
|
||||||
|
|
||||||
|
EXAMPLE = """...........
|
||||||
|
.....###.#.
|
||||||
|
.###.##..#.
|
||||||
|
..#.#...#..
|
||||||
|
....#.#....
|
||||||
|
.##..S####.
|
||||||
|
.##..#...#.
|
||||||
|
.......##..
|
||||||
|
.##.#.####.
|
||||||
|
.##..##.##.
|
||||||
|
...........
|
||||||
|
"""
|
||||||
|
|
||||||
|
def solve(input: Input):
|
||||||
|
g = input.grid2()
|
||||||
|
s = g.find('S')[0]
|
||||||
|
g[s] = 'O'
|
||||||
|
steps = 64
|
||||||
|
seen = set()
|
||||||
|
for i in range(steps):
|
||||||
|
os = tuple(g.find('O'))
|
||||||
|
if os in seen:
|
||||||
|
seen.add(os)
|
||||||
|
print(f"SEEN {i}")
|
||||||
|
break
|
||||||
|
|
||||||
|
for o in os:
|
||||||
|
g[o] = '.'
|
||||||
|
|
||||||
|
for o in os:
|
||||||
|
for nb in g.neighbors_ort(o):
|
||||||
|
if not g[nb] == "#":
|
||||||
|
g[nb] = 'O'
|
||||||
|
return len(g.find('O'))
|
||||||
|
|
||||||
|
|
||||||
|
def plot(xs, poss):
|
||||||
|
os.system("clear")
|
||||||
|
rcoords = [x[0] for x in xs]
|
||||||
|
ccoords = [x[1] for x in xs]
|
||||||
|
rmin = min(rcoords)
|
||||||
|
rmax = max(rcoords)
|
||||||
|
cmin = min(ccoords)
|
||||||
|
cmax = max(ccoords)
|
||||||
|
for r in range(rmin, rmax + 1):
|
||||||
|
s = ""
|
||||||
|
for c in range(cmin, cmax + 1):
|
||||||
|
if (r, c) in xs:
|
||||||
|
s += "#"
|
||||||
|
elif (r, c) in poss:
|
||||||
|
s += "O"
|
||||||
|
else:
|
||||||
|
s += " "
|
||||||
|
print(s)
|
||||||
|
|
||||||
|
def move(xs, roff, coff):
|
||||||
|
rcoords = [x[0] for x in xs]
|
||||||
|
ccoords = [x[1] for x in xs]
|
||||||
|
rd = max(rcoords) - min(rcoords) + 3
|
||||||
|
cd = max(ccoords) - min(ccoords) + 3
|
||||||
|
newxs = [(x[0] + roff * rd, x[1] + coff * cd) for x in xs]
|
||||||
|
return set(newxs)
|
||||||
|
|
||||||
|
def iter(poss, stones):
|
||||||
|
nposs = set()
|
||||||
|
for r, c in poss:
|
||||||
|
for ro, co in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
|
||||||
|
nr, nc = r + ro, c + co
|
||||||
|
if not (nr, nc) in stones:
|
||||||
|
nposs.add((nr, nc))
|
||||||
|
return nposs
|
||||||
|
|
||||||
|
def get_bounds(size, ro, co):
|
||||||
|
rmin = size * ro
|
||||||
|
rmax = size + size * ro
|
||||||
|
cmin = size * co
|
||||||
|
cmax = size + size * co
|
||||||
|
return rmin, rmax, cmin, cmax
|
||||||
|
|
||||||
|
def count(poss, size, ro, co):
|
||||||
|
rmin, rmax, cmin, cmax = get_bounds(size, ro, co)
|
||||||
|
res = 0
|
||||||
|
for (r, c) in poss:
|
||||||
|
if (rmin <= r < rmax) and (cmin <= c < cmax):
|
||||||
|
res += 1
|
||||||
|
return res
|
||||||
|
|
||||||
|
def solve2(ip: Input):
|
||||||
|
base_stones = set()
|
||||||
|
poss = set()
|
||||||
|
|
||||||
|
size = len(ip.lines())
|
||||||
|
assert size == len(ip.lines()[0])
|
||||||
|
|
||||||
|
for r, row in enumerate(ip.lines()):
|
||||||
|
for c, col in enumerate(row):
|
||||||
|
if col == "#":
|
||||||
|
base_stones.add((r, c))
|
||||||
|
if col == "S":
|
||||||
|
poss.add((r, c))
|
||||||
|
|
||||||
|
stones = base_stones.copy()
|
||||||
|
|
||||||
|
off = 19 // 2
|
||||||
|
for ro in range(-off, off + 1):
|
||||||
|
for co in range(-off, off + 1):
|
||||||
|
stones |= move(base_stones, ro, co)
|
||||||
|
|
||||||
|
|
||||||
|
hists = {}
|
||||||
|
for ro in range(-off, off + 1):
|
||||||
|
for co in range(-off, off + 1):
|
||||||
|
hists[(ro, co)] = []
|
||||||
|
|
||||||
|
#for step in range(590):
|
||||||
|
# if step % 1 == 0:
|
||||||
|
# sanity = 0
|
||||||
|
# os.system("clear")
|
||||||
|
# for ro in range(-off, off + 1):
|
||||||
|
# s = ""
|
||||||
|
# for co in range(-off, off + 1):
|
||||||
|
# v = count(poss, size, ro, co)
|
||||||
|
# sanity += v
|
||||||
|
# if v > 0:
|
||||||
|
# hists[(ro, co)].append(v)
|
||||||
|
# s += f"{v:6}"
|
||||||
|
# else:
|
||||||
|
# s += 6 * " "
|
||||||
|
# print(s)
|
||||||
|
# # input(f"{step=} {step//size=} {len(poss)} ({sanity}) cont...")
|
||||||
|
# print(f"{step=} {step//size=} {len(poss)} ({sanity}) cont...")
|
||||||
|
# poss = iter(poss, stones)
|
||||||
|
|
||||||
|
# 66, 197, 328 459 # cycle starts
|
||||||
|
# 196, 327, 458, 589 # targets
|
||||||
|
def calc(len, xs):
|
||||||
|
if len % 2 == 0:
|
||||||
|
return len // 2 * sum(xs)
|
||||||
|
else:
|
||||||
|
return len // 2 * sum(xs) + xs[0]
|
||||||
|
|
||||||
|
target = 196
|
||||||
|
target = 327
|
||||||
|
target = 458
|
||||||
|
target = 589
|
||||||
|
target = 26501365
|
||||||
|
# for target in [196, 327, 458, 589]:
|
||||||
|
print()
|
||||||
|
print(target)
|
||||||
|
cycle = 131
|
||||||
|
c = target // cycle
|
||||||
|
d = (target // cycle) * 2 + 1 - 2
|
||||||
|
print(f"{c=} {d=}")
|
||||||
|
res = 0
|
||||||
|
res += 5698 + 5703 + 5709 + 5704 # corners
|
||||||
|
res += c * 964 + c * 984 + c * 968 + c * 978 # outer
|
||||||
|
res += (c - 1) * 6637 + (c - 1) * 6624 + (c - 1) * 6643 + (c - 1) * 6619 # inner
|
||||||
|
|
||||||
|
for i in range(d, 0, -2):
|
||||||
|
res += calc(i, [7623, 7558])
|
||||||
|
|
||||||
|
for i in range(d - 2, 0, -2):
|
||||||
|
res += calc(i, [7623, 7558])
|
||||||
|
|
||||||
|
print(res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
# def get_till(xs, ts):
|
||||||
|
# ts = ts[:]
|
||||||
|
# r = []
|
||||||
|
# for x in xs:
|
||||||
|
# r.append(x)
|
||||||
|
# if x in ts:
|
||||||
|
# ts.remove(x)
|
||||||
|
# if ts == []:
|
||||||
|
# break
|
||||||
|
# return r
|
||||||
|
|
||||||
|
osz_values = hists[(0, 4)][-2:]
|
||||||
|
# se = get_till(hists[0, 5], osz_values)
|
||||||
|
# sn = get_till(hists[-5, 0], osz_values)
|
||||||
|
# ss = get_till(hists[5, 0], osz_values)
|
||||||
|
# sw = get_till(hists[0, -5], osz_values)
|
||||||
|
# print(se)
|
||||||
|
# print(sn)
|
||||||
|
# print(sw)
|
||||||
|
# print(ss)
|
||||||
|
|
||||||
|
# sne = get_till(hists[-5, 5], osz_values)
|
||||||
|
# sse = get_till(hists[5, 5], osz_values)
|
||||||
|
# ssw = get_till(hists[5, -5], osz_values)
|
||||||
|
# snw = get_till(hists[-5, -5], osz_values)
|
||||||
|
# print(sne)
|
||||||
|
# print(sse)
|
||||||
|
# print(ssw)
|
||||||
|
# print(snw)
|
||||||
|
|
||||||
|
# for i in range(3, 10):
|
||||||
|
# print(hists[(0, i)][:5])
|
||||||
|
# print(hists[(0, -i)][:5])
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DAY_INPUT = "i21.txt"
|
||||||
|
# print("Example 1:", solve(Input(EXAMPLE)))
|
||||||
|
# print("Solution 1:", solve(Input(DAY_INPUT)))
|
||||||
|
# print("Example 2:", solve2(Input(EXAMPLE)))
|
||||||
|
print("Solution 2:", solve2(Input(DAY_INPUT)))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
121
d22.py
Normal file
121
d22.py
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
from lib import *
|
||||||
|
|
||||||
|
EXAMPLE = """1,0,1~1,2,1
|
||||||
|
0,0,2~2,0,2
|
||||||
|
0,2,3~2,2,3
|
||||||
|
0,0,4~0,2,4
|
||||||
|
2,0,5~2,2,5
|
||||||
|
0,1,6~2,1,6
|
||||||
|
1,1,8~1,1,9
|
||||||
|
"""
|
||||||
|
|
||||||
|
def overlap(a, b):
|
||||||
|
# Could be made generic with for loop.
|
||||||
|
[(ax1, ax2), (ay1, ay2), (az1, az2)] = a
|
||||||
|
[(bx1, bx2), (by1, by2), (bz1, bz2)] = b
|
||||||
|
xo = (bx1 <= ax2 and bx2 >= ax1)
|
||||||
|
yo = (by1 <= ay2 and by2 >= ay1)
|
||||||
|
zo = (bz1 <= az2 and bz2 >= az1)
|
||||||
|
return xo and yo and zo
|
||||||
|
|
||||||
|
def can_overlap(a, b):
|
||||||
|
a, b = list(a), list(b)
|
||||||
|
za1, za2 = a[2]
|
||||||
|
zb1, zb2 = b[2]
|
||||||
|
|
||||||
|
zad = za1 - 1
|
||||||
|
a[2] = (za1 - zad, za2 - zad)
|
||||||
|
|
||||||
|
zbd = zb1 - 1
|
||||||
|
b[2] = (zb1 - zbd, zb2 - zbd)
|
||||||
|
return overlap(a, b)
|
||||||
|
|
||||||
|
def solve(input: Input, second=False):
|
||||||
|
bricks = []
|
||||||
|
for line in input.lines():
|
||||||
|
s = tuple(str_to_ints(line))
|
||||||
|
s = tuple([tuple(sorted([s[i], s[i + 3]])) for i in range(3)])
|
||||||
|
bricks.append(s)
|
||||||
|
|
||||||
|
# Check which bricks can overlap in general
|
||||||
|
d = {i: [] for i in range(len(bricks))}
|
||||||
|
for a in range(len(bricks)):
|
||||||
|
for b in range(a + 1, len(bricks)):
|
||||||
|
if can_overlap(bricks[a], bricks[b]):
|
||||||
|
# print(f"{bricks[a]} can overlap with {bricks[b]}")
|
||||||
|
d[a].append(b)
|
||||||
|
d[b].append(a)
|
||||||
|
|
||||||
|
# Lower bricks as much as possible
|
||||||
|
idxs = sorted(range(len(bricks)), key=lambda i: bricks[i][2][1])
|
||||||
|
for idx_active_idx, idx_active in enumerate(idxs):
|
||||||
|
b = list(bricks[idx_active])
|
||||||
|
lowest_z = 1
|
||||||
|
for idx_to_check in idxs[:idx_active_idx]:
|
||||||
|
if idx_to_check in d[idx_active]:
|
||||||
|
lowest_z = max(lowest_z, bricks[idx_to_check][2][1] + 1)
|
||||||
|
zp = list(b[2])
|
||||||
|
zp[0], zp[1] = lowest_z, zp[1] - (zp[0] - lowest_z)
|
||||||
|
b[2] = tuple(zp)
|
||||||
|
# print(f"{bricks[idx_active]} -> {b}")
|
||||||
|
bricks[idx_active] = b
|
||||||
|
|
||||||
|
# for l, b in zip(LETTERS_UPPER, bricks): print(l, b)
|
||||||
|
|
||||||
|
if second:
|
||||||
|
# Create a map that for each objects, shows what objects it is
|
||||||
|
# supported by.
|
||||||
|
supported_by = {i: set() for i in range(len(bricks))}
|
||||||
|
for i in range(len(bricks)):
|
||||||
|
b = bricks[i]
|
||||||
|
zl = b[2][0]
|
||||||
|
if zl == 1:
|
||||||
|
supported_by[i].add(-1)
|
||||||
|
for ni in d[i]:
|
||||||
|
if bricks[ni][2][1] + 1 == zl:
|
||||||
|
supported_by[i].add(ni)
|
||||||
|
|
||||||
|
res = 0
|
||||||
|
for i in range(len(bricks)):
|
||||||
|
removed = set([i])
|
||||||
|
to_process = [i]
|
||||||
|
while to_process:
|
||||||
|
ri = to_process.pop()
|
||||||
|
for ni in d[ri]:
|
||||||
|
if supported_by[ni].issubset(removed) and not ni in removed:
|
||||||
|
removed.add(ni)
|
||||||
|
to_process.append(ni)
|
||||||
|
res += len(removed) - 1
|
||||||
|
return res
|
||||||
|
|
||||||
|
else:
|
||||||
|
support_map = {i: [] for i in range(len(bricks))}
|
||||||
|
support_bricks = set()
|
||||||
|
# For all bricks, check if it would fall if a particular brick was removed.
|
||||||
|
for i in range(len(bricks)):
|
||||||
|
b = bricks[i]
|
||||||
|
zl = b[2][0]
|
||||||
|
if zl == 1:
|
||||||
|
continue # supported by floor
|
||||||
|
|
||||||
|
for ni in d[i]:
|
||||||
|
if bricks[ni][2][1] + 1 == zl:
|
||||||
|
support_map[i].append(ni)
|
||||||
|
if len(support_map[i]) == 1:
|
||||||
|
support_bricks.add(support_map[i][0])
|
||||||
|
# print(f"{bricks[i]} supported by {support_map[i]}")
|
||||||
|
return len(bricks) - len(support_bricks)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DAY_INPUT = "i22.txt"
|
||||||
|
|
||||||
|
print("Example 1:", solve(Input(EXAMPLE)))
|
||||||
|
print("Solution 1:", solve(Input(DAY_INPUT)))
|
||||||
|
assert solve(Input(DAY_INPUT)) == 428
|
||||||
|
|
||||||
|
print("Example 2:", solve(Input(EXAMPLE), True))
|
||||||
|
print("Solution 2:", solve(Input(DAY_INPUT), True))
|
||||||
|
assert solve(Input(DAY_INPUT), True) == 35654
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
146
d23.py
Normal file
146
d23.py
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
from lib import *
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
EXAMPLE = """#.#####################
|
||||||
|
#.......#########...###
|
||||||
|
#######.#########.#.###
|
||||||
|
###.....#.>.>.###.#.###
|
||||||
|
###v#####.#v#.###.#.###
|
||||||
|
###.>...#.#.#.....#...#
|
||||||
|
###v###.#.#.#########.#
|
||||||
|
###...#.#.#.......#...#
|
||||||
|
#####.#.#.#######.#.###
|
||||||
|
#.....#.#.#.......#...#
|
||||||
|
#.#####.#.#.#########v#
|
||||||
|
#.#...#...#...###...>.#
|
||||||
|
#.#.#v#######v###.###v#
|
||||||
|
#...#.>.#...>.>.#.###.#
|
||||||
|
#####v#.#.###v#.#.###.#
|
||||||
|
#.....#...#...#.#.#...#
|
||||||
|
#.#########.###.#.#.###
|
||||||
|
#...###...#...#...#.###
|
||||||
|
###.###.#.###v#####v###
|
||||||
|
#...#...#.#.>.>.#.>.###
|
||||||
|
#.###.###.#.###.#.#v###
|
||||||
|
#.....###...###...#...#
|
||||||
|
#####################.#
|
||||||
|
"""
|
||||||
|
|
||||||
|
SLOPES = {
|
||||||
|
"^": (-1, 0),
|
||||||
|
">": (0, 1),
|
||||||
|
"v": (1, 0),
|
||||||
|
"<": (0, -1),
|
||||||
|
}
|
||||||
|
|
||||||
|
def first(input):
|
||||||
|
g = input.grid2()
|
||||||
|
start = (0, 1)
|
||||||
|
end = (g.n_rows - 1, g.n_cols - 2)
|
||||||
|
longest = 0
|
||||||
|
paths = [(set([start]), start)]
|
||||||
|
while True:
|
||||||
|
new_paths = []
|
||||||
|
for p in paths:
|
||||||
|
hist, pos = p
|
||||||
|
for d in g.COORDS_ORTH:
|
||||||
|
nb = add2(pos, d)
|
||||||
|
if nb[0] < 0 or nb[0] >= g.n_rows or nb[1] < 0 or nb[1] >= g.n_cols:
|
||||||
|
continue
|
||||||
|
c = g[nb]
|
||||||
|
if c in SLOPES.keys() and d != SLOPES[c]:
|
||||||
|
continue
|
||||||
|
if c == "#" or nb in hist:
|
||||||
|
continue
|
||||||
|
if nb == end:
|
||||||
|
l = len(hist)
|
||||||
|
if l > longest:
|
||||||
|
longest = l
|
||||||
|
continue
|
||||||
|
nhist = hist.copy()
|
||||||
|
nhist.add(nb)
|
||||||
|
new_paths.append((nhist, nb))
|
||||||
|
paths = new_paths
|
||||||
|
if len(paths) == 0:
|
||||||
|
break
|
||||||
|
return longest
|
||||||
|
|
||||||
|
def solve(input: Input, second=False):
|
||||||
|
if not second:
|
||||||
|
return first(input)
|
||||||
|
|
||||||
|
g = input.grid2()
|
||||||
|
start = (0, 1)
|
||||||
|
end = (g.n_rows - 1, g.n_cols - 2)
|
||||||
|
|
||||||
|
seen = set()
|
||||||
|
q = deque([[start, (1, 1)]])
|
||||||
|
|
||||||
|
# The intuition is that we can brute force much quicker if we have a pure
|
||||||
|
# graph instead of following the maze along the whole time. So, we create
|
||||||
|
# a graph from the maze and then brute force on the maze.
|
||||||
|
sg = {start: set()} # {node: {(node, dist), ...}}
|
||||||
|
|
||||||
|
while q:
|
||||||
|
trail = q.popleft()
|
||||||
|
pos = trail[-1]
|
||||||
|
while True:
|
||||||
|
nbs = []
|
||||||
|
for d in g.COORDS_ORTH:
|
||||||
|
nb = add2(pos, d)
|
||||||
|
if nb[0] < 0 or nb[0] >= g.n_rows or nb[1] < 0 or nb[1] >= g.n_cols:
|
||||||
|
continue
|
||||||
|
if g[nb] == "#" or nb == trail[-2]:
|
||||||
|
continue
|
||||||
|
nbs.append(nb)
|
||||||
|
if len(nbs) == 1:
|
||||||
|
pos = nbs[0]
|
||||||
|
trail.append(pos)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not pos in sg:
|
||||||
|
sg[pos] = set()
|
||||||
|
|
||||||
|
dist = len(trail) - 1
|
||||||
|
sg[trail[0]].add((pos, dist))
|
||||||
|
sg[pos].add((trail[0], dist))
|
||||||
|
|
||||||
|
seen.add(pos)
|
||||||
|
for nb in nbs:
|
||||||
|
if not nb in seen:
|
||||||
|
seen.add(nb)
|
||||||
|
q.append([pos, nb])
|
||||||
|
|
||||||
|
# for key, value in sg.items():
|
||||||
|
# print(key, value)
|
||||||
|
|
||||||
|
# Brute force in bf order.
|
||||||
|
longest = 0
|
||||||
|
q = deque([(set(), start, 0)])
|
||||||
|
while q:
|
||||||
|
hist, pos, dist = q.popleft()
|
||||||
|
if pos == end:
|
||||||
|
if dist > longest:
|
||||||
|
longest = dist
|
||||||
|
continue
|
||||||
|
|
||||||
|
for nb, d in sg[pos]:
|
||||||
|
if nb in hist:
|
||||||
|
continue
|
||||||
|
nhist = hist.copy()
|
||||||
|
nhist.add(nb)
|
||||||
|
q.append((nhist, nb, dist + d))
|
||||||
|
|
||||||
|
return longest
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DAY_INPUT = "i23.txt"
|
||||||
|
# print("Example 1:", solve(Input(EXAMPLE)))
|
||||||
|
# print("Solution 1:", solve(Input(DAY_INPUT)))
|
||||||
|
print("Example 2:", solve(Input(EXAMPLE), True))
|
||||||
|
print("Solution 2:", solve(Input(DAY_INPUT), True))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
74
d24.py
Normal file
74
d24.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
from lib import *
|
||||||
|
import sympy as sp
|
||||||
|
|
||||||
|
EXAMPLE = """19, 13, 30 @ -2, 1, -2
|
||||||
|
18, 19, 22 @ -1, -1, -2
|
||||||
|
20, 25, 34 @ -2, -2, -4
|
||||||
|
12, 31, 28 @ -1, -2, -1
|
||||||
|
20, 19, 15 @ 1, -5, -3
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def solve1(input: Input):
|
||||||
|
if len(input.lines()) == 5:
|
||||||
|
lb = 7
|
||||||
|
ub = 27
|
||||||
|
else:
|
||||||
|
lb = 200000000000000
|
||||||
|
ub = 400000000000000
|
||||||
|
|
||||||
|
# On paper:
|
||||||
|
# (px - sx1) / vx1 = (py - sy1) / vy1
|
||||||
|
# (px - sx1) * vy1 = (py - sy1) * vx1
|
||||||
|
# (px - sx1) * vy1 - (py - sy1) * vx1 = 0
|
||||||
|
res = 0
|
||||||
|
eqs = [str_to_ints(l) for l in input.lines()]
|
||||||
|
for i, eq1 in enumerate(eqs):
|
||||||
|
for eq2 in eqs[:i]:
|
||||||
|
sx1, sy1, _, vx1, vy1, _ = eq1
|
||||||
|
sx2, sy2, _, vx2, vy2, _ = eq2
|
||||||
|
|
||||||
|
px, py = sp.symbols("px py")
|
||||||
|
es = [
|
||||||
|
vy1 * (px - sx1) - vx1 * (py - sy1),
|
||||||
|
vy2 * (px - sx2) - vx2 * (py - sy2),
|
||||||
|
]
|
||||||
|
r = sp.solve(es)
|
||||||
|
if not r:
|
||||||
|
continue
|
||||||
|
|
||||||
|
x, y = r[px], r[py]
|
||||||
|
if lb <= x <= ub and lb <= y < ub:
|
||||||
|
t1 = (x - sx1) / vx1
|
||||||
|
t2 = (x - sx2) / vx2
|
||||||
|
if (t1 > 0 and t2 > 0):
|
||||||
|
res += 1
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def solve2(input: Input):
|
||||||
|
eqs = [str_to_ints(l) for l in input.lines()]
|
||||||
|
px, py, pz, vxo, vyo, vzo = sp.symbols("px py pz vxo vyo vzo")
|
||||||
|
es = []
|
||||||
|
|
||||||
|
# The first six equations are enough to find a solution for my problem set.
|
||||||
|
# Might have to be increased depending on input.
|
||||||
|
for i, (x, y, z, vx, vy, vz) in enumerate(eqs[:6]):
|
||||||
|
t = sp.symbols(f"t{i}")
|
||||||
|
es.append(px + vxo * t - x - vx * t)
|
||||||
|
es.append(py + vyo * t - y - vy * t)
|
||||||
|
es.append(pz + vzo * t - z - vz * t)
|
||||||
|
r = sp.solve(es)[0]
|
||||||
|
return r[px] + r[py] + r[pz]
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DAY_INPUT = "i24.txt"
|
||||||
|
print("Solution 1:", solve1(Input(EXAMPLE)))
|
||||||
|
print("Solution 1:", solve1(Input(DAY_INPUT)))
|
||||||
|
print("Example 2:", solve2(Input(EXAMPLE)))
|
||||||
|
print("Solution 2:", solve2(Input(DAY_INPUT)))
|
||||||
|
return
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
87
d25.py
Normal file
87
d25.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
from lib import *
|
||||||
|
from random import choice
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
|
||||||
|
# def plot(graph):
|
||||||
|
# import networkx as nx
|
||||||
|
# import matplotlib
|
||||||
|
# import matplotlib.pyplot as plt
|
||||||
|
# G = nx.Graph()
|
||||||
|
# for node, connected_nodes in graph.items():
|
||||||
|
# for connected_node in connected_nodes:
|
||||||
|
# G.add_edge(node, connected_node)
|
||||||
|
# # pos = nx.spring_layout(G, k=2.0, iterations=20) # Adjust k as needed
|
||||||
|
# pos = nx.shell_layout(G)
|
||||||
|
# nx.draw(G, with_labels=True, node_color='lightblue', edge_color='gray', node_size=2000, font_size=15, font_weight='bold')
|
||||||
|
# matplotlib.use('qtagg')
|
||||||
|
# plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
def solve(input: Input):
|
||||||
|
graph = {}
|
||||||
|
edges = {}
|
||||||
|
|
||||||
|
for line in input.lines():
|
||||||
|
src, dsts = line.split(":")
|
||||||
|
dsts = dsts.strip().split(" ")
|
||||||
|
|
||||||
|
if not src in graph:
|
||||||
|
graph[src] = []
|
||||||
|
|
||||||
|
for dst in dsts:
|
||||||
|
graph[src].append(dst)
|
||||||
|
if not dst in graph:
|
||||||
|
graph[dst] = []
|
||||||
|
graph[dst].append(src)
|
||||||
|
|
||||||
|
edge = tuple(sorted([src, dst]))
|
||||||
|
edges[edge] = 0
|
||||||
|
|
||||||
|
for _ in range(100):
|
||||||
|
first_node = choice(list(graph.keys()))
|
||||||
|
seen = set([first_node])
|
||||||
|
visit = deque([first_node])
|
||||||
|
while visit:
|
||||||
|
node = visit.popleft()
|
||||||
|
for nb in graph[node]:
|
||||||
|
if not nb in seen:
|
||||||
|
seen.add(nb)
|
||||||
|
visit.append(nb)
|
||||||
|
edge = tuple(sorted([node, nb]))
|
||||||
|
edges[edge] += 1
|
||||||
|
|
||||||
|
# Orignally, I used `plot(graph)` to visually find the nodes that have to
|
||||||
|
# be removed. I then came up with this heuristic approach. The idea is that
|
||||||
|
# we have to cross one of the three nodes when we do a breadth first
|
||||||
|
# search. By repeatedly doing that we can identify the "bridges" as the
|
||||||
|
# three edges that are used the most often.
|
||||||
|
most_visited = sorted(edges.items(), key=lambda t: t[1], reverse=True)[:3]
|
||||||
|
|
||||||
|
# to_remove = (("plt", "mgb"), ("jxm", "qns"), ("dbt", "tjd")) # found visually
|
||||||
|
# for node, count in most_visited:
|
||||||
|
# print(node, count) # should print the same as `to_remove`
|
||||||
|
|
||||||
|
for (a, b), _ in most_visited:
|
||||||
|
graph[a].remove(b)
|
||||||
|
graph[b].remove(a)
|
||||||
|
|
||||||
|
to_visit = [choice(list(graph.keys()))]
|
||||||
|
seen = set(to_visit)
|
||||||
|
while to_visit:
|
||||||
|
node = to_visit.pop()
|
||||||
|
for nb in graph[node]:
|
||||||
|
if not nb in seen:
|
||||||
|
seen.add(nb)
|
||||||
|
to_visit.append(nb)
|
||||||
|
|
||||||
|
return len(seen) * (len(graph) - len(seen))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
DAY_INPUT = "i25.txt"
|
||||||
|
print("Solution 1:", solve(Input(DAY_INPUT)), "(hands-free)")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
58
d3.py
58
d3.py
@@ -1,6 +1,6 @@
|
|||||||
import re
|
import re
|
||||||
|
from lib import *
|
||||||
|
|
||||||
NUMBERS = "0123456789"
|
|
||||||
EXAMPLE = """
|
EXAMPLE = """
|
||||||
467..114..
|
467..114..
|
||||||
...*......
|
...*......
|
||||||
@@ -14,6 +14,42 @@ EXAMPLE = """
|
|||||||
.664.598..
|
.664.598..
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def solve(i: Input, second=False):
|
||||||
|
"""
|
||||||
|
This is a new version I have implemented after the fact to test the
|
||||||
|
improvements to my library. My original versions are `solve1` and `solve2`.
|
||||||
|
"""
|
||||||
|
g = i.grid2()
|
||||||
|
if not second:
|
||||||
|
parts = g.find_not(NUMBERS + ".")
|
||||||
|
else:
|
||||||
|
parts = g.find("*")
|
||||||
|
|
||||||
|
res = 0
|
||||||
|
numbers = []
|
||||||
|
for p in parts:
|
||||||
|
numbers_gear = []
|
||||||
|
for n in g.neighbors_adj(p):
|
||||||
|
if g[n] in NUMBERS:
|
||||||
|
while n in g and g[n] in NUMBERS:
|
||||||
|
n = (n[0], n[1] - 1)
|
||||||
|
number = ""
|
||||||
|
n = (n[0], n[1] + 1)
|
||||||
|
start = n
|
||||||
|
while n in g and g[n] in NUMBERS:
|
||||||
|
number += g[n]
|
||||||
|
n = (n[0], n[1] + 1)
|
||||||
|
numbers.append((int(number), n[0], start))
|
||||||
|
numbers_gear.append(int(number))
|
||||||
|
numbers_gear = list(set(numbers_gear))
|
||||||
|
if len(numbers_gear) == 2:
|
||||||
|
res += numbers_gear[0] * numbers_gear[1]
|
||||||
|
|
||||||
|
if second:
|
||||||
|
return res
|
||||||
|
else:
|
||||||
|
return sum([n for n, _, _ in list(set(numbers))])
|
||||||
|
|
||||||
def clean(text: str) -> list[str]:
|
def clean(text: str) -> list[str]:
|
||||||
return list(filter(lambda l: l.strip() != "", text.splitlines()))
|
return list(filter(lambda l: l.strip() != "", text.splitlines()))
|
||||||
|
|
||||||
@@ -29,7 +65,7 @@ def is_adj_to_symbol(x, y, lines):
|
|||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def solve(lines: list[str]):
|
def solve1(lines: list[str]):
|
||||||
d = ""
|
d = ""
|
||||||
adj_to_symbol = False
|
adj_to_symbol = False
|
||||||
s = 0
|
s = 0
|
||||||
@@ -86,16 +122,18 @@ def solve2(lines: list[str]):
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
example = clean(EXAMPLE)
|
input = Input(EXAMPLE)
|
||||||
print("Example 1:", solve(example))
|
print("Example 1:", solve(input))
|
||||||
data = clean(open("i3.txt").read())
|
|
||||||
print("Solution 1:", solve(data))
|
|
||||||
|
|
||||||
example = clean(EXAMPLE)
|
input = Input("i3.txt")
|
||||||
print("Example 2:", solve2(example))
|
print("Solution 1:", solve(input))
|
||||||
|
|
||||||
data = clean(open("i3.txt").read())
|
input = Input(EXAMPLE)
|
||||||
print("Solution 2:", solve2(data))
|
print("Example 2:", solve(input, True))
|
||||||
|
|
||||||
|
input = Input("i3.txt")
|
||||||
|
print("Solution 2:", solve(input, True))
|
||||||
|
return
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
4
d9.py
4
d9.py
@@ -9,7 +9,7 @@ EXAMPLE = """
|
|||||||
def solve(lines: list[str]):
|
def solve(lines: list[str]):
|
||||||
res = 0
|
res = 0
|
||||||
for (i, line) in enumerate(lines):
|
for (i, line) in enumerate(lines):
|
||||||
digits = lib.str_to_int_list(line)
|
digits = lib.str_to_ints(line)
|
||||||
last_digits = []
|
last_digits = []
|
||||||
while not all(d == 0 for d in digits):
|
while not all(d == 0 for d in digits):
|
||||||
last_digits.append(digits[-1])
|
last_digits.append(digits[-1])
|
||||||
@@ -24,7 +24,7 @@ def solve(lines: list[str]):
|
|||||||
def solve2(lines: list[str]):
|
def solve2(lines: list[str]):
|
||||||
res = 0
|
res = 0
|
||||||
for (i, line) in enumerate(lines):
|
for (i, line) in enumerate(lines):
|
||||||
digits = lib.str_to_int_list(line)
|
digits = lib.str_to_ints(line)
|
||||||
first_digits = []
|
first_digits = []
|
||||||
while not all(d == 0 for d in digits):
|
while not all(d == 0 for d in digits):
|
||||||
first_digits.append(digits[0])
|
first_digits.append(digits[0])
|
||||||
|
|||||||
37
dx.py
37
dx.py
@@ -1,37 +0,0 @@
|
|||||||
import lib
|
|
||||||
|
|
||||||
EXAMPLE = """
|
|
||||||
"""
|
|
||||||
|
|
||||||
def solve(lines: list[str]):
|
|
||||||
res = 0
|
|
||||||
for (i, line) in enumerate(lines):
|
|
||||||
print(i, line)
|
|
||||||
# digits = lib.str_to_int_list(line)
|
|
||||||
# digit = lib.str_to_single_int(line)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def solve2(lines: list[str]):
|
|
||||||
res = 0
|
|
||||||
for (i, line) in enumerate(lines):
|
|
||||||
print(i, line)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def main():
|
|
||||||
lines = lib.str_to_lines_no_empty(EXAMPLE)
|
|
||||||
print("Example 1:", solve(lines))
|
|
||||||
return
|
|
||||||
|
|
||||||
lines = lib.str_to_lines_no_empty(open("ix.txt").read())
|
|
||||||
print("Solution 1:", solve(lines))
|
|
||||||
return
|
|
||||||
|
|
||||||
lines = lib.str_to_lines_no_empty(EXAMPLE)
|
|
||||||
print("Example 2:", solve2(lines))
|
|
||||||
return
|
|
||||||
|
|
||||||
lines = lib.str_to_lines_no_empty(open("ix.txt").read())
|
|
||||||
print("Solution 2:", solve2(lines))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
50
lib.py
50
lib.py
@@ -1,50 +0,0 @@
|
|||||||
import re
|
|
||||||
|
|
||||||
def prime_factors(n):
|
|
||||||
"""
|
|
||||||
Returns a list of prime factors for n.
|
|
||||||
|
|
||||||
:param n: number for which prime factors should be returned
|
|
||||||
"""
|
|
||||||
factors = []
|
|
||||||
rest = n
|
|
||||||
divisor = 2
|
|
||||||
while rest % divisor == 0:
|
|
||||||
factors.append(divisor)
|
|
||||||
rest //= divisor
|
|
||||||
divisor = 3
|
|
||||||
while divisor * divisor <= rest:
|
|
||||||
while rest % divisor == 0:
|
|
||||||
factors.append(divisor)
|
|
||||||
rest //= divisor
|
|
||||||
divisor += 2
|
|
||||||
if rest != 1:
|
|
||||||
factors.append(rest)
|
|
||||||
return factors
|
|
||||||
|
|
||||||
def lcm(numbers: list[int]) -> int:
|
|
||||||
fs = []
|
|
||||||
for n in numbers:
|
|
||||||
fs += prime_factors(n)
|
|
||||||
s = 1
|
|
||||||
fs = list(set(fs))
|
|
||||||
for f in fs:
|
|
||||||
s *= f
|
|
||||||
return s
|
|
||||||
|
|
||||||
def str_to_single_int(line: str) -> int:
|
|
||||||
line = line.replace(" ", "")
|
|
||||||
r = re.compile(r"-?\d+")
|
|
||||||
for m in r.findall(line):
|
|
||||||
return int(m)
|
|
||||||
raise Exception("No single digit sequence in '{line}'")
|
|
||||||
|
|
||||||
def str_to_int_list(line: str) -> list[int]:
|
|
||||||
r = re.compile(r"-?\d+")
|
|
||||||
return list(map(int, r.findall(line)))
|
|
||||||
|
|
||||||
def str_to_lines_no_empty(text: str) -> list[str]:
|
|
||||||
return list(filter(lambda l: l.strip() != "", text.splitlines()))
|
|
||||||
|
|
||||||
def str_to_lines(text: str) -> list[str]:
|
|
||||||
return list(text.splitlines())
|
|
||||||
1
monitor.py
Symbolic link
1
monitor.py
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../aocpy/monitor.py
|
||||||
Reference in New Issue
Block a user