2021-12-12 - Passage Pathing
(original .ipynb)
Day 12 puzzle input describes a collection of named caves (lower-case = small caves, and upper-case = big caves) and how they are connected (mine is here). Part 1 involves finding the number of possible paths from "start" to "end" with the restriction that you can only visit small caves once. Part 2 similarly involves finding the number of possible paths, but now you can visit one small cave twice in each run.
from collections import defaultdict puzzle_input_str = open("puzzle_input/day12.txt").read() test_input_str = """start-A start-b A-c A-b b-d A-end b-end""" def parse_input(input_str): edges = [line.split("-") for line in input_str.split("\n")] graph = defaultdict(list) for src, dst in edges: graph[src].append(dst) graph[dst].append(src) return graph def paths_to_target(graph, at, target, visited, can_visit): visited[at] += 1 to_visit = [cave for cave in graph[at] if can_visit(cave, visited)] if at == "end": return 1 elif len(to_visit) == 0: return 0 else: return sum(paths_to_target(graph, cave, target, visited.copy(), can_visit) for cave in to_visit) def can_visit_part_one(cave, visited): return cave.isupper() or not (cave in visited) def part_one(input_str): graph = parse_input(input_str) visited = defaultdict(lambda:0) return paths_to_target(graph, "start", "end", visited, can_visit_part_one) assert 10 == part_one(test_input_str) print("part one:", part_one(puzzle_input_str))
part one: 4549
def can_visit_part_two(cave, visited): if cave.isupper(): return True if cave == "start": return False if visited[cave] == 0: return True return not any(count > 1 for cave,count in visited.items() if cave.islower()) def part_two(input_str): graph = parse_input(input_str) visited = defaultdict(lambda:0) return paths_to_target(graph, "start", "end", visited, can_visit_part_two) assert 36 == part_two(test_input_str) print("part two:", part_two(puzzle_input_str))
part two: 120535