def main(stdscr): # Clear screen stdscr.clear() curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK) curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK) input_queue = Queue() output_queue = Queue() program = IntcodeProgram(io_scheme=IntcodeProgram.IOScheme.QUEUE, input_queue=input_queue, output_queue=output_queue) program.initialize_memory_from_file('input.txt') droid = RepairDroid() program.queue_input(droid.movement) while not droid.at_o2_system: try: program.execute_next() except WaitingForInput: droid.process_program_outcome(program) droid.draw_map(stdscr) o2_system = droid.position droid.draw_map(stdscr, message=f'O2 System at {o2_system}') stdscr.getch() shortest_path = astar(droid.path_map, Point(0, 0), o2_system) droid.draw_map(stdscr, draw_path=shortest_path, message=f'Part One: {len(shortest_path) - 1}') stdscr.getch() while droid.path_unseen: try: program.execute_next() except WaitingForInput: droid.process_program_outcome(program) droid.draw_map(stdscr) droid.draw_map(stdscr, message=f'end map') stdscr.getch() all_paths = astar(droid.path_map, o2_system, Point(1000, 1000)) max_depth = max(all_paths, key=lambda x: x.g) longest_path = astar(droid.path_map, o2_system, max_depth.position) droid.draw_map(stdscr, message=f'end map', draw_path=longest_path) droid.draw_map(stdscr, draw_path=longest_path, message=f'Part Two: {len(longest_path) - 1}') stdscr.getch()
def main(): input_queue = Queue() output_queue = Queue() program = IntcodeProgram(io_scheme=IntcodeProgram.IOScheme.QUEUE, input_queue=input_queue, output_queue=output_queue) program.initialize_memory_from_file('input.txt') memory_dump = program.dump_memory() survey_hull(program, memory_dump) survey_hull(program, memory_dump, run=True)
def d2p2(): prod = product(range(100), range(100)) result = None for noun, verb in prod: ip = IntcodeProgram(d2_input) ip.intcode[1], ip.intcode[2] = noun, verb while not ip.halted: ip.next() if ip.intcode[0] == 19690720: result = int(str(noun).zfill(2) + str(verb).zfill(2)) break return result
def count_blocks(inputs, display=False, debug=False): '''Executes the Intcode program on the provided inputs and finds out the number of blocks on the screen when the game exits. It also returns the board when the game exits (i.e. the initial board since no game was actually played). :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :param display: Whether or not to display the board after the game exits. :type display: bool :param debug: Whether or not the IntcodeProgram should debug its execution at each instruction processing. :type debug: bool :return: Inital board (when no game was played) and number of blocks on the screen when the game exits. :rtype: dict, int ''' # prepare the board board = {} # prepare the program instance to read the given inputs as an Intcode # program program = IntcodeProgram(inputs, debug=debug) running = True # execute the program until it halts (but pause every 3 outputs) while running: # execute until 3 digits have been outputted state = program.run(pause_every=3) # check for state: # . if paused: parse outputs and apply the actions if state == 'pause': x, y, id = program.output program.reset_output() if id == 0: marker = None elif id == 1: marker = 'W' elif id == 2: marker = 'B' elif id == 3: marker = 'H' else: marker = 'O' board[(x, y)] = marker # . else: stop the program elif state is None: running = False break if display: display_board(board) return board, sum([1 if m == 'B' else 0 for m in board.values()])
def main(): input_queue = Queue() output_queue = Queue() program = IntcodeProgram(io_scheme=IntcodeProgram.IOScheme.QUEUE, input_queue=input_queue, output_queue=output_queue) program.initialize_memory_from_file('input.txt') memory_dump = program.dump_memory() left_edge_coordinates, right_edge_coordinates = part_one( program, memory_dump) part_two(program, memory_dump, left_edge_coordinates, right_edge_coordinates)
def find_pair(inputs, wanted_output): '''A brute-force algorithm to systematically try all possible input pairs until we find the one that gave the desired output (we can determine a finished set of possible candidates since we know that each number is in the [0, 99] range). :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :param wanted_output: Desired output of the program. :type wanted_output: int :return: Specific checksum that matches the desired output. :rtype: int ''' # prepare program program = IntcodeProgram(inputs) for noun in range(0, 100): # range is [0, 100[ = [0, 99] for verb in range(0, 100): # reset program to initial state program.reset() # set up noun and verb program.program[1] = noun program.program[2] = verb # run and compare result program.run() if program.program[0] == wanted_output: return 100 * noun + verb
def get_map(inputs, display=False, debug=False): '''Executes the Intcode program on the provided inputs and finds out the required number of moves to reach the oxygen system in the room. :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :param display: Whether or not to display the map at the end of the scan. :type display: bool :param debug: Whether or not the IntcodeProgram should debug its execution at each instruction processing. :type debug: bool :return: Current map of the scaffolds and simplified map. :rtype: list(str), dict(tuple(int, int), int) ''' # prepare the program instance to read the given inputs as an Intcode # program program = IntcodeProgram(inputs, debug=debug) # run the program to get the current map program.run() map = [chr(x) for x in program.output] # get simple map with only the coordinates of the scaffolds and of the robot # (ignore empty space) simple_map = {} # . get map (width, height) dimensions w = map.index('\n') h = len(map) // w # . remove new lines from map to avoid offset m = [item for item in map if item != '\n'] for y in range(h): for x in range(w): v = m[x + y * w] if v == '#': simple_map[(x, y)] = 1 elif v == '^': simple_map[(x, y)] = 2 elif v == '>': simple_map[(x, y)] = 3 elif v == 'v': simple_map[(x, y)] = 4 elif v == '<': simple_map[(x, y)] = 5 # optionally display the map if display: display_map(map) return map, simple_map
def find_oxygen_system(inputs, display=False, export=False, debug=False): '''Executes the Intcode program on the provided inputs and finds out the required number of moves to reach the oxygen system in the room. :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :param display: Whether or not to display the board at the end. :type display: bool :param export: Whether or not to export the board at each iteration of exploration and path building. :type export: bool :param debug: Whether or not the IntcodeProgram should debug its execution at each instruction processing. :type debug: bool :return: MazeSolver instance and number of moves required to reach the oxygen system (i.e. length of the shortest path to the system). :rtype: MazeSolver, int ''' # prepare the program instance to read the given inputs as an Intcode # program program = IntcodeProgram(inputs, debug=debug) # prepare and run the maze solver to explore the maze fully solver = MazeSolver(program, export=export, export_size=(41, 41)) solver.explore() # use Dijkstra's algorithm to get the shortest path between the start # position of the robot and the target position (position of the oxygen # system) path = solver.find_shortest_path() # optionally print the board and the shortest path if display: solver.print_board(path) return solver, len(path) - 1 # remove 1 for the start position
def process_inputs(inputs): '''Executes the Intcode program on the provided inputs and computes the final result. Here, we use the [0, 4] phase settings range and no feedback loop (so we only go through the amplifiers chain once). :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :return: Maximum input to the thrusters. :rtype: int ''' # prepare all possible permutations for phase settings: # we have X possibilities for the first one, X-1 for the second one, # X-2 for the third one... (no replacement) n_amplifiers = 5 candidate_phase_settings = itertools.permutations(range(5), n_amplifiers) thrusts = [] IntcodeProgram.INSTANCE_ID = 0 # reset global instances IDs amplifiers = [ IntcodeProgram(inputs) for _ in range(n_amplifiers) ] for phase_settings in candidate_phase_settings: # reset all amplifiers for amp in amplifiers: amp.reset() # prepare input for first amplifier amplifiers[0].push_memory(0) for current_amplifier in range(n_amplifiers): phase = phase_settings[current_amplifier] amplifiers[current_amplifier].check_running(phase) # execute program amplifiers[current_amplifier].run_multiple(amplifiers) # remember the power sent to the thrusters with these settings output = amplifiers[current_amplifier].output[-1] thrusts.append(output) return max(thrusts)
def __init__(self, master, memory, **kwargs): self.master = master self.scale = 10 self.width = 500 self.height = 500 self.canvas = tk.Canvas( self.master, width=self.width, height=self.height, bg='#333' ) self.canvas.pack() first_droid = Droid(self, IntcodeProgram(memory), [(0, 0)]) self.droids = OrderedDict() self.droids[first_droid.id] = first_droid self.oxygens = OrderedDict() self.wall_mapping = {} self.oxygen_mapping = {} self.moves = [1, 2, 3, 4] self.move_offset = { 1: lambda p: (p[0], p[1]-1), 2: lambda p: (p[0], p[1]+1), 3: lambda p: (p[0]-1, p[1]), 4: lambda p: (p[0]+1, p[1]) } self.oxygen_mode = False self.oxygen_start = None self.master.after(0, self.refresh)
def find_closest_square(inputs, square_size): '''Executes the Intcode program on the provided inputs and searches for the closet square of given size that fits in the tractor beam. Returns a custom checksum for its top-left corner coordinate. :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :param square_size: Size of the edge of the square to find in the grid. :type square_size: int :return: Top-left corner coordinate checksum. :rtype: int ''' # prepare the program instance to read the given inputs as an Intcode # program program = IntcodeProgram(inputs) # get a large grid to inspect size = 2000 map = get_map(program, size) # find a square in it... if possible! # (else, retry with a larger size) candidates = set() for position in map: x, y = position if ((x, y + square_size - 1)) in map and \ ((x + square_size - 1, y)) in map and \ ((x + square_size - 1, y + square_size - 1)) in map: candidates.add((x, y)) if len(candidates) > 0: square = sorted(candidates, key=lambda x: x[0] * 10000 + x[1])[0] else: return -1 # compute checksum of square return square[0] * 10000 + square[1]
def get_affected_positions(inputs, display=False, debug=False): '''Executes the Intcode program on the provided inputs and checks how many positions on the grid are affected by the tractor beam. :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :param display: Whether or not to display the grid at the end of the scan. :type display: bool :param debug: Whether or not the IntcodeProgram should debug its execution at each instruction processing. :type debug: bool :return: Number of positions affected by the tractor beam. :rtype: int ''' # prepare the program instance to read the given inputs as an Intcode # program program = IntcodeProgram(inputs) # get the map of 50x50 map = get_map(program, 50) # optionally display the map if display: for y in range(50): row = '' for x in range(50): row += '#' if (x, y) in map else '.' print(row) return len(map)
def make_tests(): '''Performs tests on the provided examples to check the result of the computation functions is ok.''' # test new instructions ref = '109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99' program = IntcodeProgram([ 109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99 ]) program.run() assert ','.join([str(x) for x in program.output]) == ref # Part I + II assert len(str(process_inputs([1102, 34915192, 34915192, 7, 4, 7, 99, 0]))) == 16 assert process_inputs([104, 1125899906842624, 99]) == 1125899906842624
def process_inputs(inputs, restore_gravity_assist=False): '''Executes the Intcode program on the provided inputs and computes the final result. :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :param restore_gravity_assist: Whether or not to restore the gravity assist by modifying the input program. :type restore_gravity_assist: bool :return: Final output of the program. :rtype: int ''' # restore gravity assist? if restore_gravity_assist: inputs[1] = 12 inputs[2] = 2 # create and execute program program = IntcodeProgram(inputs) program.run() # isolate final result return program.program[0]
def start_on_black(): input_queue = Queue() output_queue = Queue() program = IntcodeProgram(io_scheme=IntcodeProgram.IOScheme.QUEUE, input_queue=input_queue, output_queue=output_queue) program.initialize_memory_from_file('input.txt') input_queue.put(BLACK) painting_robot = Robot() painted_grid = defaultdict(int) while True: try: program.execute_next() except WaitingForInput: new_color = output_queue.get() direction = output_queue.get() painted_grid[painting_robot.get_grid_pos()] = new_color painting_robot.move(direction) input_queue.put(painted_grid[painting_robot.get_grid_pos()]) except ProgramHalted: print('Program Complete') print(f'Painted {len(painted_grid.keys())} tiles at least once') break
def paint(starting_panel_color): ip = IntcodeProgram(d9_input) dir_index = 0 robot_pos = (0, 0) panels = defaultdict(int) panels[robot_pos] = starting_panel_color while 1: color, next_dir, halted = cycle(ip, panels[robot_pos]) if halted: break panels[robot_pos] = color dir_index += 1 if next_dir else -1 robot_pos = dirs_move[dirs[dir_index % 4]](robot_pos) return panels
def __init__(self, master, memory, **kwargs): self.master = master self.scale = 5 self.padding = 50 self.memory = memory self.memory[0] = 2 self.program = IntcodeProgram(self.memory) self.program.run_till_input() self.state_history = [] op = OutputParser(self.program.output) self.canvas = tk.Canvas( self.master, width=self.coord(op.width)+self.padding, height=self.coord(op.height)+self.padding ) self.tile_mapping = {(x, y): Tile( self.canvas, self.scale, (x, y), (self.coord(x), self.coord(y)), ttype ) for x, y, ttype in op.tiles} self.score = 0 self.score_label = self.canvas.create_text((50, 20), text='Score: 0') self.state_label = self.canvas.create_text( (250, 20), text='State: IDLE') self.canvas.pack() self.move_history = [] self.moved = False self.canvas.bind("<Right>", lambda ev: self.set_next_dir(1)) self.canvas.bind("<Left>", lambda ev: self.set_next_dir(-1)) self.canvas.bind("<Up>", lambda ev: self.set_next_dir(0)) self.canvas.bind("<Down>", lambda ev: self.step_back()) self.canvas.bind("<1>", lambda event: self.canvas.focus_set()) self.canvas.focus_set() self.master.after(0, self.refresh)
def d7p2(): highest_signal = 0 for settings in permutations(range(5, 10)): curr_signal = 0 ip_mapping = list( map(lambda curr_setting: IntcodeProgram(d7_input, curr_setting), settings)) while all(not ip.halted for ip in ip_mapping): for ip in ip_mapping: ip.set_input(curr_signal) ip.run_till_output() if not ip.halted: curr_signal = int(''.join(ip.output)) ip.reset_output() highest_signal = max(highest_signal, curr_signal) return highest_signal
def process_inputs_feedback(inputs): '''Executes the Intcode program on the provided inputs and computes the final result. Here, we use the [5, 9] phase settings range and a feedback loop to pass through the amplifiers multiple times. :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :return: Maximum input to the thrusters. :rtype: int ''' # prepare all possible permutations for phase settings: # we have X possibilities for the first one, X-1 for the second one, # X-2 for the third one... (no replacement) n_amplifiers = 5 candidate_phase_settings = itertools.permutations(range(5, 10), n_amplifiers) thrusts = [] IntcodeProgram.INSTANCE_ID = 0 # reset global instances IDs amplifiers = [ IntcodeProgram(inputs) for _ in range(n_amplifiers) ] for phase_settings in candidate_phase_settings: # reset all amplifiers for amp in amplifiers: amp.reset() # prepare input for first amplifier amplifiers[0].push_memory(0) current_amplifier = 0 running = True while running: # if necessary, initialize amplifier phase = phase_settings[current_amplifier] amplifiers[current_amplifier].check_running(phase) # run amplifier (either from scratch or from where it last # stopped) next_amp = amplifiers[current_amplifier].run_multiple(amplifiers) # . if we errored somewhere if next_amp is None: return None # . else if amplifiers loop has halted elif next_amp == -1: running = False # . else reassign the current amplifier index for next iteration else: current_amplifier = next_amp # remember the power sent to the thrusters with these settings output = amplifiers[current_amplifier].output[-1] thrusts.append(output) return max(thrusts)
def d7p1(): highest_signal = 0 for settings in permutations(range(5), 5): curr_signal = 0 for curr_setting in settings: ip = IntcodeProgram(d7_input, curr_setting) ip.set_input(curr_signal) ip.run() curr_signal = int(''.join(ip.output)) highest_signal = max(highest_signal, curr_signal) return highest_signal
def process_inputs(inputs, input): '''Executes the Intcode program on the provided inputs and computes the final result. :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :param input: Specific input for the program execution. :type input: int :return: Final output of the program. :rtype: int ''' # create program program = IntcodeProgram(inputs) # insert input in memory program.push_memory(input) # execute program program.run() # return last output return program.output[-1]
def start_on_white(): input_queue = Queue() output_queue = Queue() program = IntcodeProgram(io_scheme=IntcodeProgram.IOScheme.QUEUE, input_queue=input_queue, output_queue=output_queue) program.initialize_memory_from_file('input.txt') input_queue.put(WHITE) painting_robot = Robot() painted_grid = defaultdict(int) while True: try: program.execute_next() except WaitingForInput: new_color = output_queue.get() direction = output_queue.get() painted_grid[painting_robot.get_grid_pos()] = new_color painting_robot.move(direction) input_queue.put(painted_grid[painting_robot.get_grid_pos()]) except ProgramHalted: break grid = [key for key in painted_grid.keys()] max_x = max(grid, key=lambda val: val[0])[0] max_y = max(grid, key=lambda val: val[1])[1] min_x = min(grid, key=lambda val: val[0])[0] min_y = min(grid, key=lambda val: val[1])[1] for y in range(max_y, min_y - 1, -1): for x in range(min_x, max_x + 1): print(' ' if painted_grid[(x, y)] == BLACK else '█', end='') print()
def process_inputs(inputs, input=None, debug=False): '''Executes the Intcode program on the provided inputs and computes the final result. :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :param input: Integer to provide as input to the program. :type input: int :param debug: Whether or not the IntcodeProgram should debug its execution at each instruction processing. :type debug: bool :return: Last output of the program. :rtype: int ''' # create program program = IntcodeProgram(inputs, debug=debug) # insert input in memory if need be if input is not None: program.push_memory(input) # run program program.run() # get final result return program.output[-1]
def compute_score(board, inputs, export=False, debug=False): '''Executes the Intcode program on the provided inputs and finds out the score of the player when the last block has been destroyed. :param board: Initial board. :type board: dict :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :param export: Whether or not to export the board as an image at each game move. :type export: bool :param debug: Whether or not the IntcodeProgram should debug its execution at each instruction processing. :type debug: bool :return: Final score of the player. :rtype: int ''' # get paddle and ball coordinates for (x, y), marker in board.items(): if marker == 'H': px = x elif marker == 'O': bx = x init_n_blocks = None last_n_blocks = None # prepare the program instance to read the given inputs as an Intcode # program program = IntcodeProgram(inputs, debug=debug) # insert quarters to run in "free mode" program.program[0] = 2 running = True score = None time = 0 print('Remaining block(s):') # execute the program until it halts (but pause every 3 outputs) while running: # move the paddle to catch the ball and continue the game if px < bx: # move right program.insert_memory(1) elif px > bx: # move left program.insert_memory(-1) else: # reset movement to null program.insert_memory(0) # execute until 3 digits have been outputted state = program.run(pause_every=3) # check for state: # . if paused: parse outputs and apply the actions if state == 'pause': x, y, id = program.output program.reset_output() if x == -1 and y == 0: score = id # if outputting score and no more blocks: game ends if n_blocks == 0: # (export board?) if export: export_board(time, board) running = False print('\n') break else: if id == 0: marker = None elif id == 1: marker = 'W' elif id == 2: marker = 'B' elif id == 3: marker = 'H' px = x else: marker = 'O' bx = x board[(x, y)] = marker # . else: stop the program elif state is None: running = False break # . check to see if all blocks have disappeared n_blocks = sum([1 if m == 'B' else 0 for m in board.values()]) # (initial blocks count and initial export, if need be) if init_n_blocks is None: init_n_blocks = n_blocks if export: export_board(0, board) time += 1 # (if number of remaining blocks changed, output it) if last_n_blocks != n_blocks: c = 50 * n_blocks // init_n_blocks suffix = ('{:%dd}' % (len(str(init_n_blocks)))).format(n_blocks) bar = '■' * c + ' ' * (50 - c) print('{} {} / {}'.format(bar, suffix, init_n_blocks), end='\r') last_n_blocks = n_blocks # (export board?) if export and n_blocks != init_n_blocks: if time % 2 == 0: export_board(time // 2, board) time += 1 return score
def d5p1(): ip = IntcodeProgram(d5_input, 1) ip.run() return int(''.join(ip.output))
def main(): input_queue = Queue() output_queue = Queue() program = IntcodeProgram(io_scheme=IntcodeProgram.IOScheme.QUEUE, input_queue=input_queue, output_queue=output_queue) program.initialize_memory_from_file('input.txt') memory_dump = program.dump_memory() program.run_to_end() scaffolding = build_scaffolding(program) alignment_parameters = [] for scaffold in scaffolding: if scaffolding.issuperset(scaffold.get_neighbors()): alignment_parameters.append(scaffold.x * scaffold.y) print(f'Part One: {sum(alignment_parameters)}') program.load_memory(memory_dump) program.set_memory_address(0, 2) drive_around(program)
class App(object): def __init__(self, master, memory, **kwargs): self.master = master self.scale = 5 self.padding = 50 self.memory = memory self.memory[0] = 2 self.program = IntcodeProgram(self.memory) self.program.run_till_input() self.state_history = [] op = OutputParser(self.program.output) self.canvas = tk.Canvas( self.master, width=self.coord(op.width)+self.padding, height=self.coord(op.height)+self.padding ) self.tile_mapping = {(x, y): Tile( self.canvas, self.scale, (x, y), (self.coord(x), self.coord(y)), ttype ) for x, y, ttype in op.tiles} self.score = 0 self.score_label = self.canvas.create_text((50, 20), text='Score: 0') self.state_label = self.canvas.create_text( (250, 20), text='State: IDLE') self.canvas.pack() self.move_history = [] self.moved = False self.canvas.bind("<Right>", lambda ev: self.set_next_dir(1)) self.canvas.bind("<Left>", lambda ev: self.set_next_dir(-1)) self.canvas.bind("<Up>", lambda ev: self.set_next_dir(0)) self.canvas.bind("<Down>", lambda ev: self.step_back()) self.canvas.bind("<1>", lambda event: self.canvas.focus_set()) self.canvas.focus_set() self.master.after(0, self.refresh) def step_back(self): print('STEP BACK!') if len(self.state_history) > 0: self.program = loads(self.state_history.pop()) self.move_history.pop() self.render_output() print(self.move_history) @property def ball(self): ball, = [t for t in self.tile_mapping.values() if t.ttype == 4] return (ball.x, ball.y) @property def paddle(self): paddle, = [t for t in self.tile_mapping.values() if t.ttype == 3] return (paddle.x, paddle.y) def set_next_dir(self, next_dir): print('NEXT DIR!', next_dir) self.move_history.append(next_dir) self.moved = False self.canvas.itemconfigure(self.state_label, text='State: MOVING') print(self.move_history) def coord(self, x): return x*self.scale+self.padding def refresh(self): if self.moved: self.master.after(5, self.refresh) return if len(self.state_history) == 100: self.state_history.pop(0) self.state_history.append(dumps(self.program)) self.program.set_input(self.move_history[-1]) self.program.next() self.program.run_till_input() self.moved = True self.canvas.itemconfigure(self.state_label, text='State: IDLE') self.render_output() if self.check_state(): self.master.after(5, self.refresh) def render_output(self): op = OutputParser(self.program.output) for x, y, ttype in op.tiles: self.tile_mapping[(x, y)].update(ttype) if op.score is not None: self.canvas.itemconfigure( self.score_label, text='Score: %s' % op.score) def check_state(self): # if self.ball[1] >= self.paddle[1]: # return False if len([t for t in self.tile_mapping.values() if t.ttype == 2]) == 0: return False return True
def main(stdscr): automatic = True # Clear screen stdscr.clear() input_queue = Queue() output_queue = Queue() program = IntcodeProgram(io_scheme=IntcodeProgram.IOScheme.QUEUE, input_queue=input_queue, output_queue=output_queue) program.initialize_memory_from_file('input.txt') program.set_memory_address(0, 2) tiles = {} while True: try: program.execute_next() except WaitingForInput: process_frame(program, tiles) draw_screen(stdscr, tiles) if automatic: sleep(.05) paddle = get_paddle_x(tiles) ball = get_ball_x(tiles) if paddle > ball: program.queue_input(-1) elif paddle < ball: program.queue_input(1) else: program.queue_input(0) else: capture_key = stdscr.getch() if capture_key == curses.KEY_LEFT: program.queue_input(-1) elif capture_key == curses.KEY_RIGHT: program.queue_input(1) else: program.queue_input(0) except ProgramHalted: process_frame(program, tiles) break draw_screen(stdscr, tiles) stdscr.getch()
def save_robots(simple_map, inputs, export=False, debug=False): '''Saves the robots by walking on the scaffolds, and also collects dust. :param simple_map: Map to walk. :type simple_map: dict(tuple(int, int), int) :param inputs: List of integers to execute as an Intcode program. :type inputs: list(int) :param export: Whether or not to ask for a continuous video feed and to export the resulting images. :type export: bool :param debug: Whether or not the IntcodeProgram should debug its execution at each instruction processing. :type debug: bool :return: Amount of dust collected during the process. :rtype: int ''' # prepare the program instance to read the given inputs as an Intcode # program program = IntcodeProgram(inputs, debug=debug) # force the robot to wake up program.program[0] = 2 # get full path (x, y), dir = [(k, v) for k, v in simple_map.items() if v != 1][0] visited = set([(x, y)]) path = [] steps = 0 while len(visited) != len(simple_map): # get all possible directions from current state (the robot cannot make # a U-turn!) possible_dirs = DIRECTION_POSSIBILITES[dir] # get relevant neighbors neighbors = {} for move_dir in range(2, 6): # robot cannot make a U-turn! if move_dir not in possible_dirs: continue dx, dy = DIRECTION_DELTAS[move_dir] nx, ny = x + dx, y + dy if (nx, ny) in simple_map: val = 1 if (nx, ny) in visited else 0 if move_dir == dir: val -= 1 neighbors[(nx, ny)] = (val, move_dir) # get best neighbor: if possible, not already visited neighbor, (_, d) = sorted(neighbors.items(), key=lambda k: k[1][0])[0] x, y = neighbor if d != dir: if steps > 0: steps += 1 path.append(str(steps)) steps = 0 if possible_dirs.index(d) > possible_dirs.index(dir): path.append('R') dir = dir + 1 if dir < 5 else 2 else: path.append('L') dir = dir - 1 if dir > 2 else 5 else: steps += 1 visited.add((x, y)) if path[-1] == 'L' or path[-1] == 'R': path = path[:-1] path_str = ','.join(path) print(path_str) # movements = { 'A': [], 'B': [], 'C': [] } # movement = '' # c = 0 # for m in [ 'A', 'B', 'C' ]: # while movement in path_str[len(movement):]: # movement += path_str[c] # c += 1 # movements[m] = movement # print(movements) # # A L,11,R,3,R,3,L,5, # # B L,11,R,3,R,3,R,11, # # A L,11,R,3,R,3,L,5, # # C L,9,L,5,R,3, # # A L,11,R,3,R,3,L,5, # # B L,11,R,3,R,3,R,11, # # C L,9,L,5,R,3, # # B L,11,R,3,R,3,R,11, # # C L,9,L,5,R,3, # # A/B L,11,R,3,R,3 # # movements_chain = [] # for move, pattern in movements.items(): # if path_str.startswith(pattern): # movements_chain.append(move) # path_str = path_str[len(move)-1:] # print(path_str) # print(movements_chain) # return # separate paths into subpatterns (done by hand) A = encode_movement('L,12,R,4,R,4,L,6') B = encode_movement('L,12,R,4,R,4,R,12') C = encode_movement('L,10,L,6,R,4') movements = encode_movement('A,B,A,C,A,B,C,B,C,A') # prepare movements program.push_memory(movements) program.push_memory(A) program.push_memory(B) program.push_memory(C) program.push_memory([ord('y' if export else 'n'), 10]) # run the program to move the robot program.run() print('Finished computing.') # export maps if need be if export: full_map = ''.join([chr(x) for x in program.output]) all_maps = [ m for m in full_map.split('\n\n') if len(m) > 0 and m[0] == '.' ] if not os.path.exists(EXPORT_DIR): os.makedirs(EXPORT_DIR) for i, map in enumerate(all_maps): export_map(i, map) return program.output[-1]
def get_point(self, x, y): ip = IntcodeProgram(self.memory) ip.set_input(x) ip.set_input(y) ip.run() return int(ip.output[0])