shortest-path.py 1.9 KB
Newer Older
M
Moshe Zadka 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import heapq


map = """\
.......X..
.......X..
....XXXX..
..........
..........
"""


def parse_map(map):
    lines = map.splitlines()
    origin = 0, 0
D
David Amos 已提交
16
    destination = len(lines[-1]) - 1, len(lines) - 1
M
Moshe Zadka 已提交
17 18 19 20 21
    return lines, origin, destination


def is_valid(lines, position):
    x, y = position
D
David Amos 已提交
22
    if not (0 <= y < len(lines) and 0 <= x < len(lines[y])):
M
Moshe Zadka 已提交
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
        return False
    if lines[y][x] == "X":
        return False
    return True


def get_neighbors(lines, current):
    x, y = current
    for dx in [-1, 0, 1]:
        for dy in [-1, 0, 1]:
            if dx == 0 and dy == 0:
                continue
            position = x + dx, y + dy
            if is_valid(lines, position):
                yield position


def get_shorter_paths(tentative, positions, through):
    path = tentative[through] + [through]
    for position in positions:
        if position in tentative and len(tentative[position]) <= len(path):
            continue
        yield position, path


def find_path(map):
    lines, origin, destination = parse_map(map)
    tentative = {origin: []}
    candidates = [(0, origin)]
D
David Amos 已提交
52 53
    certain = set()
    while destination not in certain and len(candidates) > 0:
M
Moshe Zadka 已提交
54
        _ignored, current = heapq.heappop(candidates)
D
David Amos 已提交
55
        if current in certain:
M
Moshe Zadka 已提交
56
            continue
D
David Amos 已提交
57 58 59 60
        certain.add(current)
        neighbors = set(get_neighbors(lines, current)) - certain
        shorter = get_shorter_paths(tentative, neighbors, current)
        for neighbor, path in shorter:
M
Moshe Zadka 已提交
61 62
            tentative[neighbor] = path
            heapq.heappush(candidates, (len(path), neighbor))
D
David Amos 已提交
63 64 65 66
    if destination in tentative:
        return tentative[destination] + [destination]
    else:
        raise ValueError("no path")
M
Moshe Zadka 已提交
67 68 69 70 71 72 73 74 75 76 77


def show_path(path, map):
    lines = map.splitlines()
    for x, y in path:
        lines[y] = lines[y][:x] + "@" + lines[y][x + 1 :]
    return "\n".join(lines) + "\n"


path = find_path(map)
print(show_path(path, map))