Last active
January 25, 2020 22:52
-
-
Save cdcarter/7851dd6901f208c864c98c3be5474c08 to your computer and use it in GitHub Desktop.
Advent of Code Day 2: Basic IntCode Machine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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