-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpathfinding.py
More file actions
58 lines (48 loc) · 2.12 KB
/
Copy pathpathfinding.py
File metadata and controls
58 lines (48 loc) · 2.12 KB
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
"""Breadth-first search on a rectangular, unweighted grid. No GUI dependencies."""
from __future__ import annotations
from collections import deque
from dataclasses import dataclass
@dataclass(frozen=True)
class SearchResult:
path: tuple[tuple[int, int], ...] | None
visited: tuple[tuple[int, int], ...]
def shortest_path(width, height, start, end, walls=()):
"""Return a shortest path (including endpoints), or None when unreachable.
Coordinates are (x, y), with (0, 0) at the top left. Movement is orthogonal
and every move costs one. The input wall collection is never mutated.
"""
if any(type(n) is not int or n <= 0 for n in (width, height)):
raise ValueError("Grid dimensions must be positive integers.")
def point(value):
if not isinstance(value, (tuple, list)) or len(value) != 2:
raise ValueError("Each coordinate must contain x and y.")
x, y = value
if type(x) is not int or type(y) is not int:
raise ValueError("Coordinates must be integers.")
if not (0 <= x < width and 0 <= y < height):
raise ValueError("Coordinates must be inside the grid.")
return x, y
start, end = point(start), point(end)
blocked = {point(wall) for wall in walls}
if start in blocked or end in blocked:
raise ValueError("Start and end cannot be walls.")
queue = deque([start])
parents = {start: None}
visited = []
while queue:
current = queue.popleft()
visited.append(current)
if current == end:
path = []
while current is not None:
path.append(current)
current = parents[current]
return SearchResult(tuple(reversed(path)), tuple(visited))
x, y = current
for neighbor in ((x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)):
nx, ny = neighbor
if (0 <= nx < width and 0 <= ny < height
and neighbor not in blocked and neighbor not in parents):
parents[neighbor] = current
queue.append(neighbor)
return SearchResult(None, tuple(visited))