def test_large_ints(): # Should output a 16-digit number prog = [1102, 34915192, 34915192, 7, 4, 7, 99, 0] output = [] res = run_until_halt(load_program(prog), output_cb=lambda x: output.append(x)) assert len(output) == 1 and len(str(output[0])) == 16 # Should output the number in the middle prog = [104, 1125899906842624, 99] output = [] res = run_until_halt(load_program(prog), output_cb=lambda x: output.append(x)) assert len(output) == 1 and output[0] == 1125899906842624
def run_row(prog, row, xs, xe): print("run_row()", row, xs, xe) context = { "output": [0 for x in range(xs, xe + 1)], "curr_pos": [xs, row], "input_axis": 0, } def handle_input(): axis = context["input_axis"] res = context["curr_pos"][axis] #print("Axis {}: {}".format(axis, res)) context["input_axis"] = (context["input_axis"] + 1) % 2 return res def handle_output(x): coord = context["curr_pos"] context["output"][coord[0] - xs] = x context["curr_pos"][0] += 1 while context["curr_pos"][0] < xe: vm_prog = intcode.load_program(prog.copy()) intcode.run_until_halt(vm_prog, handle_input, handle_output) if 1 in context["output"]: first_one = xs + context["output"].index(1) last_one = xs + len( context["output"]) - context["output"][::-1].index(1) else: first_one = 0 last_one = 0 return (context["output"], first_one, last_one)
def run_program_step1(prog): context = { "grid": [[0 for x in range(50)] for y in range(50)], "curr_pos": [0, 0], "input_axis": 0, } def handle_input(): axis = context["input_axis"] res = context["curr_pos"][axis] #time.sleep(0.1) #print("Axis {}: {}".format(axis, res)) context["input_axis"] = (context["input_axis"] + 1) % 2 return res def handle_output(x): coord = context["curr_pos"] context["grid"][coord[1]][coord[0]] = x context["curr_pos"][0] += 1 if context["curr_pos"][0] >= 50: context["curr_pos"][1] += 1 context["curr_pos"][0] = 0 while context["curr_pos"][1] < 50: vm_prog = intcode.load_program(prog.copy()) intcode.run_until_halt(vm_prog, handle_input, handle_output) return context
def run_row(row): global GRID, PROG context = { "curr_pos": [0, row], "input_axis": 0, } def handle_input(): axis = context["input_axis"] res = context["curr_pos"][axis] context["input_axis"] = (context["input_axis"] + 1) % 2 return res def handle_output(x): coord = context["curr_pos"] GRID[(coord[1] * 50) + coord[0]] = x context["curr_pos"][0] += 1 if context["curr_pos"][0] >= 50: context["curr_pos"][1] += 1 context["curr_pos"][0] = 0 while context["curr_pos"][1] == row: vm_prog = intcode.load_program(PROG) intcode.run_until_halt(vm_prog, handle_input, handle_output)
def test_quine(): prog = [ 109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99 ] output = [] res = run_until_halt(load_program(prog), output_cb=lambda x: output.append(x)) assert output == prog
def main(): progs = [ [1, 0, 0, 0, 99], [2, 3, 0, 3, 99], [2, 4, 4, 5, 99, 0], [1, 1, 1, 4, 99, 5, 6, 0, 99], [1, 1, 1, 4, 99, 5, 6, 0, 99], [1002, 4, 3, 4, 33], # LESS THAN and EQUALS tests [3, 9, 8, 9, 10, 9, 4, 9, 99, -1, 8], [3, 9, 7, 9, 10, 9, 4, 9, 99, -1, 8], [3, 3, 1108, -1, 8, 3, 4, 3, 99], [3, 3, 1107, -1, 8, 3, 4, 3, 99], # JUMP tests [3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9], [3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1], [ 3, 21, 1008, 21, 8, 20, 1005, 20, 22, 107, 8, 21, 20, 1006, 20, 31, 1106, 0, 36, 98, 0, 0, 1002, 21, 125, 20, 4, 20, 1105, 1, 46, 104, 999, 1105, 1, 46, 1101, 1000, 1, 20, 4, 20, 1105, 1, 46, 98, 99 ], # Quine [ 109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99 ] ] for prog in progs: run_and_disassemble(prog) with open("../09/input.txt", "rt") as f: prog = [int(x) for x in f.read().strip().split(",") if len(x) > 0] print("Disassembling BOOST (test mode)...") prog_c = intcode.load_program(prog) for ctx in intcode.run_single_steps(prog_c, input_cb=lambda: 1): print(intcode.disassemble(ctx, prog_c)) print("") print("Running BOOST (full)...") intcode.run_until_halt(intcode.load_program(prog), input_cb=lambda: 2) print("DONE")
def test_complex(): prog = load_program([ 3, 21, 1008, 21, 8, 20, 1005, 20, 22, 107, 8, 21, 20, 1006, 20, 31, 1106, 0, 36, 98, 0, 0, 1002, 21, 125, 20, 4, 20, 1105, 1, 46, 104, 999, 1105, 1, 46, 1101, 1000, 1, 20, 4, 20, 1105, 1, 46, 98, 99 ]) for test in [(7, 999), (8, 1000), (9, 1001)]: output = [] res = run_until_halt(prog.copy(), lambda: test[0], lambda x: output.append(x)) assert output[0] == test[1]
def test_simple(): programs = [ ([1, 0, 0, 0, 99], [2, 0, 0, 0, 99]), ([2, 3, 0, 3, 99], [2, 3, 0, 6, 99]), ([2, 4, 4, 5, 99, 0], [2, 4, 4, 5, 99, 9801]), ([1, 1, 1, 4, 99, 5, 6, 0, 99], [30, 1, 1, 4, 2, 5, 6, 0, 99]), ([1, 1, 1, 4, 99, 5, 6, 0, 99], [30, 1, 1, 4, 2, 5, 6, 0, 99]), ([1002, 4, 3, 4, 33], [1002, 4, 3, 4, 99]), ] for prog in programs: res = run_until_halt(load_program(prog[0]), lambda: 0, lambda x: 0) assert list(res.values()) == prog[1]
def test_less_than_equals(): programs = [ ([3, 9, 8, 9, 10, 9, 4, 9, 99, -1, 8], [(8, 1), (7, 0), (9, 0)]), ([3, 9, 7, 9, 10, 9, 4, 9, 99, -1, 8], [(8, 0), (7, 1), (9, 0)]), ([3, 3, 1108, -1, 8, 3, 4, 3, 99], [(8, 1), (7, 0), (9, 0)]), ([3, 3, 1107, -1, 8, 3, 4, 3, 99], [(8, 0), (7, 1), (9, 0)]), ] for prog in programs: for test in prog[1]: output = [] res = run_until_halt(load_program(prog[0]), lambda: test[0], lambda x: output.append(x)) assert output[0] == test[1]
def test_jumps(): programs = [ ([3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9], [(-1, 1), (0, 0), (1, 1)]), ([3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1], [(-1, 1), (0, 0), (1, 1)]), ] for prog in programs: for test in prog[1]: output = [] res = run_until_halt(load_program(prog[0]), lambda: test[0], lambda x: output.append(x)) assert output[0] == test[1]
def run_program_step2(prog, view_feed, seq): # TODO: parse movement sequence and find longest repeating patterns for function definitions # TODO: convert 3 longest repeated sequences to function definitions and update main_routine to be a list of function calls print("TODO: compress sequence: {}".format(seq)) main_routine = "A,A,B,C,B,C,B,C,B,A" functions = [ "R,10,L,12,R,6", "R,6,R,10,R,12,R,6", "R,10,L,12,L,12", ] context = { "output": "", "input_idx": 0, } # Concatenate the main_routine, functions and live feed answer to a # single string of characters to sent as input to the robot command_str = main_routine + "\n" + "\n".join(functions) + "\n" + ( "y" if view_feed else "n") + "\n" def handle_input(): res = 0 if context["input_idx"] < len(command_str): res = ord(command_str[context["input_idx"]]) context["input_idx"] += 1 return res def handle_output(x): if view_feed: if x == 10: context["output"] += "\n" print(context["output"]) else: context["output"] += chr(x) else: context["output"] = x # Wake up robot prog[0] = 2 prog = intcode.load_program(prog) intcode.run_until_halt(prog, handle_input, handle_output) return context
def run_program_step1(prog): context = { "output": "", } def handle_input(): return 0 def handle_output(x): if x == 10: context["output"] += "\n" else: context["output"] += chr(x) prog = intcode.load_program(prog) intcode.run_until_halt(prog, handle_input, handle_output) return context
import intcode from threading import Thread from queue import Queue from numpy import array import time program = intcode.load_program('input.dat') def executor(program, qi, qo): print('Running a program...') intcode.execute(program, lambda: qi.get(timeout=2), qo.put) print('Finished running a program...') qo.put(None) def rotate(direction, left=True): directions = ((0, -1), (1, 0), (0, 1), (-1, 0)) shift = -1 if left else 1 return directions[(directions.index(direction) + shift) % len(directions)] def painter(qi, qo, start_color=0): panels = {} position = array((0, 0)) direction = (0, -1) print('Start painting...') qo.put(start_color) while True: paint = qi.get(timeout=1) if paint is None: print(f'Received {paint}. Finish painting...')
def run_and_disassemble(prog): print("Disassembling...") prog = intcode.load_program(prog) for ctx in intcode.run_single_steps(prog): print(intcode.disassemble(ctx, prog)) print("")
def run_program(prog, break_on_oxy_unit): """ Runs the repair droid IntCode program and handles I/O. The I/O routines effectively perform a depth-first search (DFS) through the maze contained within the program. """ context = { # Current repair droid position (only for rendering) "droid_pos": [0, 0], # Index of the current cell in the open list "curr_cell": 0, # Closed list: cells fully explored (all neighbours visited or # a wall) "closed_cells": [], # Open list: cells whose surroundings have not been fully # explored "open_cells": [ { "pos": (0, 0), "dir": 1, "typ": 1 }, ], # Location of the oxygen generator (once found) "oxy_unit": None, } def handle_input(): if context["curr_cell"] < 0 or context["curr_cell"] >= len( context["open_cells"]): return 0 cell = context["open_cells"][context["curr_cell"]] d = cell["dir"] # If the current cell's direction is None then all directions # have been explored, so move it to the closed list and move # the droid back to the previous cell in the open list if d is None: return move_backwards(context) # Check all directions from the current cell that haven't # already been explored while True: new_pos = get_new_coord(cell["pos"], d) if not is_in_list(new_pos, context): break d += 1 if d > 4: break # If an unexplored direction exists, follow it and add the new # cell to the open list if d <= 4: cell["dir"] = d context["open_cells"].append({ "pos": get_new_coord(cell["pos"], d), "dir": 1, "typ": None, }) context["curr_cell"] += 1 return d # Otherwise, this cell has been fully explored so move it to # the closed list and move the droid back to the previous cell # in the open list else: return move_backwards(context) def handle_output(x): cell = context["open_cells"][context["curr_cell"]] # Droid hit a wall and remained at its previous location if x == 0: cell["typ"] = 0 context["closed_cells"].append(context["open_cells"].pop(-1)) back_track(context) # Droid moved to the new cell elif x == 1: cell["typ"] = 1 context["droid_pos"][0] = cell["pos"][0] context["droid_pos"][1] = cell["pos"][1] # Droid moved to the new cell and the oxygen unit was # discovered there elif x == 2: cell["typ"] = 2 context["droid_pos"][0] = cell["pos"][0] context["droid_pos"][1] = cell["pos"][1] context["oxy_unit"] = cell prog = intcode.load_program(prog) # Single-step the program and break as soon as the oxygen unit is found # (if necessary) for vm_ctx in intcode.run_single_steps(prog, handle_input, handle_output): if break_on_oxy_unit and context["oxy_unit"] is not None: break return context
from intcode import load_program, run_program program = load_program("./5.in") # stdin is defined as [1] in the problem run_program(program, [1])