class robot: ''' This robot paints panels. It starts on panel (0,0) which is either black (0) or white (1). Then it asks the Intcode computer for input. This returns the color to paint the current panel and the direction to take. The robot will keep moving and painting until the Intcode computer is done running the input code. ''' def __init__(self, start_c): self.dir = np.array( (1, 0)) #Left: (0,-1), Down: (-1,0), Right: (0,1), Up: (1,0) self.panels = {} self.panels[(0, 0)] = start_c #Start on panel that is black or white self.cur_pos = np.array((0, 0)) self.brain = Intcode(input_code) self.anti_clockwise = { (-1, 0): (0, -1), (0, -1): (1, 0), (1, 0): (0, 1), (0, 1): (-1, 0) } self.clockwise = { (-1, 0): (0, 1), (0, 1): (1, 0), (1, 0): (0, -1), (0, -1): (-1, 0) } def next_instruction(self): return self.brain.RunIntcode([self.cur_color]) def paint(self, c): self.panels[tuple(self.cur_pos)] = c def move(self, d): if d == 0: #Anti-clockwise (turn 90 degrees left) self.dir = np.array(self.anti_clockwise[tuple(self.dir)]) elif d == 1: #turn 90 degrees right self.dir = np.array(self.clockwise[tuple(self.dir)]) else: raise Exception('Invalid direction encountered.') #Update position based on this direction self.cur_pos = self.cur_pos + self.dir if tuple(self.cur_pos) not in self.panels: self.panels[tuple(self.cur_pos)] = 0 #Initiate this panel to black self.cur_color = self.panels[tuple(self.cur_pos)] def run(self): self.cur_color = self.panels[tuple( self.cur_pos)] #0 is black, 1 is white next_inst = self.next_instruction() while len(next_inst) != 0: c, d = next_inst #Color to paint and direction to turn self.paint(c) self.move(d) next_inst = self.next_instruction() def paint_panels(self): #Paint the panels on a grid and show result all_y = [a[0] for a in self.panels.keys()] all_x = [a[1] for a in self.panels.keys()] min_x = np.min(all_x) max_x = np.max(all_x) diff_x = max_x - min_x + 1 min_y = np.min(all_y) max_y = np.max(all_y) diff_y = max_y - min_y + 1 grid = np.zeros((diff_y, diff_x)) for i, y_i in enumerate(range(min_y, max_y + 1)): row = [] for x_i in range(min_x, max_x + 1): if (y_i, x_i) in self.panels: row.append(self.panels[(y_i, x_i)]) else: row.append(0) grid[i, :] = row plt.imshow(grid.T, origin='lower') plt.savefig('./Day11/message.png')
class droid: ''' Droid class that will explore the maze. ''' def __init__(self): self.cur_pos = np.array([0,0]) #Current position of the droid (2D coordinate) self.game = Intcode(input_code) #north (1), south (2), west (3), and east (4) self.in_to_dirs = {1:tuple([1,0]), 2:tuple([-1,0]), 3:tuple([0,-1]), 4:tuple([0,1])} self.dirs_to_in = {tuple(v): int(k) for k, v in self.in_to_dirs.items()} #Reverse dictionary self.unexplored = [] #Buffer list for unexplored tiles adjacent to original position self.anti_clockwise = {1:3, 3:2, 2:4, 4:1} self.clockwise = {1:4, 4:2, 2:3, 3:1} self.reverse_dir = {1:2, 2:1, 3:4, 4:3} #0 = wall, 1=empty, 2=oxygen self.panels = {tuple(self.cur_pos): 0} #Panels of which we know the identity self.grid_num = 0 #counter for plots def move(self, dir): #Move in direction north (1), south (2), west (3), and east (4). output = self.game.RunIntcode([dir]) #Gives back 0 for wall, 1 for open space, 2 for oxygen system in list of length 1 assert len(output) == 1 return output[0] def test_move(self, dir): #Test a move but do not actually move there (so move droid back to original position) output = self.game.RunIntcode([dir]) #Move in dir assert len(output) == 1 if output[0] != 0: #If it was not a wall, move back (if it was a wall droid is still in original position) revdir = self.reverse_dir[dir] self.game.RunIntcode([revdir]) #move back return output def fill_unexplored(self): for r in [1,2,3,4]: #Check every direction possible_next = self.cur_pos + np.array(self.in_to_dirs[r]) if tuple(possible_next) not in self.panels: self.unexplored.append(tuple(possible_next)) def explore(self): #Returns a list of possible next positions from self.cur_pos self.fill_unexplored() #Get surrounding unexplored positions possible_next = [] while len(self.unexplored) != 0: #As long as there are unexplored surrounding positions to go to next_pos = np.array(self.unexplored.pop()) input_dir = self.dirs_to_in[tuple(next_pos - self.cur_pos)] out = self.test_move(input_dir)[0] #Check what's at the position if out == 0: #not moved, destination was a wall self.panels[tuple(next_pos)] = 0.5 #WALL elif out == 1: #moved to position possible_next.append(next_pos) elif out == 2: #moved to position, oxygen system is here possible_next.append(next_pos) return possible_next def show_grid(self, save=False): #Convert coordinates to only positive numbers y_map = np.arange(-19, 22) #-19 - 21, 41 numbers x_map = np.arange(-21, 20) #-21 - 20, 41 numbers grid = np.zeros((41,41)) for i in range(grid.shape[0]): row = np.zeros(grid.shape[1]) for k in self.panels.keys(): if int(np.where(y_map == k[0])[0]) == i: row[int(np.where(x_map == k[1])[0])] = self.panels[k] grid[i,:] = row plt.xticks([]) plt.yticks([]) plt.imshow(grid, origin='lower', vmin=0, vmax=3, cmap='Greys') if save: plt.savefig('./Day15/grids/'+str(self.grid_num)+'.png', format="png") plt.clf() self.grid_num+=1 else: plt.show() def run(self): path_taken = LifoQueue() path_taken.put(self.cur_pos) split_points = [] #Coordinates from which two or more directions can be taken counter = 0 while not path_taken.empty(): counter+=1 if counter % 10 == 0: print('Counter: {}'.format(counter)) pn = self.explore() if len(pn) > 1: #More than 1 unexplored path to take split_points.append(tuple(self.cur_pos)) next_pos = pn.pop() output = self.move(self.dirs_to_in[tuple(next_pos - self.cur_pos)]) if output == 1: self.panels[tuple(next_pos)] = 1 elif output == 2: self.panels[tuple(next_pos)] = 2 self.cur_pos = next_pos path_taken.put(tuple(self.cur_pos)) elif len(pn) == 1: #One unexplored path to take next_pos = pn.pop() output = self.move(self.dirs_to_in[tuple(next_pos - self.cur_pos)]) if output == 1: self.panels[tuple(next_pos)] = 1 elif output == 2: self.panels[tuple(next_pos)] = 2 self.cur_pos = next_pos path_taken.put(tuple(self.cur_pos)) else: #Need to go back to split point last_step = self.cur_pos while (tuple(last_step) not in split_points) & (not path_taken.empty()): last_step = path_taken.get() #If previous position not the same as current position, move back to previous position if tuple(np.array(last_step) - np.array(self.cur_pos)) != (0,0): input_dir = self.dirs_to_in[tuple(np.array(last_step) - np.array(self.cur_pos))] out = self.move(input_dir) self.cur_pos = last_step if not path_taken.empty(): path_taken.put(last_step) split_points.remove(last_step) self.cur_pos = last_step
import numpy as np from Intcode import * from itertools import permutations with open("./Day9/input.txt") as file: input_code = [int(s) for s in file.read().strip().split(',')] IntComputer1 = Intcode(input_code) answer_part1 = IntComputer1.RunIntcode([1])[0] IntComputer2 = Intcode(input_code) answer_part2 = IntComputer2.RunIntcode([2])[0] print('Answer part 1: {}'.format(answer_part1)) print('Answer part 2: {}'.format(answer_part2))