mirror of
https://github.com/thib8956/advent-of-code.git
synced 2024-12-25 21:36: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