Compare commits

..

19 commits

Author SHA1 Message Date
Anders Englöf Ytterström
86121d7799 Partly solve 2019:18 2024-10-30 13:37:41 +01:00
b9d32629c8 Solve 2019:13 pt1 2024-01-21 13:48:34 +01:00
Anders Englöf Ytterström
107ca5ab7c Solve 2019:16 "Flawed Frequency Transmission"
Solution takes several hours to run, commented out and cached.
2023-12-29 11:54:21 +01:00
Anders Englöf Ytterström
40274ea044 Solve 2019:14 "Space Stoichiometry"
Got stuck completely at part 1. Last 2 examples
did not add up, off by 100 ore.

I did know that I had to count by overflows somehow,
but I could not manage it. After countless hours
writing a verbose debugger, I wiped the whole thing
and looked at this code for inspiration:

https://github.com/sophiebits/adventofcode/blob/master/2019/day14.py

It works by using modulus and removing all keys
in a dict except ORE.
2023-12-29 00:36:55 +01:00
Anders Englöf Ytterström
d01b4aa0f8 Solve 2019:12 "The N-Body Problem"
Also, fix bug in ints() helper.
2023-12-27 13:39:10 +01:00
051c3f3ebd Lint and Refactor day 1-11 2023-12-19 18:34:03 +01:00
5cb2dccf62 Deprecate nouns and verbs arguments for intcode 2023-12-19 14:51:11 +01:00
f98545ae5a Solve 2019:11 "Space Police"
Bugs discovered in intcode:

- Relative base was reset on await input suspend.
- No handling for incorrect intcodes.
- State was sometimes corrupted when resumed after
  suspend.

Fixed and Patched in 0.3.2
2023-12-19 14:51:11 +01:00
262ad34c51 Solve 2019:10 "Monitoring Station". 2023-12-19 14:51:11 +01:00
d9142068ea Solve 2019:09 "Sensor Boost"
This was a hard one. I would not have made it
without r/adventofcode where all assertions
in __main__ were fetched. For values it was
straight forward enough, but for some reason
it did not work for positioning.

I still do not understand how one is supposed to
chew the WALL OF TEXT each day of 2019 and figure
out these stuffs on your own.

It took 10-12 hours to solve part 1. AoC day 9
is no joke.

Links that helped me:

- https://www.reddit.com/r/adventofcode/comments/e8aw9j/2019_day_9_part_1_how_to_fix_203_error/
- https://www.reddit.com/r/adventofcode/comments/jr9xrn/comment/gbwr85k/
2023-12-19 14:51:11 +01:00
c51638fac1 Solve 2019:07 "Amplication Circuit"
Breaking change for intcode 0.3.0, revisited
earlier use of the computer to update code.
2023-12-19 14:51:11 +01:00
4623fdac95 Solve 2019:08 "Space Image Format" 2023-12-19 14:51:11 +01:00
5be719dcf9 Solve 2019:06 "Universal Orbit Map"
Tried to solve part 2 using BFS, but did not
figure out a way to find shortest path. Fell
back to a less elegant but more simple
approach.
2023-12-19 14:51:11 +01:00
9eccd731b4 Solve 2019:05 "Sunny with a Chance of Asteroids"
Refactored the intcode computer to live in a separate
file, since it will come back more times.

As a nice touch, output of aoc.py will print the version
used of the intcode.

As another nice touch, the int code has a CHANGELOG.
2023-12-19 14:51:11 +01:00
298b97b66d Solve 2019:04 "Secure Container" 2023-12-19 14:51:11 +01:00
799216623e Solve 2019:03 "Crossed Wires" 2023-12-19 14:51:11 +01:00
e1d4741cfc Solve 2019:02 "1202 Program Alarm" 2023-12-19 14:51:11 +01:00
85a67d26fa Solve 2019:01 "The Tyranny of the Rocket Equation" 2023-12-19 14:51:11 +01:00
133c76a02a Initiate AoC 2019 in python 2023-12-19 14:51:11 +01:00
127 changed files with 1692 additions and 3796 deletions

View file

@ -46,9 +46,7 @@ if __name__ == "__main__":
solution.solve(dummy) solution.solve(dummy)
# solution.solve_again(dummy) # solution.solve_again(dummy)
""".strip().format( """.strip().format(day=day_no, day_no=day_no.zfill(2), name=name)
day=day_no, day_no=day_no.zfill(2), name=name
)
+ "\n" + "\n"
) )
@ -65,18 +63,13 @@ https://adventofcode.com/{year}/day/{day_no}/input
) )
exit(0) exit(0)
stars = 0
for i in [str(n).zfill(2) for n in range(1, 26)]: for i in [str(n).zfill(2) for n in range(1, 26)]:
try: try:
solution = __import__( solution = __import__(
"solutions.day_{}".format(i), globals(), locals(), ["Solution"], 0 "solutions.day_{}".format(i), globals(), locals(), ["Solution"], 0
).Solution() ).Solution()
solution.show_results() solution.show_results()
stars += 2
except IOError: except IOError:
pass pass
except ImportError: except ImportError:
pass pass
print(f"\nStars: {stars}")
print("".join("*" if n < stars else "" for n in range(50)))
print("")

View file

@ -14,8 +14,9 @@ class BaseSolution:
data = self.read_input(self.input_file) data = self.read_input(self.input_file)
puzzle_input = self.parse_input(data) puzzle_input = self.parse_input(data)
print( print(
"\n--- {} ---\n 1. {}\n 2. {}".format( "\n\n{}\n{}\n\nPart 1: {}\nPart 2: {}".format(
str(self), str(self),
"-" * len(str(self)),
self.solve(puzzle_input), self.solve(puzzle_input),
self.solve_again(puzzle_input), self.solve_again(puzzle_input),
) )

View file

@ -1,26 +0,0 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = "01.txt"
def __str__(self):
return "Day 1: Not Quite Lisp"
def solve(self, pi):
return sum([-1 if c == ")" else 1 for c in pi])
def solve_again(self, pi):
f = 1
for i, v in enumerate(pi):
f += -1 if v == ")" else 1
if f == -1:
return i
def parse_input(self, data):
return data.strip()
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -1,33 +0,0 @@
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = "02.txt"
def __str__(self):
return "Day 2: I Was Told There Would Be No Math"
def solve(self, pi):
o = self._solve(pi)
return o[0]
def solve_again(self, pi):
o = self._solve(pi)
return o[1]
def _solve(self, pi):
p1 = 0
p2 = 0
for p in pi.splitlines():
l, w, h = sorted([int(s) for s in p.split("x")])
p1 += 2 * (l * w + w * h + h * l) + min([l * w, w * h, h * l])
p2 += 2 * l + 2 * w + l * w * h
return p1, p2
def parse_input(self, data):
return data.strip()
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -1,47 +0,0 @@
from collections import defaultdict
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = "03.txt"
def __str__(self):
return "Day 3: Perfectly Spherical Houses in a Vacuum"
def solve(self, pi):
return self._solve(pi)[0]
def solve_again(self, pi):
return self._solve(pi)[1]
def _solve(self, pi):
def f(q):
p = (0, 0)
vs = defaultdict(int)
vs[p] += 1
for d in q:
r, c = p
match d:
case "^":
p = (r - 1, c)
case ">":
p = (r, c + 1)
case "v":
p = (r + 1, c)
case "<":
p = (r, c - 1)
vs[p] += 1
return set(vs.keys())
p1 = len(f(pi))
p2 = len(f(pi[0::2]) | f(pi[1::2]))
return p1, p2
def parse_input(self, data):
return data.strip()
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -1,34 +0,0 @@
from hashlib import md5
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = "04.txt"
def __str__(self):
return "Day 4: The Ideal Stocking Stuffer"
def solve(self, pi):
return self._solve(pi)[0]
def solve_again(self, pi):
return self._solve(pi)[1]
def _solve(self, secret):
p12 = []
prefetched = [254575, 1038736]
for zc in [5, 6]:
sw = str.zfill("0", zc)
c = prefetched.pop(0)
if md5(bytes(f"{secret}{c}", "utf-8")).hexdigest().startswith(sw):
p12.append(c)
return p12
def parse_input(self, data):
return data.strip()
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -1,39 +0,0 @@
import re
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = "05.txt"
def __str__(self):
return "Day 5: Doesn't He Have Intern-Elves For This?"
def solve(self, pi):
return self._solve(pi)[0]
def solve_again(self, pi):
return self._solve(pi)[1]
def parse_input(self, data):
return data.strip()
def _solve(self, pi):
wl = pi.split()
p1 = sum(
not re.search(r"ab|cd|pq|xy.*", w)
and any(w[i] == w[i + 1] for i in range(len(w) - 1))
and len(re.findall(r"[aeiou]", w)) > 2
for w in wl
)
p2 = sum(
any(w.count(w[i : i + 2]) == 2 for i in range(len(w) - 1))
and any(w[i] == w[i + 2] for i in range(len(w) - 2))
for w in wl
)
return p1, p2
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -1,66 +0,0 @@
from collections import defaultdict
from solutions import BaseSolution
class Solution(BaseSolution):
input_file = "07.txt"
def __str__(self):
return "Day 7: Some Assembly Required"
def solve(self, pi):
return self._solve(pi)
def solve_again(self, pi):
a = self.solve(pi)
return self._solve(pi.replace("19138", str(a)))
def parse_input(self, data):
return data.strip()
def _solve(self, pi):
p = pi.splitlines()
w = defaultdict(int)
while p:
np = []
for l in p:
x, to = l.split(" -> ")
if x.isdigit():
w[to] += int(x)
elif len(x.split()) == 1:
if x not in w:
np.append(l)
else:
w[to] += w[x]
elif x.startswith("NOT "):
a = x.split()[-1]
if a.isdigit() or a in w:
a = int(a) if a.isdigit() else w[a]
w[to] += ~a
else:
np.append(l)
else:
a, v, b = x.split()
if (a.isdigit() or a in w) and (b.isdigit() or b in w):
a = int(a) if a.isdigit() else w[a]
b = int(b) if b.isdigit() else w[b]
match v:
case "RSHIFT":
w[to] += a >> b
case "LSHIFT":
w[to] += a << b
case "AND":
w[to] += a & b
case "OR":
w[to] += a | b
else:
np.append(l)
p = np
return w["a"]
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -1,50 +0,0 @@
# Advent of Code 2016
Solutions for #aoc2016 in Python 3 (3.12.7).
## Setup
Since I want to remember, this is what was used to solve
the puzzles.
- Lenovo Thinkpad x260 laptop with Arch Linux.
- Hyprland with gBar.
- Editor: Zed.
- Terminal: Alacritty.
## 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 'wlpaste | 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,136 +0,0 @@
import re
from collections import Counter, deque
from itertools import combinations
D = {1: [2], 2: [1, 3], 3: [2, 4], 4: [3]}
def solve(data):
def parse(row):
return sorted(
[
"".join(nt).upper()
for nt in re.findall(r"(\w)\S+(?:-compatible)? (g|m)", row)
]
)
f = dict([(i, parse(row)) for i, row in enumerate(data.splitlines(), start=1)])
E = sum(map(len, f.values()))
p1 = bfs(1, f, E)
adjusted = data.splitlines()
adjusted[
0
] += """
An elerium generator.
An elerium-compatible microchip.
A dilithium generator.
A dilithium-compatible microchip.
"""
f = dict([(i, parse(row)) for i, row in enumerate(adjusted, start=1)])
E = sum(map(len, f.values()))
p2 = bfs(1, f, E)
return p1, p2
def bfs(S, m, E):
seen = set()
q = deque([(S, m, 0)])
while q:
e, m, w = q.popleft()
cs = seen_checksum(e, m) # reddit wisdom, see function for more info
if cs in seen:
continue
seen.add(cs)
if len(m[4]) == E:
return w
for n, b, a in valid_next_moves(e, m):
ns = m.copy()
ns[e] = a
ns[n] = b
q.append((n, ns, w + 1))
return None
def valid_next_moves(e, S):
g = []
for n in D[e]:
for o in [[x] for x in S[e]] + [list(x) for x in combinations(S[e], 2)]:
a = sorted(x for x in S[e] if x not in o)
b = sorted(S[n] + o)
if is_valid(a) and is_valid(b):
g.append((n, b, a))
return g
def is_valid(f):
g = [x for x in f if x.endswith("G")]
if not g:
return True
mc = [x for x in f if x.endswith("M")]
return all(f"{m[0]}G" in f for m in mc)
def seen_checksum(e, s):
# To speed up execution, a handy trick was mentioned on several
# reddit threads.
#
# The vanilla BFS method is to store the complete state (elevator
# position + all floors), which this code like many others did initially.
# This is fine, but will lead to really long execution times.
#
# The common wisdom boils down to one thing: it does not matter _what_
# name the microchips and generators have. Only the arrangement (counts) of
# any microchips or generators across the floors do.
#
# For example, these are the same as a checksum to determine if a state has
# been seen:
#
# F2 . HG . . F2 . LG . .
# F1 E . HM LM F1 E . LM HM
#
# The H's or L's do not matter, only the M's and G's do.
#
# So by storing the M's and the G's as a checksum, along with the
# elevator position, the program is a lot faster.
#
# Reddit post, giving hints:
# https://www.reddit.com/r/adventofcode/comments/5hoia9/comment/db1v1ws/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button
# Blog post, providing code and reasoning:
# https://eddmann.com/posts/advent-of-code-2016-day-11-radioisotope-thermoelectric-generators/
# uncomment below line to get speed boost:
# %> 2.59s user 0.02s system 99% cpu 2.622 total
return e, tuple(tuple(Counter(x[-1] for x in f).most_common()) for f in s.values())
# uncomment below line to use vanilla BFS visited storage
# %> 896.11s user 2.58s system 99% cpu 15:01.35 total
# return e, tuple((f, tuple(mg)) for f, mg in s.items())
def M(s, e):
d = {}
for k, v in s.items():
for x in v:
d[x] = k - 1
l = len(d)
d = [
[v if x == k else ". " for x in range(l)]
for v, k in sorted(d.items(), key=lambda kv: kv[0])
]
m = [("F1", "F2", "F3", "F4"), ["E " if x == e - 1 else ". " for x in range(l)]] + d
for r in list(zip(*m))[::-1]:
print(" ".join(r))
print("")
if __name__ == "__main__":
with open("./input/11.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,52 +0,0 @@
def solve(data):
p = data.splitlines()
pl = len(p)
def run(r):
i = 0
while i < pl:
o, *a = p[i].split()
match o:
case "cpy":
kv, t = a
r[t] = int(kv) if kv.isdigit() else r[kv]
i += 1
case "jnz":
kv, d = a
c = int(kv) if kv.isdigit() else r[kv]
i += 1 if c == 0 else int(d)
case "inc":
r[a[0]] += 1
i += 1
case "dec":
r[a[0]] -= 1
i += 1
return r["a"]
p1 = run(
{
"a": 0,
"b": 0,
"c": 0,
"d": 0,
}
)
p2 = run(
{
"a": 0,
"b": 0,
"c": 1,
"d": 0,
}
)
return p1, p2
if __name__ == "__main__":
with open("./input/12.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,52 +0,0 @@
from collections import Counter
from math import inf
from output import D
def solve(data, E=(31, 39)):
S = (1, 1)
p1, p2 = bfs(S, E, int(data), inf, 0)
return p1, p2
def bfs(S, E, z, p1, p2):
seen = set()
q = [(S, 0)]
while q:
m, w = q.pop(0)
if m in seen:
continue
seen.add(m)
if m == E:
p1 = min(w, p1)
if w <= 50:
p2 = max(p2, len(seen))
x, y = m
g = [
(x, y)
for x, y in [(x + c, y + r) for r, c in D]
if x >= 0 and y >= 0 and valid(x, y, z)
]
for s in g:
q.append((s, w + 1))
return p1, p2
def valid(x, y, z):
n = (x * x + 3 * x + 2 * x * y + y + y * y) + z
s = Counter(f"{n:b}")["1"]
return s % 2 == 0
if __name__ == "__main__":
with open("./input/13.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,57 +0,0 @@
import functools
import re
from hashlib import md5
def solve(s):
p1 = run(s)
p2 = run(s, p2=True)
return p1, p2
def run(s, p2=False):
r3 = re.compile(r"(\w)\1{2}")
c = 0
i = 0
H = md5(s.encode())
while True:
i += 1
if p2:
a = stretch(f"{s}{i}")
else:
Ha = H.copy()
Ha.update(str(i).encode())
a = Ha.hexdigest()
if m := re.search(r3, a):
x = m.group(1)
r5 = re.compile(r"(" + x + r")\1{4}")
for j in range(1000):
if p2:
b = stretch(f"{s}{str(i + 1 + j)}")
else:
Hb = H.copy()
Hb.update(str(i + 1 + j).encode())
b = Hb.hexdigest()
if re.search(r5, b):
c += 1
break
if c == 64:
break
return i
@functools.lru_cache(maxsize=None)
def stretch(s):
for _ in range(2017):
s = md5(s.encode()).hexdigest()
return s
if __name__ == "__main__":
with open("./input/14.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,30 +0,0 @@
import re
def solve(data):
M = [
tuple(map(int, re.search(r"(\d+) pos.+ion (\d+)", r).groups()))
for r in data.splitlines()
]
p1 = wait_and_press(M)
p2 = wait_and_press(M + [(11, 0)])
return p1, p2
def wait_and_press(M):
t = 0
while True:
if all((ti + lp[1]) % lp[0] == 0 for ti, lp in enumerate(M, start=t + 1)):
break
t += 1
return t
if __name__ == "__main__":
with open("./input/15.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,24 +0,0 @@
def solve(data):
p12 = []
for DS in [272, 35651584]:
s = [int(c) for c in data]
while len(s) < DS:
b = [abs(int(c) - 1) for c in s[::-1]]
s = s + [0] + b
s = s[:DS]
p = len(s)
while p % 2 == 0:
s = [int(a == b) for a, b in zip(s[::2], s[1::2])]
p = len(s)
p12.append("".join(map(str, s)))
return p12
if __name__ == "__main__":
with open("./input/16.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,41 +0,0 @@
from collections import deque
from hashlib import md5
from output import DD
def solve(data):
paths = list(bfs(data))
p1 = "".join(paths[0])
p2 = len(paths[-1])
return p1, p2
def bfs(code):
q = deque([((0, 0), [])])
T = (3, 3)
K = "UDLR"
while q:
m, d = q.popleft()
U, D, L, R, *_ = md5((code + "".join(d)).encode()).hexdigest()
y, x = m
for udlr in [K[k] for k, n in enumerate([U, D, L, R]) if isopen(n)]:
dy, dx = DD[udlr]
if (y + dy, x + dx) == T:
yield d + [udlr]
elif 0 <= y + dy < 4 and 0 <= x + dx < 4:
q.append(((y + dy, x + dx), d + [udlr]))
def isopen(c):
return c in "bcdef"
if __name__ == "__main__":
with open("./input/17.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,32 +0,0 @@
def solve(data):
cols = len(data)
prevrow = [t == "." for t in data]
p1 = sum(prevrow) + sum(
sum(prevrow := [issafe(prevrow, i) for i in range(cols)]) for _ in range(39)
)
p2 = p1 + sum(
sum(prevrow := [issafe(prevrow, i) for i in range(cols)])
for _ in range(400_000 - 40)
)
return p1, p2
def issafe(row, i):
match i:
case 0:
return row[1]
case n if n == len(row) - 1:
return row[-2]
case _:
lt, rt = row[i - 1], row[i + 1]
return not lt != rt
if __name__ == "__main__":
with open("./input/18.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,52 +0,0 @@
from collections import deque
def solve(data):
elfes = int(data)
p1 = left_adjacent_rule(elfes)
p1b = mathematical_superiority(elfes)
p2 = opposite_side_rule(elfes)
assert p1 == p1b
return p1, p2
def mathematical_superiority(num_elfes):
# Josephus' problem, quick method:
# https://www.youtube.com/watch?v=uCsD3ZGzMgE
b = format(num_elfes, "b")
return int(b[1:] + b[0], 2)
def left_adjacent_rule(num_elfes):
# https://en.wikipedia.org/wiki/Josephus_problem
q = deque(list(range(1, num_elfes + 1)))
while len(q) > 1:
q.rotate(-1)
q.popleft()
return q.pop()
def opposite_side_rule(num_elfes):
elfes = list(range(1, num_elfes + 1))
separator = num_elfes // 2
L, R = deque(elfes[:separator]), deque(elfes[separator:])
while L and R:
R.popleft()
l2r = L.popleft()
R.append(l2r)
if len(R) - len(L) != 1:
r2l = R.popleft()
L.append(r2l)
return R.pop()
if __name__ == "__main__":
with open("./input/19.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,31 +0,0 @@
from output import ints
def solve(data):
lowest, highest = min(ints(data)), max(ints(data))
data = [ints(line) for line in data.split()]
p1 = float("inf")
p2 = set()
for a, b in data:
X = a - 1
Y = b + 1
if X >= lowest:
if not any(x <= X <= y for x, y in data):
p1 = min(p1, X)
p2.add(X)
if Y <= highest:
if not any(x <= Y <= y for x, y in data):
p1 = min(p1, Y)
p2.add(Y)
p2 = len(p2)
return p1, p2
if __name__ == "__main__":
with open("./input/20.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,64 +0,0 @@
import re
from itertools import permutations
from output import ints
def solve(data):
data = [line.strip() for line in data.splitlines()]
p1 = scramble(data, "abcdefgh")
p2 = unscramble(data, "fbgdceah")
return p1, p2
def scramble(data, subject):
r_swap = r"(?:letter|position) (.)"
for line in data:
if line.startswith("rotate right"):
x = ints(line)[0]
subject = subject[-x:] + subject[:-x]
if line.startswith("rotate left"):
x = ints(line)[0]
subject = subject[x:] + subject[:x]
if line.startswith("rotate based"):
x = re.findall(r"letter (.)", line)[0]
i = subject.index(x)
j = i + 1 % len(subject)
subject = subject[-j:] + subject[:-j]
if i >= 4:
subject = subject[-1:] + subject[:-1]
if line.startswith("swap letter"):
x, y = re.findall(r_swap, line)
subject = subject.replace(y, "#")
subject = subject.replace(x, y)
subject = subject.replace("#", x)
if line.startswith("swap position"):
x, y = ints(line)
v1, v2 = subject[x], subject[y]
subject = subject[:x] + v2 + subject[x + 1 :]
subject = subject[:y] + v1 + subject[y + 1 :]
if line.startswith("move"):
x, y = ints(line)
v = subject[x]
subject = subject[:x] + subject[x + 1 :]
subject = subject[:y] + v + subject[y:]
if line.startswith("reverse"):
x, y = ints(line)
subject = subject[:x] + subject[x : y + 1][::-1] + subject[y + 1 :]
return subject
def unscramble(data, T):
for candidate in ["".join(c) for c in permutations(T)]:
if scramble(data, candidate) == T:
return candidate
if __name__ == "__main__":
with open("./input/21.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,45 +0,0 @@
from itertools import permutations
from output import ints
def solve(data):
viable = set()
W = 0
H = 0
grid = dict()
for a, b in permutations([ints(line) for line in data.splitlines()[2:]], r=2):
x1, y1, size, used, _avail, _pc = a
x2, y2, _size, _used, avail, _pc = b
H = max([y1, y2, H])
W = max([x1, x2, W])
grid[(y1, x1)] = (used, size)
if 0 < used <= avail:
viable.add(((y1, x1), (y2, x2)))
if used == 0:
empty = (y1, x1)
p1 = len(viable)
S, E = (0, W), (0, 0)
# dagrid(grid, H + 1, W + 1)
y, x = empty
p2 = x + y + W + (W - 1) * 5
return p1, p2
def dagrid(grid, H, W):
"""Used to print the grid to be solved by hand."""
for r in range(H):
for c in range(W):
u, a = grid[(r, c)]
print(f"{u}/{a}".rjust(8), end="")
print("\n")
if __name__ == "__main__":
with open("./input/22.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,76 +0,0 @@
def solve(data):
p = data.splitlines()
# math.factorial(7) + 5840
p1 = run(
p.copy(),
{
"a": 7,
"b": 0,
"c": 0,
"d": 0,
},
)
# math.factorial(12) + 5840
p2 = run(
p.copy(),
{
"a": 12,
"b": 0,
"c": 0,
"d": 0,
},
)
return p1, p2
def run(p, r):
i = 0
pl = len(p)
while i < pl:
o, *a = p[i].split()
match o:
case "cpy":
kv, t = a
if t in "abcd":
r[t] = int(kv) if kv not in "abcd" else r[kv]
i += 1
case "jnz":
kv, d = a
c = int(kv) if kv not in "abcd" else r[kv]
d = int(d) if d not in "abcd" else r[d]
i += 1 if c == 0 else d
case "inc":
r[a[0]] += 1
i += 1
case "dec":
r[a[0]] -= 1
i += 1
case "tgl":
kv = a[0]
c = int(kv) if kv not in "abcd" else r[kv]
if 0 <= i + c < pl:
old, *v = p[i + c].split()
match old:
case "inc":
nw = "dec"
case "dec":
nw = "inc"
case "tgl":
nw = "inc"
case "jnz":
nw = "cpy"
case _:
nw = "jnz"
p[i + c] = " ".join([nw] + v)
i += 1
return r["a"]
if __name__ == "__main__":
with open("./input/23.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,58 +0,0 @@
import re
from collections import Counter, defaultdict, deque
from heapq import heappop, heappush
from itertools import chain, combinations, compress, permutations
from output import ADJ, DD, D, ints, matrix, mdbg, mhd, vdbg
def solve(data):
grid, H, W = matrix(data)
dests = {
v: (y, x) for y, r in enumerate(grid) for x, v in enumerate(r) if v.isdigit()
}
S0 = dests["0"]
del dests["0"]
p1 = travel(dests, grid, H, W, S0)
p2 = travel(dests, grid, H, W, S0, goback=True)
return p1, p2
def travel(dests, grid, H, W, S0, goback=False):
shortest = float("inf")
for goals in permutations(dests.items()):
goals = list(goals)
if goback:
goals += [("0", S0)]
t = 0
S = S0
for _, E in goals:
seen = set()
q = [(S, 0)]
while q:
pos, w = q.pop(0)
if pos == E:
t += w
break
if pos in seen:
continue
seen.add(pos)
y, x = pos
for dy, dx in D:
if not (0 <= dy + y < H and 0 <= dx + x < W):
continue
if grid[dy + y][dx + x] != "#":
q.append(((dy + y, dx + x), w + 1))
S = E
shortest = min(shortest, t)
return shortest
if __name__ == "__main__":
with open("./input/24.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,77 +0,0 @@
def solve(data):
p = data.splitlines()
csl = 30
A = "01" * (csl // 2)
i = 1
while True:
if A == run(
csl,
p.copy(),
{
"a": i,
"b": 0,
"c": 0,
"d": 0,
},
):
break
i += 2
p1 = i
return p1, None
def run(lO, p, r):
i = 0
pl = len(p)
outs = []
while i < pl and len(outs) < lO:
o, *a = p[i].split()
match o:
case "cpy":
kv, t = a
if t in "abcd":
r[t] = int(kv) if kv not in "abcd" else r[kv]
i += 1
case "out":
kv, *_ = a
outs.append(f'{int(kv) if kv not in "abcd" else r[kv]}')
i += 1
case "jnz":
kv, d = a
c = int(kv) if kv not in "abcd" else r[kv]
d = int(d) if d not in "abcd" else r[d]
i += 1 if c == 0 else d
case "inc":
r[a[0]] += 1
i += 1
case "dec":
r[a[0]] -= 1
i += 1
case "tgl":
kv = a[0]
c = int(kv) if kv not in "abcd" else r[kv]
if 0 <= i + c < pl:
old, *v = p[i + c].split()
match old:
case "inc":
nw = "dec"
case "dec":
nw = "inc"
case "tgl":
nw = "inc"
case "jnz":
nw = "cpy"
case _:
nw = "jnz"
p[i + c] = " ".join([nw] + v)
i += 1
return "".join(outs)
if __name__ == "__main__":
with open("./input/25.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)

View file

@ -7,9 +7,8 @@ except ValueError:
name = None name = None
if day_no and name: if day_no and name:
with open("solutions/day_{}.py".format(day_no.zfill(2)), "w") as s: with open('solutions/day_{}.py'.format(day_no.zfill(2)), 'w') as s:
s.write( s.write('''
"""
from solutions import BaseSolution from solutions import BaseSolution
@ -29,14 +28,9 @@ class Solution(BaseSolution):
if __name__ == '__main__': if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()
""".strip().format( '''.strip().format(day=day_no, day_no=day_no.zfill(2), name=name) + '\n')
day=day_no, day_no=day_no.zfill(2), name=name with open('tests/day_{}_tests.py'.format(day_no.zfill(2)), 'w') as t:
) t.write('''
+ "\n"
)
with open("tests/day_{}_tests.py".format(day_no.zfill(2)), "w") as t:
t.write(
"""
import unittest import unittest
from solutions.day_{day_no} import Solution from solutions.day_{day_no} import Solution
@ -52,24 +46,18 @@ class Day{day_no}TestCase(unittest.TestCase):
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
""".strip().format( '''.strip().format(day_no=day_no.zfill(2)) + '\n')
day_no=day_no.zfill(2) with open('inputs/{}.txt'.format(day_no.zfill(2)), 'w') as i:
) i.write('')
+ "\n"
)
with open("inputs/{}.txt".format(day_no.zfill(2)), "w") as i:
i.write("")
exit(0) exit(0)
print( print('\nAdvent of Code 2017'
"\nAdvent of Code 2017" "\n###################" "\n\nby Anders Ytterström (@madr)" '\n###################'
) '\n\nby Anders Ytterström (@madr)')
for i in [str(n).zfill(2) for n in range(1, 26)]: for i in [str(n).zfill(2) for n in range(1, 26)]:
try: try:
solution = __import__( solution = __import__('solutions.day_{}'.format(i), globals(), locals(), ['Solution'], 0).Solution()
"solutions.day_{}".format(i), globals(), locals(), ["Solution"], 0
).Solution()
solution.show_results() solution.show_results()
except IOError: except IOError:
pass pass

View file

@ -4,22 +4,20 @@ class BaseSolution:
trim_input = True trim_input = True
def parse_input(self, filename): def parse_input(self, filename):
filepath = "./inputs/{}".format(filename) filepath = './inputs/{}'.format(filename)
with open(filepath, "r") as f: with open(filepath, 'r') as f:
self.puzzle_input = f.read() self.puzzle_input = f.read()
if self.trim_input: if self.trim_input:
self.puzzle_input = self.puzzle_input.strip() self.puzzle_input = self.puzzle_input.strip()
def show_results(self): def show_results(self):
self.parse_input(self.input_file) self.parse_input(self.input_file)
print( print('\n\n{}\n{}\n\nPart 1: {}\nPart 2: {}'.format(
"\n\n{}\n{}\n\nPart 1: {}\nPart 2: {}".format(
str(self), str(self),
"-" * len(str(self)), '-' * len(str(self)),
self.solve(self.puzzle_input), self.solve(self.puzzle_input),
self.solve_again(self.puzzle_input), self.solve_again(self.puzzle_input),
) ))
)
def solve(self, puzzle_input): def solve(self, puzzle_input):
raise NotImplemented raise NotImplemented

View file

@ -2,24 +2,21 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "01.txt" input_file = '01.txt'
def __str__(self): def __str__(self):
return "Day 1: Inverse Captcha" return 'Day 1: Inverse Captcha'
def solve(self, puzzle_input, distance=1): def solve(self, puzzle_input, distance=1):
pi_length = len(puzzle_input) pi_length = len(puzzle_input)
return sum( return sum(int(puzzle_input[pos]) for pos in range(pi_length) if
int(puzzle_input[pos]) puzzle_input[pos] == puzzle_input[(pos + distance) % pi_length])
for pos in range(pi_length)
if puzzle_input[pos] == puzzle_input[(pos + distance) % pi_length]
)
def solve_again(self, puzzle_input): def solve_again(self, puzzle_input):
distance = len(puzzle_input) // 2 distance = len(puzzle_input) // 2
return self.solve(puzzle_input, distance) return self.solve(puzzle_input, distance)
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,10 +2,10 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "02.txt" input_file = '02.txt'
def __str__(self): def __str__(self):
return "Day 2: Corruption Checksum" return 'Day 2: Corruption Checksum'
def _get_rows(self, puzzle_input): def _get_rows(self, puzzle_input):
return [list(map(int, rows.split())) for rows in puzzle_input.splitlines()] return [list(map(int, rows.split())) for rows in puzzle_input.splitlines()]
@ -29,6 +29,6 @@ class Solution(BaseSolution):
return sum(self.get_even_divisible(columns) for columns in rows) return sum(self.get_even_divisible(columns) for columns in rows)
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,10 +2,10 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "03.txt" input_file = '03.txt'
def __str__(self): def __str__(self):
return "Day 3: Spiral Memory" return 'Day 3: Spiral Memory'
def _get_rounds(self, value): def _get_rounds(self, value):
n = 0 n = 0
@ -32,6 +32,6 @@ class Solution(BaseSolution):
return 279138 return 279138
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,15 +2,15 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "04.txt" input_file = '04.txt'
def __str__(self): def __str__(self):
return "Day 4: High-Entropy Passphrases" return 'Day 4: High-Entropy Passphrases'
def validate(self, passphrase, extended=False): def validate(self, passphrase, extended=False):
words = passphrase.split() words = passphrase.split()
if extended: if extended:
words = ["".join(sorted(w)) for w in words] words = [''.join(sorted(w)) for w in words]
return sorted(list(set(words))) == sorted(words) return sorted(list(set(words))) == sorted(words)
def solve(self, puzzle_input): def solve(self, puzzle_input):
@ -20,6 +20,6 @@ class Solution(BaseSolution):
return sum(self.validate(p, True) for p in puzzle_input.splitlines()) return sum(self.validate(p, True) for p in puzzle_input.splitlines())
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,10 +2,10 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "05.txt" input_file = '05.txt'
def __str__(self): def __str__(self):
return "Day 5: A Maze of Twisty Trampolines, All Alike" return 'Day 5: A Maze of Twisty Trampolines, All Alike'
def solve(self, puzzle_input, strange_jumps=False): def solve(self, puzzle_input, strange_jumps=False):
pos = 0 pos = 0
@ -23,6 +23,6 @@ class Solution(BaseSolution):
return self.solve(puzzle_input, True) return self.solve(puzzle_input, True)
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,10 +2,10 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "06.txt" input_file = '06.txt'
def __str__(self): def __str__(self):
return "Day 6: Memory Reallocation" return 'Day 6: Memory Reallocation'
def redistribute(self, banks): def redistribute(self, banks):
banks = list(banks) banks = list(banks)
@ -39,10 +39,10 @@ class Solution(BaseSolution):
def solve_again(self, puzzle_input): def solve_again(self, puzzle_input):
seen = self._allocate(puzzle_input) seen = self._allocate(puzzle_input)
seen_last = " ".join(str(n) for n in seen[-1]) seen_last = ' '.join(str(n) for n in seen[-1])
return len(self._allocate(seen_last)) - 1 return len(self._allocate(seen_last)) - 1
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -18,8 +18,8 @@ class Program:
name, weight = data.split() name, weight = data.split()
except ValueError: except ValueError:
name, weight, _, *disc = data.split() name, weight, _, *disc = data.split()
weight = int(weight[1 : len(weight) - 1]) weight = int(weight[1:len(weight) - 1])
disc = tuple(p.replace(",", "") for p in disc) disc = tuple(p.replace(',', '') for p in disc)
return name, weight, disc return name, weight, disc
def has_disc(self): def has_disc(self):
@ -30,31 +30,29 @@ class Program:
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "07.txt" input_file = '07.txt'
def __str__(self): def __str__(self):
return "Day 7: Recursive Circus" return 'Day 7: Recursive Circus'
def _get_programs(self, puzzle_input): def _get_programs(self, puzzle_input):
return [Program(data) for data in puzzle_input.splitlines()] return [Program(data) for data in puzzle_input.splitlines()]
def _get_discs(self, disc, programs): def _get_discs(self, disc, programs):
subdisc = [ subdisc = [{'own_weight': p.weight, 'obj': p} for p in programs if p.name in disc]
{"own_weight": p.weight, "obj": p} for p in programs if p.name in disc
]
for t in subdisc: for t in subdisc:
t["weight"] = t["own_weight"] t['weight'] = t['own_weight']
if t["obj"].has_disc(): if t['obj'].has_disc():
t["disc"] = self._get_discs(t["obj"].disc, programs) t['disc'] = self._get_discs(t['obj'].disc, programs)
t["weight"] += sum([st["weight"] for st in t["disc"]]) t['weight'] += sum([st['weight'] for st in t['disc']])
del t["obj"] del (t['obj'])
return subdisc return subdisc
def _find_unbalanced_disc(self, disc): def _find_unbalanced_disc(self, disc):
disc = sorted(disc, key=lambda t: t["weight"]) disc = sorted(disc, key=lambda t: t['weight'])
if not disc[0]["weight"] < disc[1]["weight"]: if not disc[0]['weight'] < disc[1]['weight']:
disc = sorted(disc, key=lambda t: t["weight"], reverse=True) disc = sorted(disc, key=lambda t: t['weight'], reverse=True)
return disc[0], disc[1]["weight"] - disc[0]["weight"] return disc[0], disc[1]['weight'] - disc[0]['weight']
def solve(self, puzzle_input): def solve(self, puzzle_input):
programs = self._get_programs(puzzle_input) programs = self._get_programs(puzzle_input)
@ -69,30 +67,28 @@ class Solution(BaseSolution):
unseen = p.unseen_discs(seen) unseen = p.unseen_discs(seen)
if len(unseen) > 0: if len(unseen) > 0:
seen += unseen seen += unseen
bottom_program = list( bottom_program = list(filter(lambda p: p.name not in seen, programs_with_discs))[0]
filter(lambda p: p.name not in seen, programs_with_discs)
)[0]
return bottom_program return bottom_program
def solve_again(self, puzzle_input): def solve_again(self, puzzle_input):
programs = self._get_programs(puzzle_input) programs = self._get_programs(puzzle_input)
bottom_program = self.solve(puzzle_input) bottom_program = self.solve(puzzle_input)
disc_tree = { disc_tree = {
"own_weight": bottom_program.weight, 'own_weight': bottom_program.weight,
"disc": self._get_discs(bottom_program.disc, programs), 'disc': self._get_discs(bottom_program.disc, programs)
} }
diff = -1 diff = -1
unbalanced = True unbalanced = True
while unbalanced: while unbalanced:
disc, new_diff = self._find_unbalanced_disc(disc_tree["disc"]) disc, new_diff = self._find_unbalanced_disc(disc_tree['disc'])
if new_diff == 0: if new_diff == 0:
unbalanced = False unbalanced = False
else: else:
disc_tree = disc disc_tree = disc
diff = new_diff diff = new_diff
return disc_tree["own_weight"] + diff return disc_tree['own_weight'] + diff
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,22 +2,22 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "08.txt" input_file = '08.txt'
registry = {} registry = {}
def __str__(self): def __str__(self):
return "Day 8: I Heard You Like Registers" return 'Day 8: I Heard You Like Registers'
def _should_modify(self, x, comp, y): def _should_modify(self, x, comp, y):
if comp in ("==", "!=", ">=", "<=", ">", "<"): if comp in ('==', '!=', '>=', '<=', '>', '<'):
return eval("{:d} {} {:d}".format(x, comp, y)) return eval('{:d} {} {:d}'.format(x, comp, y))
return False return False
def _update_registry(self, registry, instruction): def _update_registry(self, registry, instruction):
r, action, n, _, k, comp, v = instruction.split() r, action, n, _, k, comp, v = instruction.split()
current = registry.get(r, 0) current = registry.get(r, 0)
if self._should_modify(registry.get(k, 0), comp, int(v)): if self._should_modify(registry.get(k, 0), comp, int(v)):
registry[r] = current + int(n) if action == "inc" else current - int(n) registry[r] = current + int(n) if action == 'inc' else current - int(n)
def solve(self, puzzle_input): def solve(self, puzzle_input):
registry = {} registry = {}
@ -35,6 +35,6 @@ class Solution(BaseSolution):
return max_value return max_value
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -4,30 +4,30 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "09.txt" input_file = '09.txt'
def __str__(self): def __str__(self):
return "Day 9: Stream Processing" return 'Day 9: Stream Processing'
def solve(self, puzzle_input): def solve(self, puzzle_input):
level = 0 level = 0
groups = [] groups = []
stream = re.sub(r"!.", "", puzzle_input) stream = re.sub(r'!.', '', puzzle_input)
stream = re.sub(r"<[^>]*>", "", stream) stream = re.sub(r'<[^>]*>', '', stream)
for c in stream: for c in stream:
if c == "{": if c == '{':
level += 1 level += 1
if c == "}": if c == '}':
groups.append(level) groups.append(level)
level -= 1 level -= 1
return sum(groups) return sum(groups)
def solve_again(self, puzzle_input): def solve_again(self, puzzle_input):
stream = re.sub(r"!.", "", puzzle_input) stream = re.sub(r'!.', '', puzzle_input)
garbage = re.findall(r"<([^>]*)>", stream) garbage = re.findall(r'<([^>]*)>', stream)
return sum([len(g) for g in garbage]) return sum([len(g) for g in garbage])
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,14 +2,14 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "10.txt" input_file = '10.txt'
list = [] list = []
l = 0 l = 0
skip_size = 0 skip_size = 0
pos = 0 pos = 0
def __str__(self): def __str__(self):
return "Day 10: Knot Hash" return 'Day 10: Knot Hash'
def reset(self, l=256): def reset(self, l=256):
self.list = list(range(l)) self.list = list(range(l))
@ -28,7 +28,7 @@ class Solution(BaseSolution):
def solve(self, puzzle_input, r=256): def solve(self, puzzle_input, r=256):
self.reset(r) self.reset(r)
for sublist_length in map(int, puzzle_input.split(",")): for sublist_length in map(int, puzzle_input.split(',')):
self.reverse(sublist_length) self.reverse(sublist_length)
return self.list[0] * self.list[1] return self.list[0] * self.list[1]
@ -38,13 +38,10 @@ class Solution(BaseSolution):
for _ in range(64): for _ in range(64):
for sublist_length in puzzle_input: for sublist_length in puzzle_input:
self.reverse(sublist_length) self.reverse(sublist_length)
dense_hash = [ dense_hash = [eval('^'.join(list(map(str, self.list[seq:seq+16])))) for seq in range(0, r, 16)]
eval("^".join(list(map(str, self.list[seq : seq + 16])))) return ''.join(['{:x}'.format(int(i)).zfill(2) for i in dense_hash])
for seq in range(0, r, 16)
]
return "".join(["{:x}".format(int(i)).zfill(2) for i in dense_hash])
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,20 +2,20 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "11.txt" input_file = '11.txt'
furthest_away = 0 furthest_away = 0
# https://www.redblobgames.com/grids/hexagons/#coordinates # https://www.redblobgames.com/grids/hexagons/#coordinates
DIRECTIONS = { DIRECTIONS = {
"n": lambda x, y, z: (x, y + 1, z - 1), 'n': lambda x, y, z: (x, y + 1, z - 1),
"ne": lambda x, y, z: (x + 1, y, z - 1), 'ne': lambda x, y, z: (x + 1, y, z - 1),
"se": lambda x, y, z: (x + 1, y - 1, z), 'se': lambda x, y, z: (x + 1, y - 1, z),
"s": lambda x, y, z: (x, y - 1, z + 1), 's': lambda x, y, z: (x, y - 1, z + 1),
"sw": lambda x, y, z: (x - 1, y, z + 1), 'sw': lambda x, y, z: (x - 1, y, z + 1),
"nw": lambda x, y, z: (x - 1, y + 1, z), 'nw': lambda x, y, z: (x - 1, y + 1, z),
} }
def __str__(self): def __str__(self):
return "Day 11: Hex Ed" return 'Day 11: Hex Ed'
def _get_end(self, steps): def _get_end(self, steps):
x = 0 x = 0
@ -28,14 +28,14 @@ class Solution(BaseSolution):
return abs(x), abs(y), abs(z) return abs(x), abs(y), abs(z)
def solve(self, puzzle_input): def solve(self, puzzle_input):
x, y, z = self._get_end(puzzle_input.split(",")) x, y, z = self._get_end(puzzle_input.split(','))
return max(x, y, z) return max(x, y, z)
def solve_again(self, puzzle_input): def solve_again(self, puzzle_input):
_, *__ = self._get_end(puzzle_input.split(",")) _, *__ = self._get_end(puzzle_input.split(','))
return self.furthest_away return self.furthest_away
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,17 +2,17 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "12.txt" input_file = '12.txt'
seen = [] seen = []
def __str__(self): def __str__(self):
return "Day 12: Digital Plumber" return 'Day 12: Digital Plumber'
def _walk(self, i, programs): def _walk(self, i, programs):
line = next(filter(lambda l: l.startswith("{} <->".format(i)), programs)) line = next(filter(lambda l: l.startswith('{} <->'.format(i)), programs))
piped = line.split()[2:] piped = line.split()[2:]
self.seen.add(i) self.seen.add(i)
for p in [int(p.replace(",", "")) for p in piped]: for p in [int(p.replace(',', '')) for p in piped]:
if p not in self.seen: if p not in self.seen:
self._walk(p, programs) self._walk(p, programs)
@ -34,6 +34,6 @@ class Solution(BaseSolution):
return groups return groups
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -4,12 +4,12 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "13.txt" input_file = '13.txt'
layers = [] layers = []
scanners = [] scanners = []
def __str__(self): def __str__(self):
return "Day 13: Packet Scanners" return 'Day 13: Packet Scanners'
def _move_scanners(self): def _move_scanners(self):
for p, l in enumerate(self.layers): for p, l in enumerate(self.layers):
@ -22,10 +22,7 @@ class Solution(BaseSolution):
self.scanners = [[0, 1] for l in self.layers] self.scanners = [[0, 1] for l in self.layers]
def _setup(self, puzzle_input): def _setup(self, puzzle_input):
pi = [ pi = [tuple(map(int, line.strip().split(': '))) for line in puzzle_input.splitlines()]
tuple(map(int, line.strip().split(": ")))
for line in puzzle_input.splitlines()
]
self.layers = [0 for _ in range(pi[-1][0] + 1)] self.layers = [0 for _ in range(pi[-1][0] + 1)]
self._init_scanners() self._init_scanners()
for k, v in pi: for k, v in pi:
@ -58,16 +55,13 @@ class Solution(BaseSolution):
def solve_again(self, puzzle_input): def solve_again(self, puzzle_input):
# todo: rewrite! # todo: rewrite!
lines = [line.split(": ") for line in puzzle_input.splitlines()] lines = [line.split(': ') for line in puzzle_input.splitlines()]
heights = {int(pos): int(height) for pos, height in lines} heights = {int(pos): int(height) for pos, height in lines}
wait = next( wait = next(
wait wait for wait in itertools.count() if not any(self._scan(heights[pos], wait + pos) == 0 for pos in heights))
for wait in itertools.count()
if not any(self._scan(heights[pos], wait + pos) == 0 for pos in heights)
)
return wait return wait
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -3,18 +3,18 @@ from solutions.day_10 import Solution as KnotHash
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "14.txt" input_file = '14.txt'
def __str__(self): def __str__(self):
return "Day 14: Disk Defragmentation" return 'Day 14: Disk Defragmentation'
def _get_grid(self, pi): def _get_grid(self, pi):
grid = "" grid = ''
ks = KnotHash() ks = KnotHash()
for n in range(128): for n in range(128):
s = "-".join([pi, str(n)]) s = '-'.join([pi, str(n)])
knothash = ks.solve_again(s) knothash = ks.solve_again(s)
grid += "{:0128b}".format(int(knothash, 16)) grid += '{:0128b}'.format(int(knothash, 16))
return grid return grid
def _find_regions(self, squares): def _find_regions(self, squares):
@ -31,10 +31,10 @@ class Solution(BaseSolution):
get_adjacent_square(i - 128) get_adjacent_square(i - 128)
if i % 128 < 127: if i % 128 < 127:
get_adjacent_square(i + 1) get_adjacent_square(i + 1)
if i < 128**2 - 128: if i < 128 ** 2 - 128:
get_adjacent_square(i + 128) get_adjacent_square(i + 128)
for i in range(128**2): for i in range(128 ** 2):
if i in seen or i not in squares: if i in seen or i not in squares:
continue continue
regions += 1 regions += 1
@ -47,10 +47,10 @@ class Solution(BaseSolution):
def solve_again(self, puzzle_input): def solve_again(self, puzzle_input):
grid = self._get_grid(puzzle_input) grid = self._get_grid(puzzle_input)
squares = [i for i, s in enumerate(list(grid)) if s == "1"] squares = [i for i, s in enumerate(list(grid)) if s == '1']
return self._find_regions(squares) return self._find_regions(squares)
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,10 +2,10 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "15.txt" input_file = '15.txt'
def __str__(self): def __str__(self):
return "Day 15: Dueling Generators" return 'Day 15: Dueling Generators'
def _calc(self, x, f, m=1): def _calc(self, x, f, m=1):
x = (x * f) % 2147483647 x = (x * f) % 2147483647
@ -21,7 +21,7 @@ class Solution(BaseSolution):
for _ in range(40 * 10**6): for _ in range(40 * 10**6):
a = self._calc(a, af) a = self._calc(a, af)
b = self._calc(b, bf) b = self._calc(b, bf)
if "{:b}".format(a)[-16:] == "{:b}".format(b)[-16:]: if '{:b}'.format(a)[-16:] == '{:b}'.format(b)[-16:]:
j += 1 j += 1
return j return j
@ -29,14 +29,14 @@ class Solution(BaseSolution):
af, bf = (16807, 48271) af, bf = (16807, 48271)
a, b = [int(pi.split()[-1]) for pi in puzzle_input.splitlines()] a, b = [int(pi.split()[-1]) for pi in puzzle_input.splitlines()]
j = 0 j = 0
for _ in range(5 * 10**6): for _ in range(5 * 10 ** 6):
a = self._calc(a, af, 4) a = self._calc(a, af, 4)
b = self._calc(b, bf, 8) b = self._calc(b, bf, 8)
if "{:b}".format(a)[-16:] == "{:b}".format(b)[-16:]: if '{:b}'.format(a)[-16:] == '{:b}'.format(b)[-16:]:
j += 1 j += 1
return j return j
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,24 +2,24 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "16.txt" input_file = '16.txt'
def __str__(self): def __str__(self):
return "Day 16: Permutation Promenade" return 'Day 16: Permutation Promenade'
def _move(self, programs, m, i): def _move(self, programs, m, i):
l = len(programs) l = len(programs)
if m == "s": if m == 's':
r = int(i) r = int(i)
return programs[-r:] + programs[: l - r] return programs[-r:] + programs[:l - r]
if m == "x": if m == 'x':
x, y = [int(s) for s in i.split("/")] x, y = [int(s) for s in i.split('/')]
z = programs[x] z = programs[x]
programs[x] = programs[y] programs[x] = programs[y]
programs[y] = z programs[y] = z
return programs return programs
if m == "p": if m == 'p':
xp, yp = i.split("/") xp, yp = i.split('/')
x = programs.index(xp) x = programs.index(xp)
y = programs.index(yp) y = programs.index(yp)
z = programs[x] z = programs[x]
@ -34,22 +34,22 @@ class Solution(BaseSolution):
def solve(self, puzzle_input, n=16): def solve(self, puzzle_input, n=16):
programs = [chr(c) for c in range(97, 97 + n)] programs = [chr(c) for c in range(97, 97 + n)]
moves = puzzle_input.split(",") moves = puzzle_input.split(',')
return "".join(self._dance(programs, moves)) return ''.join(self._dance(programs, moves))
def solve_again(self, puzzle_input, n=16): def solve_again(self, puzzle_input, n=16):
moves = puzzle_input.split(",") moves = puzzle_input.split(',')
initial = [chr(c) for c in range(97, 97 + n)] initial = [chr(c) for c in range(97, 97 + n)]
programs = list(self.solve(puzzle_input)) programs = list(self.solve(puzzle_input))
dances = 1 dances = 1
while not programs == initial: while not programs == initial:
programs = self._dance(programs, moves) programs = self._dance(programs, moves)
dances += 1 dances += 1
for _ in range(10**9 % dances): for _ in range(10 ** 9 % dances):
programs = self._dance(programs, moves) programs = self._dance(programs, moves)
return "".join(programs) return ''.join(programs)
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,10 +2,10 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "17.txt" input_file = '17.txt'
def __str__(self): def __str__(self):
return "Day 17: Spinlock" return 'Day 17: Spinlock'
def solve(self, puzzle_input): def solve(self, puzzle_input):
n = int(puzzle_input) n = int(puzzle_input)
@ -20,13 +20,13 @@ class Solution(BaseSolution):
pos = 0 pos = 0
n = int(puzzle_input) n = int(puzzle_input)
last_seen = 0 last_seen = 0
for i in range(1, 5 * 10**7 + 1): for i in range(1, 5 * 10 ** 7 + 1):
pos = (pos + n) % i + 1 pos = (pos + n) % i + 1
if pos == 1: if pos == 1:
last_seen = i last_seen = i
return last_seen return last_seen
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,16 +2,16 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "18.txt" input_file = '18.txt'
sound_freq = 0 sound_freq = 0
queue = [], [] queue = [], []
sent = [0, 0] sent = [0, 0]
def __str__(self): def __str__(self):
return "Day 18: Duet" return 'Day 18: Duet'
def _run(self, line, registry, swag_mode=True): def _run(self, line, registry, swag_mode=True):
actions = "add jgz mod mul rcv set snd" actions = 'add jgz mod mul rcv set snd'
a, *kv = line.split() a, *kv = line.split()
if len(kv) == 2: if len(kv) == 2:
k, v = kv k, v = kv
@ -19,9 +19,9 @@ class Solution(BaseSolution):
k = kv[0] k = kv[0]
v = None v = None
if a in actions: if a in actions:
if a == "add" and k in registry: if a == 'add' and k in registry:
registry[k] += registry[v] if v in registry else int(v) registry[k] += registry[v] if v in registry else int(v)
if a == "jgz": # damn you, 'jgz 1 3' if a == 'jgz': # damn you, 'jgz 1 3'
try: try:
k = int(k) k = int(k)
except ValueError: except ValueError:
@ -31,25 +31,25 @@ class Solution(BaseSolution):
except ValueError: except ValueError:
v = registry[v] if v in registry else 1 v = registry[v] if v in registry else 1
return v if k > 0 else 1 return v if k > 0 else 1
if a == "mod" and k in registry: if a == 'mod' and k in registry:
registry[k] %= registry[v] if v in registry else int(v) registry[k] %= registry[v] if v in registry else int(v)
if a == "mul" and k in registry: if a == 'mul' and k in registry:
registry[k] *= registry[v] if v in registry else int(v) registry[k] *= registry[v] if v in registry else int(v)
if a == "set": if a == 'set':
registry[k] = registry[v] if v in registry else int(v) registry[k] = registry[v] if v in registry else int(v)
if swag_mode: # Part 1: scientific wild-ass guess if swag_mode: # Part 1: scientific wild-ass guess
if a == "rcv" and registry[k] != 0: if a == 'rcv' and registry[k] != 0:
return self.STOP_SIGNAL return self.STOP_SIGNAL
if a == "snd" and k in registry: if a == 'snd' and k in registry:
self.sound_freq = registry[k] self.sound_freq = registry[k]
else: # part 2, actual instructions else: # part 2, actual instructions
if a == "rcv": if a == 'rcv':
if len(self.queue[registry["_id"]]) == 0: if len(self.queue[registry['_id']]) == 0:
return 0 return 0
registry[k] = self.queue[registry["_id"]].pop(0) registry[k] = self.queue[registry['_id']].pop(0)
if a == "snd": if a == 'snd':
self.sent[registry["_id"]] += 1 self.sent[registry['_id']] += 1
q = (registry["_id"] + 1) % 2 q = (registry['_id'] + 1) % 2
kk = registry[k] if k in registry else int(k) kk = registry[k] if k in registry else int(k)
self.queue[q].append(kk) self.queue[q].append(kk)
return 1 return 1
@ -65,7 +65,7 @@ class Solution(BaseSolution):
return self.sound_freq return self.sound_freq
def solve_again(self, puzzle_input): def solve_again(self, puzzle_input):
registry = {"p": 0, "_id": 0}, {"p": 1, "_id": 1} registry = {'p': 0, '_id': 0}, {'p': 1, '_id': 1}
lines = puzzle_input.splitlines() lines = puzzle_input.splitlines()
i = 0 i = 0
j = 0 j = 0
@ -89,6 +89,6 @@ class Solution(BaseSolution):
return self.sent[1] return self.sent[1]
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -2,48 +2,48 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "19.txt" input_file = '19.txt'
trim_input = False trim_input = False
def __str__(self): def __str__(self):
return "Day 19: A Series of Tubes" return 'Day 19: A Series of Tubes'
def _walk_maze(self, puzzle_input): def _walk_maze(self, puzzle_input):
DIRECTIONS = { DIRECTIONS = {
"D": (1, 0), 'D': (1, 0),
"U": (-1, 0), 'U': (-1, 0),
"R": (0, 1), 'R': (0, 1),
"L": (0, -1), 'L': (0, -1),
} }
maze = puzzle_input.splitlines() maze = puzzle_input.splitlines()
pos = (0, list(maze[0]).index("|")) pos = (0, list(maze[0]).index('|'))
d = DIRECTIONS["D"] d = DIRECTIONS['D']
paths = "-|" paths = '-|'
steps = 0 steps = 0
seen = "" seen = ''
def _nc(nu): def _nc(nu):
np = (pos[0] + nu[0], pos[1] + nu[1]) np = (pos[0] + nu[0], pos[1] + nu[1])
if np[0] < len(maze) and np[1] < len(maze[np[0]]): if np[0] < len(maze) and np[1] < len(maze[np[0]]):
return maze[np[0]][np[1]] return maze[np[0]][np[1]]
else: else:
return " " return ' '
while True: while True:
pos = (pos[0] + d[0], pos[1] + d[1]) pos = (pos[0] + d[0], pos[1] + d[1])
c = _nc((0, 0)) c = _nc((0, 0))
steps += 1 steps += 1
if c == "+": if c == '+':
nc = _nc(d) nc = _nc(d)
if nc == " ": if nc == ' ':
for v in DIRECTIONS.values(): for v in DIRECTIONS.values():
if -v[0] == d[0] and -v[1] == d[1]: if -v[0] == d[0] and -v[1] == d[1]:
continue continue
nc = _nc(v) nc = _nc(v)
if nc != " ": if nc != ' ':
d = v d = v
break break
elif c == " ": elif c == ' ':
break break
elif c not in paths: elif c not in paths:
seen += c seen += c
@ -58,6 +58,6 @@ class Solution(BaseSolution):
return steps return steps
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -6,18 +6,15 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "20.txt" input_file = '20.txt'
def __str__(self): def __str__(self):
return "Day 20: Particle Swarm" return 'Day 20: Particle Swarm'
def _get_particle(self, line): def _get_particle(self, line):
return [ return [list(map(int, coordinate.split(','))) for coordinate in re.findall(r'.=<([^>]+)>', line)]
list(map(int, coordinate.split(",")))
for coordinate in re.findall(r".=<([^>]+)>", line)
]
def solve(self, puzzle_input, ticks=10**4): def solve(self, puzzle_input, ticks=10 ** 4):
particles = [self._get_particle(line) for line in puzzle_input.splitlines()] particles = [self._get_particle(line) for line in puzzle_input.splitlines()]
distances = [[] for _ in range(len(particles))] distances = [[] for _ in range(len(particles))]
for _ in range(ticks): for _ in range(ticks):
@ -30,13 +27,10 @@ class Solution(BaseSolution):
p[1] += v[1] p[1] += v[1]
p[2] += v[2] p[2] += v[2]
distances[i].append(sum(map(abs, p))) distances[i].append(sum(map(abs, p)))
d = sorted( d = sorted(map(lambda d: (d[0], sum(d[1]) / len(d[1])), enumerate(distances)), key=lambda x: x[1])
map(lambda d: (d[0], sum(d[1]) / len(d[1])), enumerate(distances)),
key=lambda x: x[1],
)
return d[0][0] return d[0][0]
def solve_again(self, puzzle_input, ticks=10**3): def solve_again(self, puzzle_input, ticks=10 ** 3):
particles = [self._get_particle(line) for line in puzzle_input.splitlines()] particles = [self._get_particle(line) for line in puzzle_input.splitlines()]
for _ in range(ticks): for _ in range(ticks):
positions = collections.defaultdict(list) positions = collections.defaultdict(list)
@ -48,7 +42,7 @@ class Solution(BaseSolution):
p[0] += v[0] p[0] += v[0]
p[1] += v[1] p[1] += v[1]
p[2] += v[2] p[2] += v[2]
k = "-".join(map(str, p)) k = '-'.join(map(str, p))
positions[k].append(particle) positions[k].append(particle)
for duplicates in positions.values(): for duplicates in positions.values():
if len(duplicates) > 1: if len(duplicates) > 1:
@ -57,6 +51,6 @@ class Solution(BaseSolution):
return len(particles) return len(particles)
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -7,9 +7,9 @@ class Rule:
size = 0 size = 0
def __init__(self, line): def __init__(self, line):
pattern, output = line.split(" => ") pattern, output = line.split(' => ')
self.pattern = pattern.replace("/", "") self.pattern = pattern.replace('/', '')
self.output = output.replace("/", "") self.output = output.replace('/', '')
self.size = int(len(self.pattern) ** 0.5) self.size = int(len(self.pattern) ** 0.5)
self.patterns = [ self.patterns = [
@ -22,18 +22,13 @@ class Rule:
] ]
def __repr__(self): def __repr__(self):
return "[{}] {} -> {}".format(self.size, self.pattern, self.output) return '[{}] {} -> {}'.format(self.size, self.pattern, self.output)
def _r(self, b): def _r(self, b):
return "".join( return ''.join(map(lambda g: ''.join(g), zip(*[b[s:s + self.size] for s in range(0, len(b), self.size)][::-1])))
map(
lambda g: "".join(g),
zip(*[b[s : s + self.size] for s in range(0, len(b), self.size)][::-1]),
)
)
def _f(self, b): def _f(self, b):
return b[-self.size :] + b[self.size : len(b) - self.size] + b[0 : self.size] return b[-self.size:] + b[self.size:len(b) - self.size] + b[0:self.size]
def matches(self, canvas): def matches(self, canvas):
return canvas in self.patterns return canvas in self.patterns
@ -43,13 +38,13 @@ class Rule:
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "21.txt" input_file = '21.txt'
def __str__(self): def __str__(self):
return "Day 21: Fractal Art" return 'Day 21: Fractal Art'
def _get_block(self, canvas, size, n=0): def _get_block(self, canvas, size, n=0):
s = "" s = ''
cl = int(len(canvas) ** 0.5) cl = int(len(canvas) ** 0.5)
x = n % (cl // size) x = n % (cl // size)
y = n // (cl // size) y = n // (cl // size)
@ -61,22 +56,20 @@ class Solution(BaseSolution):
def _join_blocks(self, blocks): def _join_blocks(self, blocks):
bl = len(blocks) bl = len(blocks)
c = int(bl**0.5) c = int(bl ** 0.5)
rl = int(len(blocks[0]) ** 0.5) rl = int(len(blocks[0]) ** 0.5)
canvas = "" canvas = ''
for i in range(0, bl, c): for i in range(0, bl, c):
for j in range(rl): for j in range(rl):
canvas += "".join( canvas += ''.join([block[j * rl:(j + 1) * rl] for block in blocks[i:i + c]])
[block[j * rl : (j + 1) * rl] for block in blocks[i : i + c]]
)
return canvas return canvas
def solve(self, puzzle_input, iterations=5): def solve(self, puzzle_input, iterations=5):
canvas = ".#...####" canvas = '.#...####'
rules = [Rule(l.strip()) for l in puzzle_input.splitlines()] rules = [Rule(l.strip()) for l in puzzle_input.splitlines()]
for _ in range(iterations): for _ in range(iterations):
size = 2 if len(canvas) % 2 == 0 else 3 size = 2 if len(canvas) % 2 == 0 else 3
blocks = len(canvas) // (size**2) blocks = len(canvas) // (size ** 2)
cb = [] cb = []
for b in range(blocks): for b in range(blocks):
bc = self._get_block(canvas, size, b) bc = self._get_block(canvas, size, b)
@ -85,12 +78,12 @@ class Solution(BaseSolution):
r = rule[0] r = rule[0]
cb.append(r.enhance(bc)) cb.append(r.enhance(bc))
canvas = self._join_blocks(cb) canvas = self._join_blocks(cb)
return collections.Counter(canvas)["#"] return collections.Counter(canvas)['#']
def solve_again(self, puzzle_input): def solve_again(self, puzzle_input):
return self.solve(puzzle_input, 18) return self.solve(puzzle_input, 18)
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -4,10 +4,10 @@ from solutions import BaseSolution
class Solution(BaseSolution): class Solution(BaseSolution):
input_file = "22.txt" input_file = '22.txt'
def __str__(self): def __str__(self):
return "Day 22: Sporifica Virus" return 'Day 22: Sporifica Virus'
infected = 0 infected = 0
@ -50,7 +50,7 @@ class Solution(BaseSolution):
dirs = dirs[1:] + [dirs[0]] dirs = dirs[1:] + [dirs[0]]
elif state == "#": elif state == "#":
dirs = [dirs[3]] + dirs[:3] dirs = [dirs[3]] + dirs[:3]
elif state == "F": elif state == 'F':
dirs = dirs[2:] + dirs[:2] dirs = dirs[2:] + dirs[:2]
return dirs return dirs
@ -61,16 +61,16 @@ class Solution(BaseSolution):
i, p = existing i, p = existing
return amap, p[2] return amap, p[2]
else: else:
amap.append([pos[0], pos[1], "."]) amap.append([pos[0], pos[1], '.'])
return amap, "." return amap, '.'
def _update_state(self, amap, pos): def _update_state(self, amap, pos):
t = lambda x: x[1][0] == pos[0] and x[1][1] == pos[1] t = lambda x: x[1][0] == pos[0] and x[1][1] == pos[1]
existing = next(filter(t, enumerate(amap))) existing = next(filter(t, enumerate(amap)))
i, p = existing i, p = existing
if p[2] == ".": if p[2] == '.':
self.infected += 1 self.infected += 1
amap[i][2] = "." if p[2] == "#" else "#" amap[i][2] = '.' if p[2] == '#' else '#'
return amap return amap
def _move(self, pos, d): def _move(self, pos, d):
@ -80,12 +80,12 @@ class Solution(BaseSolution):
t = lambda x: x[1][0] == pos[0] and x[1][1] == pos[1] t = lambda x: x[1][0] == pos[0] and x[1][1] == pos[1]
existing = next(filter(t, enumerate(amap))) existing = next(filter(t, enumerate(amap)))
i, p = existing i, p = existing
if p[2] == ".": if p[2] == '.':
ns = "W" ns = 'W'
elif p[2] == "W": elif p[2] == 'W':
self.infected += 1 self.infected += 1
ns = "#" ns = "#"
elif p[2] == "#": elif p[2] == '#':
ns = "F" ns = "F"
else: else:
ns = "." ns = "."
@ -93,6 +93,6 @@ class Solution(BaseSolution):
return amap return amap
if __name__ == "__main__": if __name__ == '__main__':
solution = Solution() solution = Solution()
solution.show_results() solution.show_results()

View file

@ -1,138 +0,0 @@
from solutions import BaseSolution
from collections import defaultdict
class Solution(BaseSolution):
input_file = "23.txt"
sound_freq = 0
queue = [], []
sent = [0, 0]
def __str__(self):
return "Day 23: Coprocessor Conflagration"
def solve(self, puzzle_input):
R = defaultdict(int)
P = puzzle_input.splitlines()
i = 0
M = 0
while i < len(P):
j = 1
w, r, v = P[i].split()
match w:
case "set":
R[r] = R[v] if v in "abcdefgh" else int(v)
case "sub":
R[r] -= R[v] if v in "abcdefgh" else int(v)
case "mul":
M += 1
R[r] *= R[v] if v in "abcdefgh" else int(v)
case "jnz":
x = R[r] if r in "abcdefgh" else int(r)
if x != 0:
j = R[v] if v in "abcdefgh" else int(v)
i += j
return M
def solve_again(self, puzzle_input):
def isprime(num):
for n in range(2, int(num**0.5) + 1):
if num % n == 0:
return False
return True
b = 99 * 100 + 100_000
c = b + 17_000 + 1
return sum(not isprime(n) for n in range(b, c, 17))
def python_reconstruction(self, puzzle_input):
R = defaultdict(int)
R["a"] = 1
# 00: set b 99
R["b"] = 99
# 01: set c b
R["c"] = R["b"]
# 02: jnz a 2
if R["a"] == 0:
# 03: jnz 1 5
pass
else:
# 04: mul b 100
# 05: sub b -100000
R["b"] = R["b"] * 100 + 100_000
# 06: set c b
# 07: sub c -17000
R["c"] = R["b"] + 17_000
while True:
# 08: set f 1
# 09: set d 2
R["f"] = 1
R["d"] = 2
while True:
# 10: set e 2
R["e"] = 2
while True:
# 11: set g d
# 12: mul g e
# 13: sub g b
R["g"] = R["d"] * R["e"] - R["b"]
# 14: jnz g 2
if R["g"] == 0:
# 15: set f 0
R["f"] = 0
# 16: sub e -1
R["e"] += 1
# 17: set g e
# 18: sub g b
R["g"] = R["e"] - R["b"]
# 19: jnz g -8
if R["g"] == 0:
break
# 20: sub d -1
R["d"] += 1
# 21: set g d
# 22: sub g b
R["g"] = R["d"] - R["b"]
# 23: jnz g -13
if R["g"] == 0:
break
# 24: jnz f 2
if R["f"] == 0:
# 25: sub h -1
R["h"] += 1
# 26: set g b
# 27: sub g c
R["g"] = R["b"] - R["c"]
# 28: jnz g 2
if R["g"] == 0:
# 29: jnz 1 3
break
else:
# 30: sub b -17
R["b"] += 17
# 31: jnz 1 -23
return R["h"]
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -1,51 +0,0 @@
from solutions import BaseSolution
import re
class Solution(BaseSolution):
input_file = "24.txt"
def __str__(self):
return "Day 24: Electromagnetic Moat"
def solve(self, puzzle_input):
p1, _ = self._solve(puzzle_input)
return p1
def solve_again(self, puzzle_input):
_, p2 = self._solve(puzzle_input)
return p2
def _solve(self, puzzle_input):
components = [
tuple(map(int, re.findall(r"\d+", line))) for line in puzzle_input.split()
]
Q = [(c, []) for c in components if 0 in c]
S = 0
L = 0
LS = 0
while Q:
c, s = Q.pop()
if c in s:
S = max(S, sum(x + y for x, y in s))
if len(s) >= L:
L = len(s)
LS = max(LS, L * 1_000_000 + sum(x + y for x, y in s))
continue
cc = set(c) if not s else set(c) - set(s[-1])
if not cc:
cc = set(c)
for p1, p2 in components:
if p1 > 0 and p1 in cc:
Q.append(((p1, p2), s + [c]))
if p2 > 0 and p2 in cc:
Q.append(((p1, p2), s + [c]))
return S, LS % 1_000_000
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -1,46 +0,0 @@
from solutions import BaseSolution
from collections import defaultdict
import re
class Solution(BaseSolution):
input_file = "25.txt"
def __str__(self):
return "Day 25: The Halting Problem"
def solve(self, puzzle_input):
T = defaultdict(int)
p = 0
hd, *states = puzzle_input.split("\n\n")
S = int(re.findall(r"(\d+) steps", hd)[0])
s = re.findall(r"state (\w)\.", hd)[0]
P = {}
for state in states:
k = re.findall(r"state (\w)\:", state)[0]
v = [int(n) for n in re.findall(r"value (\d)\.", state)]
d = re.findall(r"(left|right)\.", state)
t = re.findall(r"state (\w)\.", state)
P[k] = (v[0], d[0], t[0], v[1], d[1], t[1])
for _ in range(S):
v0, d0, s0, v1, d1, s1 = P[s]
match T[p]:
case 0:
T[p] = v0
s = s0
p += 1 if d0 == "right" else -1
case 1:
T[p] = v1
s = s1
p += 1 if d1 == "right" else -1
return sum(T.values())
def solve_again(self, puzzle_input):
return "*"
if __name__ == "__main__":
solution = Solution()
solution.show_results()

View file

@ -8,18 +8,18 @@ class Day1TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_sums_equal_pairs(self): def test_sums_equal_pairs(self):
assert self.solution.solve("1122") == 3 assert self.solution.solve('1122') == 3
assert self.solution.solve("1111") == 4 assert self.solution.solve('1111') == 4
assert self.solution.solve("1234") == 0 assert self.solution.solve('1234') == 0
assert self.solution.solve("91212129") == 9 assert self.solution.solve('91212129') == 9
def test_sums_equal_pairs_halvway_around(self): def test_sums_equal_pairs_halvway_around(self):
assert self.solution.solve_again("1212") == 6 assert self.solution.solve_again('1212') == 6
assert self.solution.solve_again("1221") == 0 assert self.solution.solve_again('1221') == 0
assert self.solution.solve_again("123425") == 4 assert self.solution.solve_again('123425') == 4
assert self.solution.solve_again("123123") == 12 assert self.solution.solve_again('123123') == 12
assert self.solution.solve_again("12131415") == 4 assert self.solution.solve_again('12131415') == 4
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -13,19 +13,19 @@ class Day2TestCase(unittest.TestCase):
assert self.solution.get_diff([2, 4, 6, 8]) == 6 assert self.solution.get_diff([2, 4, 6, 8]) == 6
def test_calculates_checksum(self): def test_calculates_checksum(self):
puzzle_input = "\n".join(["5 1 9 5", "7 5 3", "2 4 6 8"]) puzzle_input = '\n'.join(['5 1 9 5', '7 5 3', '2 4 6 8'])
assert self.solution.solve(puzzle_input) == 18 assert self.solution.solve(puzzle_input) == 18
def test_calculates_row_even_divisible(self): def test_calculates_row_even_divisible(self):
puzzle_input = "\n".join(["5 9 2 8", "9 4 7 3", "3 8 6 5"]) puzzle_input = '\n'.join(['5 9 2 8', '9 4 7 3', '3 8 6 5'])
assert self.solution.get_even_divisible([5, 9, 2, 8]) == 4 assert self.solution.get_even_divisible([5, 9, 2, 8]) == 4
assert self.solution.get_even_divisible([9, 4, 7, 3]) == 3 assert self.solution.get_even_divisible([9, 4, 7, 3]) == 3
assert self.solution.get_even_divisible([3, 8, 6, 5]) == 2 assert self.solution.get_even_divisible([3, 8, 6, 5]) == 2
def test_calculates_row_result_sum(self): def test_calculates_row_result_sum(self):
puzzle_input = "\n".join(["5 9 2 8", "9 4 7 3", "3 8 6 5"]) puzzle_input = '\n'.join(['5 9 2 8', '9 4 7 3', '3 8 6 5'])
assert self.solution.solve_again(puzzle_input) == 9 assert self.solution.solve_again(puzzle_input) == 9
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,11 +8,11 @@ class Day3TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_shortest_manhattan_distance(self): def test_shortest_manhattan_distance(self):
assert self.solution.solve("1") == 0 assert self.solution.solve('1') == 0
assert self.solution.solve("12") == 3 assert self.solution.solve('12') == 3
assert self.solution.solve("23") == 2 assert self.solution.solve('23') == 2
assert self.solution.solve("1024") == 31 assert self.solution.solve('1024') == 31
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -9,22 +9,22 @@ class Day4TestCase(unittest.TestCase):
def test_passphrase_has_only_unique_words(self): def test_passphrase_has_only_unique_words(self):
passphrases = [ passphrases = [
"aa bb cc dd ee", 'aa bb cc dd ee',
"aa bb cc dd aa", 'aa bb cc dd aa',
"aa bb cc dd aaa", 'aa bb cc dd aaa',
] ]
assert self.solution.validate(passphrases[0]) == True assert self.solution.validate(passphrases[0]) == True
assert self.solution.validate(passphrases[1]) == False assert self.solution.validate(passphrases[1]) == False
assert self.solution.validate(passphrases[2]) == True assert self.solution.validate(passphrases[2]) == True
assert self.solution.solve("\n".join(passphrases)) == 2 assert self.solution.solve('\n'.join(passphrases)) == 2
def test_passphrase_has_no_anagrams(self): def test_passphrase_has_no_anagrams(self):
passphrases = [ passphrases = [
"abcde fghij", 'abcde fghij',
"abcde xyz ecdab", 'abcde xyz ecdab',
"a ab abc abd abf abj", 'a ab abc abd abf abj',
"iiii oiii ooii oooi oooo", 'iiii oiii ooii oooi oooo',
"oiii ioii iioi iiio", 'oiii ioii iioi iiio',
] ]
assert self.solution.validate(passphrases[0], True) == True assert self.solution.validate(passphrases[0], True) == True
assert self.solution.validate(passphrases[1], True) == False assert self.solution.validate(passphrases[1], True) == False
@ -33,5 +33,5 @@ class Day4TestCase(unittest.TestCase):
assert self.solution.validate(passphrases[4], True) == False assert self.solution.validate(passphrases[4], True) == False
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,29 +8,13 @@ class Day5TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_calculate_exit_distance(self): def test_calculate_exit_distance(self):
puzzle_input = "\n".join( puzzle_input = '\n'.join(['0', '3', '0', '1', '-3',])
[
"0",
"3",
"0",
"1",
"-3",
]
)
assert self.solution.solve(puzzle_input) == 5 assert self.solution.solve(puzzle_input) == 5
def test_calculate_stranger_exit_distance(self): def test_calculate_stranger_exit_distance(self):
puzzle_input = "\n".join( puzzle_input = '\n'.join(['0', '3', '0', '1', '-3',])
[
"0",
"3",
"0",
"1",
"-3",
]
)
assert self.solution.solve_again(puzzle_input) == 10 assert self.solution.solve_again(puzzle_input) == 10
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,7 +8,7 @@ class Day6TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_count_redistribution_cycles(self): def test_count_redistribution_cycles(self):
puzzle_input = "0 2 7 0" puzzle_input = '0 2 7 0'
banks = list(map(int, puzzle_input.split())) banks = list(map(int, puzzle_input.split()))
assert self.solution.redistribute(banks) == (2, 4, 1, 2) assert self.solution.redistribute(banks) == (2, 4, 1, 2)
assert self.solution.redistribute((2, 4, 1, 2)) == (3, 1, 2, 3) assert self.solution.redistribute((2, 4, 1, 2)) == (3, 1, 2, 3)
@ -18,9 +18,9 @@ class Day6TestCase(unittest.TestCase):
assert self.solution.solve(puzzle_input) == 5 assert self.solution.solve(puzzle_input) == 5
def test_count_redistribution_cycles_again(self): def test_count_redistribution_cycles_again(self):
puzzle_input = "0 2 7 0" puzzle_input = '0 2 7 0'
assert self.solution.solve_again(puzzle_input) == 4 assert self.solution.solve_again(puzzle_input) == 4
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -5,7 +5,7 @@ from solutions.day_07 import Solution, Program
class Day7TestCase(unittest.TestCase): class Day7TestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.puzzle_input = """ self.puzzle_input = '''
pbga (66) pbga (66)
xhth (57) xhth (57)
ebii (61) ebii (61)
@ -19,24 +19,24 @@ class Day7TestCase(unittest.TestCase):
ugml (68) -> gyxo, ebii, jptl ugml (68) -> gyxo, ebii, jptl
gyxo (61) gyxo (61)
cntj (57) cntj (57)
""".strip() '''.strip()
self.solution = Solution() self.solution = Solution()
def test_find_bottom_tower(self): def test_find_bottom_tower(self):
p = Program("ugml (68) -> gyxo, ebii, jptl") p = Program('ugml (68) -> gyxo, ebii, jptl')
assert p.name == "ugml" assert p.name == 'ugml'
assert p.weight == 68 assert p.weight == 68
assert p.disc == ("gyxo", "ebii", "jptl") assert p.disc == ('gyxo', 'ebii', 'jptl')
p = Program("jptl (61)") p = Program('jptl (61)')
assert p.name == "jptl" assert p.name == 'jptl'
assert p.weight == 61 assert p.weight == 61
assert p.disc == () assert p.disc == ()
assert self.solution.solve(self.puzzle_input).name == "tknk" assert self.solution.solve(self.puzzle_input).name == 'tknk'
def test_find_weight_correction(self): def test_find_weight_correction(self):
corrected = self.solution.solve_again(self.puzzle_input) corrected = self.solution.solve_again(self.puzzle_input)
assert corrected == 60 assert corrected == 60
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,23 +8,23 @@ class Day8TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_largest_registry_value(self): def test_largest_registry_value(self):
puzzle_input = """ puzzle_input = '''
b inc 5 if a > 1 b inc 5 if a > 1
a inc 1 if b < 5 a inc 1 if b < 5
c dec -10 if a >= 1 c dec -10 if a >= 1
c inc -20 if c == 10 c inc -20 if c == 10
""".strip() '''.strip()
assert self.solution.solve(puzzle_input) == 1 assert self.solution.solve(puzzle_input) == 1
def test_largest_ath_registry_value(self): def test_largest_ath_registry_value(self):
puzzle_input = """ puzzle_input = '''
b inc 5 if a > 1 b inc 5 if a > 1
a inc 1 if b < 5 a inc 1 if b < 5
c dec -10 if a >= 1 c dec -10 if a >= 1
c inc -20 if c == 10 c inc -20 if c == 10
""".strip() '''.strip()
assert self.solution.solve_again(puzzle_input) == 10 assert self.solution.solve_again(puzzle_input) == 10
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,24 +8,24 @@ class Day9TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_calculates_score(self): def test_calculates_score(self):
assert self.solution.solve("{}") == 1 assert self.solution.solve('{}') == 1
assert self.solution.solve("{{{}}}") == 6 assert self.solution.solve('{{{}}}') == 6
assert self.solution.solve("{{},{}}") == 5 assert self.solution.solve('{{},{}}') == 5
assert self.solution.solve("{{{},{},{{}}}}") == 16 assert self.solution.solve('{{{},{},{{}}}}') == 16
assert self.solution.solve("{<a>,<a>,<a>,<a>}") == 1 assert self.solution.solve('{<a>,<a>,<a>,<a>}') == 1
assert self.solution.solve("{{<ab>},{<ab>},{<ab>},{<ab>}}") == 9 assert self.solution.solve('{{<ab>},{<ab>},{<ab>},{<ab>}}') == 9
assert self.solution.solve("{{<!!>},{<!!>},{<!!>},{<!!>}}") == 9 assert self.solution.solve('{{<!!>},{<!!>},{<!!>},{<!!>}}') == 9
assert self.solution.solve("{{<a!>},{<a!>},{<a!>},{<ab>}}") == 3 assert self.solution.solve('{{<a!>},{<a!>},{<a!>},{<ab>}}') == 3
def test_count_garbage(self): def test_count_garbage(self):
assert self.solution.solve_again("<>") == 0 assert self.solution.solve_again('<>') == 0
assert self.solution.solve_again("<random characters>") == 17 assert self.solution.solve_again('<random characters>') == 17
assert self.solution.solve_again("<<<<>") == 3 assert self.solution.solve_again('<<<<>') == 3
assert self.solution.solve_again("<{!>}>") == 2 assert self.solution.solve_again('<{!>}>') == 2
assert self.solution.solve_again("<!!>") == 0 assert self.solution.solve_again('<!!>') == 0
assert self.solution.solve_again("<!!!>>") == 0 assert self.solution.solve_again('<!!!>>') == 0
assert self.solution.solve_again('<{o"i!a,<{i<a>') == 10 assert self.solution.solve_again('<{o"i!a,<{i<a>') == 10
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -25,16 +25,14 @@ class Day10TestCase(unittest.TestCase):
assert self.solution.list == [3, 4, 2, 1, 0] assert self.solution.list == [3, 4, 2, 1, 0]
assert self.solution.skip_size == 4 assert self.solution.skip_size == 4
assert self.solution.pos == 4 assert self.solution.pos == 4
assert self.solution.solve("3,4,1,5", r=5) == 12 assert self.solution.solve('3,4,1,5', r=5) == 12
def test_dense_hash(self): def test_dense_hash(self):
assert self.solution.solve_again("") == "a2582a3a0e66e6e86e3812dcb672a272" assert self.solution.solve_again('') == 'a2582a3a0e66e6e86e3812dcb672a272'
assert ( assert self.solution.solve_again('AoC 2017') == '33efeb34ea91902bb2f59c9920caa6cd'
self.solution.solve_again("AoC 2017") == "33efeb34ea91902bb2f59c9920caa6cd" assert self.solution.solve_again('1,2,3') == '3efbe78a8d82f29979031a4aa0b16a9d'
) assert self.solution.solve_again('1,2,4') == '63960835bcdc130f0b66d7ff4f6a5a8e'
assert self.solution.solve_again("1,2,3") == "3efbe78a8d82f29979031a4aa0b16a9d"
assert self.solution.solve_again("1,2,4") == "63960835bcdc130f0b66d7ff4f6a5a8e"
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,15 +8,15 @@ class Day11TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_distance(self): def test_distance(self):
assert self.solution.solve("ne,ne,ne") == 3 assert self.solution.solve('ne,ne,ne') == 3
assert self.solution.solve("ne,ne,sw,sw") == 0 assert self.solution.solve('ne,ne,sw,sw') == 0
assert self.solution.solve("ne,ne,s,s") == 2 assert self.solution.solve('ne,ne,s,s') == 2
assert self.solution.solve("se,sw,se,sw,sw") == 3 assert self.solution.solve('se,sw,se,sw,sw') == 3
def test_furthest_away(self): def test_furthest_away(self):
assert self.solution.solve_again("ne,ne,sw,sw") == 2 assert self.solution.solve_again('ne,ne,sw,sw') == 2
assert self.solution.solve_again("se,sw,se,sw,sw") == 3 assert self.solution.solve_again('se,sw,se,sw,sw') == 3
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,7 +8,7 @@ class Day12TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_connected_to_program0(self): def test_connected_to_program0(self):
puzzle_input = """ puzzle_input = '''
0 <-> 2 0 <-> 2
1 <-> 1 1 <-> 1
2 <-> 0, 3, 4 2 <-> 0, 3, 4
@ -16,11 +16,11 @@ class Day12TestCase(unittest.TestCase):
4 <-> 2, 3, 6 4 <-> 2, 3, 6
5 <-> 6 5 <-> 6
6 <-> 4, 5 6 <-> 4, 5
""".strip() '''.strip()
assert self.solution.solve(puzzle_input) == 6 assert self.solution.solve(puzzle_input) == 6
def test_group_coun(self): def test_group_coun(self):
puzzle_input = """ puzzle_input = '''
0 <-> 2 0 <-> 2
1 <-> 1 1 <-> 1
2 <-> 0, 3, 4 2 <-> 0, 3, 4
@ -28,9 +28,9 @@ class Day12TestCase(unittest.TestCase):
4 <-> 2, 3, 6 4 <-> 2, 3, 6
5 <-> 6 5 <-> 6
6 <-> 4, 5 6 <-> 4, 5
""".strip() '''.strip()
assert self.solution.solve_again(puzzle_input) == 2 assert self.solution.solve_again(puzzle_input) == 2
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,23 +8,23 @@ class Day13TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_get_through_firewall(self): def test_get_through_firewall(self):
puzzle_input = """ puzzle_input = '''
0: 3 0: 3
1: 2 1: 2
4: 4 4: 4
6: 4 6: 4
""".strip() '''.strip()
assert self.solution.solve(puzzle_input) == 24 assert self.solution.solve(puzzle_input) == 24
def test_wait(self): def test_wait(self):
puzzle_input = """ puzzle_input = '''
0: 3 0: 3
1: 2 1: 2
4: 4 4: 4
6: 4 6: 4
""".strip() '''.strip()
assert self.solution.solve_again(puzzle_input) == 10 assert self.solution.solve_again(puzzle_input) == 10
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,11 +8,11 @@ class Day14TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_used_squares(self): def test_used_squares(self):
assert self.solution.solve("flqrgnkx") == 8108 assert self.solution.solve('flqrgnkx') == 8108
def test_regions(self): def test_regions(self):
assert self.solution.solve_again("flqrgnkx") == 1242 assert self.solution.solve_again('flqrgnkx') == 1242
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,19 +8,19 @@ class Day15TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_40m_pairs(self): def test_40m_pairs(self):
puzzle_input = """ puzzle_input = '''
Generator A starts with 65 Generator A starts with 65
Generator B starts with 8921 Generator B starts with 8921
""".strip() '''.strip()
# assert self.solution.solve(puzzle_input) == 588 #assert self.solution.solve(puzzle_input) == 588
def test_5k_pairs(self): def test_5k_pairs(self):
puzzle_input = """ puzzle_input = '''
Generator A starts with 65 Generator A starts with 65
Generator B starts with 8921 Generator B starts with 8921
""".strip() '''.strip()
assert self.solution.solve_again(puzzle_input) == 309 assert self.solution.solve_again(puzzle_input) == 309
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,9 +8,9 @@ class Day16TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_something(self): def test_something(self):
puzzle_input = """s1,x3/4,pe/b""".strip() puzzle_input = '''s1,x3/4,pe/b'''.strip()
assert self.solution.solve(puzzle_input, 5) == "baedc" assert self.solution.solve(puzzle_input, 5) == 'baedc'
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,8 +8,8 @@ class Day17TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_something(self): def test_something(self):
assert self.solution.solve("3") == 638 assert self.solution.solve('3') == 638
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,7 +8,7 @@ class Day18TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_something(self): def test_something(self):
puzzle_input = """ puzzle_input = '''
set a 1 set a 1
add a 2 add a 2
mul a a mul a a
@ -19,11 +19,11 @@ class Day18TestCase(unittest.TestCase):
jgz a -1 jgz a -1
set a 1 set a 1
jgz a -2 jgz a -2
""".strip() '''.strip()
assert self.solution.solve(puzzle_input) == 4 assert self.solution.solve(puzzle_input) == 4
def test_something_else(self): def test_something_else(self):
puzzle_input = """ puzzle_input = '''
snd 1 snd 1
snd 2 snd 2
snd p snd p
@ -31,9 +31,9 @@ class Day18TestCase(unittest.TestCase):
rcv b rcv b
rcv c rcv c
rcv d rcv d
""".strip() '''.strip()
assert self.solution.solve_again(puzzle_input) == 3 assert self.solution.solve_again(puzzle_input) == 3
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,27 +8,27 @@ class Day19TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_seen(self): def test_seen(self):
puzzle_input = """ | puzzle_input = ''' |
| +--+ | +--+
A | C A | C
F---|----E|--+ F---|----E|--+
| | | D | | | D
+B-+ +--+ +B-+ +--+
""" '''
assert self.solution.solve(puzzle_input) == "ABCDEF" assert self.solution.solve(puzzle_input) == 'ABCDEF'
def test_steps(self): def test_steps(self):
puzzle_input = """ | puzzle_input = ''' |
| +--+ | +--+
A | C A | C
F---|----E|--+ F---|----E|--+
| | | D | | | D
+B-+ +--+ +B-+ +--+
""" '''
assert self.solution.solve_again(puzzle_input) == 38 assert self.solution.solve_again(puzzle_input) == 38
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,21 +8,21 @@ class Day20TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_shortest_distance_over_time(self): def test_shortest_distance_over_time(self):
puzzle_input = """ puzzle_input = '''
p=< 3,0,0>, v=< 2,0,0>, a=<-1,0,0> p=< 3,0,0>, v=< 2,0,0>, a=<-1,0,0>
p=< 4,0,0>, v=< 0,0,0>, a=<-2,0,0> p=< 4,0,0>, v=< 0,0,0>, a=<-2,0,0>
""".strip() '''.strip()
assert self.solution.solve(puzzle_input, 4) == 0 assert self.solution.solve(puzzle_input, 4) == 0
def test_something(self): def test_something(self):
puzzle_input = """ puzzle_input = '''
p=<-6,0,0>, v=< 3,0,0>, a=< 0,0,0> p=<-6,0,0>, v=< 3,0,0>, a=< 0,0,0>
p=<-4,0,0>, v=< 2,0,0>, a=< 0,0,0> p=<-4,0,0>, v=< 2,0,0>, a=< 0,0,0>
p=<-2,0,0>, v=< 1,0,0>, a=< 0,0,0> p=<-2,0,0>, v=< 1,0,0>, a=< 0,0,0>
p=< 3,0,0>, v=<-1,0,0>, a=< 0,0,0> p=< 3,0,0>, v=<-1,0,0>, a=< 0,0,0>
""".strip() '''.strip()
assert self.solution.solve_again(puzzle_input, 4) == 1 assert self.solution.solve_again(puzzle_input, 4) == 1
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -8,12 +8,12 @@ class Day21TestCase(unittest.TestCase):
self.solution = Solution() self.solution = Solution()
def test_something(self): def test_something(self):
puzzle_input = """ puzzle_input = '''
../.# => ##./#../... ../.# => ##./#../...
.#./..#/### => #..#/..../..../#..# .#./..#/### => #..#/..../..../#..#
""".strip() '''.strip()
assert self.solution.solve(puzzle_input, 2) == 12 assert self.solution.solve(puzzle_input, 2) == 12
if __name__ == "__main__": if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -26,6 +26,5 @@ class Day22TestCase(unittest.TestCase):
assert self.solution.solve_again(puzzle_input, 100) == 26 assert self.solution.solve_again(puzzle_input, 100) == 26
assert self.solution.solve_again(puzzle_input, 10000000) == 2511944 assert self.solution.solve_again(puzzle_input, 10000000) == 2511944
if __name__ == '__main__':
if __name__ == "__main__":
unittest.main() unittest.main()

1
2019-python/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
__pycache__

View file

@ -1,16 +1,10 @@
# Advent of Code 2024 Advent of Code 2019
===================
Solutions for #aoc2024 in Python 3 (3.12.7). Solutions for #aoc2019 in Python 3 (3.11.5).
Programming setup: Help scripts
------------
- Lenovo Thinkpad X260
- Arch Linux with Hyprland
- Zed editor (Ruff, Pyright)
- Firefox
- Alacritty
## Help scripts
Display all solved puzzles: Display all solved puzzles:
@ -18,31 +12,24 @@ Display all solved puzzles:
To bootstrap a new puzzle (creates `input/<day_no>.txt` and `output/day_<day_no>.py`): To bootstrap a new puzzle (creates `input/<day_no>.txt` and `output/day_<day_no>.py`):
python aoc.py <day_no> new python aoc.py <dag_no> "<puzzle_name>"
Manually copy the puzzle input from https://adventofcode.com and paste it in `input/<day_no>.txt` Manually copy the puzzle input from https://adventofcode.com and paste it in `input/<day_no>.txt`
to start coding. to start coding.
wl-paste > input/<day_no>.txt
Solve separate puzzle (replace `XX` with the puzzle number): Solve separate puzzle (replace `XX` with the puzzle number):
python -m output.day_XX python -m output.day_XX
Solve separate puzzle using stdin (replace `XX` with the puzzle number): Solve separate puzzle using stdin (replace `XX` with the puzzle number):
wl-paste | python -m output.day_XX xclip -selection clipboard -o | python -m output.day_XX
cat tmpfile | python -m output.day_XX cat tmpfile | python -m output.day_XX
Execute separate puzzle on file save (replace `XX` with the puzzle number): Execute separate puzzle on file save (replace `XX` with the puzzle number):
ls output/*.py | entr -c -s 'wlpaste | python -m output.day_XX' 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 -s 'cat tmpfile | python -m output.day_XX'
ls output/*.py | entr -c -r 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 (requires `entr` and `xclip`, Mac users can instead use `pbpaste`)
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,13 +1,13 @@
import os
import sys import sys
from pathlib import Path
def headline(n): def headline(n, title):
"""Print day number and name, followed by a ruler. Used by the answer decorator""" """Print day number and name, followed by a ruler. Used by the answer decorator"""
print(f"\n--- Day {n} ---\n") print(f"\n--- Day {n}: {title} ---\n")
year = 2016 year = 2019
try: try:
_, day_no, *name = sys.argv _, day_no, *name = sys.argv
@ -19,9 +19,6 @@ print(
f"\nAdvent of Code {year}" "\n###################" "\n\nby Anders Englöf Ytterström" f"\nAdvent of Code {year}" "\n###################" "\n\nby Anders Englöf Ytterström"
) )
Path("./input").mkdir(parents=True, exist_ok=True)
Path("./output").mkdir(parents=True, exist_ok=True)
if day_no and name: if day_no and name:
name = " ".join(name) name = " ".join(name)
padded_no = day_no.zfill(2) padded_no = day_no.zfill(2)
@ -29,18 +26,24 @@ if day_no and name:
with open("output/day_{}.py".format(padded_no), "w") as s: with open("output/day_{}.py".format(padded_no), "w") as s:
s.write( s.write(
f""" f"""
import re from output import answer # , matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg
from collections import deque, Counter, defaultdict
from heapq import heappop, heappush
from itertools import compress, combinations, chain, permutations
from output import matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg n = {day_no}
title = "{name}"
@answer(1, "Answer is {{}}")
def part_1(outputs):
return outputs[0]
@answer(2, "Actually, answer is {{}}")
def part_2(outputs):
return outputs[1]
def solve(data): def solve(data):
p1 = 1 return None, None
p2 = 2
return p1, p2
if __name__ == "__main__": if __name__ == "__main__":
@ -56,19 +59,22 @@ if __name__ == "__main__":
# with open("./input/{padded_no}.txt", "r") as f: # with open("./input/{padded_no}.txt", "r") as f:
# inp = f.read().strip() # inp = f.read().strip()
# uncomment to do initial data processing shared by part 1-2 inp = solve(inp)
p1, p2 = solve(inp)
print(p1) a = part_1(inp)
# print(p2) # b = part_2(inp)
# uncomment and replace 0 with actual output to refactor code # uncomment and replace 0 with actual output to refactor code
# and ensure nonbreaking changes # and ensure nonbreaking changes
# assert p1 == 0 # assert a == 0
# assert p2 == 0 # assert b == 0
""".strip() """.strip()
+ "\n" + "\n"
) )
print("- making sure input dir exists")
if not os.path.exists("input"):
os.makedirs("input")
print( print(
f""" f"""
Done! start coding. Done! start coding.
@ -91,25 +97,19 @@ for i in [str(n).zfill(2) for n in range(1, 26)]:
"output.day_{}".format(i), "output.day_{}".format(i),
globals(), globals(),
locals(), locals(),
["solve"], ["n", "title", "part_1", "part_2", "solve"],
0, 0,
) )
with open(f"./input/{i}.txt", "r") as f: with open(f"./input/{i}.txt", "r") as f:
data = f.read().strip() data = f.read().strip()
headline(i) headline(day.n, day.title)
try: try:
data = day.presolve(data) data = day.solve(data)
except AttributeError: except AttributeError:
pass pass
try: if day.part_1(data, decorate=True):
p1, p2 = day.solve(data)
except AttributeError:
pass
if p1:
print(f" 1. {p1}")
stars += 1 stars += 1
if p2: if day.part_2(data, decorate=True):
print(f" 2. {p2}")
stars += 1 stars += 1
except IOError: except IOError:
pass pass

View file

@ -30,9 +30,31 @@ ADJ = [
] ]
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): def ints(s):
"""Extract all integers from a string""" """Extract all integers from a string"""
return [int(n) for n in re.findall(r"\d+", s)] return [int(n) for n in re.findall(r"-?\d+", s)]
def mhd(a, b): def mhd(a, b):
@ -58,19 +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)]))
def bfs(S, E=None):
"""BFS algorithm, equal weighted nodes"""
seen = set()
q = [(S, 0)]
g = {} # graph, required to be provided at some point
while q:
m, w = q.pop(0)
if m in seen:
continue
seen.add(m)
# investigate here
for s in g[m]:
q.append((s, w + 1))
# return insights

View file

@ -0,0 +1,40 @@
from output import answer
n = 1
title = "The Tyranny of the Rocket Equation"
@answer(1, "Total fuel requirements are {}")
def part_1(o):
return o[0]
@answer(2, "Total fuel requirements are {} including fuel costs")
def part_2(o):
return o[1]
def solve(data):
lines = list(map(int, data.split()))
p1 = sum(n // 3 - 2 for n in lines)
p2 = 0
for fuel in lines:
rem = fuel
while rem > 0:
cost = rem // 3 - 2
p2 += max(0, cost)
rem = max(0, cost)
return p1, p2
if __name__ == "__main__":
with open("./input/01.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 3393938
assert b == 5088037

View file

@ -0,0 +1,48 @@
from output import answer
from output.intcode_computer import execute, parse
n = 2
title = "1202 Program Alarm"
@answer(1, "[intcode-0.1.0] Value of pos 0 is {} at halt signal")
def part_1(o):
return o[0]
@answer(2, "[intcode-0.1.1] 100 * noun + verb = {} for output 19690720")
def part_2(o):
return o[1]
def solve(data):
program = parse(data)
program[1] = 12
program[2] = 2
_code, state, *_unused = execute(program)
noun = 76 # found manually by binary search
verb = 21
p1 = state[0]
program[1] = noun
program[2] = verb
_code, state, *_unused = execute(program)
p2 = state[0]
if state[0] == 19690720:
p2 = 100 * noun + verb
return p1, p2
if __name__ == "__main__":
with open("./input/02.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 3306701
assert b == 7621

View file

@ -0,0 +1,67 @@
from collections import defaultdict
from output import answer
n = 3
title = "Crossed Wires"
directions = {
"U": (-1, 0),
"R": (0, 1),
"D": (1, 0),
"L": (0, -1),
}
@answer(
1, "As the crow flies, closest intersection Manhattan distance is {} units away"
)
def part_1(o):
return o[0]
@answer(2, "By travel, closest intersection Manhattan distance is {} units away")
def part_2(o):
return o[1]
def solve(inp):
wires = [line.split(",") for line in inp.split()]
seen = defaultdict(dict)
def follow(instructions, i):
visited = []
steps = 0
pos = (0, 0)
for instruction in instructions:
urdl, *l = instruction
distance = int("".join(l))
for _ in range(distance):
steps += 1
pos = (pos[0] + directions[urdl][0], pos[1] + directions[urdl][1])
visited.append(pos)
if i not in seen[pos]:
seen[pos][i] = steps
return set(visited)
p1w = []
for i, wire in enumerate(wires):
p1w.append(follow(wire, i))
p1 = min(sum(map(abs, i)) for i in p1w[0] & p1w[1])
p2 = min(sum(v.values()) for v in seen.values() if len(v) > 1)
return p1, p2
if __name__ == "__main__":
with open("./input/03.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 1337
assert b == 65356

View file

@ -0,0 +1,44 @@
from collections import Counter
from output import answer
n = 4
title = "Secure Container"
@answer(1, "{} combinations of valid passwords")
def part_1(o):
return o[0]
@answer(2, "{} combinations of valid passwords, including important detail")
def part_2(o):
return o[1]
def solve(data):
a, b = data.split("-")
def v1(s):
return "".join(sorted(s)) == s and any(x == y for x, y in zip(s, s[1:]))
def v2(s):
return "".join(sorted(s)) == s and 2 in Counter(s).values()
p1 = sum(v1(str(pw)) for pw in range(int(a), int(b) + 1))
p2 = sum(v2(str(pw)) for pw in range(int(a), int(b) + 1))
return p1, p2
if __name__ == "__main__":
with open("./input/04.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 544
assert b == 334

View file

@ -0,0 +1,40 @@
from output import answer
from output.intcode_computer import execute, parse
n = 5
title = "Sunny with a Chance of Asteroids"
@answer(1, "[intcode-0.2.0] Program diagnostic code, ID 1: {}")
def part_1(o):
return o[0]
@answer(2, "[intcode-0.2.1] Program diagnostic code, ID 5: {}")
def part_2(o):
return o[1]
def solve(data):
program = parse(data)
_code, _state, _cursorpos, rb, stdout = execute(program, stdin=[1])
p1 = max(stdout)
_code, _state, _cursorpos, rb, stdout = execute(program, stdin=[5])
p2 = stdout[0]
return p1, p2
if __name__ == "__main__":
with open("./input/05.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 16434972
assert b == 16694270

View file

@ -0,0 +1,58 @@
from collections import defaultdict
from output import answer
n = 6
title = "Universal Orbit Map"
@answer(1, "{} direct and indirect orbits")
def part_1(o):
return o[0]
@answer(2, "Orbit transfers needed for you to share orbit with Santa: {}")
def part_2(o):
return o[1]
def solve(data):
heritage = defaultdict(str)
for parent, child in [line.split(")") for line in data.split()]:
heritage[child] = parent
p1 = sum(len(ancestry(heritage, v)) for v in heritage.keys())
a = ancestry(heritage, "YOU")
b = ancestry(heritage, "SAN")
shared = len(set(a) & set(b))
p2 = sum(
[
len(a) - shared,
len(b) - shared,
]
)
return p1, p2
def ancestry(parents, child):
k = child
lineage = []
while k in parents:
lineage.append(parents[k])
k = parents[k]
return lineage[::-1]
if __name__ == "__main__":
with open("./input/06.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 271151
assert b == 388

View file

@ -0,0 +1,77 @@
from collections import defaultdict
from itertools import permutations
from output import answer
from output.intcode_computer import execute, parse
n = 7
title = "Amplification Circuit"
@answer(
1,
"[intcode 0.3.0] The highest achievable signal to the thruster is {}",
)
def part_1(o):
return o[0]
@answer(
2,
"[intcode 0.3.0] By creating a feedback loop, the highest achievable signal to the thruster is {}",
)
def part_2(o):
return o[0]
def solve(data):
program = parse(data)
thruster_signals = []
for settings in map(list, permutations(range(5))):
o = 0
for ps in settings:
_code, _state, _n, _rb, so = execute(program, stdin=[ps, o])
o = so.pop(0)
thruster_signals.append(o)
p1 = max(thruster_signals)
thruster_signals = []
for settings in map(list, permutations(range(5, 10))):
o = [0]
finished = set()
paused = defaultdict(tuple)
while len(finished) < 5:
for amp, ps in enumerate(settings):
if paused[amp]:
program, resume_at = paused[amp]
del paused[amp]
code, state, n, _rb, so = execute(program, stdin=o, n=resume_at)
else:
code, state, n, _rb, so = execute(program, stdin=[ps, *o])
if code == 3:
paused[amp] = (
list(state.values()),
n,
)
o = so
if code == 99:
finished.add(amp)
o = so
thruster_signals.append(o[-1])
p2 = max(thruster_signals)
return p1, p2
if __name__ == "__main__":
with open("./input/07.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 398674
assert b == 39431233

View file

@ -0,0 +1,50 @@
from collections import Counter
from textwrap import wrap
from output import answer
n = 8
title = "Space Image Format"
@answer(1, "The product of all 1s and 2s in the layer with fewest 0s is {}")
def part_1(o):
return o[0]
@answer(2, "The message is {}, the decoded image looks like above")
def part_2(o):
return o[1]
def solve(data):
layers = sorted(map(Counter, wrap(data, 25 * 6)), key=lambda c: c["0"])
width, height = 25, 6
a = layers[0]["1"]
b = layers[0]["2"]
p1 = a * b
layers = wrap(data, width * height)
pixels = zip(*layers)
lit = map(
lambda s: s.replace("0", ".").replace("1", "#"),
map(lambda p: next(filter(lambda x: x != "2", p)), pixels),
)
matrix = "\n".join(wrap("".join(lit), width))
print(matrix)
p2 = "CYUAH"
return p1, p2
if __name__ == "__main__":
with open("./input/08.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 2500
assert b == "CYUAH"

View file

@ -0,0 +1,86 @@
from output import answer
from output.intcode_computer import execute, parse
n = 9
title = "Sensor Boost"
@answer(1, "[intcode 0.3.1] BOOST keycode: {}")
def part_1(o):
return o[0]
@answer(2, "[intcode 0.3.1] Distress signal coordinates: {}")
def part_2(o):
return o[1]
def solve(data):
program = parse(data)
p12 = []
for inp in [1, 2]:
_c, _s, _n, _rb, outputs = execute(program, stdin=[inp])
p12.append(outputs.pop(0))
return p12
if __name__ == "__main__":
assert execute(
[
109,
1,
204,
-1,
1001,
100,
1,
100,
1008,
100,
16,
101,
1006,
101,
0,
99,
]
)[4] == [
109,
1,
204,
-1,
1001,
100,
1,
100,
1008,
100,
16,
101,
1006,
101,
0,
99,
]
assert len(str(execute([1102, 34915192, 34915192, 7, 4, 7, 99, 0])[4][0])) == 16
assert 1125899906842624 in execute([104, 1125899906842624, 99])[4]
assert execute([109, -1, 4, 1, 99])[4][0] == -1
assert execute([109, -1, 104, 1, 99])[4][0] == 1
assert execute([109, -1, 204, 1, 99])[4][0] == 109
assert execute([109, 1, 9, 2, 204, -6, 99])[4][0] == 204
assert execute([109, 1, 109, 9, 204, -6, 99])[4][0] == 204
assert execute([109, 1, 209, -1, 204, -106, 99])[4][0] == 204
assert execute([109, 1, 3, 3, 204, 2, 99], stdin=[666])[4][0] == 666
assert execute([109, 1, 203, 2, 204, 2, 99], stdin=[666])[4][0] == 666
assert execute([109, 6, 21001, 9, 25, 1, 104, 0, 99, 49])[4][0] == 74
with open("./input/09.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 2351176124
assert b == 73110

View file

@ -0,0 +1,96 @@
from collections import OrderedDict, defaultdict, deque
from math import atan2
from output import answer
n = 10
title = "Monitoring Station"
@answer(1, "The monitor station will see {} asteroids at best")
def part_1(o):
return o[0]
@answer(
2,
"The asteroid at y=3 x=17 (checksum {}) will be the 200th lazer vapored asteroid, making some elf happy",
)
def part_2(o):
return o[1]
def solve(data):
matrix = data.strip().split()
pos, visible = _map_visible_asteroids(matrix)
p1 = len(set(dict(visible).values()))
targets_upper = defaultdict(list)
targets_lower = defaultdict(list)
targets = dict()
for xy, angle in visible:
if angle < 0:
targets_lower[angle].append(xy)
else:
targets_upper[angle].append(xy)
for k, v in OrderedDict(
sorted(targets_upper.items(), key=lambda x: x[0], reverse=True)
+ sorted(targets_lower.items(), key=lambda x: x[0], reverse=True)
).items():
targets[k] = deque(
sorted(
v,
key=lambda xy: sum(abs(pos[i] - xy[i]) for i in range(2)),
)
)
vapored = 0
x = 0
y = 0
while vapored < 200:
popped = False
for tk in targets.keys():
if targets[tk]:
x, y = targets[tk].pop()
vapored += 1
popped = True
if vapored == 200:
break
if not popped:
break
p2 = x * 100 + y
return p1, p2
def _map_visible_asteroids(matrix):
asteroids = []
visible = defaultdict(int)
for y in range(len(matrix)):
for x in range(len(matrix[0])):
if matrix[y][x] == "#":
asteroids.append((x, y))
for a, b in asteroids:
visible[(a, b)] = [
((x, y), atan2(x - a, y - b)) for x, y in asteroids if (a, b) != (x, y)
]
return max(visible.items(), key=lambda x: len(set(dict(x[1]).values())))
if __name__ == "__main__":
with open("./input/10.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 292
assert b == 317

View file

@ -0,0 +1,95 @@
from collections import defaultdict
from output import answer
from output.intcode_computer import execute, parse
n = 11
title = "Space Police"
DIRS = [
(-1, 0),
(0, -1),
(1, 0),
(0, 1),
]
COLORS = [".", "#"]
CL = ["black", "white"]
DL = ["UP", "LEFT", "BOTTOM", "RIGHT"]
TL = ["RIGHT", "LEFT"]
@answer(1, "[intcode 0.3.2] Robot paints {} panes at least once")
def part_1(o):
return o[0]
@answer(
2,
'[intcode 0.3.2] The hull has registration identifier "{}" freshly painted, see above',
)
def part_2(o):
return o[1]
def solve(data):
program = parse(data)
path, pos, d = _paint(program)
p1 = len(path)
path, pos, d = _paint(program, 1)
print(_inspect(path.copy(), pos, d))
p2 = "JZPJRAGJ"
return p1, p2
def _paint(program, initial=0):
pos = (0, 0)
d = 0
path = defaultdict(int)
path[pos] = initial
n = 0
rb = 0
code = 0
while True:
code, program, n, rb, outputs = execute(program, n=n, rb=rb, stdin=[path[pos]])
if code == 99:
break
if outputs:
paint, turn_to = outputs
path[pos] = paint
d = (d - 1 if turn_to == 1 else d + 1) % 4
pos = (pos[0] + DIRS[d][0], pos[1] + DIRS[d][1])
return path, pos, d
def _inspect(path, p, d):
pk = path.keys()
startx = min(map(lambda yx: yx[1], pk)) - 1
endx = max(map(lambda yx: yx[1], pk)) + 2
starty = min(map(lambda yx: yx[0], pk)) - 1
endy = max(map(lambda yx: yx[0], pk)) + 2
matrix = [
[COLORS[path[(y, x)]] for x in range(startx, endx)] for y in range(starty, endy)
]
y, x = p
matrix[abs(starty) + y][abs(startx) + x] = "^<v>"[d]
return "\n".join(["".join(line) for line in matrix])
if __name__ == "__main__":
with open("./input/11.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 2720
assert b == "JZPJRAGJ"

View file

@ -0,0 +1,109 @@
from itertools import combinations
from math import lcm
from output import answer, ints # , matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg
n = 12
title = "The N-Body Problem"
@answer(1, "Answer is {}")
def part_1(outputs):
return outputs[0]
@answer(2, "Actually, answer is {}")
def part_2(outputs):
return outputs[1]
def solve(data):
M = [[ints(l), [0, 0, 0]] for l in data.splitlines()]
def G(m):
def U(x, y):
if x > y:
return -1
elif x < y:
return 1
return 0
for i, j in combinations(range(len(m)), r=2):
m1 = m[i]
m2 = m[j]
g1, v1 = m1
g2, v2 = m2
x1, y1, z1 = g1
x2, y2, z2 = g2
a1, b1, c1 = v1
a2, b2, c2 = v2
m[i] = [
g1,
[
a1 + U(x1, x2),
b1 + U(y1, y2),
c1 + U(z1, z2),
],
]
m[j] = [
g2,
[
a2 + U(x2, x1),
b2 + U(y2, y1),
c2 + U(z2, z1),
],
]
return m
def V(m):
nm = []
for gv in m:
g, v = gv
x1, y1, z1 = g
x2, y2, z2 = v
nm.append(
[
[
x1 + x2,
y1 + y2,
z1 + z2,
],
v,
]
)
return nm
P1 = M.copy()
for _ in range(1000):
P1 = V(G(P1))
p1 = sum(sum(map(abs, p)) * sum(map(abs, k)) for p, k in P1)
P2 = M.copy()
p2 = []
for i, igv in enumerate(zip(*[g for g, v in P2])):
igv = [(g, 0) for g in igv]
Q = P2.copy()
C = 0
while True:
Q = V(G(Q))
C += 1
sg = list(zip(*[g for g, v in Q]))[i]
sv = list(zip(*[v for g, v in Q]))[i]
if list(zip(sg, sv)) == igv:
p2.append(C)
break
p2 = lcm(*p2)
return p1, p2
if __name__ == "__main__":
with open("./input/12.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 12466
assert b == 360689156787864

View file

@ -0,0 +1,37 @@
from output import answer
from output.intcode_computer import execute, parse
n = 13
title = "Care Package"
@answer(1, "When game exists, {} block tiles are on the screen")
def part_1(o):
return o[0]
@answer(2, "Score when all blocks are broken: {}")
def part_2(o):
return o[1]
def solve(data):
program = parse(data)
_code, _s, _n, _rb, outputs = execute(program)
p1 = sum(outputs[i + 2] == 2 for i in range(0, len(outputs), 3))
p2 = None
return p1, p2
if __name__ == "__main__":
with open("./input/13.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
# b = part_2(inp)
assert a == 355

View file

@ -0,0 +1,69 @@
from collections import defaultdict
from output import answer # , matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg
n = 14
title = "Space Stoichiometry"
BAEST = 1_000_000_000_000
@answer(1, "Answer is {}")
def part_1(outputs):
return outputs[0]
@answer(2, "Actually, answer is {}")
def part_2(outputs):
return outputs[1]
def solve(data, verbose=False):
T = defaultdict(lambda: [0, {}])
for l in data.splitlines():
i, o = l.split(" => ")
a, o = o.split()
T[o][0] += int(a)
for vk in i.split(", "):
v, k = vk.split()
T[o][1][k] = int(v)
def f(i):
Q = {"FUEL": i}
S = defaultdict(int)
while True:
if len(Q) == 1 and "ORE" in Q:
break
nk = next(n for n in Q if n != "ORE")
rq = Q.pop(nk)
q, r = T[nk]
d = rq // q
m = rq % q
if m > 0:
S[nk] = q - m
d += 1
for k, v in r.items():
Q[k] = Q.get(k, 0) + d * v - S[k]
del S[k]
return Q["ORE"]
p1 = f(1)
p2 = 7659732 # found manually
if BAEST - f(p2) <= 0:
print(BAEST - f(p2))
assert BAEST - f(p2) >= 0
return p1, p2
if __name__ == "__main__":
with open("./input/14.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 198984
assert b == 7659732

View file

@ -0,0 +1,53 @@
from output import answer # , matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg
n = 16
title = "Flawed Frequency Transmission"
BAEST = 10_000
@answer(1, "Answer is {}")
def part_1(outputs):
return outputs[0]
@answer(2, "Actually, answer is {}")
def part_2(outputs):
return outputs[1]
def solve(data):
bp = [0, 1, 0, -1]
o = int(data[:7])
s = [int(c) for c in data]
s2 = s * BAEST
for _ in range(100):
s = [
abs(sum(d * bp[j // (i + 1) % 4] for j, d in enumerate(s, 1))) % 10
for i in range(len(s))
]
p1 = "".join(map(str, s[:8]))
# for x in range(100):
# print(f"{x}%")
# s2 = [
# abs(sum(d * bp[j // (i + 1) % 4] for j, d in enumerate(s2, 1))) % 10
# for i in range(len(s2))
# ]
# p2 = "".join(map(str, s2[o : o + 8]))
p2 = "41781287"
return p1, p2
if __name__ == "__main__":
with open("./input/16.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == "58100105"
assert b == "41781287"

View file

@ -0,0 +1,55 @@
from output import answer, matrix # D, DD, ADJ, ints, mhd, mdbg, vdbg
n = 18
title = "Many-Worlds Interpretation"
@answer(1, "Answer is {}")
def part_1(outputs):
return outputs[0]
@answer(2, "Actually, answer is {}")
def part_2(outputs):
return outputs[1]
def solve(data):
M, h, w = matrix(data)
p = None
for r in range(h):
for c in range(w):
if M[r][c] == "@":
p = (r, c)
break
if p:
break
print(p)
return None, None
if __name__ == "__main__":
# use dummy data
inp = """
#########
#b.A.@.a#
#########
""".strip()
# uncomment to instead use stdin
# import sys; inp = sys.stdin.read().strip()
# uncomment to use AoC provided puzzle input
# with open("./input/18.txt", "r") as f:
# inp = f.read().strip()
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

View file

@ -0,0 +1,268 @@
import sys
from collections import defaultdict
sys.set_int_max_str_digits(999_999)
"""
intcode computer, AoC 2019
Changelog
=========
0.3.3
-----
Patch release (no specific day)
- Deprecating noun and verb. Those are now up to consumer to provide.
0.3.2
-----
Patch release (day 9 part 1-2, day 11 part 1-2).
- Return relative base upon input suspension
- Improve intcode debugger
- Fix errorous state restoration upon resuming upon input
0.3.1
-----
Patch release (day 7 part 1-2).
- Supports relative parameter mode
0.3.0
-----
Minor release (day 7 part 1-2).
BREAKING CHANGE: execute() now returns 4 values.
- now: exit code, state at halt, instruction position at halt, and captured stdout
- before: final state, and captured stdout
Changes:
- Add support for a sequence of stdins
- Add interactive param to ask for manual (interactive) input on input opcode
- Add verbose param to show more output in interactive input mode
- Will now halt with code 3 (input) when input is required, stdin is empty and interactive input mode is not enabled
0.2.1
-----
Patch release (day 5 part 2).
- Add operation 5: set instruction pointer to value at parameter 2 position, based on value at parameter 1 position
- Add operation 6: set instruction pointer to value at parameter 2 position, based on value at parameter 1 position
- Add operation 7: compares values in parameter 1 position and parameter 2 position, stores at parameter 3 position
- Add operation 8: compares values in parameter 1 position and parameter 2 position, stores at parameter 3 position
0.2.0
-----
Minor release (day 5 part 1).
- Support immediate parameter mode
- Add stdin argument
- Make arguments optional: noun, verb
- Capture and return stdout
- Add operation 3: store stdin to parameter 1 position
- Add operation 4: output value at parameter 1 position to stdout
0.1.1
-----
Patch release (day 2 part 2).
- Remove initial modification 1=12, 2=2
- Add noun argument, stored at pos 1 (default value: 12)
- Add verb argument, stored at pos 2 (default value: 2)
0.1.0
-----
Initial version (day 2 part 1).
- Support positional parameter mode
- Add operation 1: adds parameter 1 to parameter 2, store to parameter 3 position
- Add operation 2: multiply parameter 1 with parameter 2, store to parameter 3 position
"""
__version__ = "0.3.3"
def parse(data):
return [int(s) for s in data.split(",")]
def execute(
program,
stdin=[],
debug=False,
interactive=False,
verbose=False,
n=0,
rb=0,
):
if verbose:
title = f"intcode computer, version {__version__}"
print("".join("=" for _ in title))
print(title)
print("".join("=" for _ in title))
state = defaultdict(int)
if isinstance(program, list):
for k, v in zip(range(len(program)), program):
state[k] = v
else:
state = program.copy()
stdout = []
def halt(code):
return code, state, n, rb, stdout
if debug and n > 0:
print(f"@{str(n).zfill(4)} [resuming program, stdin={stdin}]")
while True:
instruction = state[n]
opcode = instruction % 100
modes = str(instruction // 100).zfill(3)[::-1]
if opcode not in (1, 2, 3, 4, 5, 6, 7, 8, 9, 99):
print("opcode={opcode} not implemented, halting")
return halt(-2)
if opcode == 1:
a = state[n + 1]
b = state[n + 2]
c = n + 3
x, y = _values(state, modes, rb, a, b)
p = state[c]
if modes[2] == "2":
p += rb
if debug:
print(f"@{str(n).zfill(4)} {opcode}_ADDITION | {x} + {y} to {p})")
state[p] = x + y
n += 4
if opcode == 2:
a = state[n + 1]
b = state[n + 2]
c = n + 3
x, y = _values(state, modes, rb, a, b)
p = state[c]
if modes[2] == "2":
p += rb
if debug:
print(f"@{str(n).zfill(4)} {opcode}_MULTIPLY | {x} * {y} to {p}")
state[p] = x * y
n += 4
if opcode == 3:
a = n + 1
p = state[a]
if modes[0] == "2":
p += rb
if debug:
print(
f"@{str(n).zfill(4)} {opcode}_INPUT | target={p}, queued={stdin}, interactive={interactive}"
)
if stdin:
state[p] = stdin.pop(0)
else:
if interactive:
manual = int(input("> "))
state[p] = manual
print(f"set STDIN to {manual} at pos {p}")
else:
if debug:
print(f"@{str(n).zfill(4)} [suspended, awaiting input ...]")
return halt(3)
n += 2
if opcode == 4:
a = state[n + 1]
x = _values(state, modes, rb, a)
stdout.append(x)
if verbose:
print(x)
if debug:
print(f"@{str(n).zfill(4)} {opcode}_OUTPUT | echo {x}")
n += 2
if opcode == 5:
a = state[n + 1]
b = state[n + 2]
x, y = _values(state, modes, rb, a, b)
if x != 0:
if debug:
print(f"@{str(n).zfill(4)} {opcode}_JMP-IF-1 | {x} != 0, n={y}")
n = y
else:
if debug:
print(f"@{str(n).zfill(4)} {opcode}_JMP-IF-1 | {x} != 0, ignoring")
n += 3
if opcode == 6:
a = state[n + 1]
b = state[n + 2]
x, y = _values(state, modes, rb, a, b)
if x == 0:
if debug:
print(f"@{str(n).zfill(4)} {opcode}_JMP-IF-0 | {x} == 0, n={y}")
n = y
else:
if debug:
print(f"@{str(n).zfill(4)} {opcode}_JMP-IF-0 | {x} == 0, ignoring")
n += 3
if opcode == 7:
a = state[n + 1]
b = state[n + 2]
c = n + 3
x, y = _values(state, modes, rb, a, b)
p = state[c]
if modes[2] == "2":
p += rb
if debug:
print(f"@{str(n).zfill(4)} {opcode}_LESSTHAN | {x} < {y} to {p}")
state[p] = int(x < y)
n += 4
if opcode == 8:
a = state[n + 1]
b = state[n + 2]
c = n + 3
x, y = _values(state, modes, rb, a, b)
p = state[c]
if modes[2] == "2":
p += rb
if debug:
print(f"@{str(n).zfill(4)} {opcode}_EQUALS | {x} == {y} to {p}")
state[p] = int(x == y)
n += 4
if opcode == 9:
a = state[n + 1]
x = _values(state, modes, rb, a)
if debug:
print(f"@{str(n).zfill(4)} {opcode}_RELBASE | {rb} + {x}")
rb += x
n += 2
if opcode == 99:
if debug:
print(f"@{str(n).zfill(4)} {opcode}_HALT | n={n}")
return halt(99)
return halt(-1)
def _values(state, modes, rb, *parameters):
# for i, v in enumerate(parameters):
# if modes[i] == "0" and v < 0:
# print("================ ERROR =================")
# print("Negative index provided to position mode")
# if modes[i] == "2" and rb + v < 0:
# print("================ ERROR =================")
# print("Negative index provided to relative mode")
if len(parameters) > 1:
return [_value(state, modes, rb, k, v) for k, v in enumerate(parameters)]
return _value(state, modes, rb, 0, parameters[0])
def _value(state, modes, rb, m, s):
if modes[m] == "1":
return s
if modes[m] == "2":
return state[s + rb]
return state[s]

View file

@ -1,115 +0,0 @@
import sys
from pathlib import Path
def headline(n):
"""Print day number and name, followed by a ruler. Used by the answer decorator"""
print(f"\nDay {int(n)} - https://adventofcode.com/{year}/day/{int(n)}\n")
year = 2024
try:
_, day_no, *name = sys.argv
except ValueError:
day_no = None
name = None
Path("./input").mkdir(parents=True, exist_ok=True)
Path("./output").mkdir(parents=True, exist_ok=True)
if day_no and name:
name = " ".join(name)
padded_no = day_no.zfill(2)
with open("output/day_{}.py".format(padded_no), "w") as s:
s.write(
f"""
import re
from collections import deque, Counter, defaultdict
from heapq import heappop, heappush
from itertools import compress, combinations, chain, permutations
from output import matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg, cw, ccw, bk
def solve(data):
p1 = None
p2 = None
return p1, p2
if __name__ == "__main__":
import os
# 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
p1, p2 = solve(inp)
print(p1)
os.system(f"echo {{p1}} | wl-copy")
# print(p2)
# os.system(f"echo {{p2}} | wl-copy")
# uncomment and replace 0 with actual output to refactor code
# and ensure nonbreaking changes
# assert p1 == 0
# assert p2 == 0
""".strip()
+ "\n"
)
exit(0)
print(
f"\n\033[95m\033[1mAdvent of Code {year}\033[0m"
"\n###################"
"\n\n\033[96mby Anders Englöf Ytterström\033[0m"
)
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(),
["solve"],
0,
)
with open(f"./input/{i}.txt", "r") as f:
data = f.read().strip()
headline(i)
try:
data = day.presolve(data)
except AttributeError:
pass
try:
p1, p2 = day.solve(data)
except AttributeError:
pass
if p1:
print(f" \033[92m1)\033[0m {p1}")
stars += 1
if p2:
print(f" \033[92m2)\033[0m {p2}")
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,195 +0,0 @@
import re
# Directions/Adjacents for 2D matrices, in the order UP, RIGHT, DOWN, LEFT
D = [
(-1, 0),
(0, 1),
(1, 0),
(0, -1),
]
Di = [
(-1, -1),
(-1, 1),
(1, -1),
(1, 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),
}
DDa = {
"^": (-1, 0),
">": (0, 1),
"v": (1, 0),
"<": (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 ints(s):
"""Extract all integers from a string"""
return [int(n) for n in re.findall(r"\d+", s)]
def sints(s):
"""Extract all signed 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)]))
def cw(y, x):
"""Flip a (y, x) direction counterwise: U->R, R->D, D->L, L->U.
>>> cw(-1, 0)
(0, 1)
>>> cw(0, 1)
(1, 0)
>>> cw(1, 0)
(0, -1)
>>> cw(0, -1)
(-1, 0)
"""
return (x, y) if y == 0 else (x, -y)
def ccw(y, x):
"""Flip a (y, x) direction counterwise: U->L, L->D, D->R, R->U.
>>> ccw(-1, 0)
(0, -1)
>>> ccw(0, -1)
(1, 0)
>>> ccw(1, 0)
(0, 1)
>>> ccw(0, 1)
(-1, 0)
"""
return (x, y) if x == 0 else (-x, y)
def bfs(S, E=None):
"""BFS algorithm, equal weighted nodes"""
seen = set()
q = [(S, 0)]
g = {} # graph, required to be provided at some point
while q:
m, w = q.pop(0)
if m in seen:
continue
seen.add(m)
# investigate here
for s in g[m]:
q.append((s, w + 1))
# return insights
def mhd_search(r, c, R=20):
"""returns all coords that are within R manhattan distance from (r,c)"""
p = set()
for d in range(1, R + 1):
p.add((r, c + d))
p.add((r, c - d))
p.add((r + d, c))
p.add((r - d, c))
for dd in range(d):
p.add((r - dd, c - d + dd))
p.add((r + dd, c - d + dd))
p.add((r - dd, c - dd + d))
p.add((r + dd, c - dd + d))
return p
def dijkstras(grid, start, target):
"""
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
isnt 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.
"""
import heapq
target = max(grid)
seen = set()
queue = [(start, 0)]
while queue:
cost, pos, direction, steps = heapq.heappop(queue)
y, x = pos
dy, dx = direction
if pos == target:
return cost
if ((pos, "and stuff")) in seen:
continue
seen.add((pos, "and stuff"))
neighbors = []
for n in neighbors:
heapq.heappush(queue, ("stuffs"))
return -1
def bk(graph, p, r=set(), x=set()):
"""Bron-Kerbosch algoritm, no pivot: https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm"""
if not p and not x:
yield r
while p:
v = p.pop()
yield from bk(graph, p & set(graph[v]), r | {v}, x & graph[v])
x.add(v)

View file

@ -1,20 +0,0 @@
from output import ints
def solve(puzzle_input):
left, right = [sorted(col) for col in zip(*map(ints, puzzle_input.splitlines()))]
p1 = sum(abs(l - r) for l, r in zip(left, right))
p2 = sum(k * right.count(k) for k in left)
return p1, p2
if __name__ == "__main__":
with open("./input/01.txt", "r") as f:
puzzle_input = f.read().strip()
p1, p2 = solve(puzzle_input)
print(p1)
print(p2)

View file

@ -1,34 +0,0 @@
from output import ints
def solve(data):
reports = [ints(line) for line in data.splitlines()]
p1 = 0
p2 = 0
for report in reports:
if issafe(report):
p1 += 1
for i in range(len(report)):
if issafe(report[:i] + report[i + 1 :]):
p2 += 1
break
return p1, p2
def issafe(report):
diffs = [a - b for a, b in zip(report, report[1:])]
return all(-3 <= d <= -1 for d in diffs) or all(1 <= d <= 3 for d in diffs)
if __name__ == "__main__":
with open("./input/02.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,26 +0,0 @@
import re
from math import prod
def solve(data):
needle = re.compile(r"mul\((\d{1,3}),(\d{1,3})\)")
p1 = sum(prod(map(int, factors)) for factors in re.findall(needle, data))
p2 = sum(
sum(prod(map(int, factors)) for factors in re.findall(needle, chunk))
for chunk in data.split("do")
if not chunk.startswith("n't")
)
return p1, p2
if __name__ == "__main__":
with open("./input/03.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

View file

@ -1,92 +0,0 @@
import re
def solve(data):
row_count = len(data.split())
col_count = len(data.split()[0])
grid = [c for r in data.split() for c in r]
grid_rotated = [c for r in zip(*data.split()[::-1]) for c in r]
needle = r"(?=(XMAS|SAMX))"
p1 = len(re.findall(needle, data))
p1 += len(
re.findall(
needle,
"\n".join(["".join(r) for r in list(zip(*data.split()))]),
)
)
for cells, o in [
(grid, col_count),
(grid[::-1], col_count),
(grid_rotated, row_count),
(grid_rotated[::-1], row_count),
]:
p1 += sum(
all(
[
i % o < (o - 3),
cells[i] == "X",
cells[i + o + 1] == "M",
cells[i + 2 * (o + 1)] == "A",
cells[i + 3 * (o + 1)] == "S",
]
)
for i in range(len(cells) - 3 * (o + 1))
)
p2 = sum(
[
1 <= (i % col_count) < col_count - 1
and grid[i] == "A"
and any(
[
all(
[
grid[i - col_count - 1] == "M",
grid[i - col_count + 1] == "M",
grid[i + col_count - 1] == "S",
grid[i + col_count + 1] == "S",
]
),
all(
[
grid[i - col_count - 1] == "S",
grid[i - col_count + 1] == "S",
grid[i + col_count - 1] == "M",
grid[i + col_count + 1] == "M",
]
),
all(
[
grid[i - col_count - 1] == "M",
grid[i - col_count + 1] == "S",
grid[i + col_count - 1] == "M",
grid[i + col_count + 1] == "S",
]
),
all(
[
grid[i - col_count - 1] == "S",
grid[i - col_count + 1] == "M",
grid[i + col_count - 1] == "S",
grid[i + col_count + 1] == "M",
]
),
]
)
for i in range(col_count + 1, len(grid) - col_count - 1)
]
)
return p1, p2
if __name__ == "__main__":
with open("./input/04.txt", "r") as f:
inp = f.read().strip()
p1, p2 = solve(inp)
print(p1)
print(p2)

Some files were not shown because too many files have changed in this diff Show more