Compare commits
10 commits
08288964bd
...
178d96494a
| Author | SHA1 | Date | |
|---|---|---|---|
| 178d96494a | |||
| fb468c2199 | |||
| a90269f7f9 | |||
| c832e30dcf | |||
| 3de54ce0e9 | |||
| 4fa1b1e14c | |||
| 9ca8607f8b | |||
| a1bf11a5ed | |||
| b681e5cdb7 | |||
| 741f7b89d8 |
20 changed files with 712 additions and 113 deletions
|
|
@ -36,3 +36,7 @@ Execute separate puzzle on file save (replace `XX` with the puzzle number):
|
|||
|
||||
(requires `entr` and `wl-paste`, Mac users can instead use `pbpaste`. If you
|
||||
prefer X at Linux, use `xclip -selection clipboard -o`).
|
||||
|
||||
To lint files:
|
||||
|
||||
ls output/*.py | entr -r -c flake8 output --ignore=E741,E501,E203
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ if __name__ == "__main__":
|
|||
# import sys; inp = sys.stdin.read().strip()
|
||||
|
||||
# uncomment to use AoC provided puzzle input
|
||||
# with open(f"./input/{padded_no}.txt", "r") as f:
|
||||
# inp = f.read()
|
||||
# with open("./input/{padded_no}.txt", "r") as f:
|
||||
# inp = f.read().strip()
|
||||
|
||||
# uncomment to do initial data processing shared by part 1-2
|
||||
inp = solve(inp)
|
||||
|
|
|
|||
|
|
@ -80,3 +80,59 @@ def vdbg(seen, h, w):
|
|||
"""Print-debug visited positions of a matrix"""
|
||||
for r in range(h):
|
||||
print("".join(["#" if (r, c) in seen else "." for c in range(w)]))
|
||||
|
||||
|
||||
"""
|
||||
1. Create an array that holds the distance of each vertex from the starting
|
||||
vertex. Initially, set this distance to infinity for all vertices except
|
||||
the starting vertex which should be set to 0.
|
||||
2. Create a priority queue (heap) and insert the starting vertex with its
|
||||
distance of 0.
|
||||
3. While there are still vertices left in the priority queue, select the vertex
|
||||
with the smallest recorded distance from the starting vertex and visit its
|
||||
neighboring vertices.
|
||||
4. For each neighboring vertex, check if it is visited already or not. If it
|
||||
isn’t visited yet, calculate its tentative distance by adding its weight
|
||||
to the smallest distance found so far for its parent/previous node
|
||||
(starting vertex in case of first-level vertices).
|
||||
5. If this tentative distance is smaller than previously recorded value
|
||||
(if any), update it in our ‘distances’ array.
|
||||
6. Finally, add this visited vertex with its updated distance to our priority
|
||||
queue and repeat step-3 until we have reached our destination or exhausted
|
||||
all nodes.
|
||||
"""
|
||||
|
||||
|
||||
# https://pieriantraining.com/understanding-dijkstras-algorithm-in-python/
|
||||
def dijkstra_algorithm(graph, start_node):
|
||||
def min_distance(distances, visited):
|
||||
min_val = float("inf")
|
||||
min_index = -1
|
||||
|
||||
for i in range(len(distances)):
|
||||
if distances[i] < min_val and i not in visited:
|
||||
min_val = distances[i]
|
||||
min_index = i
|
||||
|
||||
return min_index
|
||||
|
||||
num_nodes = len(graph)
|
||||
|
||||
distances = [float("inf")] * num_nodes
|
||||
visited = []
|
||||
|
||||
distances[start_node] = 0
|
||||
|
||||
for i in range(num_nodes):
|
||||
current_node = min_distance(distances, visited)
|
||||
|
||||
visited.append(current_node)
|
||||
|
||||
for j in range(num_nodes):
|
||||
if graph[current_node][j] != 0:
|
||||
|
||||
new_distance = distances[current_node] + graph[current_node][j]
|
||||
|
||||
if new_distance < distances[j]:
|
||||
distances[j] = new_distance
|
||||
return distances
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ def part_2(data):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open(f"./input/01.txt", "r") as f:
|
||||
with open("./input/01.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
a = part_1(inp)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ def part_2(data):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open(f"./input/02.txt", "r") as f:
|
||||
with open("./input/02.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
a = part_1(inp)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ def presolve(data):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open(f"./input/03.txt", "r") as f:
|
||||
with open("./input/03.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
parsed = presolve(inp)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ def presolve(data):
|
|||
b = set(re.findall(r"\d+", b))
|
||||
ab = len(a & b)
|
||||
if ab > 0:
|
||||
scores.append(2**(ab - 1))
|
||||
scores.append(2 ** (ab - 1))
|
||||
count[cid] += 1
|
||||
for i in range(cid + 1, cid + ab + 1):
|
||||
count[i] += count[cid]
|
||||
|
|
@ -35,7 +35,7 @@ def presolve(data):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open(f"./input/04.txt", "r") as f:
|
||||
with open("./input/04.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
inp = presolve(inp)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import re
|
||||
from itertools import repeat
|
||||
from math import inf
|
||||
from multiprocessing import Pool, freeze_support
|
||||
from output import answer
|
||||
|
||||
n = 5
|
||||
|
|
@ -9,91 +7,67 @@ title = "If You Give A Seed A Fertilizer"
|
|||
|
||||
|
||||
@answer(1, "Nearest location for seed id list is {}")
|
||||
def part_1(data):
|
||||
seeds, *process = data.split("\n\n")
|
||||
seeds = [f"{v} 1" for v in seeds.split()[1:]]
|
||||
return _bruteforce(seeds, process, 1)
|
||||
def part_1(presolved):
|
||||
l, _ = presolved
|
||||
return l
|
||||
|
||||
|
||||
@answer(2, "Interpreting ranges of seeds, nearest location is {}")
|
||||
def part_2(data):
|
||||
def part_2(presolved):
|
||||
_, l = presolved
|
||||
return l
|
||||
|
||||
|
||||
def presolve(data):
|
||||
seeds, *process = data.split("\n\n")
|
||||
seeds = re.findall(r"\d+ \d+", seeds)
|
||||
return _bruteforce(seeds, process, 8)
|
||||
seed_ranges = [[int(x) for x in ar.split()] for ar in re.findall(r"\d+ \d+", seeds)]
|
||||
seed_values = [int(v) for v in seeds.split()[1:]]
|
||||
processes = [
|
||||
[tuple(map(int, line.split())) for line in step.splitlines()[1:]]
|
||||
for step in process
|
||||
]
|
||||
|
||||
p1 = _process(seed_values, processes)
|
||||
|
||||
p2 = 26829000 # takes 5m if starting from 0
|
||||
while True:
|
||||
g = _process([p2], processes, reverse=True)
|
||||
if any(g >= a and g < a + r for a, r in seed_ranges):
|
||||
break
|
||||
p2 += 1
|
||||
|
||||
return p1, p2
|
||||
|
||||
|
||||
def _bruteforce(seeds, process, p=1):
|
||||
processes = [[tuple(map(int, line.split())) for line in step.splitlines()[1:]] for step in process]
|
||||
|
||||
sm = []
|
||||
for start_r in seeds:
|
||||
pool = Pool()
|
||||
start, r = start_r.split()
|
||||
d = int(r) // p
|
||||
parts = [(d * n + int(start), d * n + int(start) + d) for n in range(p)]
|
||||
sm += pool.starmap(_nearest, zip(parts, repeat(processes)))
|
||||
return min(sm)
|
||||
def _process(seeds, processes, reverse=False):
|
||||
n = inf
|
||||
for start in seeds:
|
||||
n = min(n, _nearest(start, processes, reverse=reverse))
|
||||
return n
|
||||
|
||||
|
||||
def _nearest(start_r, processes):
|
||||
a, b = start_r
|
||||
nearest = inf
|
||||
for i in range(a, b):
|
||||
v = i
|
||||
for steps in processes:
|
||||
nid = -1
|
||||
for line in steps:
|
||||
dest, src, r = line
|
||||
if v >= src and v < src + r:
|
||||
v = dest + v - src
|
||||
break
|
||||
nearest = min(nearest, v)
|
||||
|
||||
return nearest
|
||||
def _nearest(start, processes, reverse=False):
|
||||
procs = processes if not reverse else processes[::-1]
|
||||
v = start
|
||||
for steps in procs:
|
||||
for line in steps:
|
||||
dest, src, r = line
|
||||
if reverse:
|
||||
dest, src = src, dest
|
||||
if v >= src and v < src + r:
|
||||
v = dest + v - src
|
||||
break
|
||||
return v
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# use dummy data
|
||||
inp = """
|
||||
seeds: 79 14 55 13
|
||||
with open("./input/05.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
seed-to-soil map:
|
||||
50 98 2
|
||||
52 50 48
|
||||
|
||||
soil-to-fertilizer map:
|
||||
0 15 37
|
||||
37 52 2
|
||||
39 0 15
|
||||
|
||||
fertilizer-to-water map:
|
||||
49 53 8
|
||||
0 11 42
|
||||
42 0 7
|
||||
57 7 4
|
||||
|
||||
water-to-light map:
|
||||
88 18 7
|
||||
18 25 70
|
||||
|
||||
light-to-temperature map:
|
||||
45 77 23
|
||||
81 45 19
|
||||
68 64 13
|
||||
|
||||
temperature-to-humidity map:
|
||||
0 69 1
|
||||
1 0 69
|
||||
|
||||
humidity-to-location map:
|
||||
60 56 37
|
||||
56 93 4
|
||||
""".strip()
|
||||
|
||||
# inp = parse_input()
|
||||
inp = presolve(inp)
|
||||
|
||||
a = part_1(inp)
|
||||
b = part_2(inp)
|
||||
|
||||
# assert a == 278755257
|
||||
# assert b == 26829166
|
||||
assert a == 278755257
|
||||
assert b == 26829166
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ def presolve(data):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open(f"./input/06.txt", "r") as f:
|
||||
with open("./input/06.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
inp = presolve(inp)
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ if __name__ == "__main__":
|
|||
|
||||
doctest.testmod()
|
||||
|
||||
with open(f"./input/07.txt", "r") as f:
|
||||
with open("./input/07.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
a = part_1(inp)
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ def presolve(data):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open(f"./input/08.txt", "r") as f:
|
||||
with open("./input/08.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
inp = presolve(inp)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ def _solve(lines):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open(f"./input/09.txt", "r") as f:
|
||||
with open("./input/09.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
a = part_1(inp)
|
||||
|
|
|
|||
|
|
@ -1,44 +1,94 @@
|
|||
from collections import deque
|
||||
from output import answer
|
||||
|
||||
n = 10
|
||||
title = "dddd"
|
||||
title = "Pipe Maze"
|
||||
|
||||
|
||||
@answer(1, "Answer is {}")
|
||||
def part_1(data):
|
||||
return data
|
||||
D = (-1, 0), (0, 1), (1, 0), (0, -1)
|
||||
|
||||
C = {
|
||||
(-1, 0): ["|", "7", "F"],
|
||||
(0, 1): ["-", "7", "J"],
|
||||
(1, 0): ["|", "L", "J"],
|
||||
(0, -1): ["-", "L", "F"],
|
||||
}
|
||||
|
||||
A = {
|
||||
"S": [0, 1, 2, 3],
|
||||
"-": [1, 3],
|
||||
"|": [0, 2],
|
||||
"F": [1, 2],
|
||||
"L": [0, 1],
|
||||
"7": [2, 3],
|
||||
"J": [0, 3],
|
||||
}
|
||||
|
||||
|
||||
@answer(2, "Actually, answer is {}")
|
||||
def part_2(data):
|
||||
return data
|
||||
@answer(1, "Farthest away pipe is at {}")
|
||||
def part_1(presolved):
|
||||
return presolved[0]
|
||||
|
||||
|
||||
# uncomment to solve parts in one go
|
||||
# def presolve(data):
|
||||
# return data
|
||||
@answer(2, "{} spots are encapsulated by pipes")
|
||||
def part_2(presolved):
|
||||
return presolved[1]
|
||||
|
||||
|
||||
def presolve(data):
|
||||
matrix = data.split()
|
||||
w = len(matrix[0])
|
||||
h = len(matrix)
|
||||
q = deque()
|
||||
visited = set()
|
||||
|
||||
for r in range(h):
|
||||
if "S" in matrix[r]:
|
||||
start = (r, matrix[r].index("S"))
|
||||
q.append(start)
|
||||
break
|
||||
|
||||
while q:
|
||||
o = q.popleft()
|
||||
visited.add(o)
|
||||
for di in A[matrix[o[0]][o[1]]]:
|
||||
d = D[di]
|
||||
r = o[0] + d[0]
|
||||
c = o[1] + d[1]
|
||||
if r >= 0 and r < h and c >= 0 and c < w:
|
||||
t = matrix[r][c]
|
||||
p = (r, c)
|
||||
if p not in visited and t != "." and t in C[d]:
|
||||
q.append(p)
|
||||
p1 = len(visited) // 2
|
||||
|
||||
p2 = 0
|
||||
for y in range(h):
|
||||
for x in range(w):
|
||||
if (y, x) in visited:
|
||||
continue
|
||||
crosses = 0
|
||||
y2, x2 = y, x
|
||||
while y2 < h and x2 < w:
|
||||
c2 = matrix[y2][x2]
|
||||
if (y2, x2) in visited and c2 not in "L7":
|
||||
crosses += 1
|
||||
x2 += 1
|
||||
y2 += 1
|
||||
if crosses % 2 == 1:
|
||||
p2 += 1
|
||||
|
||||
return p1, p2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# use dummy data
|
||||
inp = """
|
||||
replace me
|
||||
""".strip()
|
||||
with open("./input/10.txt", "r") as f:
|
||||
inp = f.read()
|
||||
|
||||
# uncomment to instead use stdin
|
||||
# import sys; inp = sys.stdin.read().strip()
|
||||
|
||||
# uncomment to use AoC provided puzzle input
|
||||
# with open(f"./input/10.txt", "r") as f:
|
||||
# inp = f.read()
|
||||
|
||||
# uncomment to do initial data processing shared by part 1-2
|
||||
# inp = presolve(inp)
|
||||
inp = presolve(inp)
|
||||
|
||||
a = part_1(inp)
|
||||
# b = part_2(inp)
|
||||
b = part_2(inp)
|
||||
|
||||
# uncomment and replace 0 with actual output to refactor code
|
||||
# and ensure nonbreaking changes
|
||||
# assert a == 0
|
||||
# assert b == 0
|
||||
assert a == 6846
|
||||
assert b == 325
|
||||
|
|
|
|||
63
2023-python/output/day_11.py
Normal file
63
2023-python/output/day_11.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
from itertools import combinations
|
||||
from output import answer
|
||||
|
||||
n = 11
|
||||
title = "Cosmic Expansion"
|
||||
|
||||
|
||||
@answer(1, "Sum of all galaxy shortest distances is {}")
|
||||
def part_1(data):
|
||||
return data[0]
|
||||
|
||||
|
||||
@answer(2, "Exapanding by 1M, sum is {}")
|
||||
def part_2(data):
|
||||
return data[1]
|
||||
|
||||
|
||||
def presolve(data):
|
||||
m = data.splitlines()
|
||||
er = set()
|
||||
ec = set()
|
||||
for i, r in enumerate(m):
|
||||
if "#" not in r:
|
||||
er.add(i)
|
||||
for i, c in enumerate(zip(*m)):
|
||||
if "#" not in c:
|
||||
ec.add(i)
|
||||
h = len(m)
|
||||
w = len(m[0])
|
||||
g1 = []
|
||||
g2 = []
|
||||
e = 1e6
|
||||
for r in range(h):
|
||||
for c in range(w):
|
||||
if m[r][c] == "#":
|
||||
ro = len(er & set(range(r)))
|
||||
co = len(ec & set(range(c)))
|
||||
g1.append((r + ro, c + co))
|
||||
g2.append((ro * e + r - ro, co * e + c - co))
|
||||
p1 = sum(
|
||||
abs(rc1[0] - rc2[0]) + abs(rc1[1] - rc2[1]) for rc1, rc2 in combinations(g1, 2)
|
||||
)
|
||||
p2 = int(
|
||||
sum(
|
||||
abs(rc1[0] - rc2[0]) + abs(rc1[1] - rc2[1])
|
||||
for rc1, rc2 in combinations(g2, 2)
|
||||
)
|
||||
)
|
||||
|
||||
return p1, p2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./input/11.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
inp = presolve(inp)
|
||||
|
||||
a = part_1(inp)
|
||||
b = part_2(inp)
|
||||
|
||||
assert a == 9370588
|
||||
assert b == 746207878188
|
||||
64
2023-python/output/day_12.py
Normal file
64
2023-python/output/day_12.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
from functools import cache
|
||||
from output import answer
|
||||
|
||||
n = 12
|
||||
title = "Hot Springs"
|
||||
|
||||
|
||||
@answer(1, "sum of all possible combinations is {}")
|
||||
def part_1(presolved):
|
||||
return presolved[0]
|
||||
|
||||
|
||||
@answer(2, "sum of all possible combinations is {} when unfolded")
|
||||
def part_2(presolved):
|
||||
return presolved[1]
|
||||
|
||||
|
||||
def presolve(data):
|
||||
lines = [
|
||||
(a, list(map(int, b.split(","))))
|
||||
for a, b in (line.split() for line in data.splitlines())
|
||||
]
|
||||
p1 = sum(_inspect(a, tuple(b)) for a, b in lines)
|
||||
p2 = sum(_inspect("?".join([a] * 5), tuple(b * 5)) for a, b in lines)
|
||||
return p1, p2
|
||||
|
||||
|
||||
@cache
|
||||
def _inspect(s, cs):
|
||||
r = len(s)
|
||||
csl = len(cs)
|
||||
if r == 0:
|
||||
return 1 if csl == 0 else 0
|
||||
o, *f = s
|
||||
f = "".join(f)
|
||||
if o == ".":
|
||||
return _inspect(f, cs)
|
||||
if o == "?":
|
||||
return _inspect("." + f, cs) + _inspect("#" + f, cs)
|
||||
if not csl:
|
||||
return 0
|
||||
g = cs[0]
|
||||
if g > r or "." in s[0:g]:
|
||||
return 0
|
||||
elif csl > 1:
|
||||
if g + 1 > r or s[g] == "#":
|
||||
return 0
|
||||
else:
|
||||
return _inspect(s[g + 1 :], cs[1:])
|
||||
elif csl == 1:
|
||||
return _inspect(s[g:], ())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./input/12.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
inp = presolve(inp)
|
||||
|
||||
a = part_1(inp)
|
||||
b = part_2(inp)
|
||||
|
||||
assert a == 7118
|
||||
assert b == 7030194981795
|
||||
56
2023-python/output/day_13.py
Normal file
56
2023-python/output/day_13.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
from output import answer
|
||||
|
||||
n = 13
|
||||
title = "Point of Incidence"
|
||||
|
||||
|
||||
@answer(1, "Summarizing the notes gives {}")
|
||||
def part_1(presolved):
|
||||
return presolved[0]
|
||||
|
||||
|
||||
@answer(2, "Summarizing the notes allowing off-by-1 gives {}")
|
||||
def part_2(presolved):
|
||||
return presolved[1]
|
||||
|
||||
|
||||
def presolve(data):
|
||||
g = [l.split() for l in data.split("\n\n")]
|
||||
|
||||
p1 = sum(d * n for d, n in _inspect(g))
|
||||
p2 = sum(d * n for d, n in _inspect(g, 1))
|
||||
|
||||
return p1, p2
|
||||
|
||||
|
||||
def _inspect(g, a=0):
|
||||
af = []
|
||||
for m in g:
|
||||
for d, n in [(100, m), (1, tuple(zip(*m)))]:
|
||||
af.append((d, _compare(n, a)))
|
||||
return af
|
||||
|
||||
|
||||
def _compare(l, a=0):
|
||||
for i in range(1, len(l)):
|
||||
if (
|
||||
sum(
|
||||
sum(a != b for a, b in zip(x, y)) for x, y in zip(l[i - 1 :: -1], l[i:])
|
||||
)
|
||||
== a
|
||||
):
|
||||
return i
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./input/13.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
inp = presolve(inp)
|
||||
|
||||
a = part_1(inp)
|
||||
b = part_2(inp)
|
||||
|
||||
assert a == 29213
|
||||
assert b == 37453
|
||||
83
2023-python/output/day_14.py
Normal file
83
2023-python/output/day_14.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
from output import answer
|
||||
|
||||
n = 14
|
||||
title = "Parabolic Reflector Dish"
|
||||
|
||||
|
||||
@answer(1, "Total initial load on the northern beams: {}")
|
||||
def part_1(presolved):
|
||||
return presolved[0]
|
||||
|
||||
|
||||
@answer(2, "After some humble load testing, the northern beam load is {}")
|
||||
def part_2(presolved):
|
||||
return presolved[1]
|
||||
|
||||
|
||||
BAEST = 1000_000_000
|
||||
|
||||
|
||||
def presolve(data):
|
||||
m = [list(l) for l in data.split()]
|
||||
s = len(m[0])
|
||||
m1 = _tilt(m)
|
||||
|
||||
p1 = sum(sum((s - w) * o.count("O") for o in r) for w, r in enumerate(m1))
|
||||
|
||||
def impl(rc):
|
||||
return "".join(["".join(r) for r in rc])
|
||||
|
||||
i = 0
|
||||
seen = []
|
||||
while True:
|
||||
i += 1
|
||||
for _ in range(4):
|
||||
m = _tilt(m)
|
||||
m = _rotate(m)
|
||||
im = impl(m)
|
||||
if im in seen:
|
||||
break
|
||||
else:
|
||||
seen.append(im)
|
||||
m2 = m
|
||||
c = seen.index(im) + 1
|
||||
for _ in range((BAEST - i) % (i - c)):
|
||||
for j in range(4):
|
||||
m2 = _tilt(m2)
|
||||
m2 = _rotate(m2)
|
||||
p2 = sum(sum((s - w) * o.count("O") for o in r) for w, r in enumerate(m2))
|
||||
return p1, p2
|
||||
|
||||
|
||||
def _rotate(m):
|
||||
return [list(l) for l in zip(*m[::-1])]
|
||||
|
||||
|
||||
def _tilt(m):
|
||||
m = [list(l) for l in zip(*m)]
|
||||
h = len(m[0])
|
||||
for c in m:
|
||||
u = True
|
||||
while u:
|
||||
u = False
|
||||
for i in range(h - 1):
|
||||
j = i + 1
|
||||
if c[i] == "#" or c[j] == "#":
|
||||
continue
|
||||
if c[i] < c[j]:
|
||||
c[j], c[i] = c[i], c[j]
|
||||
u = True
|
||||
return [list(l) for l in zip(*m)]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./input/14.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
inp = presolve(inp)
|
||||
|
||||
a = part_1(inp)
|
||||
b = part_2(inp)
|
||||
|
||||
assert a == 109596
|
||||
assert b == 96105
|
||||
63
2023-python/output/day_15.py
Normal file
63
2023-python/output/day_15.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
from collections import OrderedDict, defaultdict
|
||||
|
||||
from output import answer
|
||||
|
||||
n = 15
|
||||
title = "Lens Library"
|
||||
|
||||
|
||||
@answer(1, "Sum of HASH algorithm results: {}")
|
||||
def part_1(presolved):
|
||||
return presolved[0]
|
||||
|
||||
|
||||
@answer(2, "Focusing power of the resulting configuration: {}")
|
||||
def part_2(presolved):
|
||||
return presolved[1]
|
||||
|
||||
|
||||
def presolve(data):
|
||||
def h(s):
|
||||
v = 0
|
||||
for a in s:
|
||||
if a == "\n":
|
||||
continue
|
||||
v += ord(a)
|
||||
v *= 17
|
||||
v = v % 256
|
||||
return v
|
||||
|
||||
p1 = sum(h(c) for c in data.split(","))
|
||||
|
||||
b = defaultdict(OrderedDict)
|
||||
for lr in data.split(","):
|
||||
if "=" in lr:
|
||||
l, r = lr.split("=")
|
||||
if r == "":
|
||||
continue
|
||||
k = h(l)
|
||||
b[k][l] = r
|
||||
if "-" in lr:
|
||||
l, _r = lr.split("-")
|
||||
k = h(l)
|
||||
if l in b[k]:
|
||||
del b[k][l]
|
||||
p2 = 0
|
||||
for i, c in b.items():
|
||||
for j, f in enumerate(b[i].values(), 1):
|
||||
p2 += (i + 1) * j * int(f)
|
||||
|
||||
return p1, p2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./input/16.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
inp = presolve(inp)
|
||||
|
||||
a = part_1(inp)
|
||||
b = part_2(inp)
|
||||
|
||||
assert a == 509784
|
||||
assert b == 230197
|
||||
86
2023-python/output/day_16.py
Normal file
86
2023-python/output/day_16.py
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
from itertools import chain
|
||||
|
||||
from output import D, answer, matrix
|
||||
|
||||
n = 16
|
||||
title = "The Floor Will Be Lava"
|
||||
|
||||
|
||||
@answer(1, "Energized tiles count, starting at top-left facing right: {}")
|
||||
def part_1(presolved):
|
||||
return presolved[0]
|
||||
|
||||
|
||||
@answer(2, "Max energized tiles count, starting from all edges: {}")
|
||||
def part_2(presolved):
|
||||
return presolved[1]
|
||||
|
||||
|
||||
def presolve(data):
|
||||
m, w, h = matrix(data)
|
||||
p1 = 0
|
||||
p2 = 0
|
||||
for sp in chain(
|
||||
[(h - 1, n, 0) for n in range(w)],
|
||||
[(n, 0, 1) for n in range(h)],
|
||||
[(0, n, 2) for n in range(w)],
|
||||
[(n, w - 1, 3) for n in range(h)],
|
||||
):
|
||||
q = [sp]
|
||||
seen = set()
|
||||
while q:
|
||||
rcd = q.pop(0)
|
||||
if (rcd) in seen:
|
||||
continue
|
||||
r, c, d = rcd
|
||||
if r < 0 or r >= h or c < 0 or c >= w:
|
||||
continue
|
||||
seen.add((r, c, d))
|
||||
match m[r][c]:
|
||||
case ".":
|
||||
o1, o2 = D[d]
|
||||
q.append((o1 + r, o2 + c, d))
|
||||
case "|":
|
||||
if d in [0, 2]:
|
||||
o1, o2 = D[d]
|
||||
q.append((o1 + r, o2 + c, d))
|
||||
else:
|
||||
for d in [(d - 1) % 4, (d + 1) % 4]:
|
||||
o1, o2 = D[d]
|
||||
q.append((o1 + r, o2 + c, d))
|
||||
case "-":
|
||||
if d in [1, 3]:
|
||||
o1, o2 = D[d]
|
||||
q.append((o1 + r, o2 + c, d))
|
||||
else:
|
||||
for d in [(d - 1) % 4, (d + 1) % 4]:
|
||||
o1, o2 = D[d]
|
||||
q.append((o1 + r, o2 + c, d))
|
||||
case "\\":
|
||||
d += 1 if d in [1, 3] else -1
|
||||
d = d % 4
|
||||
o1, o2 = D[d]
|
||||
q.append((o1 + r, o2 + c, d))
|
||||
case "/":
|
||||
d += 1 if d in [0, 2] else -1
|
||||
d = d % 4
|
||||
o1, o2 = D[d % 4]
|
||||
q.append((o1 + r, o2 + c, d))
|
||||
b = len(set([(r, c) for r, c, d in seen]))
|
||||
if sp == (0, 0, 1):
|
||||
p1 = b
|
||||
p2 = max(p2, b)
|
||||
return p1, p2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./input/16.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
inp = presolve(inp)
|
||||
|
||||
a = part_1(inp)
|
||||
b = part_2(inp)
|
||||
|
||||
assert a == 7884
|
||||
assert b == 8185
|
||||
100
2023-python/output/day_17.py
Normal file
100
2023-python/output/day_17.py
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
from heapq import heappop, heappush
|
||||
|
||||
from output import answer # D, DD, ADJ, ints, mhd, mdbg, vdbg
|
||||
|
||||
n = 17
|
||||
title = "Clumsy Crucible"
|
||||
|
||||
|
||||
@answer(1, "Using std crucible, least heat loss incured: {}")
|
||||
def part_1(presolved):
|
||||
return presolved[0]
|
||||
|
||||
|
||||
@answer(2, "Using ultra crucible, least heat loss incured: {}")
|
||||
def part_2(presolved):
|
||||
return presolved[1]
|
||||
|
||||
|
||||
def solve(data):
|
||||
grid = {
|
||||
(r, c): int(col)
|
||||
for r, row in enumerate(data.split())
|
||||
for c, col in enumerate(row)
|
||||
}
|
||||
p1 = least_heat_loss(grid, 1, 3)
|
||||
p2 = least_heat_loss(grid, 4, 10)
|
||||
return p1, p2
|
||||
|
||||
|
||||
def least_heat_loss(grid, minsteps, maxsteps):
|
||||
target = max(grid)
|
||||
seen = set()
|
||||
queue = [(0, (0, 0), (0, 1), 0)]
|
||||
while queue:
|
||||
cost, pos, direction, steps = heappop(queue)
|
||||
y, x = pos
|
||||
dy, dx = direction
|
||||
|
||||
if pos == target:
|
||||
return cost
|
||||
|
||||
if ((pos, direction, steps)) in seen:
|
||||
continue
|
||||
|
||||
seen.add((pos, direction, steps))
|
||||
|
||||
if steps >= minsteps:
|
||||
cwdy, cwdx = clockwise(*direction)
|
||||
if (cw := (y + cwdy, x + cwdx)) in grid:
|
||||
cwy, cwx = cw
|
||||
heappush(queue, (cost + grid[cw], (cwy, cwx), (cwdy, cwdx), 1))
|
||||
|
||||
ccwdy, ccwdx = counterclockwise(*direction)
|
||||
if (ccw := (y + ccwdy, x + ccwdx)) in grid:
|
||||
ccwy, ccwx = ccw
|
||||
heappush(queue, (cost + grid[ccw], (ccwy, ccwx), (ccwdy, ccwdx), 1))
|
||||
|
||||
if steps < maxsteps and (fwd := (y + dy, x + dx)) in grid:
|
||||
fwdy, fwdx = fwd
|
||||
heappush(queue, (cost + grid[fwd], (fwdy, fwdx), direction, steps + 1))
|
||||
|
||||
return -1
|
||||
|
||||
|
||||
def clockwise(y, x):
|
||||
"""
|
||||
>>> clockwise(-1, 0)
|
||||
(0, 1)
|
||||
>>> clockwise(0, 1)
|
||||
(1, 0)
|
||||
>>> clockwise(1, 0)
|
||||
(0, -1)
|
||||
>>> clockwise(0, -1)
|
||||
(-1, 0)
|
||||
"""
|
||||
return (x, y) if y == 0 else (x, -y)
|
||||
|
||||
|
||||
def counterclockwise(y, x):
|
||||
"""
|
||||
>>> counterclockwise(-1, 0)
|
||||
(0, -1)
|
||||
>>> counterclockwise(0, -1)
|
||||
(1, 0)
|
||||
>>> counterclockwise(1, 0)
|
||||
(0, 1)
|
||||
>>> counterclockwise(0, 1)
|
||||
(-1, 0)
|
||||
"""
|
||||
return (x, y) if x == 0 else (-x, y)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./input/17.txt", "r") as f:
|
||||
inp = f.read().strip()
|
||||
|
||||
inp = solve(inp)
|
||||
|
||||
a = part_1(inp)
|
||||
b = part_2(inp)
|
||||
Loading…
Add table
Reference in a new issue