Compare commits
No commits in common. "178d96494a26cc16952734eb7e1f4b25059b119a" and "08288964bdcbd47d11343eb0bac69d3f640f8c3b" have entirely different histories.
178d96494a
...
08288964bd
20 changed files with 113 additions and 712 deletions
|
|
@ -36,7 +36,3 @@ 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
|
(requires `entr` and `wl-paste`, Mac users can instead use `pbpaste`. If you
|
||||||
prefer X at Linux, use `xclip -selection clipboard -o`).
|
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()
|
# import sys; inp = sys.stdin.read().strip()
|
||||||
|
|
||||||
# uncomment to use AoC provided puzzle input
|
# uncomment to use AoC provided puzzle input
|
||||||
# with open("./input/{padded_no}.txt", "r") as f:
|
# with open(f"./input/{padded_no}.txt", "r") as f:
|
||||||
# inp = f.read().strip()
|
# inp = f.read()
|
||||||
|
|
||||||
# uncomment to do initial data processing shared by part 1-2
|
# uncomment to do initial data processing shared by part 1-2
|
||||||
inp = solve(inp)
|
inp = solve(inp)
|
||||||
|
|
|
||||||
|
|
@ -80,59 +80,3 @@ def vdbg(seen, h, w):
|
||||||
"""Print-debug visited positions of a matrix"""
|
"""Print-debug visited positions of a matrix"""
|
||||||
for r in range(h):
|
for r in range(h):
|
||||||
print("".join(["#" if (r, c) in seen else "." for c in range(w)]))
|
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__":
|
if __name__ == "__main__":
|
||||||
with open("./input/01.txt", "r") as f:
|
with open(f"./input/01.txt", "r") as f:
|
||||||
inp = f.read().strip()
|
inp = f.read().strip()
|
||||||
|
|
||||||
a = part_1(inp)
|
a = part_1(inp)
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ def part_2(data):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
with open("./input/02.txt", "r") as f:
|
with open(f"./input/02.txt", "r") as f:
|
||||||
inp = f.read().strip()
|
inp = f.read().strip()
|
||||||
|
|
||||||
a = part_1(inp)
|
a = part_1(inp)
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ def presolve(data):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
with open("./input/03.txt", "r") as f:
|
with open(f"./input/03.txt", "r") as f:
|
||||||
inp = f.read().strip()
|
inp = f.read().strip()
|
||||||
|
|
||||||
parsed = presolve(inp)
|
parsed = presolve(inp)
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ def presolve(data):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
with open("./input/04.txt", "r") as f:
|
with open(f"./input/04.txt", "r") as f:
|
||||||
inp = f.read().strip()
|
inp = f.read().strip()
|
||||||
|
|
||||||
inp = presolve(inp)
|
inp = presolve(inp)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import re
|
import re
|
||||||
|
from itertools import repeat
|
||||||
from math import inf
|
from math import inf
|
||||||
|
from multiprocessing import Pool, freeze_support
|
||||||
from output import answer
|
from output import answer
|
||||||
|
|
||||||
n = 5
|
n = 5
|
||||||
|
|
@ -7,67 +9,91 @@ title = "If You Give A Seed A Fertilizer"
|
||||||
|
|
||||||
|
|
||||||
@answer(1, "Nearest location for seed id list is {}")
|
@answer(1, "Nearest location for seed id list is {}")
|
||||||
def part_1(presolved):
|
def part_1(data):
|
||||||
l, _ = presolved
|
seeds, *process = data.split("\n\n")
|
||||||
return l
|
seeds = [f"{v} 1" for v in seeds.split()[1:]]
|
||||||
|
return _bruteforce(seeds, process, 1)
|
||||||
|
|
||||||
|
|
||||||
@answer(2, "Interpreting ranges of seeds, nearest location is {}")
|
@answer(2, "Interpreting ranges of seeds, nearest location is {}")
|
||||||
def part_2(presolved):
|
def part_2(data):
|
||||||
_, l = presolved
|
|
||||||
return l
|
|
||||||
|
|
||||||
|
|
||||||
def presolve(data):
|
|
||||||
seeds, *process = data.split("\n\n")
|
seeds, *process = data.split("\n\n")
|
||||||
seed_ranges = [[int(x) for x in ar.split()] for ar in re.findall(r"\d+ \d+", seeds)]
|
seeds = re.findall(r"\d+ \d+", seeds)
|
||||||
seed_values = [int(v) for v in seeds.split()[1:]]
|
return _bruteforce(seeds, process, 8)
|
||||||
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 _process(seeds, processes, reverse=False):
|
def _bruteforce(seeds, process, p=1):
|
||||||
n = inf
|
processes = [[tuple(map(int, line.split())) for line in step.splitlines()[1:]] for step in process]
|
||||||
for start in seeds:
|
|
||||||
n = min(n, _nearest(start, processes, reverse=reverse))
|
sm = []
|
||||||
return n
|
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 _nearest(start, processes, reverse=False):
|
def _nearest(start_r, processes):
|
||||||
procs = processes if not reverse else processes[::-1]
|
a, b = start_r
|
||||||
v = start
|
nearest = inf
|
||||||
for steps in procs:
|
for i in range(a, b):
|
||||||
|
v = i
|
||||||
|
for steps in processes:
|
||||||
|
nid = -1
|
||||||
for line in steps:
|
for line in steps:
|
||||||
dest, src, r = line
|
dest, src, r = line
|
||||||
if reverse:
|
|
||||||
dest, src = src, dest
|
|
||||||
if v >= src and v < src + r:
|
if v >= src and v < src + r:
|
||||||
v = dest + v - src
|
v = dest + v - src
|
||||||
break
|
break
|
||||||
return v
|
nearest = min(nearest, v)
|
||||||
|
|
||||||
|
return nearest
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
with open("./input/05.txt", "r") as f:
|
# use dummy data
|
||||||
inp = f.read().strip()
|
inp = """
|
||||||
|
seeds: 79 14 55 13
|
||||||
|
|
||||||
inp = presolve(inp)
|
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()
|
||||||
|
|
||||||
a = part_1(inp)
|
a = part_1(inp)
|
||||||
b = part_2(inp)
|
b = part_2(inp)
|
||||||
|
|
||||||
assert a == 278755257
|
# assert a == 278755257
|
||||||
assert b == 26829166
|
# assert b == 26829166
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ def presolve(data):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
with open("./input/06.txt", "r") as f:
|
with open(f"./input/06.txt", "r") as f:
|
||||||
inp = f.read().strip()
|
inp = f.read().strip()
|
||||||
|
|
||||||
inp = presolve(inp)
|
inp = presolve(inp)
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
doctest.testmod()
|
doctest.testmod()
|
||||||
|
|
||||||
with open("./input/07.txt", "r") as f:
|
with open(f"./input/07.txt", "r") as f:
|
||||||
inp = f.read().strip()
|
inp = f.read().strip()
|
||||||
|
|
||||||
a = part_1(inp)
|
a = part_1(inp)
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ def presolve(data):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
with open("./input/08.txt", "r") as f:
|
with open(f"./input/08.txt", "r") as f:
|
||||||
inp = f.read().strip()
|
inp = f.read().strip()
|
||||||
|
|
||||||
inp = presolve(inp)
|
inp = presolve(inp)
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ def _solve(lines):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
with open("./input/09.txt", "r") as f:
|
with open(f"./input/09.txt", "r") as f:
|
||||||
inp = f.read().strip()
|
inp = f.read().strip()
|
||||||
|
|
||||||
a = part_1(inp)
|
a = part_1(inp)
|
||||||
|
|
|
||||||
|
|
@ -1,94 +1,44 @@
|
||||||
from collections import deque
|
|
||||||
from output import answer
|
from output import answer
|
||||||
|
|
||||||
n = 10
|
n = 10
|
||||||
title = "Pipe Maze"
|
title = "dddd"
|
||||||
|
|
||||||
|
|
||||||
D = (-1, 0), (0, 1), (1, 0), (0, -1)
|
@answer(1, "Answer is {}")
|
||||||
|
def part_1(data):
|
||||||
C = {
|
return data
|
||||||
(-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(1, "Farthest away pipe is at {}")
|
@answer(2, "Actually, answer is {}")
|
||||||
def part_1(presolved):
|
def part_2(data):
|
||||||
return presolved[0]
|
return data
|
||||||
|
|
||||||
|
|
||||||
@answer(2, "{} spots are encapsulated by pipes")
|
# uncomment to solve parts in one go
|
||||||
def part_2(presolved):
|
# def presolve(data):
|
||||||
return presolved[1]
|
# return data
|
||||||
|
|
||||||
|
|
||||||
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__":
|
if __name__ == "__main__":
|
||||||
with open("./input/10.txt", "r") as f:
|
# use dummy data
|
||||||
inp = f.read()
|
inp = """
|
||||||
|
replace me
|
||||||
|
""".strip()
|
||||||
|
|
||||||
inp = presolve(inp)
|
# 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)
|
||||||
|
|
||||||
a = part_1(inp)
|
a = part_1(inp)
|
||||||
b = part_2(inp)
|
# b = part_2(inp)
|
||||||
|
|
||||||
assert a == 6846
|
# uncomment and replace 0 with actual output to refactor code
|
||||||
assert b == 325
|
# and ensure nonbreaking changes
|
||||||
|
# assert a == 0
|
||||||
|
# assert b == 0
|
||||||
|
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
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