87
day12/day12.py
Executable file
87
day12/day12.py
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from copy import deepcopy
|
||||
from typing import DefaultDict, MutableMapping, Set
|
||||
import pprint
|
||||
import string
|
||||
|
||||
|
||||
Vertices = DefaultDict[str, Set[str]]
|
||||
SMALL = set(string.ascii_lowercase)
|
||||
BIG = set(string.ascii_uppercase)
|
||||
|
||||
|
||||
def is_small(cave: str):
|
||||
s = set(cave)
|
||||
return (s & SMALL) == s
|
||||
|
||||
|
||||
def part1(vertices: Vertices):
|
||||
small = {cave for cave in vertices.keys() if is_small(cave)}
|
||||
big = set(vertices.keys()) - small
|
||||
assert len(big & small) == 0
|
||||
|
||||
def find_paths(current: str, visited: Set[str] = set(), path: str = "") -> Set[str]:
|
||||
visited = deepcopy(visited)
|
||||
visited |= {current}
|
||||
paths = set()
|
||||
for vertex in vertices[current]:
|
||||
if vertex == "end":
|
||||
paths |= {path}
|
||||
elif vertex in visited and vertex in small:
|
||||
continue
|
||||
else:
|
||||
paths |= find_paths(vertex, visited, path + vertex)
|
||||
return paths
|
||||
|
||||
paths = find_paths("start")
|
||||
print(len(paths))
|
||||
|
||||
|
||||
def part2(vertices: Vertices):
|
||||
small = {cave for cave in vertices.keys() if is_small(cave)}
|
||||
big = set(vertices.keys()) - small
|
||||
assert len(big & small) == 0
|
||||
|
||||
def find_paths(
|
||||
allowed: str,
|
||||
current: str,
|
||||
visited: MutableMapping[str, int] = defaultdict(lambda: 0),
|
||||
path: str = "",
|
||||
) -> Set[str]:
|
||||
if current == allowed:
|
||||
if visited[current] >= 2:
|
||||
# This is a small cave and we've already visited it twice
|
||||
return set()
|
||||
elif current in small and visited[current]:
|
||||
# Visited already
|
||||
return set()
|
||||
visited = deepcopy(visited)
|
||||
visited[current] += 1
|
||||
paths = set()
|
||||
for vertex in vertices[current]:
|
||||
if vertex == "end":
|
||||
paths |= {path}
|
||||
else:
|
||||
paths |= find_paths(allowed, vertex, visited, path + vertex)
|
||||
return paths
|
||||
|
||||
paths = set()
|
||||
for allowed in small - {"start", "end"}:
|
||||
paths |= find_paths(allowed, "start")
|
||||
print(len(paths))
|
||||
|
||||
|
||||
# Doing this comprehension so Mypy is happy
|
||||
edges = [(a, b) for a, b in map(lambda l: l.strip().split("-"), sys.stdin)]
|
||||
vertices: Vertices = defaultdict(set)
|
||||
for a, b in edges:
|
||||
vertices[a] |= {b}
|
||||
vertices[b] |= {a}
|
||||
|
||||
|
||||
print("Part 1")
|
||||
part1(vertices)
|
||||
print("Part 2")
|
||||
part2(vertices)
|
||||
Reference in New Issue
Block a user