class Y2019D9(object): def __init__(self, file_name): self.computer = Intcode(file_name) def part1(self): self.computer.reset() self.computer.run() self.computer.input(1) outputs = [] while not self.computer.halted: outputs.append(self.computer.output()) if len(outputs) == 1: print("Part 1:", outputs[-1]) else: print("Something went wrong, invalid output:") for output in outputs: print(output) def part2(self): self.computer.reset() self.computer.run() self.computer.input(2) print("Part 2:", self.computer.output())
class Y2019D5(object): def __init__(self, file_name): self.computer = Intcode(file_name) def part1(self): self.computer.reset() self.computer.run() self.computer.input(1) outputs = [] while not self.computer.halted: if self.computer.has_output: output = self.computer.output() outputs.append(output) is_valid_output = len(list(filter(None, outputs))) == 1 and outputs[-1] > 0 if is_valid_output: print("Part 1:", outputs[-1]) else: print("INVALID!") for output in outputs: print(output) def part2(self): self.computer.reset() self.computer.run() self.computer.input(5) result = self.computer.output() print("Part 2:", result)
class Y2019D11(object): def __init__(self, file_name): self.robot = Intcode(file_name) self.grid = InfiniteGrid[bool]() # False -> black; True -> white def _paint(self): self.robot.reset() self.robot.run() turtle = Turtle(direction=TurtleDirection.UP) painted_tiles = set() while not self.robot.halted: if turtle.coordinate not in self.grid: tile_is_white = False else: tile_is_white = self.grid[turtle.coordinate] self.robot.input(1 if tile_is_white else 0) new_color = self.robot.output() # Paint the current tile white or black self.grid[turtle.coordinate] = True if new_color == 1 else False painted_tiles.add(turtle.coordinate) turn = self.robot.output() if turn == 0: turtle = turtle.turn_left() else: turtle = turtle.turn_right() turtle = turtle.forward() return painted_tiles def part1(self): self.grid.clear() painted_tiles = self._paint() result = len(painted_tiles) print("Part 1:", result) def part2(self): self.grid.clear() self.grid[Coordinate(0, 0)] = True self._paint() print("Part 2:") self.grid.to_grid().print(key=lambda is_white: '#' if is_white else ' ')
class Y2019D13(object): def __init__(self, file_name): self.arcade = Intcode(file_name) self.grid: InfiniteGrid[Tile] = InfiniteGrid[Tile]() self.score = 0 def reset(self): self.score = 0 self.grid.clear() self.arcade.reset() def _get_game_update(self): while self.arcade.has_output: x = self.arcade.output() y = self.arcade.output() tile_id = self.arcade.output() if x == -1 and y == 0: self.score = tile_id else: self.grid[x, y] = Tile.from_id(tile_id) # self.grid.to_grid().print(key=lambda x: x.character) def part1(self): self.reset() self.arcade.run() self._get_game_update() result = len(self.grid.find(Tile.BLOCK)) print("Part 1:", result) def part2(self): self.reset() self.arcade.ram[0] = 2 self.arcade.run() self._get_game_update() while not self.arcade.halted: ball = self.grid.find(Tile.BALL)[0] paddle = self.grid.find(Tile.PADDLE)[0] # Move the paddle if needed if ball.x > paddle.x: self.arcade.input(1) elif ball.x < paddle.x: self.arcade.input(-1) else: self.arcade.input(0) self._get_game_update() print("Part 2:", self.score)
class Y2019D7(object): def __init__(self, file_name): self.comp_a = Intcode(file_name) self.comp_b = Intcode(file_name) self.comp_c = Intcode(file_name) self.comp_d = Intcode(file_name) self.comp_e = Intcode(file_name) @staticmethod def _iterator(start, end): for a in range(start, end + 1): for b in range(start, end + 1): for c in range(start, end + 1): for d in range(start, end + 1): for e in range(start, end + 1): if len({a, b, c, d, e}) == 5: yield [a, b, c, d, e] def _reset_and_start_computers(self): self.comp_a.reset() self.comp_a.run() self.comp_b.reset() self.comp_b.run() self.comp_c.reset() self.comp_c.run() self.comp_d.reset() self.comp_d.run() self.comp_e.reset() self.comp_e.run() def part1(self): result = 0 for settings in self._iterator(0, 4): self._reset_and_start_computers() self.comp_a.input(settings[0]) self.comp_b.input(settings[1]) self.comp_c.input(settings[2]) self.comp_d.input(settings[3]) self.comp_e.input(settings[4]) self.comp_a.input(0) self.comp_b.input(self.comp_a.output()) self.comp_c.input(self.comp_b.output()) self.comp_d.input(self.comp_c.output()) self.comp_e.input(self.comp_d.output()) result = max(result, self.comp_e.output()) print("Part 1:", result) def part2(self): result = 0 for settings in self._iterator(5, 9): self._reset_and_start_computers() self.comp_a.input(settings[0]) self.comp_b.input(settings[1]) self.comp_c.input(settings[2]) self.comp_d.input(settings[3]) self.comp_e.input(settings[4]) self.comp_a.input(0) while not self.comp_a.halted: self.comp_b.input(self.comp_a.output()) self.comp_c.input(self.comp_b.output()) self.comp_d.input(self.comp_c.output()) self.comp_e.input(self.comp_d.output()) if not self.comp_a.halted: self.comp_a.input(self.comp_e.output()) result = max(result, self.comp_e.output()) print("Part 2:", result)
class Y2019D15(object): def __init__(self, file_name): self.droid = Intcode(file_name) self.grid = InfiniteGrid[Tile]() def _map_out_grid(self): self.grid.clear() self.droid.reset() all_directions = [ TurtleDirection.NORTH, TurtleDirection.SOUTH, TurtleDirection.EAST, TurtleDirection.WEST ] droid_coordinate = Coordinate(0, 0) self.grid[droid_coordinate] = Tile.EMPTY backtrack = [] need_to_check = {droid_coordinate: all_directions.copy()} self.droid.run() while backtrack or need_to_check: test_directions = need_to_check[droid_coordinate] # Nowhere to move, try to go back if not test_directions: del need_to_check[droid_coordinate] if backtrack: back = backtrack.pop() self.droid.input(self._get_move_input(back)) self.droid.output( ) # Read an output, although we know we can move back droid_coordinate = back.move(droid_coordinate) continue direction = test_directions.pop() if backtrack and direction == backtrack[-1]: continue # Don't go back, use backtrack stack for that self.droid.input(self._get_move_input(direction)) output = self.droid.output() next_coordinate = direction.move(droid_coordinate) self.grid[next_coordinate] = Tile.from_id(output) if output != 0: droid_coordinate = next_coordinate backtrack.append(direction.opposite()) need_to_check[droid_coordinate] = all_directions.copy() def _get_move_input(self, direction: TurtleDirection): if direction == TurtleDirection.NORTH: return 1 elif direction == TurtleDirection.SOUTH: return 2 elif direction == TurtleDirection.WEST: return 3 elif direction == TurtleDirection.EAST: return 4 def part1(self): self._map_out_grid() start = Coordinate(0, 0) oxygen = self.grid.find(Tile.OXYGEN)[0] graph = self.grid.to_graph(Tile.EMPTY, Tile.OXYGEN) # -1 is because path includes start and they want "steps to oxygen" result = len(graph.find_path(start, oxygen, CoordinateHeuristic())) - 1 print("Part 1:", result) def part2(self): self._map_out_grid() empty_tiles = self.grid.find(Tile.EMPTY) minutes = 0 while empty_tiles: oxygen = self.grid.find(Tile.OXYGEN) for oxygen_space in oxygen: for neighbor in oxygen_space.neighbors(): if self.grid[neighbor] == Tile.EMPTY: self.grid[neighbor] = Tile.OXYGEN minutes += 1 empty_tiles = self.grid.find(Tile.EMPTY) print("Part 2:", minutes)
class Y2019D19(object): def __init__(self, file_name): self.drone = Intcode(file_name) def _is_beam(self, x: int, y: int): if x < 0 or y < 0: return False self.drone.reset() self.drone.run() self.drone.input(x) self.drone.input(y) return self.drone.output() == 1 def part1(self): result = 0 for x in range(50): for y in range(50): if self._is_beam(x, y): result += 1 print("Part 1:", result) def part2(self): square_side = 100 end_cache = {} def valid_y(y): # Bit of a hack, but the beam isn't continuous if y < 10: return False if y in end_cache: end_x = end_cache[y] else: start_search = BinarySearch(lambda x: self._is_beam(x, y)) start_x = start_search.earliest(0, lambda x: x + 1) end_x = start_search.latest(start_x, lambda x: x * 2) end_cache[y] = end_x test_x = end_x - square_side + 1 test_y = y + square_side - 1 beam = self._is_beam(test_x, test_y) return beam search = BinarySearch(valid_y) found_y = search.earliest(1, lambda x: x * 2) found_x = end_cache[found_y] - square_side + 1 result = found_x * 10000 + found_y print("Part 2:", result) def _find_beam_right(self, start_x: int, y: int, max_search_x=10000): seen_beam = False for x in range(start_x, max_search_x): is_beam = self._is_beam(x, y) if not is_beam and seen_beam: return x - 1 if is_beam: seen_beam = True return None