2019, take 2 #2
21 changed files with 1531 additions and 0 deletions
1
2019-python/.gitignore
vendored
Normal file
1
2019-python/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
__pycache__
|
||||
35
2019-python/README.md
Normal file
35
2019-python/README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
Advent of Code 2019
|
||||
===================
|
||||
|
||||
Solutions for #aoc2019 in Python 3 (3.11.5).
|
||||
|
||||
Help scripts
|
||||
------------
|
||||
|
||||
Display all solved puzzles:
|
||||
|
||||
python aoc.py
|
||||
|
||||
To bootstrap a new puzzle (creates `input/<day_no>.txt` and `output/day_<day_no>.py`):
|
||||
|
||||
python aoc.py <dag_no> "<puzzle_name>"
|
||||
|
||||
Manually copy the puzzle input from https://adventofcode.com and paste it in `input/<day_no>.txt`
|
||||
to start coding.
|
||||
|
||||
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):
|
||||
|
||||
xclip -selection clipboard -o | python -m output.day_XX
|
||||
cat tmpfile | python -m output.day_XX
|
||||
|
||||
Execute separate puzzle on file save (replace `XX` with the puzzle number):
|
||||
|
||||
ls output/*.py | entr -c -s 'xclip -selection clipboard -o | python -m output.day_XX'
|
||||
ls output/*.py | entr -c -s 'cat tmpfile | python -m output.day_XX'
|
||||
ls output/*.py | entr -c -r python -m output.day_XX
|
||||
|
||||
(requires `entr` and `xclip`, Mac users can instead use `pbpaste`)
|
||||
121
2019-python/aoc.py
Normal file
121
2019-python/aoc.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
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
|
||||
except ValueError:
|
||||
day_no = None
|
||||
name = None
|
||||
|
||||
print(
|
||||
f"\nAdvent of Code {year}" "\n###################" "\n\nby Anders Englöf Ytterström"
|
||||
)
|
||||
|
||||
if day_no and name:
|
||||
name = " ".join(name)
|
||||
padded_no = day_no.zfill(2)
|
||||
print(f"\n- creating output/day_{padded_no}.py")
|
||||
with open("output/day_{}.py".format(padded_no), "w") as s:
|
||||
s.write(
|
||||
f"""
|
||||
from output import answer # , matrix, D, DD, ADJ, ints, mhd, mdbg, vdbg
|
||||
|
||||
n = {day_no}
|
||||
title = "{name}"
|
||||
|
||||
|
||||
@answer(1, "Answer is {{}}")
|
||||
def part_1(outputs):
|
||||
return outputs[0]
|
||||
|
||||
|
||||
@answer(2, "Actually, answer is {{}}")
|
||||
def part_2(outputs):
|
||||
return outputs[1]
|
||||
|
||||
|
||||
def solve(data):
|
||||
return None, None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# use dummy data
|
||||
inp = \"\"\"
|
||||
replace me
|
||||
\"\"\".strip()
|
||||
|
||||
# uncomment to instead use stdin
|
||||
# import sys; inp = sys.stdin.read().strip()
|
||||
|
||||
# uncomment to use AoC provided puzzle input
|
||||
# with open("./input/{padded_no}.txt", "r") as f:
|
||||
# inp = f.read().strip()
|
||||
|
||||
inp = solve(inp)
|
||||
|
||||
a = part_1(inp)
|
||||
# b = part_2(inp)
|
||||
|
||||
# uncomment and replace 0 with actual output to refactor code
|
||||
# and ensure nonbreaking changes
|
||||
# assert a == 0
|
||||
# assert b == 0
|
||||
""".strip()
|
||||
+ "\n"
|
||||
)
|
||||
print("- making sure input dir exists")
|
||||
if not os.path.exists("input"):
|
||||
os.makedirs("input")
|
||||
|
||||
print(
|
||||
f"""
|
||||
Done! start coding.
|
||||
|
||||
Puzzle link:
|
||||
https://adventofcode.com/{year}/day/{day_no}
|
||||
|
||||
Puzzle input (copy and paste to input/{day_no.zfill(2)}.txt):
|
||||
https://adventofcode.com/{year}/day/{day_no}/input
|
||||
"""
|
||||
)
|
||||
exit(0)
|
||||
|
||||
|
||||
stars = 0
|
||||
for i in [str(n).zfill(2) for n in range(1, 26)]:
|
||||
if not day_no or day_no.zfill(2) == i:
|
||||
try:
|
||||
day = __import__(
|
||||
"output.day_{}".format(i),
|
||||
globals(),
|
||||
locals(),
|
||||
["n", "title", "part_1", "part_2", "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("")
|
||||
82
2019-python/output/__init__.py
Normal file
82
2019-python/output/__init__.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import functools
|
||||
import re
|
||||
|
||||
# Directions/Adjacents for 2D matrices, in the order UP, RIGHT, DOWN, LEFT
|
||||
D = [
|
||||
(-1, 0),
|
||||
(0, 1),
|
||||
(1, 0),
|
||||
(0, -1),
|
||||
]
|
||||
|
||||
# Directions for 2D matrices, as a dict with keys U, R, D, L
|
||||
DD = {
|
||||
"U": (-1, 0),
|
||||
"R": (0, 1),
|
||||
"D": (1, 0),
|
||||
"L": (0, -1),
|
||||
}
|
||||
|
||||
# Adjacent relative positions including diagonals for 2D matrices, in the order NW, N, NE, W, E, SW, S, SE
|
||||
ADJ = [
|
||||
(-1, -1),
|
||||
(-1, 0),
|
||||
(1, -1),
|
||||
(0, -1),
|
||||
(0, 1),
|
||||
(1, 1),
|
||||
(1, 0),
|
||||
(1, -1),
|
||||
]
|
||||
|
||||
|
||||
def answer(part_index, fmt_string):
|
||||
"""Decorator to present a solution in a human readable format"""
|
||||
|
||||
def decorator_aoc(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper_aoc(*args, **kwargs):
|
||||
decorate = kwargs.get("decorate", False)
|
||||
if decorate:
|
||||
del kwargs["decorate"]
|
||||
answer = func(*args, **kwargs)
|
||||
if not decorate:
|
||||
print(answer)
|
||||
else:
|
||||
formatted = fmt_string.format(answer)
|
||||
print(f" {part_index}) {formatted}")
|
||||
return answer
|
||||
|
||||
return wrapper_aoc
|
||||
|
||||
return decorator_aoc
|
||||
|
||||
|
||||
def ints(s):
|
||||
"""Extract all integers from a string"""
|
||||
return [int(n) for n in re.findall(r"-?\d+", s)]
|
||||
|
||||
|
||||
def mhd(a, b):
|
||||
"""Calculates the Manhattan distance between 2 positions in the format (y, x) or (x, y)"""
|
||||
ar, ac = a
|
||||
br, bc = b
|
||||
return abs(ar - br) + abs(ac - bc)
|
||||
|
||||
|
||||
def matrix(d):
|
||||
"""Transform a string into an iterable matrix. Returns the matrix, row count and col count"""
|
||||
m = [tuple(r) for r in d.split()]
|
||||
return m, len(m), len(m[0])
|
||||
|
||||
|
||||
def mdbg(m):
|
||||
"""Print-debug a matrix"""
|
||||
for r in m:
|
||||
print("".join(r))
|
||||
|
||||
|
||||
def vdbg(seen, h, w):
|
||||
"""Print-debug visited positions of a matrix"""
|
||||
for r in range(h):
|
||||
print("".join(["#" if (r, c) in seen else "." for c in range(w)]))
|
||||
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]
|
||||
Loading…
Add table
Reference in a new issue