Skip to content

Instantly share code, notes, and snippets.

@cdcarter
Last active January 25, 2020 22:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cdcarter/7851dd6901f208c864c98c3be5474c08 to your computer and use it in GitHub Desktop.
Save cdcarter/7851dd6901f208c864c98c3be5474c08 to your computer and use it in GitHub Desktop.
Advent of Code Day 2: Basic IntCode Machine
from typing import List, Callable, NamedTuple, Optional, Tuple
def scan(script: str) -> List[int]:
""" turn a string input into the ints intcode loves to eat"""
return list(map(int, script.split(",")))
class Operator(NamedTuple):
code: int
arity: int
behavior: Callable
OPERATORS = {
99: Operator(99, 0, lambda vm, args: vm.exit()),
1: Operator(
1, 3, lambda vm, args: vm.put(args[2], vm.at(args[0]) + vm.at(args[1]))
),
2: Operator(
2, 3, lambda vm, args: vm.put(args[2], vm.at(args[0]) * vm.at(args[1]))
),
}
class VM:
""" The IntCode VM
Termination
>>> VM(scan("99")).interpret()
[99]
Addition
>>> VM(scan("1,0,0,3,99")).interpret()
[1, 0, 0, 2, 99]
Multiplication
>>> VM(scan("1,1,1,4,99,5,6,0,99")).test()
30
Errors
>>> all(VM.go(script) == -1 for script in
... ["1,0,0,8,99", "3000,1,2,3"])
True
"""
run: bool = True
pc: int = 0
memory: List[int]
@staticmethod
def go(script: str) -> int:
return VM(scan(script)).test()
def __init__(self, program: List[int]):
self.memory = program
def at(self, a: int) -> int:
return self.memory[a]
def put(self, a: int, v: int):
self.memory[a] = v
def exit(self):
self.run = False
def interpret(self) -> List[int]:
while self.run:
op = OPERATORS[self.memory[self.pc]]
self.pc += 1
args = self.memory[self.pc : self.pc + op.arity]
self.pc += op.arity
op.behavior(self, args)
return self.memory
def test(self) -> int:
try:
if self.run:
self.interpret()
return self.memory[0]
except (
KeyError,
IndexError,
): # KeyError indicates bad operator, IndexError indicates trying to deal with an OOB addr
return -1
class AoCDay2(NamedTuple):
""" AoC Day 2 - We have an intcode program, but don't know the last running state needed to get it going again.
>>> input = scan("1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,10,1,19,1,5,19,23,1,23,5,27,2,27,10,31,1,5,31,35,2,35,6,39,1,6,39,43,2,13,43,47,2,9,47,51,1,6,51,55,1,55,9,59,2,6,59,63,1,5,63,67,2,67,13,71,1,9,71,75,1,75,9,79,2,79,10,83,1,6,83,87,1,5,87,91,1,6,91,95,1,95,13,99,1,10,99,103,2,6,103,107,1,107,5,111,1,111,13,115,1,115,13,119,1,13,119,123,2,123,13,127,1,127,6,131,1,131,9,135,1,5,135,139,2,139,6,143,2,6,143,147,1,5,147,151,1,151,2,155,1,9,155,0,99,2,14,0,0")
>>> AoCDay2(input).test(9706670, 12, 2) # Part 1
True
>>> AoCDay2(input).test(9706670, 9706670, 2)
False
>>> AoCDay2(input).solve(19690720)
(25, 52)
"""
program: List[int]
def test(self, goal: int, noun: int, verb: int) -> bool:
program = self.program[:]
program[1], program[2] = noun, verb
return VM(program).test() == goal
def solve(self, goal) -> Optional[Tuple[int, int]]:
for n in range(100):
for v in range(100):
if self.test(goal, n, v):
return (n, v)
return None
if __name__ == "__main__":
import doctest
doctest.testmod(verbose=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment