def fit(codes: DefaultDict[int, int], width, height: int) -> int: """find position in tractor beam that will fit our width and height Arguments: codes {DefaultDict[int, int]} -- intcode machine instructions width {[type]} -- min x area height {int} -- min y area Returns: int -- computed value based on top left area we can fit in """ tractor_bottom_left = TractorInput() tractor_bottom_left.current_x = 1 tractor_bottom_left.current_y = height tractor_top_right = TractorInput() while True: beam_bottom_left = process(copy.copy(codes), tractor_bottom_left.inputs) bottom_status = next(beam_bottom_left) if bottom_status: tractor_top_right.current_x = tractor_bottom_left.current_x + width - 1 tractor_top_right.current_y = tractor_bottom_left.current_y - height + 1 beam_top_right = process(copy.copy(codes), tractor_top_right.inputs) top_status = next(beam_top_right) if top_status: break tractor_bottom_left.current_y += 1 tractor_bottom_left.current_x += 1 return tractor_bottom_left.current_x * 10000 + tractor_top_right.current_y
def alignment_parameter(codes: DefaultDict[int, int]) -> int: """calculate the alightment parameters Arguments: codes {DefaultDict[int, int]} -- intcode machine instructions Returns: int -- calculated value """ scaffold = set() top = 0 left = 0 for code in process(codes): if code == POUND: position = (top, left) scaffold.add(position) if code == NEW_LINE: top += 1 left = 0 else: left += 1 total = 0 for position in get_intersections(scaffold): top, left = position total += top * left return total
def hull_damage(codes: DefaultDict[int, int], inputs: list = None) -> int: """hull damage report. given valid spring droid inputs bot will report hull damage. Otherwise it will output if the droid is stuck in a hole. Arguments: codes {DefaultDict[int, int]} -- intcode instructions Keyword Arguments: inputs {list} -- spring board instructions, if not given user will be prompted Returns: int -- [description] """ if inputs: droid = SpringDroid(collections.deque(inputs)) else: droid = SpringDroid() droid_logic = process(codes, droid.droid_input) outputs = [] for output in droid_logic: if output > 255: return output if output == 10: print("".join(outputs)) outputs = [] else: outputs.append(chr(output))
def tractor_beam(codes: DefaultDict[int, int], max_y: int, max_x: int) -> int: """count area effected by tractor beam Arguments: codes {DefaultDict[int, int]} -- intcode machine instructions max_y {int} -- area max y we are considering max_x {int} -- area max x we are considering Returns: int -- total count """ tractor_input = TractorInput() total = 0 start_x = 0 min_row_count = 1 current_row_count = 0 first_x = True output = [] while tractor_input.current_y < max_y: beam = process(copy.copy(codes), tractor_input.inputs) for status in beam: if status == 1: if first_x: start_x = tractor_input.current_x + 1 first_x = False repeat_count = min(min_row_count, max_x - tractor_input.current_x) output += ["#"] * repeat_count current_row_count += repeat_count tractor_input.current_x += repeat_count - 1 else: output.append("#") current_row_count += 1 else: output.append(".") if tractor_input.current_x >= start_x + int(min_row_count * 1.25) \ or tractor_input.current_x >= max_x: print("".join(output)) if current_row_count == 0: return total tractor_input.current_x = start_x output = ["."] * start_x tractor_input.current_y += 1 first_x = True min_row_count = max(min_row_count, current_row_count) total += current_row_count current_row_count = 0 else: tractor_input.current_x += 1
def paint(codes: DefaultDict[int, int], painted: Dict[tuple, int] = None) -> Dict[tuple, int]: """read paint instructions from intcode computer with provided inputs Arguments: codes {DefaultDict[int, int]} -- intcode inputs, this will output the robots paint instructions Keyword Arguments: painted {Dict[tuple, int]} -- initial haul paint (default: {None}) coords not given are assumed painted Returns: Dict[tuple, int] -- haul paint """ # init painted dictionary if painted is None: painted = {} direction = 0 # start intcode machine result = process(codes) position = (0, 0) for _ in result: _ok = result.send(painted.get(position, BLACK)) color = next(result) painted[position] = color turn = next(result) # turn if turn == TURN_RIGHT: direction += 0.25 if direction == 1: direction = 0 elif turn == TURN_LEFT: direction -= 0.25 if direction == -0.25: direction = 0.75 else: ValueError("Invalid turn: {turn}") # move forward based on direction if direction == UP: position = (position[0] - 1, position[1]) elif direction == RIGHT: position = (position[0], position[1] + 1) elif direction == DOWN: position = (position[0] + 1, position[1]) elif direction == LEFT: position = (position[0], position[1] - 1) return painted
def part_02(input_path): """Part 2""" with open(input_path) as file_input: codes = read_codes(file_input) processing = process(codes) next(processing) _ok = processing.send(5) for output in processing: print(output)
def part_01(input_path, noun, verb): """Part 1""" with open(input_path) as file_input: codes = read_codes(file_input) # replace positions 1 and 2 codes[1] = noun codes[2] = verb processing = process(codes) for _ in processing: pass print(f"result is {codes[0]}")
def run(codes: DefaultDict[int, int], phase: int = None): """run given input with provided phase Arguments: codes {DefaultDict[int, int]} -- instruction set Keyword Arguments: phase {int} -- optinal input (default: {None}) """ boosting = process(codes) if phase is not None: next(boosting) _ok = boosting.send(phase) for output in boosting: print(output)
def count_block(codes: DefaultDict[int, int]) -> int: """count all BLOCK tiles Arguments: codes {DefaultDict[int, int]} -- intcode instructions Returns: int -- total block tiles """ board = {} game = process(codes) for left in game: top = next(game) tile = next(game) board[(top, left)] = tile count = collections.Counter(board.values()) return count[BLOCK]
def part_02(input_path, target): """Part 2""" with open(input_path) as file_input: init_codes = read_codes(file_input) # loop through each possible noun and verb (0-99) for noun in range(100): for verb in range(100): codes = copy.copy(init_codes) # update starting "noun" and "verb" codes[1] = noun codes[2] = verb processing = process(codes) for _ in processing: pass if codes[0] == target: print(f"100 * {noun} + {verb} = {100 * noun + verb}") return
def amp_checks(codes: DefaultDict[int, int], phase_settings: List[int]) -> int: """check all possible phase settings for optimal settings for amplifiers Arguments: codes {DefaultDict[int, int]} -- original list of codes to start on each amp phase_settings {List[int]} -- known phase settings (not in a particular order) Returns: int -- returns the largest possible output with known phase settings """ # list of every result from all sequences results = [] sequences = itertools.permutations(phase_settings) for sequence in sequences: last_output = 0 amps = [] for phase_setting in sequence: # run through amp with a phase settings and last output from previous amp amp_codes = codes.copy() amp = process(amp_codes) next(amp) _ok = amp.send(phase_setting) amps.append(amp) last_output = 0 try: while True: for amp in amps: next(amp) _ok = amp.send(last_output) last_output = next(amp) except StopIteration: pass results.append(last_output) return max(results)
def run(self): """network loop, when output exists send to existing machine on the network""" try: machine = process(copy.copy(self.codes), self.packet_input) while True: address = next(machine) x_portion = next(machine) y_portion = next(machine) self.is_idle = False # unknown network address if address not in self.network: return address, x_portion, y_portion # queue output to proper machine network_manager = self.network[address] # recieved input, not idle network_manager.is_idle = False network_manager.packets.put((x_portion, y_portion)) except NetworkHalt: pass
def play(codes: DefaultDict[int, int], is_human: bool): """play the game! Arguments: codes {DefaultDict[int, int]} -- intcode instructions is_human {bool} -- read in user input if true (fun, but very...slow...) """ screen = curses.initscr() curses.noecho() curses.cbreak() curses.curs_set(False) screen.keypad(True) steps = 0 max_height = 0 max_width = 0 paddle = (0, 0) ball = (0, 0) # for user inputs def draw_input(): screen.refresh() key = screen.getch() if key == curses.KEY_LEFT: return -1 if key == curses.KEY_RIGHT: return 1 return 0 # for automatic inputs def draw_ai(): screen.refresh() if paddle[1] == ball[1]: return 0 if paddle[1] > ball[1]: return -1 return 1 if is_human: game = process(codes, draw_input) else: game = process(codes, draw_ai) try: for left in game: steps += 1 top = next(game) position = (top, left) max_width = max(max_width, left) max_height = max(max_height, top) if position == (0, -1): score = next(game) screen.addstr(max_height + 1, 0, f"Score: {score}") else: tile = next(game) if tile == BALL: ball = position elif tile == PADDLE: paddle = position screen.addstr(top, left, TILE_OUTPUT[tile]) message_1 = "Game Complete!" message_2 = "Press Any Key to Exit..." screen.addstr(max_height // 2, max_width // 2 - len(message_1) // 2, message_1) screen.addstr(max_height // 2 + 1, max_width // 2 - len(message_2) // 2, message_2) screen.refresh() screen.getch() except curses.error: print("Please resize the terminal in order to run the game!") finally: curses.endwin()
def init_map(self): """initialize the map state by traversing to the furthest unknown area""" self.state = process(copy.copy(self.codes), self._blind_input) self._run()
def visit_all(codes: DefaultDict[int, int]) -> int: """visit all locations and gather dust Arguments: codes {DefaultDict[int, int]} -- intcode machine instructions Returns: int -- total dust collected """ # init values bot_position = None bot_facing = None scaffold = set() visited = set() top = 0 left = 0 line = [] for code in process(codes.copy()): if code == POUND: position = (top, left) scaffold.add(position) elif code in BOT_CODE: bot_position = (top, left) scaffold.add(bot_position) visited.add(bot_position) bot_facing = code if code == NEW_LINE: top += 1 left = 0 print(''.join(line)) line = [] else: left += 1 line.append(chr(code)) # get direcions, it appears output does not have intersections at "corners" # so logic should be "turn" then move forward until end of straight away # then find the next "turn" and repeat until the end directions = [] bot_facing, direction = bot_turn(bot_facing, bot_position, scaffold) while bot_facing: # updates visited scaffolds bot_position, moved = bot_move(bot_facing, bot_position, scaffold, visited) directions.append(f"{direction} {moved}") bot_facing, direction = bot_turn(bot_facing, bot_position, scaffold) # check that all scaffolds have been visited assert scaffold == visited, "Not all positions have been visited" # once we determine the full path, we need to compress this into shorter instructions main, func_a, func_b, func_c = compress_directions(directions) def input_gen(actions): for action in ",".join(actions): yield ord(action) yield NEW_LINE main = input_gen(main) func_a = input_gen(map(lambda value: value.replace(" ", ","), func_a)) func_b = input_gen(map(lambda value: value.replace(" ", ","), func_b)) func_c = input_gen(map(lambda value: value.replace(" ", ","), func_c)) def display_prompt(): yield ord("n") yield NEW_LINE display_prompt = display_prompt() def direction_input(): for value in main: return value for value in func_a: return value for value in func_b: return value for value in func_c: return value for value in display_prompt: return value # wake bot up codes[0] = 2 bot_directions = process(codes, direction_input) output = 0 for output in bot_directions: pass return output