Compare commits
19 commits
main
...
2019-rerun
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86121d7799 | ||
| b9d32629c8 | |||
|
|
107ca5ab7c | ||
|
|
40274ea044 | ||
|
|
d01b4aa0f8 | ||
| 051c3f3ebd | |||
| 5cb2dccf62 | |||
| f98545ae5a | |||
| 262ad34c51 | |||
| d9142068ea | |||
| c51638fac1 | |||
| 4623fdac95 | |||
| 5be719dcf9 | |||
| 9eccd731b4 | |||
| 298b97b66d | |||
| 799216623e | |||
| e1d4741cfc | |||
| 85a67d26fa | |||
| 133c76a02a |
127 changed files with 1692 additions and 3796 deletions
|
|
@ -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("")
|
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class Program:
|
||||||
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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -32,11 +32,11 @@ class Solution(BaseSolution):
|
||||||
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()
|
||||||
|
|
|
||||||
|
|
@ -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,11 +34,11 @@ 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
|
||||||
|
|
@ -47,9 +47,9 @@ class Solution(BaseSolution):
|
||||||
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()
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -27,6 +27,6 @@ class Solution(BaseSolution):
|
||||||
return last_seen
|
return last_seen
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
solution = Solution()
|
solution = Solution()
|
||||||
solution.show_results()
|
solution.show_results()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,13 @@ 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()]
|
||||||
|
|
@ -30,10 +27,7 @@ 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):
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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,15 +22,10 @@ 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]
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -63,16 +58,14 @@ class Solution(BaseSolution):
|
||||||
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
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
1
2019-python/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
__pycache__
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
|
||||||
40
2019-python/output/day_01.py
Normal file
40
2019-python/output/day_01.py
Normal 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
|
||||||
48
2019-python/output/day_02.py
Normal file
48
2019-python/output/day_02.py
Normal 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
|
||||||
67
2019-python/output/day_03.py
Normal file
67
2019-python/output/day_03.py
Normal 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
|
||||||
44
2019-python/output/day_04.py
Normal file
44
2019-python/output/day_04.py
Normal 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
|
||||||
40
2019-python/output/day_05.py
Normal file
40
2019-python/output/day_05.py
Normal 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
|
||||||
58
2019-python/output/day_06.py
Normal file
58
2019-python/output/day_06.py
Normal 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
|
||||||
77
2019-python/output/day_07.py
Normal file
77
2019-python/output/day_07.py
Normal 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
|
||||||
50
2019-python/output/day_08.py
Normal file
50
2019-python/output/day_08.py
Normal 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"
|
||||||
86
2019-python/output/day_09.py
Normal file
86
2019-python/output/day_09.py
Normal 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
|
||||||
96
2019-python/output/day_10.py
Normal file
96
2019-python/output/day_10.py
Normal 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
|
||||||
95
2019-python/output/day_11.py
Normal file
95
2019-python/output/day_11.py
Normal 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"
|
||||||
109
2019-python/output/day_12.py
Normal file
109
2019-python/output/day_12.py
Normal 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
|
||||||
37
2019-python/output/day_13.py
Normal file
37
2019-python/output/day_13.py
Normal 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
|
||||||
69
2019-python/output/day_14.py
Normal file
69
2019-python/output/day_14.py
Normal 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
|
||||||
53
2019-python/output/day_16.py
Normal file
53
2019-python/output/day_16.py
Normal 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"
|
||||||
55
2019-python/output/day_18.py
Normal file
55
2019-python/output/day_18.py
Normal 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
|
||||||
268
2019-python/output/intcode_computer.py
Normal file
268
2019-python/output/intcode_computer.py
Normal 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]
|
||||||
|
|
@ -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("")
|
|
||||||
|
|
@ -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
|
|
||||||
isn’t visited yet, calculate its tentative distance by adding its weight
|
|
||||||
to the smallest distance found so far for its parent/previous node
|
|
||||||
(starting vertex in case of first-level vertices).
|
|
||||||
5. If this tentative distance is smaller than previously recorded value
|
|
||||||
(if any), update it in our ‘distances’ array.
|
|
||||||
6. Finally, add this visited vertex with its updated distance to our priority
|
|
||||||
queue and repeat step-3 until we have reached our destination or exhausted
|
|
||||||
all nodes.
|
|
||||||
"""
|
|
||||||
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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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
Loading…
Add table
Reference in a new issue