class DroidController: def __init__(self, file_str, echo=True): self.computer = IntcodeComputer(file_str, echo=False) self.x, self.y = 0, 0 self.min_x, self.max_x = 0, 0 self.min_y, self.max_y = 0, 0 self.map = {(self.x, self.y): '.'} self.path = [] self.target = None def print_map(self): for y in range(self.min_y, self.max_y + 1): for x in range(self.min_x, self.max_x + 1): if (x, y) in self.map: if (self.x, self.y) == (x, y): print('D', end='') else: tile = self.map[(x, y)] print(tile, end='') else: print(' ', end='') # New Line print('\n', end='') def find_all_unexplored(self): # Find all unexplored tiles all_unexplored = [] for coord in self.map: tile = self.map[coord] if tile == '.' or tile == 'O': unexplored = self.check_unexplored(coord) all_unexplored.extend(unexplored) return all_unexplored def find_nearest_unexplored(self): ''' Returns the (x, y) coordinate of the nearest manhatten distance unexplored tile from self.pos. ''' all_unexplored = self.find_all_unexplored() # Find distance of each tile distances = [] for tile in all_unexplored: dist_x = abs(self.x - tile[0]) dist_y = abs(self.y - tile[1]) distances.append(dist_x + dist_y) # Find shortest distance min_i = -1 min_dist = -1 for i, dist in enumerate(distances): if min_dist is -1 or dist < min_dist: min_i = i min_dist = dist return all_unexplored[min_i] def find_oxygen_system(self): for coord in self.map: if self.map[coord] == 'O': return coord return None def find_max_dist_from_oxygen(self): start = self.find_oxygen_system() if start is None: print('Oxygen System not found') return None # Search for finish from start tile_list = [start] parents = {start: None} distances = {start: 0} while len(tile_list) > 0: tile = tile_list.pop(0) tile_x, tile_y = tile tile_dist = distances[tile] if tile not in self.map: continue # Add neighbors to list if not explored or smaller distance ## Up tile_up = tile_x, tile_y - 1 if tile_up in parents: # Already searched tile. Check for shorter distance tile_up_dist = distances[tile_up] if tile_up_dist > tile_dist + 1: if tile_up in self.map: tile_list.append(tile_up) parents[tile_up] = tile distances[tile_up] = tile_dist + 1 elif tile_up in self.map: # Nonsearched explored tile. Add to list if not a wall if self.map[tile_up] != '#': tile_list.append(tile_up) parents[tile_up] = tile distances[tile_up] = tile_dist + 1 else: # Nonsearched unexplored tile. Add to path but not the search list parents[tile_up] = tile distances[tile_up] = tile_dist + 1 ## Down tile_down = tile_x, tile_y + 1 if tile_down in parents: # Already searched tile. Check for shorter distance tile_down_dist = distances[tile_down] if tile_down_dist > tile_dist + 1: if tile_down in self.map: tile_list.append(tile_down) parents[tile_down] = tile distances[tile_down] = tile_dist + 1 elif tile_down in self.map: # Nonsearched explored tile. Add to list if not a wall if self.map[tile_down] != '#': tile_list.append(tile_down) parents[tile_down] = tile distances[tile_down] = tile_dist + 1 else: # Nonsearched unexplored tile. Add to path but not the search list parents[tile_down] = tile distances[tile_down] = tile_dist + 1 ## Left tile_left = tile_x - 1, tile_y if tile_left in parents: # Already searched tile. Check for shorter distance tile_left_dist = distances[tile_left] if tile_left_dist > tile_dist + 1: if tile_left in self.map: tile_list.append(tile_left) parents[tile_left] = tile distances[tile_left] = tile_dist + 1 elif tile_left in self.map: # Nonsearched explored tile. Add to list if not a wall if self.map[tile_left] != '#': tile_list.append(tile_left) parents[tile_left] = tile distances[tile_left] = tile_dist + 1 else: # Nonsearched unexplored tile. Add to path but not the search list parents[tile_left] = tile distances[tile_left] = tile_dist + 1 ## Right tile_right = tile_x + 1, tile_y if tile_right in parents: # Already searched tile. Check for shorter distance tile_right_dist = distances[tile_right] if tile_right_dist > tile_dist + 1: if tile_right in self.map: tile_list.append(tile_right) parents[tile_right] = tile distances[tile_right] = tile_dist + 1 elif tile_right in self.map: # Nonsearched explored tile. Add to list if not a wall if self.map[tile_right] != '#': tile_list.append(tile_right) parents[tile_right] = tile distances[tile_right] = tile_dist + 1 else: # Nonsearched unexplored tile. Add to path but not the search list parents[tile_right] = tile distances[tile_right] = tile_dist + 1 max_dist = 0 for tile in distances: if distances[tile] > max_dist: max_dist = distances[tile] return max_dist def check_unexplored(self, coord): ''' Returns a list of (x, y) coordinates containing unexplored tiles in 4 cardinal directions of given coordinate ''' unexplored = [] x, y = coord # Top tile_up = x, y - 1 if tile_up not in self.map: unexplored.append(tile_up) # Bottom tile_down = x, y + 1 if tile_down not in self.map: unexplored.append(tile_down) # Left tile_left = x - 1, y if tile_left not in self.map: unexplored.append(tile_left) # Right tile_right = x + 1, y if tile_right not in self.map: unexplored.append(tile_right) return unexplored def find_path_to_target(self, start, finish): # print('Finding path from %s to %s' % (start, finish)) # Search for finish from start tile_list = [start] parents = {start: None} distances = {start: 0} while len(tile_list) > 0: tile = tile_list.pop(0) tile_x, tile_y = tile tile_dist = distances[tile] if tile not in self.map: continue # Add neighbors to list if not explored or smaller distance ## Up tile_up = tile_x, tile_y - 1 if tile_up in parents: # Already searched tile. Check for shorter distance tile_up_dist = distances[tile_up] if tile_up_dist > tile_dist + 1: if tile_up in self.map: tile_list.append(tile_up) parents[tile_up] = tile distances[tile_up] = tile_dist + 1 elif tile_up in self.map: # Nonsearched explored tile. Add to list if not a wall if self.map[tile_up] != '#': tile_list.append(tile_up) parents[tile_up] = tile distances[tile_up] = tile_dist + 1 else: # Nonsearched unexplored tile. Add to path but not the search list parents[tile_up] = tile distances[tile_up] = tile_dist + 1 ## Down tile_down = tile_x, tile_y + 1 if tile_down in parents: # Already searched tile. Check for shorter distance tile_down_dist = distances[tile_down] if tile_down_dist > tile_dist + 1: if tile_down in self.map: tile_list.append(tile_down) parents[tile_down] = tile distances[tile_down] = tile_dist + 1 elif tile_down in self.map: # Nonsearched explored tile. Add to list if not a wall if self.map[tile_down] != '#': tile_list.append(tile_down) parents[tile_down] = tile distances[tile_down] = tile_dist + 1 else: # Nonsearched unexplored tile. Add to path but not the search list parents[tile_down] = tile distances[tile_down] = tile_dist + 1 ## Left tile_left = tile_x - 1, tile_y if tile_left in parents: # Already searched tile. Check for shorter distance tile_left_dist = distances[tile_left] if tile_left_dist > tile_dist + 1: if tile_left in self.map: tile_list.append(tile_left) parents[tile_left] = tile distances[tile_left] = tile_dist + 1 elif tile_left in self.map: # Nonsearched explored tile. Add to list if not a wall if self.map[tile_left] != '#': tile_list.append(tile_left) parents[tile_left] = tile distances[tile_left] = tile_dist + 1 else: # Nonsearched unexplored tile. Add to path but not the search list parents[tile_left] = tile distances[tile_left] = tile_dist + 1 ## Right tile_right = tile_x + 1, tile_y if tile_right in parents: # Already searched tile. Check for shorter distance tile_right_dist = distances[tile_right] if tile_right_dist > tile_dist + 1: if tile_right in self.map: tile_list.append(tile_right) parents[tile_right] = tile distances[tile_right] = tile_dist + 1 elif tile_right in self.map: # Nonsearched explored tile. Add to list if not a wall if self.map[tile_right] != '#': tile_list.append(tile_right) parents[tile_right] = tile distances[tile_right] = tile_dist + 1 else: # Nonsearched unexplored tile. Add to path but not the search list parents[tile_right] = tile distances[tile_right] = tile_dist + 1 # Find path back to start new_path = [] if finish in parents: path_node = finish while path_node != start: new_path.insert(0, path_node) path_node = parents[path_node] # print('Found Path: %s' % str(new_path)) # print(new_path) return new_path def move_on_path(self): if self.path == []: print('Error: No Path to follow') return None path_x, path_y = self.path.pop(0) move_x = path_x - self.x move_y = path_y - self.y if move_y is -1 and move_x is 0: return 1 elif move_y is 1 and move_x is 0: return 2 elif move_y is 0 and move_x is -1: return 3 elif move_y is 0 and move_x is 1: return 4 else: print('%d, %d is invalid' % (move_x, move_y)) return None def make_move(self): # move = -1 # while move < 1 or move > 4: # move = int(input('Selection Direction: ')) # return move if self.path == []: self.target = self.find_nearest_unexplored() self.path = self.find_path_to_target((self.x, self.y), self.target) move = self.move_on_path() return move def run(self): running = True while self.find_all_unexplored() != []: # self.print_map() # Give input and receive response move = self.make_move() # print('Move: %d' % move) self.computer.set_input([move]) self.computer.execute_program() result = self.computer.get_last_value() # Find new position if move == 1: move_x = self.x move_y = self.y - 1 elif move == 2: move_x = self.x move_y = self.y + 1 elif move == 3: move_x = self.x - 1 move_y = self.y elif move == 4: move_x = self.x + 1 move_y = self.y # Update map bounds if move_x < self.min_x: self.min_x = move_x if move_x > self.max_x: self.max_x = move_x if move_y < self.min_y: self.min_y = move_y if move_y > self.max_y: self.max_y = move_y if result == 0: # Hit a wall if (move_x, move_y) not in self.map: self.map[(move_x, move_y)] = '#' elif result == 1: # Moved in direction if (move_x, move_y) not in self.map: self.map[(move_x, move_y)] = '.' self.x, self.y = move_x, move_y elif result == 2: # Found Oxygen System if (move_x, move_y) not in self.map: self.map[(move_x, move_y)] = 'O' self.x, self.y = move_x, move_y running = False else: print('Error: Invalid result %d' % result) running = False oxygen_distance = self.find_max_dist_from_oxygen() print(oxygen_distance)
class ScaffoldInterface: def __init__(self, file_str=None): self.computer = IntcodeComputer(file_str, echo=False) self.scaffolding = None def run(self): self.computer.program[0] = 2 self.computer.run() output = self.computer.get_output_values() self.print_output(output) # self.save_output(output, './map.txt') self.save_scaffolding(output) self.find_path() # Main Routine routine_main = [ord(char) for char in 'A,A,B,C,B,C,B,C,B,A\n'] print('Main Len %d' % len(routine_main)) # Function A routine_A = [ord(char) for char in 'L,10,L,8,R,8,L,8,R,6\n'] print('A Len %d' % len(routine_A)) # Function B routine_B = [ord(char) for char in 'R,6,R,8,R,8\n'] print('B Len %d' % len(routine_B)) # Function C routine_C = [ord(char) for char in 'R,6,R,6,L,8,L,10\n'] print('C Len %d' % len(routine_C)) # Main: self.computer.set_input(routine_main) print(routine_main) self.computer.run() output = self.computer.get_output_values() self.print_output(output) # Function A: self.computer.set_input(routine_A) print(routine_A) self.computer.run() output = self.computer.get_output_values() self.print_output(output) # Function B: self.computer.set_input(routine_B) print(routine_B) self.computer.run() output = self.computer.get_output_values() self.print_output(output) # Function C: self.computer.set_input(routine_C) print(routine_C) self.computer.run() output = self.computer.get_output_values() self.print_output(output) # Continuous Video Feed? in_yes = [ord(char) for char in 'Y,E,S\n'] in_no = [ord(char) for char in 'N,O\n'] self.computer.set_input(in_no) self.computer.run() last_value = self.computer.get_last_value() output = self.computer.get_output_values() self.print_output(output[:-1]) print(last_value) def save_scaffolding(self, output): self.scaffolding = [[]] for val in output[:-2]: if val == ord('\n'): self.scaffolding.append([]) else: self.scaffolding[-1].append(chr(val)) self.scaffolding = self.scaffolding[:-2] def find_path(self): height = len(self.scaffolding) width = len(self.scaffolding[0]) for y in range(height): for x in range(width): tile = self.scaffolding[y][x] if tile == '^': cur_x, cur_y = x, y # print('%d, %d' % (cur_x, cur_y)) # Directions ^ v < > cur_dir = '^' path = [] print('Finding path from (%d, %d)' % (cur_x, cur_y)) while True: # Find direction to trun if cur_dir == '^' or cur_dir == 'v': if cur_x > 0 and self.scaffolding[cur_y][cur_x-1] == '#': if cur_dir == '^': # print('L') path.append('L') cur_dir = '<' else: # print('R') path.append('R') cur_dir = '<' elif cur_x < width-1 and self.scaffolding[cur_y][cur_x+1] == '#': if cur_dir == '^': # print('R') path.append('R') cur_dir = '>' else: # print('L') path.append('L') cur_dir = '>' else: print('Reached the end at (%d, %d) facing %s' % (cur_x, cur_y, cur_dir)) break elif cur_dir == '<' or cur_dir == '>': if cur_y > 0 and self.scaffolding[cur_y-1][cur_x] == '#': if cur_dir == '<': # print('R') path.append('R') cur_dir = '^' else: # print('L') path.append('L') cur_dir = '^' elif cur_y < height-1 and self.scaffolding[cur_y+1][cur_x] == '#': if cur_dir == '<': # print('L') path.append('L') cur_dir = 'v' else: # print('R') path.append('R') cur_dir = 'v' else: print('Reached the end at (%d, %d) facing %s' % (cur_x, cur_y, cur_dir)) break else: print('Error: Invalid direction %s' % cur_dir) # Count steps until off scaffolding step_x, step_y = 0, 0 if cur_dir == '^': step_y = -1 elif cur_dir == 'v': step_y = 1 elif cur_dir == '<': step_x = -1 elif cur_dir == '>': step_x = 1 else: print('Error: Invalid direction %s' % cur_dir) distance = 0 next_tile = self.scaffolding[cur_y+step_y][cur_x+step_x] while next_tile != '.': distance += 1 cur_x += step_x cur_y += step_y if cur_x + step_x < 0 or cur_x + step_x >= width or cur_y + step_y < 0 or cur_y + step_y >= height: next_tile = None break next_tile = self.scaffolding[cur_y+step_y][cur_x+step_x] # print(distance) path.append(distance) print(path) with open('./path.txt', 'w') as file: for step in path: file.write(str(step)) file.write(',') def print_output(self, output): for val in output: print(chr(val), end='') def save_output(self, output, file_str): with open(file_str, 'w') as file: for val in output: file.write(chr(val))