예제 #1
0
파일: d6.py 프로젝트: Jnesselr/AdventOfCode
    def __init__(self, file_name):
        lines = Input(file_name).lines()
        self.coordinates: Set[Coordinate] = set()
        self.grid = InfiniteGrid[Coordinate]()

        for line in lines:
            x, y = line.split(', ')
            coordinate = Coordinate(int(x),
                                    int(y),
                                    system=CoordinateSystem.X_RIGHT_Y_DOWN)
            self.coordinates.add(coordinate)
            self.grid[coordinate] = coordinate

        self.bounding_box: BoundingBox = self.grid.bounding_box

        for x, y in self.bounding_box:
            test_coordinate = Coordinate(
                x, y, system=CoordinateSystem.X_RIGHT_Y_DOWN)

            distances = dict(
                (coordinate, test_coordinate.manhattan(coordinate))
                for coordinate in self.coordinates)
            _, min_distance = min(distances.items(), key=lambda i: i[1])
            min_coordinates = [
                coordinate for coordinate, distance in distances.items()
                if distance == min_distance
            ]

            if len(min_coordinates) != 1:
                continue

            self.grid[test_coordinate] = min_coordinates[0]
예제 #2
0
 def __init__(self, file_name):
     self._grid: InfiniteGrid[int] = Input(file_name).grid().map(
         lambda x: int(x))
     # self._graph = grid.to_graph(1, 2, 3, 4, 5, 6, 7, 8, 9)
     self.start = Coordinate(x=self._grid.min_x,
                             y=self._grid.min_y,
                             system=CoordinateSystem.X_RIGHT_Y_DOWN)
     self.size = self._grid.max_x + 1
     self.end = Coordinate(x=self.size - 1,
                           y=self.size - 1,
                           system=CoordinateSystem.X_RIGHT_Y_DOWN)
예제 #3
0
    def part1(self):
        grid = InfiniteGrid[bool]()

        santa = Coordinate(0, 0)
        grid[santa] = True
        for character in self.path:
            santa = santa.move(character)
            grid[santa] = True

        result = len(grid.find(True))

        print("Part 1:", result)
예제 #4
0
    def __init__(self, file_name):
        line = Input(file_name).line()

        target_regex = re.compile(r'target area: x=(-?\d+)..(-?\d+), y=(-?\d+)..(-?\d+)')
        match = target_regex.match(line)
        box = BoundingBox()
        box = box.expand_x(int(match.group(1)))
        box = box.expand_x(int(match.group(2)))
        box = box.expand_y(int(match.group(3)))
        box = box.expand_y(int(match.group(4)))
        self.target = box

        grid: InfiniteGrid[str] = InfiniteGrid[str]()

        start_x = 1
        attempt = FiringAttempt(start_x, 0)
        while attempt.step_until(self.target) is None:
            start_x += 1
            attempt = FiringAttempt(start_x, 0)

        starting_coordinate = Coordinate(start_x, 0)
        queue: Queue = Queue()
        queue.put(starting_coordinate)
        seen: Set[Coordinate] = set()
        seen.add(starting_coordinate)

        max_y = 0

        while not queue.empty():
            item: Coordinate = queue.get()
            attempt = FiringAttempt(item.x, item.y)
            end = attempt.step_until(self.target)
            if end is not None:
                max_y = max(max_y, attempt.max_y)
                grid[item] = '*'
                for dx in range(-5, 20):
                    for dy in range(-20, 20):
                        neighbor = Coordinate(item.x + dx, item.y + dy)
                        if neighbor in seen:
                            continue

                        queue.put(neighbor)
                        seen.add(neighbor)

        for x in range(self.target.min_x, self.target.max_x + 1):
            for y in range(self.target.min_y, self.target.max_y + 1):
                grid[Coordinate(x, y)] = '#'

        grid[Coordinate(0, 0)] = '!'
        # grid.to_grid().print(not_found='.')

        self.max_y = max_y
        self.hitting_velocity_count = len(grid.find(lambda _: _ in '*#'))
예제 #5
0
파일: d2.py 프로젝트: Jnesselr/AdventOfCode
    def _get_keycode(self, keypad: Grid[str]):
        coordinate = Coordinate(1, 1, system=CoordinateSystem.X_RIGHT_Y_DOWN)
        result = ""

        for line in self.lines:
            for character in line:
                new_coordinate = coordinate.move(character)
                if keypad[new_coordinate] is not None:
                    coordinate = new_coordinate
            result += keypad[coordinate]

        return result
예제 #6
0
파일: d6.py 프로젝트: Jnesselr/AdventOfCode
    def part2(self):
        result = 0

        for x, y in self.bounding_box:
            coordinate = Coordinate(x,
                                    y,
                                    system=CoordinateSystem.X_RIGHT_Y_DOWN)
            total_distance = sum(
                coordinate.manhattan(i) for i in self.coordinates)

            if total_distance < 10000:
                result += 1

        print("Part 2:", result)
예제 #7
0
    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 ' ')
예제 #8
0
    def __init__(self, file_name):
        self.instructions = Input(file_name).lines()
        self.grid: InfiniteGrid[bool] = InfiniteGrid[bool]()

        reference_coordinate = Coordinate(0, 0)

        for instruction in self.instructions:
            coordinate = reference_coordinate
            index = 0
            while index < len(instruction):
                if instruction[index] == 'n':
                    if instruction[index + 1] == 'w':
                        coordinate = coordinate.left().up()
                    elif instruction[index + 1] == 'e':
                        coordinate = coordinate.up()
                    index += 2
                elif instruction[index] == 's':
                    if instruction[index + 1] == 'w':
                        coordinate = coordinate.down()
                    elif instruction[index + 1] == 'e':
                        coordinate = coordinate.right().down()
                    index += 2
                elif instruction[index] == 'w':
                    coordinate = coordinate.left()
                    index += 1
                elif instruction[index] == 'e':
                    coordinate = coordinate.right()
                    index += 1

            if coordinate in self.grid:
                self.grid[coordinate] = not self.grid[coordinate]
            else:
                self.grid[coordinate] = True
예제 #9
0
    def to_grid(self) -> Grid[T]:
        data = {}

        min_x = min_y = 4294967296
        max_x = max_y = -4294967296

        for coordinate, item in self._data.items():
            x = coordinate.x
            y = coordinate.y
            if coordinate.system == CoordinateSystem.X_RIGHT_Y_DOWN:
                y = -y

            coordinate = Coordinate(x, y, system=CoordinateSystem.X_RIGHT_Y_UP)
            data[coordinate] = item

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

        width = max_x - min_x + 1
        height = max_y - min_y + 1

        result = Grid[T](width, height)
        for coordinate, item in data.items():
            result[coordinate.x - min_x, max_y - coordinate.y] = item

        return result
예제 #10
0
 def fold_func(coordinate: Coordinate):
     if coordinate.x > value:
         return Coordinate(x=-(coordinate.x - value) + value,
                           y=coordinate.y,
                           system=coordinate.system)
     else:
         return coordinate
예제 #11
0
    def _mutate_recursive(cls, grids: Dict[int, Grid[str]]) -> Dict[int, Grid[str]]:
        result: Dict[int, Grid[str]] = {}

        min_level = 2**24
        max_level = -2**24
        for key, grid in grids.items():
            result[key] = grid
            min_level = min(min_level, key)
            max_level = max(max_level, key)

        if len(grids[min_level].find('#')) > 0:
            min_level -= 1
            result[min_level] = grids[0].copy()
            result[min_level].fill('.')

        if len(grids[max_level].find('#')) > 0:
            max_level += 1
            result[max_level] = grids[0].copy()
            result[max_level].fill('.')

        for key, grid in result.items():
            below = grids[key + 1] if key + 1 in grids else None
            above = grids[key - 1] if key - 1 in grids else None
            current = result[key]
            next_grid = result[key].copy()
            for x in range(5):
                for y in range(5):
                    coordinate = Coordinate(x, y, system=CoordinateSystem.X_RIGHT_Y_DOWN)
                    bug_count = cls._get_bug_count(coordinate, current, above, below)
                    is_infected = cls._is_infected(current[coordinate], bug_count)
                    next_grid[coordinate] = '#' if is_infected else '.'
            result[key] = next_grid

        return result
예제 #12
0
    def _asteroids_visible_from(self, base: Coordinate):
        seen_angles = {}

        asteroid: Coordinate
        for asteroid in self.asteroids:
            dx = asteroid.x - base.x
            dy = asteroid.y - base.y

            if dx == dy == 0:
                continue

            divider = gcd(dx, dy)
            dx = dx // divider
            dy = dy // divider
            t = (dx, dy,)

            # Asteroid is closer than what we have
            if t in seen_angles and seen_angles[t] > divider:
                seen_angles[t] = divider
            elif t not in seen_angles:
                seen_angles[t] = divider

        result = []
        for position, divider in seen_angles.items():
            coordinate = Coordinate(
                position[0] * divider + base.x,
                position[1] * divider + base.y,
                system=CoordinateSystem.X_RIGHT_Y_DOWN
            )
            result.append(coordinate)

        return result
예제 #13
0
    def part2(self):
        new_grid: InfiniteGrid[int] = self._grid.copy()

        for mult_x in range(0, 5):
            for mult_y in range(0, 5):
                if mult_x == 0 and mult_y == 0:
                    continue
                for x in range(self.size):
                    for y in range(self.size):
                        if mult_y == 0:
                            previous_x = (mult_x - 1) * self.size + x
                            previous_y = y
                        else:
                            previous_x = mult_x * self.size + x
                            previous_y = (mult_y - 1) * self.size + y

                        value = new_grid[previous_x, previous_y] + 1

                        if value > 9:
                            value = 1

                        new_grid[mult_x * self.size + x,
                                 mult_y * self.size + y] = value
                print()

        new_end = Coordinate(x=new_grid.max_x,
                             y=new_grid.max_y,
                             system=CoordinateSystem.X_RIGHT_Y_DOWN)
        path = self.find_path(new_grid, self.start, new_end)
        result = sum(new_grid[x] for x in path if x != self.start)

        print("Part 2:", result)
예제 #14
0
    def part2(self):
        grid: InfiniteGrid[bool] = self.grid.copy()
        cleaner = Turtle(direction=TurtleDirection.NORTH,
                         coordinate=Coordinate(
                             0, 0, system=CoordinateSystem.X_RIGHT_Y_DOWN))
        result = 0

        for _ in range(10_000_000):
            node_type = grid[cleaner.coordinate]
            if node_type is None:
                node_type = NodeType.Clean

            if node_type == NodeType.Clean:
                cleaner = cleaner.turn_left()
                grid[cleaner.coordinate] = NodeType.Weakened
            elif node_type == NodeType.Weakened:
                result += 1
                grid[cleaner.coordinate] = NodeType.Infected
            elif node_type == NodeType.Infected:
                cleaner = cleaner.turn_right()
                grid[cleaner.coordinate] = NodeType.Flagged
            else:
                cleaner = cleaner.turn_left(2)
                grid[cleaner.coordinate] = NodeType.Clean

            cleaner = cleaner.forward()
예제 #15
0
    def part2(self):
        grid = InfiniteGrid[bool]()

        santa = Coordinate(0, 0)
        robot_santa = santa
        grid[santa] = True
        for index, character in enumerate(self.path):
            if index % 2 == 0:
                santa = santa.move(character)
                grid[santa] = True
            else:
                robot_santa = robot_santa.move(character)
                grid[robot_santa] = True

        result = len(grid.find(True))

        print("Part 2:", result)
예제 #16
0
    def cut(self, bounding_box: BoundingBox) -> Grid[T]:
        new_width = bounding_box.max_x - bounding_box.min_x + 1
        new_height = bounding_box.max_y - bounding_box.min_y + 1
        new_grid = Grid[T](new_width, new_height)

        for row in range(new_height):
            for col in range(new_width):
                old_coordinate = Coordinate(
                    col + bounding_box.min_x,
                    row + bounding_box.min_y,
                    system=CoordinateSystem.X_RIGHT_Y_DOWN)
                new_coordinate = Coordinate(
                    col, row, system=CoordinateSystem.X_RIGHT_Y_DOWN)

                new_grid[new_coordinate] = self[old_coordinate]

        return new_grid
예제 #17
0
    def fill(self, item: T):
        for row in range(self.height):
            for col in range(self.width):
                coordinate = Coordinate(col,
                                        row,
                                        system=CoordinateSystem.X_RIGHT_Y_DOWN)

                self._data[coordinate] = item
예제 #18
0
    def __init__(self, file_name):
        line = Input(file_name).line()

        start = Coordinate(0, 0)
        grid = self._build_map(start, line)

        walkable = ['X', '.', '|', '-']
        self.flood_map = grid.flood_map(start, *walkable)
예제 #19
0
    def part1(self):
        end = Coordinate(31, 39, system=CoordinateSystem.X_RIGHT_Y_DOWN)

        path = self.grid.find_path(self.start, end, True)

        result = len(path) - 1

        print("Part 1:", result)
예제 #20
0
    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)
예제 #21
0
파일: d6.py 프로젝트: Jnesselr/AdventOfCode
    def part1(self):
        excluded_coordinates: Set[Coordinate] = set()

        for x in range(self.bounding_box.min_x, self.bounding_box.max_x + 1):
            coordinate = Coordinate(x,
                                    self.bounding_box.min_y,
                                    system=CoordinateSystem.X_RIGHT_Y_DOWN)
            value = self.grid[coordinate]
            if value is not None:
                excluded_coordinates.add(value)

            coordinate = Coordinate(x,
                                    self.bounding_box.max_y,
                                    system=CoordinateSystem.X_RIGHT_Y_DOWN)
            value = self.grid[coordinate]
            if value is not None:
                excluded_coordinates.add(value)

        for y in range(self.bounding_box.min_y, self.bounding_box.max_y + 1):
            coordinate = Coordinate(self.bounding_box.min_x,
                                    y,
                                    system=CoordinateSystem.X_RIGHT_Y_DOWN)
            value = self.grid[coordinate]
            if value is not None:
                excluded_coordinates.add(value)

            coordinate = Coordinate(self.bounding_box.max_x,
                                    y,
                                    system=CoordinateSystem.X_RIGHT_Y_DOWN)
            value = self.grid[coordinate]
            if value is not None:
                excluded_coordinates.add(value)

        result = 0

        for coordinate in self.coordinates:
            if coordinate in excluded_coordinates:
                continue

            result = max(result, len(self.grid.find(coordinate)))

        print("Part 1:", result)
예제 #22
0
 def _to_coordinate(position) -> Coordinate:
     """
     We make the assumption that the grid is x right y down if they're just passing in coordinates. However, the
     user can use any coordinate system they want in an infinite grid.
     """
     if isinstance(position, tuple):
         x, y = position
         position = Coordinate(int(x),
                               int(y),
                               system=CoordinateSystem.X_RIGHT_Y_DOWN)
     return position
예제 #23
0
    def __init__(self, file_name):
        line = Input(file_name).line()

        self.furthest_distance = 0
        self.coordinate = Coordinate(0, 0)
        for step in line.split(","):
            if step == 'n':
                self.coordinate = self.coordinate.up()
            elif step == 's':
                self.coordinate = self.coordinate.down()
            elif step == 'ne':
                self.coordinate = self.coordinate.right()
            elif step == 'sw':
                self.coordinate = self.coordinate.left()
            elif step == 'nw':
                self.coordinate = self.coordinate.left().up()
            elif step == 'se':
                self.coordinate = self.coordinate.right().down()

            self.furthest_distance = max(self.furthest_distance,
                                         self._distance(self.coordinate))
예제 #24
0
    def _mutate(grid: Grid[str], moving: Moving) -> (bool, Grid[str]):
        result = grid.copy()
        max_x = grid.max_x
        max_y = grid.max_y

        locations = grid.find('>') if moving == Moving.EAST else grid.find('v')
        something_changed = False

        for location in locations:
            test = location.right(
            ) if moving == Moving.EAST else location.down()
            if test.x > max_x:
                test = Coordinate(0, test.y, system=test.system)
            if test.y > max_y:
                test = Coordinate(test.x, 0, system=test.system)

            if grid[test] == '.':
                result[test] = '>' if moving == Moving.EAST else 'v'
                result[location] = '.'
                something_changed = True

        return something_changed, result
예제 #25
0
    def _to_coordinates(line_string):
        line = line_string.split(',')
        current: Coordinate = Coordinate(0, 0)
        coordinates = []

        for element in line:
            direction = element[0]
            count = int(element[1:])

            for _ in range(count):
                current = current.move(direction)
                coordinates.append(current)

        return coordinates
예제 #26
0
 def _neighbors(coordinate: Coordinate) -> Set[Coordinate]:
     return {
         coordinate.left().up(),
         coordinate.up(),
         coordinate.down(),
         coordinate.right().down(),
         coordinate.left(),
         coordinate.right()
     }
예제 #27
0
    def _enhance(self, grid: InfiniteGrid[str], fill: str):
        result = InfiniteGrid[str]()

        min_x = grid.min_x - 2
        max_x = grid.max_x + 3
        min_y = grid.min_y - 2
        max_y = grid.max_y + 3
        for x in range(min_x, max_x):
            for y in range(min_y, max_y):
                coordinate = Coordinate(x,
                                        y,
                                        system=CoordinateSystem.X_RIGHT_Y_DOWN)

                if coordinate not in grid:
                    grid[coordinate] = fill

                neighbors = coordinate.neighbors8()
                for neighbor in neighbors:
                    if neighbor not in grid:
                        grid[neighbor] = fill

                bin_str = f"{grid[coordinate.up().left()]}" \
                          f"{grid[coordinate.up()]}" \
                          f"{grid[coordinate.up().right()]}" \
                          f"{grid[coordinate.left()]}" \
                          f"{grid[coordinate]}" \
                          f"{grid[coordinate.right()]}" \
                          f"{grid[coordinate.down().left()]}" \
                          f"{grid[coordinate.down()]}" \
                          f"{grid[coordinate.down().right()]}" \
                    .replace('.', '0') \
                    .replace('#', '1')
                index = int(bin_str, 2)
                result[coordinate] = self._enhancement[index]

        return result
예제 #28
0
    def neighbor_count(self, coordinate: Coordinate,
                       test: Union[T, Callable]) -> int:
        result = 0

        for neighbor in coordinate.neighbors():
            if neighbor not in self:
                continue
            item = self._data[neighbor]
            if callable(test):
                if test(item):
                    result += 1
            elif item == test:
                result += 1

        return result
예제 #29
0
    def part1(self):
        grid: InfiniteGrid[bool] = self.grid.copy()
        cleaner = Turtle(direction=TurtleDirection.NORTH,
                         coordinate=Coordinate(
                             0, 0, system=CoordinateSystem.X_RIGHT_Y_DOWN))
        result = 0

        for _ in range(10_000):
            clean = grid[cleaner.coordinate] in [None, NodeType.Clean]

            cleaner = cleaner.turn_left() if clean else cleaner.turn_right()
            grid[cleaner.
                 coordinate] = NodeType.Infected if clean else NodeType.Clean

            result += 1 if clean else 0

            cleaner = cleaner.forward()
예제 #30
0
    def __init__(self, file_name):
        self.favorite_number = Input(file_name).int()

        def _grid(_: MagicGrid[bool], coordinate: Coordinate) -> bool:
            x = coordinate.x
            y = coordinate.y

            if x < 0 or y < 0:
                return False

            result = x * x + 3 * x + 2 * x * y + y + y * y
            result += self.favorite_number

            return bin(result).count("1") % 2 == 0

        self.grid = MagicGrid[bool](_grid)
        self.start = Coordinate(1, 1, system=CoordinateSystem.X_RIGHT_Y_DOWN)