-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmaze.py
97 lines (80 loc) · 3.71 KB
/
maze.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
from enum import Enum
from typing import List, NamedTuple, Optional
import random
from search import Node, dfs, bfs, nodes_in_path
class Cell(str, Enum):
EMPTY = ' '
BLOCKED = '\u2588'
START = 'S'
END = 'E'
PATH = '*'
class Location(NamedTuple):
row: int
column: int
class Maze:
def __init__(self, rows: int = 25, columns: int = 250, blockade_probability: float = 0.3,
start: Location = Location(0, 0), end: Location = Location(24, 249)):
self._rows: int = rows
self._columns: int = columns
self.start: Location = start
self.end: Location = end
self._grid: List[List[Cell]] = [[Cell.EMPTY for c in range(columns)] for r in range(rows)]
self.add_blocks(rows, columns, blockade_probability)
self._grid[start.row][start.column] = Cell.START
self._grid[end.row][end.column] = Cell.END
def __str__(self):
output = ''
for row in self._grid:
output += ''.join([c.value for c in row]) + '\n'
return output
def add_blocks(self, rows: int, columns: int, blockade_probability: float):
for row in range(rows):
for column in range(columns):
if random.uniform(0, 1.0) < blockade_probability:
self._grid[row][column] = Cell.BLOCKED
def is_end(self, location: Location) -> bool:
return location == self.end
def next_steps(self, location: Location) -> List[Location]:
locations: List[Location] = []
if location.row + 1 < self._rows and self._grid[location.row + 1][location.column] != Cell.BLOCKED:
locations.append(Location(location.row + 1, location.column))
if location.row - 1 >= 0 and self._grid[location.row - 1][location.column] != Cell.BLOCKED:
locations.append(Location(location.row - 1, location.column))
if location.column + 1 < self._columns and self._grid[location.row][location.column + 1] != Cell.BLOCKED:
locations.append(Location(location.row, location.column + 1))
if location.column - 1 >= 0 and self._grid[location.row][location.column - 1] != Cell.BLOCKED:
locations.append(Location(location.row, location.column - 1))
return locations
def print_path(self, path: List[Location]) -> None:
for location in path:
self._grid[location.row][location.column] = Cell.PATH
self._grid[self.start.row][self.start.column] = Cell.START
self._grid[self.end.row][self.end.column] = Cell.END
def delete_path(self, path: List[Location]) -> None:
for location in path:
self._grid[location.row][location.column] = Cell.EMPTY
self._grid[self.start.row][self.start.column] = Cell.START
self._grid[self.end.row][self.end.column] = Cell.END
if __name__ == '__main__':
m: Maze = Maze()
# Solve the Maze with DFS
dfs_solution: Optional[Node[Location]] = dfs(m.start, m.is_end, m.next_steps)
if dfs_solution is None:
print(m)
print('No solution found with depth-first search.')
else:
dfs_path: List[Location] = nodes_in_path(dfs_solution)
m.print_path(dfs_path)
print(m)
print(f'Maze can be solved with depth-first search in {len(dfs_path)} steps.\n\n')
m.delete_path(dfs_path)
# Solve the Maze with BFS
bfs_solution: Optional[Node[Location]] = bfs(m.start, m.is_end, m.next_steps)
if bfs_solution is None:
print('No solution found with breadth-first search.')
else:
bfs_path: List[Location] = nodes_in_path(bfs_solution)
m.print_path(bfs_path)
print(m)
print(f'Maze can be solved with breadth-first search in {len(bfs_path)} steps.\n\n')
m.delete_path(bfs_path)