Example #1
0
class Robot:
    program: str
    position: Point = field(default=zero_point(2), init=False)
    hull: Dict[Point, Paint] = field(
        default_factory=lambda: defaultdict(lambda: Paint.BLACK), init=False)
    facing: Direction = field(default=Direction.UP, init=False)
    computer: Computer = field(init=False)

    def __post_init__(self):
        self.computer = Computer(Memory(map(int, self.program.split(","))))

    def scan(self):
        self.computer.data_in.put(self.hull[self.position].value)

    def paint(self):
        self.hull[self.position] = Paint(self.computer.data_out.get())

    def move(self):
        self.position += self.facing.value

    def turn(self):
        get = self.computer.data_out.get()
        self.facing = self.facing.prev() if get == 0 else self.facing.next()

    def iterate(self):
        self.scan()
        self.paint()
        self.turn()
        self.move()

    def run(self):
        threading.Thread(target=self.computer.run).start()
        while not self.computer.broken:
            self.iterate()
        return self.hull
Example #2
0
 def run(self):
     while not (self.computer.broken or self.oxygen):
         self.iterate()
     while not (self.computer.broken or self.position == zero_point(2)):
         self.iterate()
     self.p.terminate()
     self.print_hull()
Example #3
0
def make_wire(instructions: List[Instruction]) -> set:
    current = zero_point(2)
    wire = {current}
    for direction, distance in instructions:
        for i in range(1, distance + 1):
            current += directions[direction]
            wire.add(current)
    return wire
Example #4
0
def make_wire(instructions: List[Instruction]) -> Wire:
    current = zero_point(2)
    d = 0
    wire = {}
    for direction, distance in instructions:
        for i in range(1, distance + 1):
            current += directions[direction]
            d += 1
            if current not in wire:
                wire[current] = d
    return wire
Example #5
0
class Robot:
    program: str
    position: Point = field(default=zero_point(2), init=False)
    hull: Dict[Point, Paint] = field(
        default_factory=lambda: defaultdict(lambda: Paint.WHITE), init=False)
    facing: Direction = field(default=Direction.UP, init=False)
    computer: Computer = field(init=False)

    def __post_init__(self):
        self.computer = Computer(Memory(map(int, self.program.split(","))))

    def scan(self):
        self.computer.data_in.put(self.hull[self.position].value)

    def paint(self):
        self.hull[self.position] = Paint(self.computer.data_out.get())

    def move(self):
        self.position += self.facing.value

    def turn(self):
        get = self.computer.data_out.get()
        self.facing = self.facing.prev() if get == 0 else self.facing.next()

    def iterate(self):
        self.scan()
        self.paint()
        self.turn()
        self.move()

    def print_hull(self):
        min_x = min(map(lambda p: p.x, self.hull.keys()))
        min_y = min(map(lambda p: p.y, self.hull.keys()))
        max_x = max(map(lambda p: p.x, self.hull.keys()))
        max_y = max(map(lambda p: p.y, self.hull.keys()))
        for y in range(max_y, min_y - 1, -1):
            for x in range(min_x, max_x + 1):
                if Point(x, y) in self.hull:
                    if self.hull[Point(x, y)] == Paint.BLACK:
                        print("██", end='')
                    else:
                        print("░░", end='')

                else:
                    print("▒▒", end='')

            print()

    def run(self):
        threading.Thread(target=self.computer.run).start()
        while not self.computer.broken:
            self.iterate()
        self.print_hull()
Example #6
0
def solve(data=None):
    data = parse_input(data)
    r = Robot(data)
    r.run()
    g = networkx.Graph()
    paths = []
    for k, v in r.world.items():
        if v == Status.MOVED or v == Status.OXYGEN:
            paths.append(k)
    for n in paths:
        g.add_node(n)
        for d in Direction:
            p = d.value + n
            if r.world[p] == Status.MOVED or r.world[p] == Status.OXYGEN:
                g.add_edge(n, p)

    return networkx.shortest_path_length(g, zero_point(2), r.oxygen)
Example #7
0
def get_intersections(data: List[List[Tuple[str, int]]]) -> Set[Point]:
    wires = list(map(make_wire, data))
    r = reduce(set.intersection, wires)
    r.remove(zero_point(2))
    return r
Example #8
0
class Robot:
    program: str
    position: Point = field(default=zero_point(2), init=False)
    facing: Direction = field(default=Direction.UP, init=False)
    computer: Computer = field(init=False)
    world: Dict[Point, Status] = field(
        default_factory=lambda: defaultdict(lambda: Status.UNEXPLORED),
        init=False)
    oxygen: Point = field(default=None, init=False)

    def __post_init__(self):
        self.computer = computer_from_string(self.program)
        self.p = Process(target=self.computer.run)
        self.p.start()

    def move(self):
        result = self.execute()
        probing = self.position + self.facing.value
        self.world[probing] = result
        if result == Status.WALL:
            self.facing = self.facing.prev()
        else:
            self.position = probing
            if result == Status.OXYGEN:
                self.oxygen = self.position

    def execute(self) -> Status:
        signal = Movement[Conversion(self.facing).name].value
        self.computer.data_in.put(signal)
        return Status(self.computer.data_out.get())

    def iterate(self):
        self.explore()
        if self.world[self.position + self.facing.next().value] == Status.WALL:
            # There is a wall to our right, move forward
            self.move()
        else:
            self.facing = self.facing.next()
            self.move()

    def print_hull(self):
        min_x = min(map(lambda p: p.x, self.world.keys()))
        min_y = min(map(lambda p: p.y, self.world.keys()))
        max_x = max(map(lambda p: p.x, self.world.keys()))
        max_y = max(map(lambda p: p.y, self.world.keys()))
        for y in range(max_y, min_y - 1, -1):
            for x in range(min_x, max_x + 1):
                status = self.world[Point(x, y)]
                if status == Status.WALL:
                    print("██", end='')
                elif status == Status.MOVED:
                    print("░░", end='')
                elif status == Status.OXYGEN:
                    print("()", end='')
                else:
                    print("  ", end='')

            print()

    def run(self):
        while not (self.computer.broken or self.oxygen):
            self.iterate()
        while not (self.computer.broken or self.position == zero_point(2)):
            self.iterate()
        self.p.terminate()
        self.print_hull()

    def explore(self):
        # Pick an unexplored direction and move there
        explorable: List[Point] = [
            d for d in Direction
            if self.world[self.position + d.value] == Status.UNEXPLORED
        ]
        for d in explorable:
            self.check_out(d)

    def check_out(self, d: Direction):
        old_direction = self.facing
        old_position = self.position
        self.facing = d
        self.move()
        self.facing = d.next().next()
        if old_position != self.position:
            self.move()
        self.facing = old_direction
Example #9
0
 def test_length(self):
     for size in range(10):
         with self.subTest(f'length of {size}'):
             self.assertEqual(size, len(zero_point(size)))
Example #10
0
 def __post_init__(self):
     if not self.velocity:
         self.velocity = zero_point(len(self.position))
     self.initial_position = self.position