2018: d20: ex2: add solution
This commit is contained in:
parent
9b6cb7bd45
commit
4cb48ee71c
89
2018/d20/ex2/ex2.py
Executable file
89
2018/d20/ex2/ex2.py
Executable file
|
@ -0,0 +1,89 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import collections
|
||||||
|
import copy
|
||||||
|
import enum
|
||||||
|
import sys
|
||||||
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
|
||||||
|
class Point(NamedTuple):
|
||||||
|
x: int
|
||||||
|
y: int
|
||||||
|
|
||||||
|
|
||||||
|
class Direction(enum.StrEnum):
|
||||||
|
NORTH = "N"
|
||||||
|
SOUTH = "S"
|
||||||
|
WEST = "W"
|
||||||
|
EAST = "E"
|
||||||
|
|
||||||
|
def apply(self, p: Point) -> Point:
|
||||||
|
delta: Point
|
||||||
|
match self:
|
||||||
|
case Direction.NORTH:
|
||||||
|
delta = Point(-1, 0)
|
||||||
|
case Direction.SOUTH:
|
||||||
|
delta = Point(1, 0)
|
||||||
|
case Direction.WEST:
|
||||||
|
delta = Point(0, -1)
|
||||||
|
case Direction.EAST:
|
||||||
|
delta = Point(0, 1)
|
||||||
|
return Point(p.x + delta.x, p.y + delta.y)
|
||||||
|
|
||||||
|
|
||||||
|
START = Point(0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def solve(input: str) -> int:
|
||||||
|
def to_graph(regex: str) -> dict[Point, set[Point]]:
|
||||||
|
res: dict[Point, set[Point]] = collections.defaultdict(set)
|
||||||
|
stack: list[set[Point]] = [{START}]
|
||||||
|
current_branches: set[Point] = set()
|
||||||
|
for c in regex.removeprefix("^").removesuffix("$"):
|
||||||
|
if c == "(":
|
||||||
|
stack.append(copy.deepcopy(stack[-1]))
|
||||||
|
current_branches = set()
|
||||||
|
elif c == "|":
|
||||||
|
current_branches |= stack.pop()
|
||||||
|
stack.append(copy.deepcopy(stack[-1]))
|
||||||
|
elif c == ")":
|
||||||
|
current_branches |= stack.pop()
|
||||||
|
stack[-1] = current_branches
|
||||||
|
else:
|
||||||
|
dir = Direction(c)
|
||||||
|
for p in stack[-1]:
|
||||||
|
neighbour = dir.apply(p)
|
||||||
|
res[p].add(neighbour)
|
||||||
|
res[neighbour].add(p)
|
||||||
|
stack[-1] = {dir.apply(p) for p in stack[-1]}
|
||||||
|
|
||||||
|
return dict(res)
|
||||||
|
|
||||||
|
def start_distances(graph: dict[Point, set[Point]]) -> dict[Point, int]:
|
||||||
|
queue = collections.deque([(0, START)])
|
||||||
|
distances: dict[Point, int] = {}
|
||||||
|
|
||||||
|
while queue:
|
||||||
|
dist, p = queue.popleft()
|
||||||
|
if p in distances:
|
||||||
|
continue
|
||||||
|
distances[p] = dist
|
||||||
|
for n in graph.get(p, set()):
|
||||||
|
queue.append((dist + 1, n))
|
||||||
|
|
||||||
|
return distances
|
||||||
|
|
||||||
|
# Remove the anchors, we don't use them in the parsing code
|
||||||
|
graph = to_graph(input.strip())
|
||||||
|
distances = start_distances(graph)
|
||||||
|
return sum(d >= 1000 for d in distances.values())
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
input = sys.stdin.read()
|
||||||
|
print(solve(input))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in a new issue