|
1 | 1 | from collections.abc import Iterable, Container
|
| 2 | +from heapq import heappush, heappop |
2 | 3 |
|
3 | 4 | from utils.file import read_input, int_list_line
|
4 | 5 |
|
5 | 6 | falling_bytes = [complex(x,y) for x,y in (int_list_line(line, ",") for line in read_input(__package__))]
|
6 | 7 | start = 0j
|
7 | 8 | goal = 70+70j if len(falling_bytes) > 50 else 6+6j
|
8 | 9 |
|
9 |
| -def neighbors(node: complex, walls: Container[complex]) -> Iterable[complex]: |
10 |
| - for direction in [1, -1, 1j, -1j]: |
11 |
| - neighbor = node + direction |
12 |
| - if neighbor.real < 0 or neighbor.imag < 0 or neighbor.real > goal.real or neighbor.imag > goal.imag: |
13 |
| - continue |
14 |
| - if neighbor not in walls: |
15 |
| - yield neighbor |
16 |
| - |
17 |
| -def heuristic(node: complex, goal: complex) -> int: |
18 |
| - return int(abs(node.real - goal.real) + abs(node.imag - goal.imag)) + int(abs(node.real - node.imag)) |
19 | 10 |
|
20 |
| -def walk(corruption_size: int) -> Iterable[tuple[list[complex], int]]: |
| 11 | +def astar(corruption_size: int) -> int: |
21 | 12 | corrupted = set(falling_bytes[:corruption_size])
|
| 13 | + |
| 14 | + def neighbors(current: complex) -> Iterable[complex]: |
| 15 | + for direction in [1, 1j, -1, -1j]: |
| 16 | + neighbor = current + direction |
| 17 | + if 0 <= neighbor.real <= goal.real and 0 <= neighbor.imag <= goal.imag and neighbor not in corrupted: |
| 18 | + yield neighbor |
| 19 | + |
| 20 | + def heuristic(current: complex) -> int: |
| 21 | + return int(abs(current.real - goal.real) + abs(current.imag - goal.imag)) |
| 22 | + |
22 | 23 | best_score = None
|
23 |
| - visited = {start: 0} |
24 |
| - # position, score, path |
25 |
| - stack: list[tuple[complex, int, list[complex]]] = [(start, 0, [start])] |
26 |
| - while stack: |
27 |
| - stack.sort(key=lambda x: x[1] + heuristic(x[0], goal)) |
28 |
| - node, score, path = stack.pop() |
29 |
| - if score > best_score: |
| 24 | + visited = {} |
| 25 | + h = [] |
| 26 | + heappush(h, (0, 0, (start.real, start.imag))) |
| 27 | + |
| 28 | + while h: |
| 29 | + _, score, node_t = heappop(h) |
| 30 | + node = complex(*node_t) |
| 31 | + if node in visited: |
30 | 32 | continue
|
| 33 | + if node == goal: |
| 34 | + return score |
31 | 35 | visited[node] = score if node not in visited else min(score, visited[node])
|
32 |
| - for neighbor in neighbors(node, corrupted): |
| 36 | + for neighbor in neighbors(node): |
33 | 37 | new_score = score + 1
|
34 |
| - if neighbor == goal: |
35 |
| - #print("Path found", new_score, len(stack)) |
36 |
| - best_score = min(best_score, new_score) if best_score else new_score |
37 |
| - yield path + [neighbor], new_score |
38 |
| - if neighbor not in visited or new_score < visited[neighbor]: |
39 |
| - stack.append((neighbor, new_score, path + [neighbor])) |
40 |
| - |
41 |
| -paths = list(walk(1024)) |
42 |
| -print("Part 1:", min(score for path, score in paths)) |
| 38 | + heappush(h, (new_score + heuristic(neighbor), new_score, (neighbor.real, neighbor.imag))) |
| 39 | + raise RuntimeError("No path found") |
| 40 | + |
| 41 | +print("Part 1:", astar(1024)) |
0 commit comments