Example #1
0
File: day18.py Project: st3fan/aoc
def main():

    # Part 1

    grid = Grid.from_file("day18.input")
    for _ in range(100):
        copy = grid.copy()
        for y in range(grid.height):
            for x in range(grid.width):
                p = Position(x, y)
                neighbours = [n for n in copy.neighbours(p) if copy.get(n) == '#']
                match grid.get(p):
                    case '#':
                        if len(neighbours) != 2 and len(neighbours) != 3:
                            grid.set(p, '.')
                    case '.':
                        if len(neighbours) == 3:
                            grid.set(p, '#')
    print("Part one:", grid.count('#'))

    # Part 2


    grid = Grid.from_file("day18.input")

    stuck = [Position(0, 0), Position(99, 0), Position(0, 99), Position(99, 99)]
    for p in stuck:
        grid.set(p, '#')

    for _ in range(100):
        copy = grid.copy()
        for y in range(grid.height):
            for x in range(grid.width):
                p = Position(x, y)
                neighbours = [n for n in copy.neighbours(p) if copy.get(n) == '#']
                match grid.get(p):
                    case '#':
                        if len(neighbours) != 2 and len(neighbours) != 3:
                            if p not in stuck:
                                grid.set(p, '.')
                    case '.':
                        if len(neighbours) == 3:
                            grid.set(p, '#')
    print("Part two:", grid.count('#'))
Example #2
0
                turtle.left()
                grid.set(turtle.position, '#')
                infected += 1
            case '#':
                turtle.right()
                grid.set(turtle.position, '.')
        turtle.forward()
        if not grid.get(turtle.position):
            grid.set(turtle.position, '.')

    print("Part one:", infected)

    # Part 2

    grid = InfiniteGrid.from_file("day22.input")
    turtle = Turtle(Position(grid.maxx() // 2, grid.maxy() // 2))

    infected = 0
    for _ in range(10_000_000):
        match grid.get(turtle.position):
            case '.':
                turtle.left()
                grid.set(turtle.position, 'W')
            case '#':
                turtle.right()
                grid.set(turtle.position, 'F')
            case 'W':
                grid.set(turtle.position, '#')
                infected += 1
            case 'F':
                turtle.right()
Example #3
0
from aoc import AOC, Position, griditer

aoc = AOC(year=2021, day=11)
data = aoc.load()
Position.set_limits(x=range(10), y=range(10))

octos = data.digits_by_line()


def clear_flashed(octos):
    # Reset octos that have flashed to 0
    for x, y in griditer(octos):
        if octos[y][x] > 9:
            octos[y][x] = 0


def step(octos):
    # Start with incremeneting every octo
    needs_increment = [(x, y) for x, y in griditer(octos)]
    flashed = set()

    while needs_increment:
        x, y = needs_increment.pop()
        octos[y][x] += 1
        if octos[y][x] > 9 and (x, y) not in flashed:
            # When an octo's energy exceeds 9, it flashes and increments those surrounding
            flashed.add((x, y))
            for adj in Position(x, y).adjacent():
                if adj.tuple not in flashed:
                    # Octo's can only flash once in a step, so skip if they've already flashed
                    needs_increment.append(adj.tuple)
Example #4
0
def move_ship(ship, offset, val):
    return Position(ship.x + offset.x * val, ship.y + offset.y * val)
Example #5
0

def move_ship(ship, offset, val):
    return Position(ship.x + offset.x * val, ship.y + offset.y * val)


def command_ship(ship, dir, ins, val):
    if ins in ["L", "R"]:
        return ship, turn_ship(dir, ins, val)
    elif ins == "F":
        return move_ship(ship, Direction[dir].position, val), dir
    else:
        return move_ship(ship, Direction[ins].position, val), dir


ship, direction = Position(0, 0), "E"
for instruction in data.parse_lines(r"(\w)(\d+)"):
    ship, direction = command_ship(ship, direction, instruction[0],
                                   int(instruction[1]))

aoc.p1(abs(ship.x) + abs(ship.y))

# Part 2


def rotate_waypoint(waypoint, ins, val):
    while val >= 90:
        if ins == "R":
            waypoint = Position(-waypoint.y, waypoint.x)
        if ins == "L":
            waypoint = Position(waypoint.y, -waypoint.x)
Example #6
0
hill = [list(line) for line in data.lines()]

# Part 1


def count_trees(slope):
    position = Position(0, 0)
    trees = 0
    while position.y < len(hill):
        if hill[position.y][position.x % len(hill[0])] == "#":
            trees += 1
        position = Position(position.x + slope.x, position.y + slope.y)
    return trees


aoc.p1(count_trees(Position(3, 1)))

# Part 2

aoc.p2(
    math.prod(
        [
            count_trees(slope)
            for slope in [
                Position(1, 1),
                Position(3, 1),
                Position(5, 1),
                Position(7, 1),
                Position(1, 2),
            ]
        ]
Example #7
0

def in_target_range(position):
    return position.x in target_x and position.y in target_y


[minX, maxX, minY, maxY] = data.nums()
target_x = range(minX, maxX + 1)
target_y = range(minY, maxY + 1)

total_max_height = 0
total_valid_velocities = 0

for dx in range(max(target_x) + 1):
    for dy in range(min(target_y) - 1, 200):
        position = Position(0, 0)
        velocity = Position(dx, dy)
        max_height = 0

        while not overshot(position) and not in_target_range(position):
            step(position, velocity)
            max_height = max(position.y, max_height)
            if velocity.x == 0 and position.x < min(target_x):
                break

        if in_target_range(position):
            total_max_height = max(total_max_height, max_height)
            total_valid_velocities += 1

aoc.p1(total_max_height)
aoc.p2(total_valid_velocities)
Example #8
0
 def from_string(cls, s):
     c = s.split()
     if s.startswith("toggle"):
         return cls(Position.from_string(c[-3]), Position.from_string(c[-1]), "toggle")
     else:
         return cls(Position.from_string(c[-3]), Position.from_string(c[-1]), c[1])
Example #9
0
from aoc import AOC, Position, flatten

aoc = AOC(year=2020, day=17)
data = aoc.load()

# Part 1

lines = data.lines()
active = set(
    flatten([[
        Position(x - len(l) // 2, y - len(lines) // 2, 0)
        for x, c in enumerate(l) if c == "#"
    ] for y, l in enumerate(lines)]))
seen = set(
    flatten([[
        Position(x - len(l) // 2, y - len(lines) // 2, 0)
        for x, c in enumerate(l)
    ] for y, l in enumerate(lines)]))
seen.update(flatten([[a for a in c.adjacent()] for c in seen]))


def cycle(active, seen):
    next_active, next_seen = set(), set(seen)
    for c in seen:
        adj = c.adjacent()
        next_seen.update(adj)
        if c in active and 2 <= len([1 for x in adj if x in active]) <= 3:
            next_active.add(c)
        elif c not in active and len([1 for x in adj if x in active]) == 3:
            next_active.add(c)
    return (next_active, next_seen)
Example #10
0
from aoc import AOC, Position, griditer
from math import prod

aoc = AOC(year=2021, day=9)
height_map = aoc.load().digits_by_line()
d = height_map

Position.set_limits(range(len(height_map[0])), range(len(height_map)))

# Part 1

aoc.p1(
    sum(
        height_map[y][x] + 1
        for x, y in griditer(height_map)
        if all(
            height_map[y][x] < height_map[a.y][a.x] for a in Position(x, y).adjacent()
        )
    )
)

visited = set()
basins = []

for x, y in griditer(height_map):
    p = Position(x, y)
    if p in visited or height_map[p.y][p.x] == 9:
        continue

    q = [p]
    basin = set([p])
Example #11
0
def first_occupied(seat, xoff, yoff):
    t = Position(seat.x + xoff, seat.y + yoff)
    if not is_in_layout(t):
        return False
    return t if layout[t.y][t.x] != "." else first_occupied(t, xoff, yoff)
Example #12
0
    ]


def becomes_unoccupied(seat, adjacent, limit):
    return (layout[seat.y][seat.x] == "#" and len(
        [adj
         for adj in adjacent(seat) if layout[adj.y][adj.x] == "#"]) >= limit)


layout = [[l for l in line.strip()] for line in data.lines()]
last_layout = None
while last_layout != layout:
    next_layout = [l[:] for l in layout]
    for y, _ in enumerate(layout):
        for x, _ in enumerate(layout[y]):
            seat = Position(x, y)
            if becomes_occupied(seat, adjacent):
                next_layout[y][x] = "#"
            elif becomes_unoccupied(seat, adjacent, 4):
                next_layout[y][x] = "L"
    last_layout = layout
    layout = next_layout

aoc.p1(sum([sum([1 if s == "#" else 0 for s in row]) for row in layout]))

# Part 2


def first_occupied(seat, xoff, yoff):
    t = Position(seat.x + xoff, seat.y + yoff)
    if not is_in_layout(t):