Compare commits

...

10 commits

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

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

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

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

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

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

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

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

Links that helped me:

- https://www.reddit.com/r/adventofcode/comments/e8aw9j/2019_day_9_part_1_how_to_fix_203_error/
- https://www.reddit.com/r/adventofcode/comments/jr9xrn/comment/gbwr85k/
2023-12-19 14:51:11 +01:00
19 changed files with 1088 additions and 270 deletions

View file

@ -1,9 +1,16 @@
import os
import sys
def headline(n, title):
"""Print day number and name, followed by a ruler. Used by the answer decorator"""
print(f"\n--- Day {n}: {title} ---\n")
year = 2019
try:
_, day_no, name = sys.argv
_, day_no, *name = sys.argv
except ValueError:
day_no = None
name = None
@ -13,53 +20,60 @@ print(
)
if day_no and name:
name = " ".join(name)
padded_no = day_no.zfill(2)
print(f"\n- creating output/day_{padded_no}.py")
with open("output/day_{}.py".format(padded_no), "w") as s:
s.write(
f"""
from output import answer, puzzleinput
from output import answer # , matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg
n = {day_no}
title = "{name}"
@puzzleinput(n)
def parse_input(data):
return data
@answer(1, "Answer is {{}}")
def part_1(data):
return data
def part_1(outputs):
return outputs[0]
@answer(2, "Actually, answer is {{}}")
def part_2(data):
return data
def part_2(outputs):
return outputs[1]
def solve(data):
return None, None
if __name__ == "__main__":
# use dummy data
parsed = \"\"\"
inp = \"\"\"
replace me
\"\"\".strip()
# uncomment to instead use stdin
# import fileinput
# parsed = "\\n".join(list(fileinput.input()))
# import sys; inp = sys.stdin.read().strip()
# uncomment to instead use content of input/{padded_no}.txt
# parsed = parse_input()
# uncomment to use AoC provided puzzle input
# with open("./input/{padded_no}.txt", "r") as f:
# inp = f.read().strip()
part_1(parsed)
# part_2(parsed)
inp = solve(inp)
a = part_1(inp)
# b = part_2(inp)
# uncomment and replace 0 with actual output to refactor code
# and ensure nonbreaking changes
# assert a == 0
# assert b == 0
""".strip()
+ "\n"
)
print(f"- creating empty input/{day_no.zfill(2)}.txt")
with open("input/{}.txt".format(day_no.zfill(2)), "w") as i:
i.write("")
print("- making sure input dir exists")
if not os.path.exists("input"):
os.makedirs("input")
print(
f"""
@ -74,22 +88,34 @@ https://adventofcode.com/{year}/day/{day_no}/input
)
exit(0)
from output import headline
stars = 0
for i in [str(n).zfill(2) for n in range(1, 26)]:
try:
day = __import__(
"output.day_{}".format(i),
globals(),
locals(),
["n", "title", "part_1", "part_2", "parse_input"],
0,
)
headline(day.n, day.title)
data = day.parse_input()
day.part_1(data, decorate=True)
day.part_2(data, decorate=True)
except IOError:
pass
except ImportError:
pass
if not day_no or day_no.zfill(2) == i:
try:
day = __import__(
"output.day_{}".format(i),
globals(),
locals(),
["n", "title", "part_1", "part_2", "solve"],
0,
)
with open(f"./input/{i}.txt", "r") as f:
data = f.read().strip()
headline(day.n, day.title)
try:
data = day.solve(data)
except AttributeError:
pass
if day.part_1(data, decorate=True):
stars += 1
if day.part_2(data, decorate=True):
stars += 1
except IOError:
pass
except ImportError:
pass
if not day_no:
print(f"\nStars: {stars}")
print("".join("*" if n < stars else "" for n in range(50)))
print("")

View file

@ -1,26 +1,38 @@
import functools
import re
# Directions/Adjacents for 2D matrices, in the order UP, RIGHT, DOWN, LEFT
D = [
(-1, 0),
(0, 1),
(1, 0),
(0, -1),
]
def puzzleinput(n, **kwargs):
filename = str(n).zfill(2)
trim_input = kwargs.get("trim_input", True)
filepath = f"./input/{filename}.txt"
# 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),
}
def decorator_pi(func):
@functools.wraps(func)
def wrapper_pi(*args, **kwargs):
with open(filepath, "r") as f:
data = f.read()
if trim_input:
return func(data.strip(), *args, **kwargs)
return func(data, *args, **kwargs)
return wrapper_pi
return decorator_pi
# Adjacent relative positions including diagonals for 2D matrices, in the order NW, N, NE, W, E, SW, S, SE
ADJ = [
(-1, -1),
(-1, 0),
(1, -1),
(0, -1),
(0, 1),
(1, 1),
(1, 0),
(1, -1),
]
def answer(part_index, fmt_string):
"""Decorator to present a solution in a human readable format"""
def decorator_aoc(func):
@functools.wraps(func)
def wrapper_aoc(*args, **kwargs):
@ -32,13 +44,39 @@ def answer(part_index, fmt_string):
print(answer)
else:
formatted = fmt_string.format(answer)
print(f"[part {part_index}] {formatted}")
print(f" {part_index}) {formatted}")
return answer
return wrapper_aoc
return decorator_aoc
def headline(n, title):
title = f"Day {n}: {title}"
print("\n".join(["", title, "".join("-" for _ in title), ""]))
def ints(s):
"""Extract all integers from a string"""
return [int(n) for n in re.findall(r"-?\d+", s)]
def mhd(a, b):
"""Calculates the Manhattan distance between 2 positions in the format (y, x) or (x, y)"""
ar, ac = a
br, bc = b
return abs(ar - br) + abs(ac - bc)
def matrix(d):
"""Transform a string into an iterable matrix. Returns the matrix, row count and col count"""
m = [tuple(r) for r in d.split()]
return m, len(m), len(m[0])
def mdbg(m):
"""Print-debug a matrix"""
for r in m:
print("".join(r))
def vdbg(seen, h, w):
"""Print-debug visited positions of a matrix"""
for r in range(h):
print("".join(["#" if (r, c) in seen else "." for c in range(w)]))

View file

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

View file

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

View file

@ -1,5 +1,6 @@
from collections import defaultdict
from output import answer, puzzleinput
from output import answer
n = 3
title = "Crossed Wires"
@ -12,37 +13,24 @@ directions = {
}
@puzzleinput(n)
def parse_input(data):
return [line.split(",") for line in data.split()]
@answer(
1, "As the crow flies, closest intersection Manhattan distance is {} units away"
)
def part_1(wires):
def follow(instructions):
seen = []
pos = (0, 0)
for instruction in instructions:
urdl, *l = instruction
distance = int("".join(l))
for _ in range(distance):
pos = (pos[0] + directions[urdl][0], pos[1] + directions[urdl][1])
seen.append(pos)
return set(seen)
wa = follow(wires[0])
wb = follow(wires[1])
return min(sum(map(abs, i)) for i in wa & wb)
def part_1(o):
return o[0]
@answer(2, "By travel, closest intersection Manhattan distance is {} units away")
def part_2(wires):
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:
@ -51,16 +39,29 @@ def part_2(wires):
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):
follow(wire, i)
p1w.append(follow(wire, i))
p1 = min(sum(map(abs, i)) for i in p1w[0] & p1w[1])
return min(sum(v.values()) for v in seen.values() if len(v) > 1)
p2 = min(sum(v.values()) for v in seen.values() if len(v) > 1)
return p1, p2
if __name__ == "__main__":
parsed = parse_input()
part_1(parsed)
part_2(parsed)
with open("./input/03.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 1337
assert b == 65356

View file

@ -1,36 +1,44 @@
from collections import Counter
from output import answer, puzzleinput
from output import answer
n = 4
title = "Secure Container"
@puzzleinput(n)
def parse_input(data):
return data.split("-")
@answer(1, "{} combinations of valid passwords")
def part_1(range_values):
a, b = range_values
def valid(s):
return "".join(sorted(s)) == s and any(x == y for x, y in zip(s, s[1:]))
return sum(valid(str(pw)) for pw in range(int(a), int(b) + 1))
def part_1(o):
return o[0]
@answer(2, "{} combinations of valid passwords, including important detail")
def part_2(range_values):
a, b = range_values
def part_2(o):
return o[1]
def valid(s):
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()
return sum(valid(str(pw)) for pw in range(int(a), int(b) + 1))
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__":
parsed = parse_input()
part_1(parsed)
part_2(parsed)
with open("./input/04.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 544
assert b == 334

View file

@ -1,29 +1,40 @@
from output import answer, puzzleinput
from output import answer
from output.intcode_computer import execute, parse
n = 5
title = "Sunny with a Chance of Asteroids"
@puzzleinput(n)
def parse_input(data):
return parse(data)
@answer(1, "[intcode-0.2.0] Program diagnostic code, ID 1: {}")
def part_1(program):
_code, _state, _cursorpos, stdout = execute(program, stdin=1)
return max(stdout)
def part_1(o):
return o[0]
@answer(2, "[intcode-0.2.1] Program diagnostic code, ID 5: {}")
def part_2(program):
_code, _state, _cursorpos, stdout = execute(program, stdin=5)
return stdout[0]
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__":
parsed = parse_input()
part_1(parsed)
part_2(parsed)
with open("./input/05.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 16434972
assert b == 16694270

View file

@ -1,35 +1,40 @@
from collections import defaultdict, deque
from output import answer, puzzleinput
from collections import defaultdict
from output import answer
n = 6
title = "Universal Orbit Map"
@puzzleinput(n)
def parse_input(data):
heritage = defaultdict(str)
for parent, child in [line.split(")") for line in data.split()]:
heritage[child] = parent
return heritage
@answer(1, "{} direct and indirect orbits")
def part_1(heritage):
return sum(len(ancestry(heritage, v)) for v in heritage.keys())
def part_1(o):
return o[0]
@answer(2, "Orbit transfers needed for you to share orbit with Santa: {}")
def part_2(heritage):
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))
return sum(
p2 = sum(
[
len(a) - shared,
len(b) - shared,
]
)
return p1, p2
def ancestry(parents, child):
k = child
@ -41,6 +46,13 @@ def ancestry(parents, child):
if __name__ == "__main__":
parsed = parse_input()
part_1(parsed)
part_2(parsed)
with open("./input/06.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 271151
assert b == 388

View file

@ -1,38 +1,41 @@
from collections import defaultdict
from itertools import permutations
from output import answer, puzzleinput
from output import answer
from output.intcode_computer import execute, parse
n = 7
title = "Amplification Circuit"
@puzzleinput(n)
def parse_input(data):
return parse(data)
@answer(
1,
"[intcode 0.3.0] Given the phase settings [0, 3, 1, 2, 4], the highest achievable signal to the thruster is {}",
"[intcode 0.3.0] The highest achievable signal to the thruster is {}",
)
def part_1(program):
thruster_signals = []
for settings in map(list, permutations(range(5))):
o = 0
for ps in settings:
_code, _state, _n, so = execute(program, stdin=[ps, o])
o = so.pop(0)
thruster_signals.append(o)
return max(thruster_signals)
def part_1(o):
return o[0]
@answer(
2,
"[intcode 0.3.0] Given the phase settings [7, 8, 5, 9, 6] and creating feedback loop, the highest achievable signal to the thruster is {}",
"[intcode 0.3.0] By creating a feedback loop, the highest achievable signal to the thruster is {}",
)
def part_2(program):
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]
@ -43,9 +46,9 @@ def part_2(program):
if paused[amp]:
program, resume_at = paused[amp]
del paused[amp]
code, state, n, so = execute(program, stdin=o, n=resume_at)
code, state, n, _rb, so = execute(program, stdin=o, n=resume_at)
else:
code, state, n, so = execute(program, stdin=[ps, *o])
code, state, n, _rb, so = execute(program, stdin=[ps, *o])
if code == 3:
paused[amp] = (
list(state.values()),
@ -56,10 +59,19 @@ def part_2(program):
finished.add(amp)
o = so
thruster_signals.append(o[-1])
return max(thruster_signals)
p2 = max(thruster_signals)
return p1, p2
if __name__ == "__main__":
parsed = parse_input()
part_1(parsed)
part_2(parsed)
with open("./input/07.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 398674
assert b == 39431233

View file

@ -1,38 +1,50 @@
from collections import Counter
from textwrap import wrap
from output import answer, puzzleinput
from output import answer
n = 8
title = "Space Image Format"
@puzzleinput(n)
def parse_input(data):
return data
@answer(1, "The product of all 1s and 2s in the layer with fewest 0s is {}")
def part_1(o):
return o[0]
@answer(1, "the product of all 1s and 2s in the layer with fewest 0s is {}")
def part_1(data):
@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"]
return a * b
p1 = a * b
@answer(2, "The message is CYUAH, the decoded image looks like this:\n\n{}")
def part_2(data, width=25, height=6):
layers = wrap(data, width * height)
l = len(layers)
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))
return matrix
print(matrix)
p2 = "CYUAH"
return p1, p2
if __name__ == "__main__":
parsed = parse_input()
part_1(parsed)
part_2(parsed)
with open("./input/08.txt", "r") as f:
inp = f.read().strip()
inp = solve(inp)
a = part_1(inp)
b = part_2(inp)
assert a == 2500
assert b == "CYUAH"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,7 @@
import sys
from collections import defaultdict
sys.set_int_max_str_digits(999_999)
"""
intcode computer, AoC 2019
@ -7,13 +9,37 @@ 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.
- now: exit code, state at halt, instruction position at halt, and captured stdout
- before: final state, and captured stdout
Changes:
@ -63,132 +89,180 @@ Initial version (day 2 part 1).
- 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
"""
v = "0.2.3"
__version__ = "0.3.3"
def parse(data):
return list(map(int, data.split(",")))
return [int(s) for s in data.split(",")]
def execute(
program,
noun=None,
verb=None,
stdin=[],
debug=False,
interactive=False,
verbose=False,
n=0,
rb=0,
):
if verbose:
title = f"intcode computer, version {v}"
title = f"intcode computer, version {__version__}"
print("".join("=" for _ in title))
print(title)
print("".join("=" for _ in title))
state = dict(zip(range(len(program)), program))
if noun:
state[1] = noun
if verb:
state[2] = verb
c = 0
state = defaultdict(int)
if isinstance(program, list):
for k, v in zip(range(len(program)), program):
state[k] = v
else:
state = program.copy()
stdout = []
if not isinstance(stdin, list):
stdin = [stdin]
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 % 100).zfill(2)
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]
p = state[n + 3]
x = a if modes[1] == "1" else state[a]
y = b if modes[0] == "1" else state[b]
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 debug:
print(f"{n}:{opcode} | {x} + {y} to {p}")
if opcode == 2:
a = state[n + 1]
b = state[n + 2]
p = state[n + 3]
x = a if modes[1] == "1" else state[a]
y = b if modes[0] == "1" else state[b]
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 debug:
print(f"{n}:{opcode} | {x} * {y} to {p}")
if opcode == 3:
p = state[n + 1]
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:
state[p] = int(input("> "))
manual = int(input("> "))
state[p] = manual
print(f"set STDIN to {manual} at pos {p}")
else:
return 3, state, n, stdout
if debug:
print(f"@{str(n).zfill(4)} [suspended, awaiting input ...]")
return halt(3)
n += 2
if debug:
print(f"{n}:{opcode} | {i} to {p}")
if opcode == 4:
a = state[n + 1]
x = a if modes[1] == "1" else state[a]
n += 2
x = _values(state, modes, rb, a)
stdout.append(x)
if verbose:
print(x)
if debug:
print(f"{n}:{opcode} | {stdout}")
print(f"@{str(n).zfill(4)} {opcode}_OUTPUT | echo {x}")
n += 2
if opcode == 5:
a = state[n + 1]
b = state[n + 2]
x = a if modes[1] == "1" else state[a]
y = b if modes[0] == "1" else state[b]
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 debug:
print(f"{n}:{opcode} | {n}")
if opcode == 6:
a = state[n + 1]
b = state[n + 2]
x = a if modes[1] == "1" else state[a]
y = b if modes[0] == "1" else state[b]
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 debug:
print(f"{n}:{opcode} | {n}")
if opcode == 7:
a = state[n + 1]
b = state[n + 2]
p = state[n + 3]
x = a if modes[1] == "1" else state[a]
y = b if modes[0] == "1" else state[b]
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 debug:
print(f"{n}:{opcode} | {x}")
if opcode == 8:
a = state[n + 1]
b = state[n + 2]
p = state[n + 3]
x = a if modes[1] == "1" else state[a]
y = b if modes[0] == "1" else state[b]
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"{n}:{opcode} | {x}")
print(f"@{str(n).zfill(4)} {opcode}_RELBASE | {rb} + {x}")
rb += x
n += 2
if opcode == 99:
break
c += 1
if debug and c % 1000 == 0:
print(f"{c} instructions done, current pos: {n}")
# if c == 3:
# break
if verbose:
title = f"intcode computer received SIGTERM"
return 99, state, n, stdout
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]