mirror of
https://github.com/thib8956/advent-of-code.git
synced 2024-12-26 13:56:29 +00:00
import 2019 aoc
This commit is contained in:
commit
c6c3acaa9a
131
.gitignore
vendored
Normal file
131
.gitignore
vendored
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# ---> Python
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
41
2019/day1/day1.py
Normal file
41
2019/day1/day1.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_fuel_iterative(mass):
|
||||||
|
total = 0
|
||||||
|
new_mass = mass
|
||||||
|
while True:
|
||||||
|
new_mass = new_mass // 3 - 2
|
||||||
|
if new_mass < 0:
|
||||||
|
break
|
||||||
|
total += new_mass
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_total_fuel_mass(input_file, mass_function=lambda x: x // 3 - 2):
|
||||||
|
total = 0
|
||||||
|
with open(input_file) as masses:
|
||||||
|
for mass in masses:
|
||||||
|
total += mass_function(int(mass))
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
def test_part2():
|
||||||
|
inputs = (14, 1969, 100756)
|
||||||
|
expected = (2, 966, 50346)
|
||||||
|
for i, inp in enumerate(inputs):
|
||||||
|
result = calculate_fuel_iterative(inp)
|
||||||
|
assert result == expected[i], "Result for {} should be {}, was {}".format(
|
||||||
|
inp, expected[i], result
|
||||||
|
)
|
||||||
|
print("All tests passed for part 2.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("Part 1 - total mass: ", calculate_total_fuel_mass("./input.txt"))
|
||||||
|
|
||||||
|
test_part2()
|
||||||
|
print(
|
||||||
|
"Part 2 -- total mass: ",
|
||||||
|
calculate_total_fuel_mass("./input.txt", calculate_fuel_iterative),
|
||||||
|
)
|
100
2019/day1/input.txt
Normal file
100
2019/day1/input.txt
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
62259
|
||||||
|
75368
|
||||||
|
93740
|
||||||
|
119724
|
||||||
|
112546
|
||||||
|
137714
|
||||||
|
96999
|
||||||
|
130673
|
||||||
|
102398
|
||||||
|
73819
|
||||||
|
100734
|
||||||
|
85337
|
||||||
|
62764
|
||||||
|
82115
|
||||||
|
127696
|
||||||
|
54391
|
||||||
|
103213
|
||||||
|
77954
|
||||||
|
112513
|
||||||
|
112392
|
||||||
|
138404
|
||||||
|
92989
|
||||||
|
108521
|
||||||
|
83163
|
||||||
|
109720
|
||||||
|
91918
|
||||||
|
114443
|
||||||
|
54306
|
||||||
|
90623
|
||||||
|
66833
|
||||||
|
58505
|
||||||
|
85919
|
||||||
|
77539
|
||||||
|
149419
|
||||||
|
128385
|
||||||
|
66452
|
||||||
|
94677
|
||||||
|
109179
|
||||||
|
62072
|
||||||
|
137245
|
||||||
|
136226
|
||||||
|
145783
|
||||||
|
60689
|
||||||
|
103320
|
||||||
|
145931
|
||||||
|
101286
|
||||||
|
63458
|
||||||
|
122468
|
||||||
|
87858
|
||||||
|
105675
|
||||||
|
146185
|
||||||
|
57417
|
||||||
|
96883
|
||||||
|
70739
|
||||||
|
97494
|
||||||
|
140951
|
||||||
|
149416
|
||||||
|
83137
|
||||||
|
66122
|
||||||
|
134319
|
||||||
|
58511
|
||||||
|
139600
|
||||||
|
102929
|
||||||
|
112240
|
||||||
|
149634
|
||||||
|
64142
|
||||||
|
83332
|
||||||
|
129526
|
||||||
|
99058
|
||||||
|
148889
|
||||||
|
50087
|
||||||
|
74961
|
||||||
|
133606
|
||||||
|
143518
|
||||||
|
68849
|
||||||
|
97045
|
||||||
|
73920
|
||||||
|
61357
|
||||||
|
115941
|
||||||
|
56740
|
||||||
|
111773
|
||||||
|
77880
|
||||||
|
90792
|
||||||
|
77103
|
||||||
|
111355
|
||||||
|
125898
|
||||||
|
56547
|
||||||
|
84918
|
||||||
|
113822
|
||||||
|
74113
|
||||||
|
98557
|
||||||
|
80928
|
||||||
|
60519
|
||||||
|
146379
|
||||||
|
59354
|
||||||
|
102490
|
||||||
|
72584
|
||||||
|
59000
|
||||||
|
63151
|
||||||
|
114253
|
69
2019/day2/day2.py
Normal file
69
2019/day2/day2.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
def interpret_intcode(input_prog):
|
||||||
|
# Instruction pointer: index of the next element to be executed
|
||||||
|
ip = 0
|
||||||
|
while ip < len(input_prog):
|
||||||
|
instruction = input_prog[ip]
|
||||||
|
if instruction == 99:
|
||||||
|
break
|
||||||
|
|
||||||
|
elif instruction == 1:
|
||||||
|
# The operands to sum are at the memory location ip+1 and ip+2.
|
||||||
|
operands = (input_prog[input_prog[ip + 1]], input_prog[input_prog[ip + 2]])
|
||||||
|
result = sum(operands)
|
||||||
|
target = input_prog[ip + 3]
|
||||||
|
input_prog[target] = result
|
||||||
|
ip += 4
|
||||||
|
|
||||||
|
elif instruction == 2:
|
||||||
|
# The operands to multiply are at the memory location ip+1 and ip+2.
|
||||||
|
operands = (input_prog[input_prog[ip + 1]], input_prog[input_prog[ip + 2]])
|
||||||
|
result = operands[0] * operands[1]
|
||||||
|
target = input_prog[ip + 3]
|
||||||
|
input_prog[target] = result
|
||||||
|
ip += 4
|
||||||
|
|
||||||
|
|
||||||
|
def tests():
|
||||||
|
inputs = (
|
||||||
|
[1, 0, 0, 0, 99], # ADD 1 + 1 to location 0
|
||||||
|
[2, 3, 0, 3, 99], # MUL 2 * 3 to location 3
|
||||||
|
[2, 4, 4, 5, 99, 0],
|
||||||
|
[1, 1, 1, 4, 99, 5, 6, 0, 99],
|
||||||
|
)
|
||||||
|
expected_outputs = (
|
||||||
|
[2, 0, 0, 0, 99], # 1 + 1 = 2
|
||||||
|
[2, 3, 0, 6, 99], # 3 * 2 = 6
|
||||||
|
[2, 4, 4, 5, 99, 9801], # 99 * 99 = 9801
|
||||||
|
[30, 1, 1, 4, 2, 5, 6, 0, 99],
|
||||||
|
)
|
||||||
|
for i, inp in enumerate(inputs):
|
||||||
|
result = inp[:]
|
||||||
|
interpret_intcode(result)
|
||||||
|
assert (
|
||||||
|
result == expected_outputs[i]
|
||||||
|
), f"Expected output for {inp} is {expected_outputs[i]}, but found {result} instead."
|
||||||
|
print("All tests passed.")
|
||||||
|
|
||||||
|
|
||||||
|
def run_program(memory, noun, verb):
|
||||||
|
memory[1] = noun
|
||||||
|
memory[2] = verb
|
||||||
|
interpret_intcode(memory)
|
||||||
|
return memory[0]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
tests()
|
||||||
|
with open("input.txt") as inp:
|
||||||
|
memory = [int(x) for x in inp.readline().strip().split(",")]
|
||||||
|
# Pass a copy to avoid modifying the original memory
|
||||||
|
print("Part 1 answer: ", run_program(memory[:], 12, 2))
|
||||||
|
|
||||||
|
# Part 2
|
||||||
|
result = 0
|
||||||
|
for verb in range(99):
|
||||||
|
for noun in range(99):
|
||||||
|
if run_program(memory[:], noun, verb) == 19690720:
|
||||||
|
print(f"Part 2: noun={noun}, verb={verb}")
|
||||||
|
print("Result = ", 100 * noun + verb)
|
||||||
|
break
|
1
2019/day2/input.txt
Normal file
1
2019/day2/input.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,1,13,19,1,9,19,23,1,6,23,27,2,27,9,31,2,6,31,35,1,5,35,39,1,10,39,43,1,43,13,47,1,47,9,51,1,51,9,55,1,55,9,59,2,9,59,63,2,9,63,67,1,5,67,71,2,13,71,75,1,6,75,79,1,10,79,83,2,6,83,87,1,87,5,91,1,91,9,95,1,95,10,99,2,9,99,103,1,5,103,107,1,5,107,111,2,111,10,115,1,6,115,119,2,10,119,123,1,6,123,127,1,127,5,131,2,9,131,135,1,5,135,139,1,139,10,143,1,143,2,147,1,147,5,0,99,2,0,14,0
|
78
2019/day3/day3.py
Normal file
78
2019/day3/day3.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
|
def manhattan_distance(p):
|
||||||
|
return abs(p[0]) + abs(p[1])
|
||||||
|
|
||||||
|
|
||||||
|
def points_for_wire(wire):
|
||||||
|
x, y, count = 0, 0, 0
|
||||||
|
points = {}
|
||||||
|
# (x, y)
|
||||||
|
directions = {"R": (1, 0), "L": (-1, 0), "U": (0, 1), "D": (0, -1)}
|
||||||
|
for p in wire:
|
||||||
|
# D42 -> for _ in range(42)
|
||||||
|
for _ in range(int(p[1:])):
|
||||||
|
offset = directions[p[0]]
|
||||||
|
x += offset[0]
|
||||||
|
y += offset[1]
|
||||||
|
count += 1
|
||||||
|
points[(x ,y)] = count
|
||||||
|
|
||||||
|
return points
|
||||||
|
|
||||||
|
|
||||||
|
def find_min_distance(wire1, wire2):
|
||||||
|
points1 = points_for_wire(wire1)
|
||||||
|
points2 = points_for_wire(wire2)
|
||||||
|
|
||||||
|
intersections = points1.keys() & points2.keys()
|
||||||
|
closest = min((intersection for intersection in intersections), key=manhattan_distance)
|
||||||
|
|
||||||
|
return manhattan_distance(closest)
|
||||||
|
|
||||||
|
|
||||||
|
def find_least_steps(wire1, wire2):
|
||||||
|
points1 = points_for_wire(wire1)
|
||||||
|
points2 = points_for_wire(wire2)
|
||||||
|
|
||||||
|
intersections = points1.keys() & points2.keys()
|
||||||
|
# Intersection with the least steps
|
||||||
|
least_steps = min(intersections, key=lambda x: points1[x] + points2[x])
|
||||||
|
|
||||||
|
return points1[least_steps] + points2[least_steps]
|
||||||
|
|
||||||
|
|
||||||
|
def tests():
|
||||||
|
inputs = (
|
||||||
|
(("R8", "U5", "L5", "D3"), ("U7", "R6", "D4", "L4")),
|
||||||
|
(("R75","D30","R83", "U83", "L12", "D49", "R71", "U7", "L72"), ("U62", "R66", "U55", "R34", "D71", "R55", "D58", "R83")),
|
||||||
|
(("R98", "U47", "R26", "D63", "R33", "U87", "L62", "D20", "R33", "U53", "R51"), ("U98", "R91", "D20", "R16", "D67", "R40", "U7", "R15", "U6", "R7"))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Part 1
|
||||||
|
expected = (6, 159, 135)
|
||||||
|
for i, inp in enumerate(inputs):
|
||||||
|
result = find_min_distance(inp[0], inp[1])
|
||||||
|
assert result == expected[i], "Result for {} should be {}, was {}".format(
|
||||||
|
inp, expected[i], result
|
||||||
|
)
|
||||||
|
|
||||||
|
# Part 2
|
||||||
|
# expected number of steps
|
||||||
|
expected_part2 = (30, 610, 410)
|
||||||
|
print("All tests passed.")
|
||||||
|
for i, inp in enumerate(inputs):
|
||||||
|
result = find_least_steps(inp[0], inp[1])
|
||||||
|
assert result == expected_part2[i], "Result for {} should be {}, was {}".format(
|
||||||
|
inp, expected_part2[i], result
|
||||||
|
)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
tests()
|
||||||
|
|
||||||
|
with open("input.txt") as raw_input:
|
||||||
|
lines = raw_input.readlines()
|
||||||
|
wire1, wire2 = [line.strip("\n").split(",") for line in lines]
|
||||||
|
print("Part 1 -- distance = ", find_min_distance(wire1, wire2))
|
||||||
|
print("Part 2 -- steps = ", find_least_steps(wire1, wire2))
|
2
2019/day3/input.txt
Normal file
2
2019/day3/input.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
R997,U849,R349,U641,R581,D39,R285,U139,R455,D346,L965,D707,R393,D302,L263,U58,R950,U731,R858,D748,R302,U211,R588,D441,L153,D417,R861,U775,R777,U204,R929,U868,L62,U163,R841,D214,L648,U626,R501,D751,L641,D961,L23,D430,L73,D692,R49,U334,L601,U996,R444,D658,R633,D30,L861,D811,R10,D394,R9,U227,L848,U420,L378,D622,L501,U397,R855,U369,R615,U591,L674,D166,L181,U61,L224,U463,L203,U594,R93,U614,L959,U198,L689,D229,L674,U255,R843,D382,R538,U923,L960,D775,L879,U97,R137,U665,L340,D941,L775,D57,R852,D167,R980,U704,L843,U989,L611,D32,L724,D790,L32,U984,L39,U671,L994,U399,R475,D85,L322,D936,R117,D261,R705,D696,L523,D433,L239,U477,L247,D465,R560,D902,L589,U682,R645,U376,L989,D121,L215,U514,R519,U407,L218,D444,R704,D436,L680,U759,R937,U400,R533,D860,R782,D233,R840,D549,L508,U380,L992,U406,L213,D403,L413,D532,L429,U186,R262,U313,L913,U873,L838,D882,R851,U70,R185,D131,R945,D595,L330,U446,R88,D243,L561,D952,R982,D395,L708,U459,L82,D885,L996,U955,L406,U697,L183,U266,L878,D839,R843,D891,R118,U772,R590,D376,L500,U370,R607,D12,L310,D436,L602,D365,R886,U239,L471,D418,L122,U18,R879,D693,R856,U848,L657,D911,L63,U431,R41,U752,R919,U323,L61,D263,L370,D85,R929,D213,R350,U818,R458,D912,R509,U394,L734,U49,R810,D87,L870,U658,R499,U550,L402,U244,L112,U859,R836,U951,R222,D944,L691,D731,R742,D52,R984,D453,L514,U692,R812,U35,L844,D177,L110,D22,R61,U253,R618,D51,R163,U835,R704,U148,R766,U297,R457,D170,L104,D441,R330,D330,R989,D538,R668,D811,R62,D67,L470,D526,R788,U376,R708,U3,R961
|
||||||
|
L1009,D381,R970,U429,L230,D909,R516,D957,R981,U609,L480,D139,L861,U168,L48,U620,R531,D466,L726,D380,R977,D454,L318,D397,R994,U402,L77,U93,L359,D72,R968,D956,L174,D22,R218,U619,R593,U32,L154,U55,L169,U415,L171,U666,R617,U109,L265,U773,R541,D808,L797,U478,R731,U379,R311,D137,L806,U298,R362,D458,L254,D539,R700,U853,R246,D588,L28,U203,L432,U946,R663,D408,R974,U59,L683,D36,L139,U738,L780,U414,L401,D93,R212,D973,L710,U892,L357,D177,R823,D4,R46,D924,L235,D898,R67,U220,L592,U87,R94,U584,R979,D843,L299,D648,L491,U360,R824,D245,L944,D24,R616,U975,L4,U42,L984,U181,R902,D835,L687,D413,L767,U632,L754,U270,R413,U51,L825,D377,L596,U960,L378,U706,L859,D708,L156,D991,L814,U351,R923,D749,L16,D651,R20,D86,R801,U811,L228,U161,L871,U129,R215,U235,L784,U896,R94,U145,R822,U494,R248,D98,R494,U156,L495,U311,R66,D873,L294,D620,L885,U395,R778,D227,R966,U756,L694,D707,R983,D950,R706,D730,R415,U886,L465,D622,L13,D938,R324,D464,R723,U804,R942,D635,L729,D317,L522,U469,R550,D141,R302,U999,L642,U509,R431,D380,R18,D676,R449,D759,L495,U901,R1,D745,L655,U449,L439,D818,R55,D541,R420,U764,L426,D176,L520,U3,L663,D221,L80,D449,L987,U349,L71,U632,L887,D231,R655,D208,R698,D639,R804,U616,R532,U846,R363,D141,R659,U470,L798,U144,L675,U483,L944,U380,L329,U72,L894,D130,R53,U109,R610,U770,R778,U493,R972,D340,L866,U980,L305,D812,R130,D954,R253,D33,L912,U950,L438,D680,R891,U725,R171,D587,R549,D367,L4,U313,R522,D128,L711,D405,L769,D496,L527,U373,R725,D261,L268,D939,L902,D58,L858,D190,L442
|
37
2019/day4/day4.py
Normal file
37
2019/day4/day4.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
|
def check_increase(number):
|
||||||
|
num = str(number)
|
||||||
|
for i in range(len(num) - 1):
|
||||||
|
if num[i+1] < num[i]:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_adjacent(number):
|
||||||
|
num = str(number)
|
||||||
|
for digit in num:
|
||||||
|
count = num.count(digit)
|
||||||
|
if count == 2: # Part one : <= 2
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def tests():
|
||||||
|
assert check_increase(123456) == True
|
||||||
|
assert check_increase(123454) == False
|
||||||
|
assert check_adjacent(112345) == True
|
||||||
|
assert check_adjacent(123445) == True
|
||||||
|
assert check_adjacent(123456) == False
|
||||||
|
|
||||||
|
|
||||||
|
def main(start, end):
|
||||||
|
matches = 0
|
||||||
|
for n in range(start, end + 1):
|
||||||
|
if check_increase(n) and check_adjacent(n):
|
||||||
|
matches += 1
|
||||||
|
return matches
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
tests()
|
||||||
|
print("Matches : ", main(367479, 893698))
|
163
2019/day5/day5.py
Normal file
163
2019/day5/day5.py
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
from collections import namedtuple
|
||||||
|
from enum import Enum
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.WARN)
|
||||||
|
|
||||||
|
|
||||||
|
def get_nth_digit(n, number):
|
||||||
|
"Returns the nth digit of the input number"
|
||||||
|
return number // 10 ** n % 10
|
||||||
|
|
||||||
|
|
||||||
|
class Operation(Enum):
|
||||||
|
ADDITION = 1
|
||||||
|
MULTIPLICATION = 2
|
||||||
|
INPUT = 3
|
||||||
|
OUTPUT = 4
|
||||||
|
JMP_IF_TRUE = 5
|
||||||
|
JMP_IF_FALSE = 6
|
||||||
|
LESS_THAN = 7
|
||||||
|
EQUALS = 8
|
||||||
|
TERMINATION = 99
|
||||||
|
|
||||||
|
|
||||||
|
class Mode(Enum):
|
||||||
|
POSITION = 0
|
||||||
|
IMMEDIATE = 1
|
||||||
|
|
||||||
|
|
||||||
|
class Instruction:
|
||||||
|
def __init__(self, opcode):
|
||||||
|
self.operation = Operation(opcode % 100)
|
||||||
|
self.modes = [Mode(get_nth_digit(n, opcode)) for n in range(2, 5)]
|
||||||
|
self.handler_name = f"handle_{self.operation.name.lower()}"
|
||||||
|
self.handler = getattr(self, self.handler_name, self.handle_termination)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Instruction({self.operation}, {self.modes})"
|
||||||
|
|
||||||
|
def handle(self, program, ip):
|
||||||
|
return self.handler(program, ip)
|
||||||
|
|
||||||
|
def handle_addition(self, program, ip):
|
||||||
|
first, second = self._get_parameters(program, ip)
|
||||||
|
logging.debug(f"ADD {first} {second}")
|
||||||
|
result = first + second
|
||||||
|
# the last mode should *always* be POSITION
|
||||||
|
program[program[ip + 3]] = result
|
||||||
|
ip += 4
|
||||||
|
return ip
|
||||||
|
|
||||||
|
def handle_multiplication(self, program, ip):
|
||||||
|
first, second = self._get_parameters(program, ip)
|
||||||
|
logging.debug(f"MUL {first} {second}")
|
||||||
|
result = first * second
|
||||||
|
# the last mode should *always* be POSITION
|
||||||
|
program[program[ip + 3]] = result
|
||||||
|
ip += 4
|
||||||
|
return ip
|
||||||
|
|
||||||
|
def handle_input(self, program, ip):
|
||||||
|
ip += 2
|
||||||
|
program[program[ip + 1]] = int(input("Enter ID > "))
|
||||||
|
return ip
|
||||||
|
|
||||||
|
def handle_output(self, program, ip):
|
||||||
|
print("OUT ", program[program[ip + 1]])
|
||||||
|
ip += 2
|
||||||
|
return ip
|
||||||
|
|
||||||
|
def handle_jmp_if_true(self, program, ip):
|
||||||
|
first, second = self._get_parameters(program, ip)
|
||||||
|
logging.debug(f"JMPT {first} {second}")
|
||||||
|
return second if first != 0 else ip + 3
|
||||||
|
|
||||||
|
def handle_jmp_if_false(self, program, ip):
|
||||||
|
first, second = self._get_parameters(program, ip)
|
||||||
|
logging.debug(f"JMPF {first} {second}")
|
||||||
|
return second if first == 0 else ip + 3
|
||||||
|
|
||||||
|
def handle_less_than(self, program, ip):
|
||||||
|
first, second = self._get_parameters(program, ip)
|
||||||
|
logging.debug(f"LT {first} {second}")
|
||||||
|
program[program[ip + 3]] = int(first < second)
|
||||||
|
ip += 4
|
||||||
|
return ip
|
||||||
|
|
||||||
|
def handle_equals(self, program, ip):
|
||||||
|
first, second = self._get_parameters(program, ip)
|
||||||
|
logging.debug(f"EQ {first} {second}")
|
||||||
|
program[program[ip + 3]] = int(first == second)
|
||||||
|
ip += 4
|
||||||
|
return ip
|
||||||
|
|
||||||
|
def handle_termination(self, program, ip):
|
||||||
|
print("HALT")
|
||||||
|
return ip
|
||||||
|
|
||||||
|
def _get_parameters(self, program, ip):
|
||||||
|
first = (
|
||||||
|
program[ip + 1]
|
||||||
|
if self.modes[0] is Mode.IMMEDIATE
|
||||||
|
else program[program[ip + 1]]
|
||||||
|
)
|
||||||
|
second = (
|
||||||
|
program[ip + 2]
|
||||||
|
if self.modes[1] is Mode.IMMEDIATE
|
||||||
|
else program[program[ip + 2]]
|
||||||
|
)
|
||||||
|
return first, second
|
||||||
|
|
||||||
|
|
||||||
|
def interpret_intcode(program, stdin=None):
|
||||||
|
ip = 0
|
||||||
|
while program[ip] != 99:
|
||||||
|
opcode = program[ip]
|
||||||
|
instruction = Instruction(opcode)
|
||||||
|
ip = instruction.handle(program, ip)
|
||||||
|
|
||||||
|
|
||||||
|
def tests():
|
||||||
|
inputs = (
|
||||||
|
[1, 0, 0, 0, 99], # ADD 1 + 1 to location 0
|
||||||
|
[2, 3, 0, 3, 99], # MUL 2 * 3 to location 3
|
||||||
|
[2, 4, 4, 5, 99, 0],
|
||||||
|
[1, 1, 1, 4, 99, 5, 6, 0, 99],
|
||||||
|
[1101, 1, 1, 0, 99], # ADD 1 + 1 to location 0 (direct access)
|
||||||
|
)
|
||||||
|
expected_outputs = (
|
||||||
|
[2, 0, 0, 0, 99], # 1 + 1 = 2
|
||||||
|
[2, 3, 0, 6, 99], # 3 * 2 = 6
|
||||||
|
[2, 4, 4, 5, 99, 9801], # 99 * 99 = 9801
|
||||||
|
[30, 1, 1, 4, 2, 5, 6, 0, 99],
|
||||||
|
[2, 1, 1, 0, 99], # 1 + 1 = 2 (direct access)
|
||||||
|
)
|
||||||
|
for i, inp in enumerate(inputs):
|
||||||
|
result = inp[:]
|
||||||
|
interpret_intcode(result)
|
||||||
|
assert (
|
||||||
|
result == expected_outputs[i]
|
||||||
|
), f"Expected output for {inp} is {expected_outputs[i]}, but found {result} instead."
|
||||||
|
print("\nAll tests passed.\n")
|
||||||
|
|
||||||
|
|
||||||
|
def run_input_program(filename):
|
||||||
|
import os
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
with open(os.path.join(SCRIPT_DIR, filename)) as inp:
|
||||||
|
print("Start of input program.")
|
||||||
|
memory = [int(x) for x in inp.readline().strip().split(",")]
|
||||||
|
interpret_intcode(memory, 5)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
tests()
|
||||||
|
# a = [3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9]
|
||||||
|
# b = [3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1]
|
||||||
|
|
||||||
|
# interpret_intcode(b, 0)
|
||||||
|
# interpret_intcode(a, 0)
|
||||||
|
run_input_program("input.txt")
|
192
2019/day5/day52.py
Normal file
192
2019/day5/day52.py
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
|
class Instruction:
|
||||||
|
code, argument_num = 0, 0
|
||||||
|
|
||||||
|
def execute(self, intcode, arguments):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def new_pc(self, intcode):
|
||||||
|
return intcode.pc + self.argument_num + 1
|
||||||
|
|
||||||
|
|
||||||
|
class PlusInstruction(Instruction):
|
||||||
|
code, argument_num = 1, 3
|
||||||
|
|
||||||
|
def execute(self, intcode, arguments):
|
||||||
|
logging.debug(f"ADD {arguments[0].value} {arguments[1].value}")
|
||||||
|
intcode.set(arguments[2].address, arguments[0].value + arguments[1].value)
|
||||||
|
return self.new_pc(intcode)
|
||||||
|
|
||||||
|
|
||||||
|
class MultiplyInstruction(Instruction):
|
||||||
|
code, argument_num = 2, 3
|
||||||
|
|
||||||
|
def execute(self, intcode, arguments):
|
||||||
|
logging.debug(f"MUL {arguments[0].value} {arguments[1].value}")
|
||||||
|
intcode.set(arguments[2].address, arguments[0].value * arguments[1].value)
|
||||||
|
return self.new_pc(intcode)
|
||||||
|
|
||||||
|
|
||||||
|
class InputInstruction(Instruction):
|
||||||
|
code, argument_num = 3, 1
|
||||||
|
|
||||||
|
def execute(self, intcode, arguments):
|
||||||
|
intcode.set(arguments[0].address, intcode.get_input())
|
||||||
|
return self.new_pc(intcode)
|
||||||
|
|
||||||
|
|
||||||
|
class OutputInstruction(Instruction):
|
||||||
|
code, argument_num = 4, 1
|
||||||
|
|
||||||
|
def execute(self, intcode, arguments):
|
||||||
|
intcode.output = arguments[0].value
|
||||||
|
return self.new_pc(intcode)
|
||||||
|
|
||||||
|
|
||||||
|
class JumpIfTrueInstruction(Instruction):
|
||||||
|
code, argument_num = 5, 2
|
||||||
|
|
||||||
|
def execute(self, intcode, arguments):
|
||||||
|
logging.debug(f"JMPT {arguments[0].value} {arguments[1].value}")
|
||||||
|
return arguments[1].value if arguments[0].value != 0 else self.new_pc(intcode)
|
||||||
|
|
||||||
|
|
||||||
|
class JumpIfFalseInstruction(Instruction):
|
||||||
|
code, argument_num = 6, 2
|
||||||
|
|
||||||
|
def execute(self, intcode, arguments):
|
||||||
|
logging.debug(f"JMPF {arguments[0].value} {arguments[1].value}")
|
||||||
|
return arguments[1].value if arguments[0].value == 0 else self.new_pc(intcode)
|
||||||
|
|
||||||
|
|
||||||
|
class LessThanInstruction(Instruction):
|
||||||
|
code, argument_num = 7, 3
|
||||||
|
|
||||||
|
def execute(self, intcode, arguments):
|
||||||
|
logging.debug(f"LT {arguments[0].value} {arguments[1].value}")
|
||||||
|
intcode.set(arguments[2].address, int(arguments[0].value < arguments[1].value))
|
||||||
|
return self.new_pc(intcode)
|
||||||
|
|
||||||
|
|
||||||
|
class EqualsInstruction(Instruction):
|
||||||
|
code, argument_num = 8, 3
|
||||||
|
|
||||||
|
def execute(self, intcode, arguments):
|
||||||
|
intcode.set(arguments[2].address, int(arguments[0].value == arguments[1].value))
|
||||||
|
return self.new_pc(intcode)
|
||||||
|
|
||||||
|
|
||||||
|
class RelativeBaseOffsetInstruction(Instruction):
|
||||||
|
code, argument_num = 9, 1
|
||||||
|
|
||||||
|
def execute(self, intcode, arguments):
|
||||||
|
intcode.relative_base += arguments[0].value
|
||||||
|
return self.new_pc(intcode)
|
||||||
|
|
||||||
|
|
||||||
|
class HaltInstruction(Instruction):
|
||||||
|
code, argument_num = 99, 0
|
||||||
|
|
||||||
|
def execute(self, intcode, arguments):
|
||||||
|
intcode.halted = True
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class Argument:
|
||||||
|
def __init__(self, value, address):
|
||||||
|
self.value = value
|
||||||
|
self.address = address
|
||||||
|
|
||||||
|
|
||||||
|
class IntCode:
|
||||||
|
def __init__(self, program, inputs=[], input_func=None):
|
||||||
|
self.program = program[:]
|
||||||
|
self.inputs = inputs[::-1]
|
||||||
|
self.input_func = input_func
|
||||||
|
self.relative_base = 0
|
||||||
|
self.memory = {}
|
||||||
|
self.output = None
|
||||||
|
self.halted = False
|
||||||
|
self.pc = 0
|
||||||
|
|
||||||
|
def _get_instruction(self, instruction_code):
|
||||||
|
return next(
|
||||||
|
cls for cls in Instruction.__subclasses__() if cls.code == instruction_code
|
||||||
|
)
|
||||||
|
|
||||||
|
def _parse_arguments(self, argument_num):
|
||||||
|
modes = str(self.program[self.pc]).zfill(5)[:3][::-1]
|
||||||
|
arguments = []
|
||||||
|
for i in range(argument_num):
|
||||||
|
value = self.program[self.pc + i + 1] + (
|
||||||
|
self.relative_base if modes[i] == "2" else 0
|
||||||
|
)
|
||||||
|
if modes[i] == "1":
|
||||||
|
arguments.append(Argument(value, value))
|
||||||
|
else:
|
||||||
|
arguments.append(
|
||||||
|
Argument(
|
||||||
|
self.program[value]
|
||||||
|
if value < len(self.program)
|
||||||
|
else self.memory.get(value, 0),
|
||||||
|
value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return arguments
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.output = None
|
||||||
|
while not self.halted and self.output is None:
|
||||||
|
instruction = self._get_instruction(self.program[self.pc] % 100)
|
||||||
|
arguments = self._parse_arguments(instruction.argument_num)
|
||||||
|
self.pc = instruction().execute(self, arguments)
|
||||||
|
return self.output
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
last_output = None
|
||||||
|
while not self.halted:
|
||||||
|
output = self.run()
|
||||||
|
if not self.halted:
|
||||||
|
last_output = output
|
||||||
|
return last_output
|
||||||
|
|
||||||
|
def clone(self):
|
||||||
|
cloned = IntCode(self.program)
|
||||||
|
cloned.inputs = self.inputs[:]
|
||||||
|
cloned.input_func = self.input_func
|
||||||
|
cloned.relative_base = self.relative_base
|
||||||
|
cloned.memory = {key: value for key, value in self.memory.items()}
|
||||||
|
cloned.output = self.output
|
||||||
|
cloned.halted = self.halted
|
||||||
|
cloned.pc = self.pc
|
||||||
|
return cloned
|
||||||
|
|
||||||
|
def get(self, address):
|
||||||
|
return (
|
||||||
|
self.program[address]
|
||||||
|
if address < len(self.program)
|
||||||
|
else self.memory.get(value + self.relative_base, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
def set(self, address, value):
|
||||||
|
target = self.program if address < len(self.program) else self.memory
|
||||||
|
target[address] = value
|
||||||
|
|
||||||
|
def input(self, value):
|
||||||
|
self.inputs = [value] + self.inputs
|
||||||
|
|
||||||
|
def get_input(self):
|
||||||
|
return self.inputs.pop() if self.inputs else self.input_func()
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
code = list(map(int, open(os.path.join(SCRIPT_DIR, "input.txt")).read().split(",")))
|
||||||
|
|
||||||
|
part2 = IntCode(code, [5]).execute()
|
||||||
|
print(part2)
|
1
2019/day5/input.txt
Normal file
1
2019/day5/input.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
3,225,1,225,6,6,1100,1,238,225,104,0,1102,67,92,225,1101,14,84,225,1002,217,69,224,101,-5175,224,224,4,224,102,8,223,223,101,2,224,224,1,224,223,223,1,214,95,224,101,-127,224,224,4,224,102,8,223,223,101,3,224,224,1,223,224,223,1101,8,41,225,2,17,91,224,1001,224,-518,224,4,224,1002,223,8,223,101,2,224,224,1,223,224,223,1101,37,27,225,1101,61,11,225,101,44,66,224,101,-85,224,224,4,224,1002,223,8,223,101,6,224,224,1,224,223,223,1102,7,32,224,101,-224,224,224,4,224,102,8,223,223,1001,224,6,224,1,224,223,223,1001,14,82,224,101,-174,224,224,4,224,102,8,223,223,101,7,224,224,1,223,224,223,102,65,210,224,101,-5525,224,224,4,224,102,8,223,223,101,3,224,224,1,224,223,223,1101,81,9,224,101,-90,224,224,4,224,102,8,223,223,1001,224,3,224,1,224,223,223,1101,71,85,225,1102,61,66,225,1102,75,53,225,4,223,99,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,1105,0,99999,1105,227,247,1105,1,99999,1005,227,99999,1005,0,256,1105,1,99999,1106,227,99999,1106,0,265,1105,1,99999,1006,0,99999,1006,227,274,1105,1,99999,1105,1,280,1105,1,99999,1,225,225,225,1101,294,0,0,105,1,0,1105,1,99999,1106,0,300,1105,1,99999,1,225,225,225,1101,314,0,0,106,0,0,1105,1,99999,8,226,226,224,102,2,223,223,1005,224,329,1001,223,1,223,1108,677,677,224,1002,223,2,223,1006,224,344,101,1,223,223,1007,226,677,224,102,2,223,223,1005,224,359,101,1,223,223,1007,677,677,224,1002,223,2,223,1006,224,374,101,1,223,223,1108,677,226,224,1002,223,2,223,1005,224,389,1001,223,1,223,108,226,677,224,102,2,223,223,1006,224,404,101,1,223,223,1108,226,677,224,102,2,223,223,1005,224,419,101,1,223,223,1008,677,677,224,102,2,223,223,1005,224,434,101,1,223,223,7,677,226,224,1002,223,2,223,1005,224,449,101,1,223,223,1008,226,226,224,102,2,223,223,1005,224,464,1001,223,1,223,107,226,677,224,1002,223,2,223,1006,224,479,1001,223,1,223,107,677,677,224,102,2,223,223,1005,224,494,1001,223,1,223,1008,226,677,224,102,2,223,223,1006,224,509,1001,223,1,223,1107,677,226,224,102,2,223,223,1005,224,524,101,1,223,223,1007,226,226,224,1002,223,2,223,1006,224,539,1001,223,1,223,107,226,226,224,102,2,223,223,1006,224,554,101,1,223,223,108,677,677,224,1002,223,2,223,1006,224,569,1001,223,1,223,7,226,677,224,102,2,223,223,1006,224,584,1001,223,1,223,8,677,226,224,102,2,223,223,1005,224,599,101,1,223,223,1107,677,677,224,1002,223,2,223,1005,224,614,101,1,223,223,8,226,677,224,102,2,223,223,1005,224,629,1001,223,1,223,7,226,226,224,1002,223,2,223,1006,224,644,1001,223,1,223,108,226,226,224,1002,223,2,223,1006,224,659,101,1,223,223,1107,226,677,224,1002,223,2,223,1006,224,674,101,1,223,223,4,223,99,226
|
Loading…
Reference in New Issue
Block a user