diff --git a/.aoc_tiles/tiles/2024/06.png b/.aoc_tiles/tiles/2024/06.png index 7780984..5c44581 100644 Binary files a/.aoc_tiles/tiles/2024/06.png and b/.aoc_tiles/tiles/2024/06.png differ diff --git a/2024/6/script.py b/2024/6/script.py index fb6a754..b5def3a 100644 --- a/2024/6/script.py +++ b/2024/6/script.py @@ -7,7 +7,7 @@ def move(pos: tuple, dir_: Dir, grid: Grid) -> tuple[tuple, Dir]: next_pos = pos + Vec2(dir_) # out of bounds or open, return next position - if not grid.in_bounds(next_pos) or not grid[next_pos] == '#': + if not grid.in_bounds(next_pos) or not grid[next_pos] in '#O': return next_pos.as_tuple(), dir_ # obstacle, rotate right 90 degrees @@ -15,20 +15,85 @@ def move(pos: tuple, dir_: Dir, grid: Grid) -> tuple[tuple, Dir]: return pos, dir_.turn_right() -def main(): - grid = Grid(aoc.read_lines()) - - pos = grid.find('^') - dir_ = Dir.UP - +def simulate(pos: tuple, dir_: Dir, grid: Grid) -> tuple[set, list, bool]: visited = set() visited.add(pos) + path = [] + path.append((pos, dir_)) + loops = False + while grid.in_bounds(pos): pos, dir_ = move(pos, dir_, grid) visited.add(pos) + if path[-1][0] == pos: + path.append((pos, dir_)) + path.append((pos, None)) + elif (pos, dir_) in path: + loops = True + break + else: + path.append((pos, dir_)) + + return visited, path, loops + + +def print_path(grid: Grid, path: list, start_pos: tuple): + dir_chars = { + Dir.UP: '|', + Dir.DOWN: '|', + Dir.LEFT: '-', + Dir.RIGHT: '-', + None: '+', + } + print(grid.render_with_overlays([{pos: dir_chars[dir_] for pos, dir_ in path}, + {start_pos: '^'}])) + + +def main(): + grid = Grid(aoc.read_lines()) + + start_pos = grid.find('^') + dir_ = Dir.UP + + visited, path, _ = simulate(start_pos, dir_, grid) + + # print(grid.render_with_overlays([{pos: 'X' for pos in visited}])) + # print("p1:", len(visited) - 1) + + # print_path(grid, path, start_pos) + + loop_obstacles = set() + tried_obstacles = set() + path = [p for p in path if p[1] is not None] + + # try placing an obstacle at every position along our initial path + for i, pair in enumerate(path): + pos = pair[0] + + # skip the start position, out-of-bounds, and previously tried obstacles + if pos == start_pos or not grid.in_bounds(pos) or pos in tried_obstacles: + continue + + # place the obstacle + grid[pos] = 'O' + tried_obstacles.add(pos) + + # start pathing from just before the newly placed obstacle + new_start_pos, new_dir = path[i-1] if path[i-1][0] != pos else path[i-2] + + new_visited, new_path, loops = simulate(new_start_pos, new_dir, grid) + + # track obstacles that cause loops + if loops: + loop_obstacles.add(pos) + # print_path(grid, new_path, new_start_pos) + print(f"{i}/{len(path)} {len(loop_obstacles)}") + + # remove the current obstacle so we can try the next one + grid[pos] = '.' - print(grid.render_with_overlays([{pos: 'X' for pos in visited}])) print("p1:", len(visited) - 1) + print("p2:", len(loop_obstacles)) if __name__ == "__main__": diff --git a/README.md b/README.md index 5a86a3d..df9a857 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,10 @@ My solutions to the yearly Advents of Code

- Advent of Code - 172/462 ⭐ + Advent of Code - 173/462 ⭐

- 2024 - 11 ⭐ - Python + 2024 - 12 ⭐ - Python