-
Notifications
You must be signed in to change notification settings - Fork 0
/
min_path.py
156 lines (122 loc) · 5.04 KB
/
min_path.py
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"""Coding Problem #23
Given an M by N matrix consisting of booleans representing a board:
Each True boolean represents a wall
Each False boolean represents a tile to walk on
Given this matrix, a start coordinate and a end coordinate:
return the minimum number of steps to reach the end coordinate
If there's no possible path, return null
Can move Up, Down, Left or Right
Not allowed: jump through walls or wrap around the edges of the board
"""
from typing import Dict, List, NamedTuple
class BoardSize(NamedTuple):
rows: int
columns: int
def __str__(self):
return f'{self.rows}x{self.columns}'
def __repr__(self):
return f'BoardSize(rows={self.rows}, columns={self.columns})'
class Position(NamedTuple):
row: int
column: int
def __str__(self):
return f'({self.row}, {self.column})'
def __repr__(self):
return f'Position(row={self.row}, column={self.column})'
class Path(NamedTuple):
steps: int
path: List[str]
def __str__(self):
path: str = ' -> '.join(self.path)
return f'{self.steps} steps: {path})'
def __repr__(self):
return f'Path(steps={self.steps}, path={self.path})'
board: List[List[bool]] = [
[False, False, False, False],
[False, True, False, True],
[False, True, False, False],
[False, False, True, False],
[False, True, False, False],
[True, True, False, True],
[False, False, False, False],
]
"""Builds a list of possible positions to go from the current position
If a given position corresponds to a wall ('True' in the board),
it will not be added to the neighbours list
"""
def get_neighbours(row: int, column: int, board: List[List[bool]]) -> List[Position]:
board_size = BoardSize(rows=len(board), columns=len(board[0]))
neighbours: List[Position] = []
positions = [
Position(row + 1, column),
Position(row - 1, column),
Position(row, column + 1),
Position(row, column - 1),
]
"""Checks if positon is a wall
If a board has a different definition of a wall, only this method
should be modified accordingly
"""
def a_wall(position: Position) -> bool:
return board[position.row][position.column]
# Verifies if the position is valid (not a wall and not out of board range)
for pos in positions:
# If it is valid, appends it to the neighbours list:
if (
0 <= pos.row < board_size.rows
and 0 <= pos.column < board_size.columns
and not a_wall(Position(pos.row, pos.column))
):
neighbours.append(Position(pos.row, pos.column))
return neighbours
"""Builds an adjacency list with valid positions to go from a given position"""
def adjacency_list(board: List[List[bool]]) -> Dict[Position, List[Position]]:
adjacency_list: Dict[Position, List[Position]] = {}
# Iterates through every row and column of the board:
for row in range(len(board)):
for column in range(len(board[row])):
# If current position is not a wall:
if board[row][column] != True:
# Inserts it in the adjacency list with all its valid moves
adjacency_list[(row, column)] = get_neighbours(row, column, board)
return adjacency_list
"""Builds a list with all the positions in the path from the start to the end position"""
def get_path(previous_pos: Dict[Position, Position], end: Position) -> Path:
# Traverses the path list from the end position back to the start
position: Position = end
path: List[str] = []
# If start == end, zero steps are needed, so steps is initialized with -1
steps: int = -1
# The previous position of the start point is (-1, -1)
while position != Position(-1, -1):
path.insert(0, str(position))
position = previous_pos[position]
steps += 1
return Path(steps, path)
"""Finds the path with the minimun number of steps, from start to end position"""
def min_path(board: List[List[bool]], start: Position, end: Position) -> Path:
neighbours_of = adjacency_list(board)
# Tracks the already checked positions:
checked: Dict[Position, bool] = {}
queue = [start]
# Tracks the previous step of a given position:
previous_steps = {start: Position(-1, -1)}
# Default value if no path is found:
path: str = f"There is no path between {start} and {end}"
while queue != []:
current_pos = queue.pop(0)
if current_pos == end:
checked[current_pos] = True
path = get_path(previous_steps, end)
break
# Gets every possible move from current_pos:
for position in neighbours_of[current_pos]:
if not position in checked:
# Marks current_pos as previous_step of position:
previous_steps[position] = current_pos
# Appends it to the queue to contiue searching for the end position
queue.append(position)
checked[current_pos] = True
return path
result = min_path(board, (6, 0), (3, 0))
print(f'{result}')