Пример #1
0
def main():
    argparse = ArgumentParser()
    argparse.add_argument("file", type=str, nargs="?", default="21-input.txt")
    args = argparse.parse_args()

    with open(args.file, "r") as f:
        program = [int(c) for c in f.read().split(',')]

    instructions = [
        ord(c) for c in "".join(l + "\n" for l in (
            # if a hole is at distance two, jump now
            "NOT B T",
            "OR T J",

            # if a hole is at distance three, jump now
            "NOT C T",
            "OR T J",

            # But only if there is ground at distance 4
            "AND D J",
            # And there is ground at distance 8
            "AND H J",

            # If we're about to walk into a hole, jump anyway
            "NOT A T",
            "OR T J",
            "RUN"))
    ]

    vm = IntVM(program,
               inputs=instructions,
               output_func=lambda s: print(chr(s), end='', flush=True)
               if s < 127 else print(s))
    vm.run()
Пример #2
0
def main():
    argparse = ArgumentParser()
    argparse.add_argument("file", type=str, nargs="?", default="21-input.txt")
    args = argparse.parse_args()

    with open(args.file, "r") as f:
        program = [int(c) for c in f.read().split(',')]

    instructions = [
        ord(c) for c in "".join(l + "\n" for l in (
            "NOT A J",  # jump if we're about to walk into a hole

            # if a hole is at distance two and there is ground at distance four,
            # jump now because maybe distance five is a hole
            "NOT B T",
            "AND D T",
            "OR T J",

            # if a hole is at distance three and there is ground at distance four,
            # jump now because maybe distance five is a hole
            "NOT C T",
            "AND D T",
            "OR T J",
            "WALK"))
    ]

    vm = IntVM(program,
               inputs=instructions,
               output_func=lambda s: print(chr(s), end='', flush=True)
               if s < 127 else print(s))
    vm.run()
Пример #3
0
class Arcade:
    EMPTY = 0
    WALL = 1
    BLOCK = 2
    PADDLE = 3
    BALL = 4
    TILES = (" ", "█", "▒", "━", "●")

    def __init__(self, program, screen_w=37, screen_h=20):
        self.screen = np.zeros((screen_w, screen_h), dtype=int)
        self.program = program
        self.vm = IntVM(program,
                        inputs=[],
                        output_func=MultiOutput(self._draw, 3))

    def _draw(self, x, y, tile):
        self.screen[x, y] = tile

    def run(self):
        self.vm.run()

    def draw_screen(self):
        for row in self.screen.T:
            print(''.join(self.TILES[t] if 0 <= t < len(self.TILES) else "?"
                          for t in row))
Пример #4
0
class Imager:
    def __init__(self, program):
        self.program = program
        self.pixel_cache = {}
        self.vm = IntVM(self.program)

    def beam_at(self, y, x):
        try:
            return self.pixel_cache[(y, x)]
        except KeyError:

            def process_pixel(val):
                self.pixel_cache[(y, x)] = val

            self.vm.input_gen = iter((x, y))
            self.vm.output_func = process_pixel
            self.vm.run()
            return self.pixel_cache[(y, x)]

    def find_left(self, y, x):
        # Assumes x to be to the left of the starting pixel
        while not self.beam_at(y, x):
            x += 1
        # Returns first pixel inside beam
        return x

    def find_right(self, y, x):
        # Assumes x to be to the left of, or inside the beam
        while not self.beam_at(y, x):
            x += 1
        while self.beam_at(y, x):
            x += 1
        # Returns last pixel inside beam
        return x - 1

    def find_square(self, size=100):
        # Top right edge
        ty, tx = 10, 0
        # Bottom left edge
        by, bx = 10 + size - 1, 0

        tx = self.find_right(ty, tx)
        bx = self.find_left(by, bx)

        while tx - bx < size - 1:
            ty += 1
            by += 1
            tx = self.find_right(ty, tx)
            bx = self.find_left(by, bx)

            print("Width of rectangle of height {} at y={}: {}..{}={}".format(
                size, ty, bx, tx, tx - bx + 1))

        return (ty, tx), (by, bx)

    def get_map(self, miny, maxy, minx, maxx):
        return np.array([[self.beam_at(y, x) for x in range(minx, maxx)]
                         for y in range(miny, maxy)],
                        dtype=int)
Пример #5
0
    def get_tractor_map(self):
        vm = IntVM(self.program)
        image = np.zeros((50, 50), dtype=int)

        for y in range(50):
            for x in range(50):
                def process_pixel(val):
                    image[y, x] = val
                vm.input_gen = iter([x, y])
                vm.output_func = process_pixel
                vm.run()

        return image
Пример #6
0
class Imager:
    SPACE = 0
    SCAFFOLD = 1

    def __init__(self, program):
        self.img_coord = 0, 0
        self.robot_coord = None
        self.program = program
        self.vm = IntVM(self.program,
                        inputs=[],
                        output_func=self.process_pixel)
        self.image = np.zeros((40, 51), dtype=int)
        self.map = ""

    def run(self):
        self.vm.run()

    def process_pixel(self, value):
        self.map = self.map + chr(value)
        x, y = self.img_coord
        if value == ord('\n'):
            x, y = 0, y + 1
        elif value == ord('.'):
            self.image[x, y] = self.SPACE
            x += 1
        elif value in (ord('#'), ord('<'), ord('^'), ord('>'), ord('v')):
            self.image[x, y] = self.SCAFFOLD
            self.robot_coord = x, y
            x += 1
        self.img_coord = x, y

    def get_answer(self):
        intersections = (self.image[1:-1, 1:-1] == self.SCAFFOLD) & \
                (self.image[0:-2, 1:-1] == self.SCAFFOLD) & \
                (self.image[2:, 1:-1] == self.SCAFFOLD) & \
                (self.image[1:-1, 0:-2] == self.SCAFFOLD) & \
                (self.image[1:-1, 2:] == self.SCAFFOLD)

        return sum((x + 1) * (y + 1) for x, y in np.argwhere(intersections))
Пример #7
0
class Arcade:
    EMPTY = 0
    WALL = 1
    BLOCK = 2
    PADDLE = 3
    BALL = 4

    TILES = (" ", "█", "▒", "═", "o")

    JOY_LEFT = -1
    JOY_MIDDLE = 0
    JOY_RIGHT = 1

    def __init__(self, program, quarters=2, fps=10):
        self.program = program.copy()
        self.program[0] = quarters
        self.vm = IntVM(self.program,
                        inputs=self,
                        output_func=MultiOutput(self._output, 3))
        self.score = 0
        self.paddle_x = 0
        self.joystick_position = self.JOY_MIDDLE
        self.scr = None
        self.last_screen = 0
        self.screen_interval = 1 / fps if fps > 0 else 0

    def __iter__(self):
        return iter(lambda: self.joystick_position, -2)

    def _output(self, x, y, tile):
        if (x, y) == (-1, 0):
            self.score = tile
        else:
            self.scr.addstr(
                y, x, self.TILES[tile] if 0 <= tile < len(self.TILES) else "?")

            if tile == self.BALL:
                self.ball_x = x
                self.adjust_joystick()
            elif tile == self.PADDLE:
                self.paddle_x = x
                self.adjust_joystick()
                self.draw_screen()

    def adjust_joystick(self):
        if self.ball_x < self.paddle_x:
            self.joystick_position = self.JOY_LEFT
        elif self.ball_x > self.paddle_x:
            self.joystick_position = self.JOY_RIGHT
        else:
            self.joystick_position = self.JOY_MIDDLE

    def run(self, stdscr):
        self.scr = stdscr
        self.scr.nodelay(1)
        curses.curs_set(0)
        self.vm.run()
        self.scr.nodelay(0)
        self.scr.addstr(
            23, 0,
            "Score: {}, GAME OVER - press key to exit".format(self.score))
        self.scr.refresh()
        self.scr.getkey()

    def draw_screen(self):
        if self.scr.getch() == 3:
            sys.exit(0)

        if self.screen_interval != 0:
            now = time.monotonic()
            if now - self.last_screen < self.screen_interval:
                time.sleep(self.screen_interval - (now - self.last_screen))

        self.scr.addstr(23, 0, "Score: {}".format(self.score))
        self.scr.refresh()
        self.last_screen = time.monotonic()
Пример #8
0
class OxygenFinder:
    NORTH = 1
    SOUTH = 2
    WEST = 3
    EAST = 4

    deltas = {
        NORTH: (0, -1),
        SOUTH: (0, +1),
        WEST: (-1, 0),
        EAST: (+1, 0),
    }

    STATUS_WALL = 0
    STATUS_MOVED = 1
    STATUS_FOUND_OXYGEN = 2

    TILE_UNKNOWN = 0
    TILE_EMPTY = 1
    TILE_WALL = 2
    TILE_OXYGEN = 3

    TILES = [".", " ", "#", "O"]

    def __init__(self, program, fps=10, explore_map=False):
        self.map = np.zeros((1000, 1000), dtype=int)
        self.current_coord = 500, 500
        self.initial_coord = self.current_coord
        self.oxygen_coord = None
        self.oxygen_distance = None
        self.map[self.current_coord] = self.TILE_EMPTY
        self.current_direction = self.WEST
        self.explore_queue = []
        x, y = self.current_coord
        self.min_x, self.min_y, self.max_x, self.max_y = x, y, x, y
        self.queue_surrounding_tiles()
        self.curses_window = None

        self.explore_map = explore_map
        self.finished = False

        self.last_screen = 0
        self.screen_interval = 1 / fps if fps > 0 else 0
        self.other_screen_interval = 0

        self.program = program
        self.vm = IntVM(self.program,
                        inputs=self,
                        output_func=self.process_output)

    def __iter__(self):
        return iter(self.get_input, -1)

    def find_path(self, start, to):
        dist_map = np.zeros(self.map.shape, dtype=int)
        dist_map.fill(10000000)

        dist_map[to] = 0
        stack = [to]

        while stack:
            x, y = stack.pop(-1)
            if (x, y) == start:
                break
            current_dist = dist_map[x, y] + 1

            for candidate in (x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1):
                if self.map[candidate] not in (self.TILE_WALL, self.TILE_OXYGEN) and \
                        (self.map[candidate] != self.TILE_UNKNOWN or self.map[x, y] != self.TILE_UNKNOWN) \
                        and current_dist < dist_map[candidate]:
                    stack.append(candidate)
                    dist_map[candidate] = current_dist

        min_dist = 100000
        min_direction = None
        x, y = start
        for direction, (dx, dy) in self.deltas.items():
            candidate = (x + dx, y + dy)
            if dist_map[candidate] < min_dist:
                min_dist = dist_map[candidate]
                min_direction = direction

        return min_direction, min_dist

    def get_input(self):
        if self.explore_queue:
            direction, _ = self.find_path(self.current_coord,
                                          self.explore_queue[-1])
            self.current_direction = direction
        else:
            self.vm.stop()
        return self.current_direction

    def get_next_coord(self, direction=None):
        direction = self.current_direction if direction is None else direction
        x, y = self.current_coord
        dx, dy = self.deltas[direction]
        return x + dx, y + dy

    def queue_surrounding_tiles(self):
        for direction in (self.NORTH, self.EAST, self.WEST, self.SOUTH):
            nx, ny = self.get_next_coord(direction)
            if self.map[nx, ny] == self.TILE_UNKNOWN and (
                    nx, ny) not in self.explore_queue:
                add = True
                if self.oxygen_distance is not None and not self.explore_map:
                    _, dist = self.find_path(self.oxygen_coord, (nx, ny))
                    add = dist < self.oxygen_distance
                if add:
                    self.explore_queue.append((nx, ny))

    def process_output(self, value):
        # Process a robot status code
        if value == self.STATUS_WALL:
            wall_coord = self.get_next_coord()
            self.map[wall_coord] = self.TILE_WALL
            try:
                self.explore_queue.remove(wall_coord)
            except ValueError:
                pass
        elif value == self.STATUS_MOVED:
            self.current_coord = self.get_next_coord()
            self.map[self.current_coord] = self.TILE_EMPTY
            try:
                self.explore_queue.remove(self.current_coord)
            except ValueError:
                pass
            self.queue_surrounding_tiles()
        elif value == self.STATUS_FOUND_OXYGEN:
            self.current_coord = self.get_next_coord()
            self.map[self.current_coord] = self.TILE_OXYGEN
            self.oxygen_coord = self.current_coord
            try:
                self.explore_queue.remove(self.current_coord)
            except ValueError:
                pass
            _, self.oxygen_distance = self.find_path(self.initial_coord,
                                                     self.oxygen_coord)

        self.finished = len(self.explore_queue) == 0

        x, y = self.current_coord
        self.min_x = min(self.min_x, x)
        self.min_y = min(self.min_y, y)
        self.max_x = max(self.max_x, x)
        self.max_y = max(self.max_y, y)

        self.frame()

    def draw_map(self):
        sx, sy = self.min_x - 1, self.min_y - 1
        my, mx = self.curses_window.getmaxyx()

        for y in range(self.min_y - 1, self.max_y + 2):
            for x in range(self.min_x - 1, self.max_x + 2):
                if y - sy + 1 < my and x - sx < mx:
                    self.curses_window.addch(y - sy + 1, x - sx,
                                             ord(self.TILES[self.map[x, y]]))

        x, y = self.current_coord
        if y - sy < my and x - sx < mx and self.current_direction:
            self.curses_window.addch(
                y - sy + 1, x - sx,
                ord(["^", "v", "<", ">"][self.current_direction - 1]))

        x, y = self.initial_coord
        if y - sy < my and x - sx < mx:
            self.curses_window.addch(y - sy + 1, x - sx, ord("X"))

    def frame(self):
        key = self.curses_window.getch()
        if key == 3:
            # Ctrl+C
            sys.exit(0)
        elif key == ord(' '):
            self.switch_ffwd()

        if self.screen_interval != 0:
            now = time.monotonic()
            if now - self.last_screen < self.screen_interval:
                time.sleep(self.screen_interval - (now - self.last_screen))

        self.draw_map()
        self.curses_window.refresh()

        self.last_screen = time.monotonic()

    def run(self):
        while not self.finished:
            self.vm.reset()
            self.current_coord = self.initial_coord
            self.vm.run()
        if self.explore_map:
            self.save_map()

    def run_curses(self, stdscr):
        self.curses_window = stdscr
        self.curses_window.nodelay(1)
        curses.curs_set(0)
        self.curses_window.addstr(0, 0, "Spacebar = speed up")
        self.run()
        self.draw_map()
        self.curses_window.nodelay(0)
        _, oxygen_distance = self.find_path(self.initial_coord,
                                            self.oxygen_coord)
        oxygen_distance += 1  # Answer is to get on top of the oxygen,
        self.curses_window.addstr(
            0, 0, "Distance {} -> {} = {}".format(self.initial_coord,
                                                  self.oxygen_coord,
                                                  oxygen_distance))
        self.curses_window.refresh()

        # Wait for ctrl+c
        while self.curses_window.getch() != 3:
            pass

    def switch_ffwd(self):
        self.screen_interval, self.other_screen_interval = self.other_screen_interval, self.screen_interval

    def save_map(self):
        sx, sy = self.min_x - 1, self.min_y - 1
        ex, ey = self.max_x + 2, self.max_y + 2

        with open("15-map.txt", "w") as f:
            f.writelines(
                ''.join(self.TILES[t] if (x, y) != self.initial_coord else "X"
                        for x, t in enumerate(self.map[sx:ex, y], sx)) + "\n"
                for y in range(sy, ey))
Пример #9
0
class Imager:
    SPACE = 0
    SCAFFOLD = 1

    NORTH = 0
    EAST = 1
    SOUTH = 2
    WEST = 3

    deltas = [(0, -1), (1, 0), (0, 1), (-1, 0)]
    directions = {'<': WEST, '>': EAST, '^': NORTH, 'v': SOUTH}

    def __init__(self, program):
        self.img_coord = 0, 0
        self.robot_coord = None
        self.robot_direction = None
        self.program = program
        self.input = []
        self.vm = IntVM(self.program,
                        inputs=self.input,
                        output_func=self.process_pixel)
        self.image = np.zeros((40, 51), dtype=int)
        self.raw_map = ""
        self.map = None

    def get_initial_image(self):
        self.vm.program[0] = 1
        self.vm.output_func = self.process_pixel
        self.vm.run()
        w, h = self.image.shape
        # Add zeroes to the edge of the map to avoid peppering everything with boundary checks
        self.map = np.zeros((w + 2, h + 2), dtype=int)
        self.map[1:-1, 1:-1] = self.image
        self.robot_coord = self.robot_coord[0] + 1, self.robot_coord[1] + 1

    def process_pixel(self, raw_value):
        x, y = self.img_coord
        value = chr(raw_value)
        self.raw_map = self.raw_map + value
        if value == '\n':
            x, y = 0, y + 1
        elif value == '.':
            self.image[x, y] = self.SPACE
            x += 1
        elif value in ('#', '<', '^', '>', 'v'):
            self.image[x, y] = self.SCAFFOLD
            if value in self.directions:
                self.robot_coord = x, y
                self.robot_direction = self.directions[value]
            x += 1
        self.img_coord = x, y

    def get_desired_route(self):
        at_end = False
        x, y = self.robot_coord
        direction = self.robot_direction
        while not at_end:
            dx, dy = self.deltas[direction]
            if self.map[x + dx, y + dy] == self.SCAFFOLD:
                x += dx
                y += dy
                yield '1'
            else:
                rdx, rdy = self.deltas[(direction + 1) % 4]
                ldx, ldy = self.deltas[(direction - 1) % 4]
                if self.map[x + rdx, y + rdy] == self.SCAFFOLD:
                    direction = (direction + 1) % 4
                    yield 'R'
                elif self.map[x + ldx, y + ldy] == self.SCAFFOLD:
                    direction = (direction - 1) % 4
                    yield 'L'
                else:
                    at_end = True

    def route_output(self, value):
        if value > 128:
            print("Answer: {}".format(value))

    def run_route(self, main_func, a_func, b_func, c_func):
        self.vm.program[0] = 2
        self.vm.output_func = self.route_output
        config_str = "\n".join([main_func, a_func, b_func, c_func, "n", ""])
        self.vm.input_gen = iter(ord(c) for c in config_str)
        self.vm.run()