Revert "2023 solutions (#6)"

This reverts commit b6e048e4a7.
This commit is contained in:
Anders Englöf Ytterström 2025-01-05 00:09:28 +01:00
parent fcbeb4bfdd
commit bad86889d6
20 changed files with 0 additions and 1291 deletions

View file

@ -1,2 +0,0 @@
__pycache__
input

View file

@ -1,42 +0,0 @@
Advent of Code 2023
===================
Solutions for #aoc2023 in Python 3 (3.11.5).
Help scripts
------------
Display all solved puzzles:
python aoc.py
To bootstrap a new puzzle (creates `input/<day_no>.txt` and `output/day_<day_no>.py`):
python aoc.py <day_no> <puzzle_name>
Manually copy the puzzle input from https://adventofcode.com and paste it in `input/<day_no>.txt`
to start coding.
wl-paste > input/<day_no>.txt
Solve separate puzzle (replace `XX` with the puzzle number):
python -m output.day_XX
Solve separate puzzle using stdin (replace `XX` with the puzzle number):
wl-paste | python -m output.day_XX
cat tmpfile | python -m output.day_XX
Execute separate puzzle on file save (replace `XX` with the puzzle number):
ls output/*.py | entr -c -s 'xclip -selection clipboard -o | python -m output.day_XX'
ls output/*.py | entr -c -s 'cat tmpfile | python -m output.day_XX'
ls output/*.py | entr -c -r python -m output.day_XX
(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

View file

@ -1,126 +0,0 @@
import os
import sys
def headline(n, title):
"""Print day number and name, followed by a ruler. Used by the answer decorator"""
print(f"\n--- Day {n}: {title} ---\n")
year = 2023
try:
_, day_no, *name = sys.argv
except ValueError:
day_no = None
name = None
print(
f"\nAdvent of Code {year}" "\n###################" "\n\nby Anders Englöf Ytterström"
)
if day_no and name:
name = " ".join(name)
padded_no = day_no.zfill(2)
print(f"\n- creating output/day_{padded_no}.py")
with open("output/day_{}.py".format(padded_no), "w") as s:
s.write(
f"""
from output import answer # , matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg
n = {day_no}
title = "{name}"
@answer(1, "Answer is {{}}")
def part_1(presolved):
return presolved[0]
@answer(2, "Actually, answer is {{}}")
def part_2(presolved):
return presolved[1]
def solve(data):
return 1, 2
if __name__ == "__main__":
# use dummy data
inp = \"\"\"
replace me
\"\"\".strip()
# uncomment to instead use stdin
# import sys; inp = sys.stdin.read().strip()
# uncomment to use AoC provided puzzle input
# 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)
a = part_1(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
""".strip()
+ "\n"
)
print("- making sure input dir exists")
if not os.path.exists("input"):
os.makedirs("input")
print(
f"""
Done! start coding.
Puzzle link:
https://adventofcode.com/{year}/day/{day_no}
Puzzle input (copy and paste to input/{day_no.zfill(2)}.txt):
https://adventofcode.com/{year}/day/{day_no}/input
"""
)
exit(0)
stars = 0
for i in [str(n).zfill(2) for n in range(1, 26)]:
if not day_no or day_no.zfill(2) == i:
try:
day = __import__(
"output.day_{}".format(i),
globals(),
locals(),
["n", "title", "part_1", "part_2"],
0,
)
with open(f"./input/{i}.txt", "r") as f:
data = f.read().strip()
headline(day.n, day.title)
try:
data = day.presolve(data)
except AttributeError:
pass
try:
data = day.solve(data)
except AttributeError:
pass
if day.part_1(data, decorate=True):
stars += 1
if day.part_2(data, decorate=True):
stars += 1
except IOError:
pass
except ImportError:
pass
if not day_no:
print(f"\nStars: {stars}")
print("".join("*" if n < stars else "" for n in range(50)))
print("")

View file

@ -1,82 +0,0 @@
import functools
import re
# Directions/Adjacents for 2D matrices, in the order UP, RIGHT, DOWN, LEFT
D = [
(-1, 0),
(0, 1),
(1, 0),
(0, -1),
]
# Directions for 2D matrices, as a dict with keys U, R, D, L
DD = {
"U": (-1, 0),
"R": (0, 1),
"D": (1, 0),
"L": (0, -1),
}
# Adjacent relative positions including diagonals for 2D matrices, in the order NW, N, NE, W, E, SW, S, SE
ADJ = [
(-1, -1),
(-1, 0),
(1, -1),
(0, -1),
(0, 1),
(1, 1),
(1, 0),
(1, -1),
]
def answer(part_index, fmt_string):
"""Decorator to present a solution in a human readable format"""
def decorator_aoc(func):
@functools.wraps(func)
def wrapper_aoc(*args, **kwargs):
decorate = kwargs.get("decorate", False)
if decorate:
del kwargs["decorate"]
answer = func(*args, **kwargs)
if not decorate:
print(answer)
else:
formatted = fmt_string.format(answer)
print(f" {part_index}) {formatted}")
return answer
return wrapper_aoc
return decorator_aoc
def ints(s):
"""Extract all integers from a string"""
return [int(n) for n in re.findall(r"\d+", s)]
def mhd(a, b):
"""Calculates the Manhattan distance between 2 positions in the format (y, x) or (x, y)"""
ar, ac = a
br, bc = b
return abs(ar - br) + abs(ac - bc)
def matrix(d):
"""Transform a string into an iterable matrix. Returns the matrix, row count and col count"""
m = [tuple(r) for r in d.split()]
return m, len(m), len(m[0])
def mdbg(m):
"""Print-debug a matrix"""
for r in m:
print("".join(r))
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)]))

View file

@ -1,51 +0,0 @@
import re
from output import answer
n = 1
title = "Trebuchet?!"
@answer(1, "Calibration values sum: {}, excluding spelled out digits")
def part_1(data):
def value(s):
s = [int(c) for c in s if c.isdigit()]
return s[0] * 10 + s[-1]
return sum(value(line) for line in data.split())
@answer(2, "Calibration values sum: {}, including spelled out digits")
def part_2(data):
mp = {
"one": 1,
"two": 2,
"three": 3,
"four": 4,
"five": 5,
"six": 6,
"seven": 7,
"eight": 8,
"nine": 9,
}
def value(l):
s = [
int(c) if c.isdigit() else mp[c]
for c in re.findall(
r"(?=(\d|one|two|three|four|five|six|seven|eight|nine))", l
)
]
return s[0] * 10 + s[-1]
return sum(value(line) for line in data.split())
if __name__ == "__main__":
with open("./input/01.txt", "r") as f:
inp = f.read().strip()
a = part_1(inp)
b = part_2(inp)
assert a == 54634
assert b == 53855

View file

@ -1,49 +0,0 @@
import re
from math import prod
from collections import defaultdict
from output import answer
n = 2
title = "Cube Conundrum"
@answer(1, "Sum of all possible game IDs: {}")
def part_1(data):
return sum(
[
id + 1
for id, game in enumerate(data.splitlines())
if not sum(
any(
[
c == "red" and int(n) > 12,
c == "green" and int(n) > 13,
c == "blue" and int(n) > 14,
]
)
for n, c in re.findall(r"(\d+) (\w+)", game)
)
]
)
@answer(2, "Sum of all cube set powers: {}")
def part_2(data):
def power(game):
seen = defaultdict(int)
for n, c in re.findall(r"(\d+) (\w+)", game):
seen[c] = max([seen[c], int(n)])
return prod([seen["blue"], seen["red"], seen["green"]])
return sum(power(game) for game in data.splitlines())
if __name__ == "__main__":
with open("./input/02.txt", "r") as f:
inp = f.read().strip()
a = part_1(inp)
b = part_2(inp)
assert a == 2439
assert b == 63711

View file

@ -1,68 +0,0 @@
from collections import deque
from output import answer
n = 3
title = "Gear Ratios"
@answer(1, "Sum of all part numbers is {} in the engine schematic")
def part_1(presolved):
s, _ = presolved
return s
@answer(2, "Gear ratio sums is {} in the engine schematic")
def part_2(presolved):
_, gr = presolved
return gr
def presolve(data):
data = data.split()
adj = (-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)
w = len(data[0])
h = len(data)
s = list()
gr = list()
for y in range(w):
for x in range(h):
if data[y][x] != "." and not data[y][x].isdigit():
seen = set()
t = list()
for oy, ox in adj:
if (y + oy, x + ox) in seen:
continue
if data[y + oy][x + ox].isdigit():
n = deque([data[y + oy][x + ox]])
i = x + ox - 1
while i in range(w) and data[y + oy][i].isdigit():
n.append(data[y + oy][i])
seen.add((y + oy, i))
i -= 1
i = x + ox + 1
while i in range(w) and data[y + oy][i].isdigit():
n.appendleft(data[y + oy][i])
seen.add((y + oy, i))
i += 1
t.append(sum(10**m * int(d) for m, d in enumerate(n)))
# part 1
s.append(sum(t))
# part 2
if data[y][x] == "*" and len(t) == 2:
a, b = t
gr.append(a * b)
return sum(s), sum(gr)
if __name__ == "__main__":
with open("./input/03.txt", "r") as f:
inp = f.read().strip()
parsed = presolve(inp)
a = part_1(parsed)
b = part_2(parsed)
assert a == 553825
assert b == 93994191

View file

@ -1,47 +0,0 @@
import re
from collections import defaultdict
from output import answer
n = 4
title = "Scratchcards"
@answer(1, "Sum of all scratchcard points are {}")
def part_1(presolved):
scores, _ = presolved
return scores
@answer(2, "Ends up wih a total of {} scratchcards")
def part_2(presolved):
_, count = presolved
return count
def presolve(data):
scores = []
count = defaultdict(int)
for cid, line in enumerate(data.splitlines()):
a, b = line.split("|")
a = set(re.findall(r"\d+", a)[1:])
b = set(re.findall(r"\d+", b))
ab = len(a & b)
if ab > 0:
scores.append(2 ** (ab - 1))
count[cid] += 1
for i in range(cid + 1, cid + ab + 1):
count[i] += count[cid]
return sum(scores), sum(count.values())
if __name__ == "__main__":
with open("./input/04.txt", "r") as f:
inp = f.read().strip()
inp = presolve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 21919
assert b == 9881048

View file

@ -1,73 +0,0 @@
import re
from math import inf
from output import answer
n = 5
title = "If You Give A Seed A Fertilizer"
@answer(1, "Nearest location for seed id list is {}")
def part_1(presolved):
l, _ = presolved
return l
@answer(2, "Interpreting ranges of seeds, nearest location is {}")
def part_2(presolved):
_, l = presolved
return l
def presolve(data):
seeds, *process = data.split("\n\n")
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 _process(seeds, processes, reverse=False):
n = inf
for start in seeds:
n = min(n, _nearest(start, processes, reverse=reverse))
return n
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__":
with open("./input/05.txt", "r") as f:
inp = f.read().strip()
inp = presolve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 278755257
assert b == 26829166

View file

@ -1,47 +0,0 @@
from math import prod, sqrt, ceil, floor
from output import answer
n = 6
title = "Wait For It"
@answer(1, "The product of all record times for all races is {}")
def part_1(presolved):
return presolved[0]
@answer(2, "The product of all record times for the single long race is {}")
def part_2(presolved):
return presolved[1]
def presolve(data):
values = data.split()
l = len(values) // 2
races = list(
map(
lambda x: (int(x[0]), int(x[1])), list(zip(values[: l + 1], values[l:]))[1:]
)
)
p1 = prod(sum(bpt * (t - bpt) > d for bpt in range(t)) for t, d in races)
t = int("".join(values[1:l]))
d = int("".join(values[l + 1 :]))
# quadratic formula:
# https://en.wikipedia.org/wiki/Quadratic_formula
l = ceil((-t + sqrt(t**2 - 4 * d)) / -2)
h = floor((-t - sqrt(t**2 - 4 * d)) / -2)
p2 = h - l + 1
return p1, p2
if __name__ == "__main__":
with open("./input/06.txt", "r") as f:
inp = f.read().strip()
inp = presolve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 1083852
assert b == 23501589

View file

@ -1,94 +0,0 @@
from collections import Counter
from output import answer
n = 7
title = "Camel Cards"
@answer(1, "Total winnings are {}")
def part_1(data):
return _solve(data)
@answer(2, "Including jokers, total winnings are {}")
def part_2(data):
return _solve(data.replace("J", "0"))
def _solve(data):
return sum(
c[-1] * i for i, c in enumerate(sorted(_hand(h) for h in data.splitlines()), 1)
)
def _hand(hb):
h, b = hb.split()
b = int(b)
h = h.translate(M)
return (_rank(h), h, b)
def _rank(h):
"""
Rank hand 0-6, letting jokers (0) aid the highest possible hand
>>> _rank("10110")
6
>>> _rank("11110")
6
>>> _rank("12110")
5
>>> _rank("12100")
5
>>> _rank("12000")
5
>>> _rank("12120")
4
>>> _rank("12300")
3
>>> _rank("12310")
3
>>> _rank("12340")
1
"""
hc = Counter(h)
hcv = hc.values()
j = hc["0"]
del hc["0"]
p = sum(c == 2 for v, c in hc.items() if v != 0)
if j == 5 or max(hcv) + j == 5:
return 6
if max(hcv) + j == 4:
return 5
if (3 in hcv and 2 in hcv) or (p == 2 and j > 0):
return 4
if max(hcv) + j == 3:
return 3
if p + j > 1:
return 2
if p + j > 0:
return 1
return 0
M = dict(
[
(ord(o), ord(n))
for o, n in {"T": "A", "J": "B", "Q": "C", "K": "D", "A": "E"}.items()
]
)
if __name__ == "__main__":
import doctest
doctest.testmod()
with open("./input/07.txt", "r") as f:
inp = f.read().strip()
a = part_1(inp)
b = part_2(inp)
assert a == 249390788
assert b == 248750248

View file

@ -1,61 +0,0 @@
import re
from math import lcm
from output import answer
n = 8
title = "Haunted Wasteland"
@answer(1, "One can reach Z in {} steps")
def part_1(presolved):
steps, _ = presolved
return steps
@answer(2, "Ghost path takes {} steps before all ghosts are at a Z positon")
def part_2(presolved):
_, ghost_meet_point = presolved
return ghost_meet_point
def presolve(data):
d, els = data.split("\n\n")
r = len(d)
e = dict()
for el in els.splitlines():
k, *lr = re.match(r"(\w+) = \((\w+), (\w+)\)", el).groups()
e[k] = lr
p1 = 0
pos = "AAA"
while pos != "ZZZ":
i = 0 if d[p1 % r] == "L" else 1
pos = e[pos][i]
p1 += 1
p2 = 0
z = list()
for spos in [p for p in e.keys() if p.endswith("A")]:
s = 0
pos = spos
while not pos.endswith("Z"):
i = 0 if d[s % r] == "L" else 1
pos = e[pos][i]
s += 1
z.append(s)
p2 = lcm(*z)
return p1, p2
if __name__ == "__main__":
with open("./input/08.txt", "r") as f:
inp = f.read().strip()
inp = presolve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 17141
assert b == 10818234074807

View file

@ -1,40 +0,0 @@
from output import answer
n = 9
title = "Mirage Maintenance"
@answer(1, "OASIS report extrapolated values sum is {}")
def part_1(data):
lines = [[int(d) for d in line.split()] for line in data.splitlines()]
return _solve(lines)
@answer(2, "Using prepending, OASIS report extrapolated values sum is {}")
def part_2(data):
lines = [[int(d) for d in line.split()[::-1]] for line in data.splitlines()]
return _solve(lines)
def _solve(lines):
vs = 0
for l in lines:
h = [l]
while any(n != 0 for n in h[-1]):
h.append([b - a for a, b in zip(h[-1], h[-1][1:])])
h = h[::-1]
for i in range(len(h)):
h[i].append(0 if i == 0 else h[i - 1][-1] + h[i][-1])
vs += h[-1][-1]
return vs
if __name__ == "__main__":
with open("./input/09.txt", "r") as f:
inp = f.read().strip()
a = part_1(inp)
b = part_2(inp)
assert a == 1702218515
assert b == 925

View file

@ -1,94 +0,0 @@
from collections import deque
from output import answer
n = 10
title = "Pipe Maze"
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(1, "Farthest away pipe is at {}")
def part_1(presolved):
return presolved[0]
@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__":
with open("./input/10.txt", "r") as f:
inp = f.read()
inp = presolve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 6846
assert b == 325

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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