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('#'))
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()
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)
def move_ship(ship, offset, val): return Position(ship.x + offset.x * val, ship.y + offset.y * val)
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)
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), ] ]
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)
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])
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)
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])
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)
] 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):