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
|
(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(f"./input/{padded_no}.txt", "r") as f:
|
# with open("./input/{padded_no}.txt", "r") as f:
|
||||||
# inp = f.read()
|
# inp = f.read().strip()
|
||||||
|
|
||||||
# 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,3 +80,59 @@ 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(f"./input/01.txt", "r") as f:
|
with open("./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(f"./input/02.txt", "r") as f:
|
with open("./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(f"./input/03.txt", "r") as f:
|
with open("./input/03.txt", "r") as f:
|
||||||
inp = f.read().strip()
|
inp = f.read().strip()
|
||||||
|
|
||||||
parsed = presolve(inp)
|
parsed = presolve(inp)
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ def presolve(data):
|
||||||
b = set(re.findall(r"\d+", b))
|
b = set(re.findall(r"\d+", b))
|
||||||
ab = len(a & b)
|
ab = len(a & b)
|
||||||
if ab > 0:
|
if ab > 0:
|
||||||
scores.append(2**(ab - 1))
|
scores.append(2 ** (ab - 1))
|
||||||
count[cid] += 1
|
count[cid] += 1
|
||||||
for i in range(cid + 1, cid + ab + 1):
|
for i in range(cid + 1, cid + ab + 1):
|
||||||
count[i] += count[cid]
|
count[i] += count[cid]
|
||||||
|
|
@ -35,7 +35,7 @@ def presolve(data):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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 = f.read().strip()
|
||||||
|
|
||||||
inp = presolve(inp)
|
inp = presolve(inp)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
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
|
||||||
|
|
@ -9,91 +7,67 @@ 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(data):
|
def part_1(presolved):
|
||||||
seeds, *process = data.split("\n\n")
|
l, _ = presolved
|
||||||
seeds = [f"{v} 1" for v in seeds.split()[1:]]
|
return l
|
||||||
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(data):
|
def part_2(presolved):
|
||||||
|
_, l = presolved
|
||||||
|
return l
|
||||||
|
|
||||||
|
|
||||||
|
def presolve(data):
|
||||||
seeds, *process = data.split("\n\n")
|
seeds, *process = data.split("\n\n")
|
||||||
seeds = re.findall(r"\d+ \d+", seeds)
|
seed_ranges = [[int(x) for x in ar.split()] for ar in re.findall(r"\d+ \d+", seeds)]
|
||||||
return _bruteforce(seeds, process, 8)
|
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):
|
def _process(seeds, processes, reverse=False):
|
||||||
processes = [[tuple(map(int, line.split())) for line in step.splitlines()[1:]] for step in process]
|
n = inf
|
||||||
|
for start in seeds:
|
||||||
sm = []
|
n = min(n, _nearest(start, processes, reverse=reverse))
|
||||||
for start_r in seeds:
|
return n
|
||||||
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_r, processes):
|
def _nearest(start, processes, reverse=False):
|
||||||
a, b = start_r
|
procs = processes if not reverse else processes[::-1]
|
||||||
nearest = inf
|
v = start
|
||||||
for i in range(a, b):
|
for steps in procs:
|
||||||
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
|
||||||
nearest = min(nearest, v)
|
return v
|
||||||
|
|
||||||
return nearest
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# use dummy data
|
with open("./input/05.txt", "r") as f:
|
||||||
inp = """
|
inp = f.read().strip()
|
||||||
seeds: 79 14 55 13
|
|
||||||
|
|
||||||
seed-to-soil map:
|
inp = presolve(inp)
|
||||||
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(f"./input/06.txt", "r") as f:
|
with open("./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(f"./input/07.txt", "r") as f:
|
with open("./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(f"./input/08.txt", "r") as f:
|
with open("./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(f"./input/09.txt", "r") as f:
|
with open("./input/09.txt", "r") as f:
|
||||||
inp = f.read().strip()
|
inp = f.read().strip()
|
||||||
|
|
||||||
a = part_1(inp)
|
a = part_1(inp)
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,94 @@
|
||||||
|
from collections import deque
|
||||||
from output import answer
|
from output import answer
|
||||||
|
|
||||||
n = 10
|
n = 10
|
||||||
title = "dddd"
|
title = "Pipe Maze"
|
||||||
|
|
||||||
|
|
||||||
@answer(1, "Answer is {}")
|
D = (-1, 0), (0, 1), (1, 0), (0, -1)
|
||||||
def part_1(data):
|
|
||||||
return data
|
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 {}")
|
@answer(1, "Farthest away pipe is at {}")
|
||||||
def part_2(data):
|
def part_1(presolved):
|
||||||
return data
|
return presolved[0]
|
||||||
|
|
||||||
|
|
||||||
# uncomment to solve parts in one go
|
@answer(2, "{} spots are encapsulated by pipes")
|
||||||
# def presolve(data):
|
def part_2(presolved):
|
||||||
# return data
|
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__":
|
if __name__ == "__main__":
|
||||||
# use dummy data
|
with open("./input/10.txt", "r") as f:
|
||||||
inp = """
|
inp = f.read()
|
||||||
replace me
|
|
||||||
""".strip()
|
|
||||||
|
|
||||||
# uncomment to instead use stdin
|
inp = presolve(inp)
|
||||||
# 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)
|
||||||
|
|
||||||
# uncomment and replace 0 with actual output to refactor code
|
assert a == 6846
|
||||||
# and ensure nonbreaking changes
|
assert b == 325
|
||||||
# assert a == 0
|
|
||||||
# assert b == 0
|
|
||||||
|
|
|
||||||
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