summaryrefslogtreecommitdiffstats
path: root/23.py
blob: e3f2ead147ed7a0dd7d0b1d0119b6a852b0863b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
from collections import defaultdict
from sys import stdin

inp = [line.rstrip("\n") for line in stdin]

start = (inp[0].index("."), 0)
end = (inp[-1].index("."), len(inp) - 1)


def neighbours(
    maze: list[str], prev: tuple[int, int], position: tuple[int, int]
) -> list[tuple[int, int]]:
    ret = []
    for dx in (-1, 1):
        nx = position[0] + dx
        if nx < 0 or nx >= len(maze[0]):
            continue
        if (nx, position[1]) == prev:
            continue
        c = maze[position[1]][nx]
        if c == "#":
            continue
        if dx < 0 and c == ">" or dx > 0 and c == "<":
            continue
        ret.append((nx, position[1]))
    for dy in (-1, 1):
        ny = position[1] + dy
        if ny < 0 or ny >= len(maze):
            continue
        if (position[0], ny) == prev:
            continue
        c = maze[ny][position[0]]
        if c == "#":
            continue
        if dy < 0 and c == "v" or dy > 0 and c == "^":
            continue
        ret.append((position[0], ny))
    return ret


to_explore: list[tuple[tuple[int, int], tuple[int, int]]] = [(start, (start[0], 1))]
edges: defaultdict[tuple[int, int], set[tuple[int, tuple[int, int]]]] = defaultdict(set)
while to_explore:
    root, pos = to_explore.pop()
    prev = root
    dist = 1
    while True:
        neighs = neighbours(inp, prev, pos)
        if len(neighs) != 1 or inp[neighs[0][1]][neighs[0][0]] in "^>v<":
            edges[root].add((dist, pos))
            for n in neighs:
                to_explore.append((pos, n))
            break
        dist += 1
        prev = pos
        pos = neighs[0]


def longest(
    start: tuple[int, int],
    goal: tuple[int, int],
    seen: frozenset[tuple[int, int]],
    edges: dict[tuple[int, int], set[tuple[int, tuple[int, int]]]],
) -> int | None:
    if start == goal:
        return 0
    maximum = 0
    new_seen = seen | {start}
    for dist, n in edges[start]:
        if n in seen:
            continue
        d = longest(n, goal, new_seen, edges)
        if d is None:
            continue
        if d + dist > maximum:
            maximum = d + dist
    return maximum if maximum > 0 else None


print(longest(start, end, frozenset(), edges))

fullgraph: defaultdict[tuple[int, int], set[tuple[int, tuple[int, int]]]] = defaultdict(
    set, ((k, v.copy()) for k, v in edges.items())
)

for k, v in edges.items():
    for dist, pos in v:
        fullgraph[pos].add((dist, k))


print(longest(start, end, frozenset(), fullgraph))