Example #1
0
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())
Example #2
0
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)
Example #3
0
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 ' ')
Example #4
0
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)
Example #5
0
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)
Example #6
0
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)
Example #7
0
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