|
2 | 2 |
|
3 | 3 | import numpy
|
4 | 4 |
|
5 |
| -from . import SeparateRunner |
| 5 | +from . import CombinedRunner |
6 | 6 |
|
7 | 7 | TURNS = (
|
8 | 8 | (-1, 1),
|
9 | 9 | (1, -1),
|
10 | 10 | )
|
11 | 11 |
|
12 | 12 |
|
13 |
| -class DayRunner(SeparateRunner): |
| 13 | +class DayRunner(CombinedRunner): |
14 | 14 | @classmethod
|
15 |
| - def part1(cls, input: str) -> int: |
| 15 | + def run_both(cls, input: str) -> tuple[int, int]: |
16 | 16 | grid = numpy.array([list(line) for line in input.strip().split("\n")])
|
17 | 17 |
|
18 | 18 | y, x = numpy.where(grid == "S")
|
19 | 19 | x, y = x[0], y[0]
|
20 | 20 |
|
21 | 21 | todo = [(0, x, y, 1, 0)]
|
22 | 22 | best = {
|
23 |
| - (x, y, 1, 0): 0, |
| 23 | + (x, y, 1, 0): (0, []), |
24 | 24 | }
|
25 | 25 |
|
26 |
| - def enqueue(dist, x, y, dx, dy): |
| 26 | + def enqueue(dist, x, y, dx, dy, cx, cy, cdx, cdy): |
27 | 27 | if grid[y, x] == "#":
|
28 | 28 | return
|
29 | 29 |
|
30 |
| - if (x, y, dx, dy) not in best or best[x, y, dx, dy] > dist: |
31 |
| - best[x, y, dx, dy] = dist |
| 30 | + if (x, y, dx, dy) not in best or best[x, y, dx, dy][0] > dist: |
| 31 | + best[x, y, dx, dy] = (dist, [(cx, cy, cdx, cdy)]) |
32 | 32 | heapq.heappush(todo, (dist, x, y, dx, dy))
|
| 33 | + elif best[x, y, dx, dy][0] == dist: |
| 34 | + best[x, y, dx, dy][1].append((cx, cy, cdx, cdy)) |
| 35 | + |
| 36 | + shortest_dist = None |
| 37 | + finishes = set() |
33 | 38 |
|
34 | 39 | while todo:
|
35 | 40 | dist, x, y, dx, dy = heapq.heappop(todo)
|
36 | 41 |
|
37 |
| - if best[x, y, dx, dy] < dist: |
| 42 | + if best[x, y, dx, dy][0] < dist: |
38 | 43 | continue
|
39 | 44 |
|
| 45 | + if shortest_dist is not None and shortest_dist < dist: |
| 46 | + break |
| 47 | + |
40 | 48 | if grid[y, x] == "E":
|
41 |
| - return dist |
| 49 | + shortest_dist = dist |
| 50 | + finishes.add((x, y, dx, dy)) |
42 | 51 |
|
43 |
| - enqueue(dist + 1, x + dx, y + dy, dx, dy) |
44 |
| - enqueue(dist + 2001, x - dx, y - dy, dx, dy) |
| 52 | + enqueue(dist + 1, x + dx, y + dy, dx, dy, x, y, dx, dy) |
| 53 | + enqueue(dist + 2001, x - dx, y - dy, dx, dy, x, y, dx, dy) |
45 | 54 |
|
46 | 55 | for tx, ty in TURNS:
|
47 | 56 | ndx = dy * ty
|
48 | 57 | ndy = dx * ty
|
49 | 58 |
|
50 |
| - enqueue(dist + 1001, x + ndx, y + ndy, ndx, ndy) |
| 59 | + enqueue(dist + 1001, x + ndx, y + ndy, ndx, ndy, x, y, dx, dy) |
51 | 60 |
|
52 |
| - raise ValueError("Did not find path to exit") |
| 61 | + assert shortest_dist is not None, "Should find a path to the exit" |
53 | 62 |
|
54 |
| - @classmethod |
55 |
| - def part2(cls, input: str) -> int: |
56 |
| - pass |
| 63 | + visited_tiles = {(x, y) for x, y, _, _ in finishes} |
| 64 | + todo2 = [f for f in finishes] |
| 65 | + visited_states = set(todo2) |
| 66 | + |
| 67 | + while todo2: |
| 68 | + state = todo2.pop() |
| 69 | + |
| 70 | + for prev in best[state][1]: |
| 71 | + if prev not in visited_states: |
| 72 | + visited_states.add(prev) |
| 73 | + visited_tiles.add((prev[0], prev[1])) |
| 74 | + todo2.append(prev) |
| 75 | + |
| 76 | + return shortest_dist, len(visited_tiles) |
0 commit comments