Skip to content

Commit b2decb7

Browse files
committed
updated code
1 parent 6a4edbe commit b2decb7

1 file changed

Lines changed: 74 additions & 73 deletions

File tree

PathPlanning/BreadthFirstSearch/dynamic_maze_solver.py

Lines changed: 74 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -5,59 +5,62 @@
55
import random
66
import matplotlib.animation as animation
77

8+
89
class MazeVisualizer:
910
"""
10-
A class to create a beautiful and interesting visualization
11-
of a dynamic maze-solving algorithm (BFS).
11+
Dynamic BFS maze-solving visualizer with moving target and evolving obstacles.
1212
"""
1313

1414
def __init__(self, maze, start, target):
15-
self.maze = np.array(maze)
15+
self.maze = np.array(maze, dtype=int)
1616
self.start_pos = start
1717
self.target_pos = target
1818
self.solver_pos = start
19-
19+
2020
self.rows, self.cols = self.maze.shape
21-
22-
# --- Configurable Parameters ---
23-
self.step_delay_ms = 200 # Animation frame delay in milliseconds
24-
self.target_move_interval = 5 # Target moves every N frames
25-
self.obstacle_change_prob = 0.01 # Probability of a wall changing
21+
self.step_delay_ms = 200 # Animation frame delay
22+
self.target_move_interval = 5 # Target moves every N frames
23+
self.obstacle_change_prob = 0.01 # Random obstacle toggle probability
2624

2725
# --- State Tracking ---
2826
self.path = []
2927
self.visited_nodes = set()
3028
self.breadcrumb_trail = [self.solver_pos]
3129
self.frame_count = 0
3230

33-
# --- Plotting Setup ---
31+
# --- Plot Setup ---
3432
self.fig, self.ax = plt.subplots(figsize=(8, 6))
3533
plt.style.use('seaborn-v0_8-darkgrid')
3634
self.fig.patch.set_facecolor('#2c2c2c')
3735
self.ax.set_facecolor('#1e1e1e')
38-
39-
# Hide axes ticks and labels for a cleaner look
36+
4037
self.ax.set_xticks([])
4138
self.ax.set_yticks([])
42-
43-
# Maze plot
39+
40+
# Base maze
4441
self.maze_plot = self.ax.imshow(self.maze, cmap='magma', interpolation='nearest')
45-
46-
# Visited nodes plot (semi-transparent overlay)
47-
self.visited_overlay = np.zeros((*self.maze.shape, 4)) # RGBA
42+
43+
# Visited overlay
44+
self.visited_overlay = np.zeros((*self.maze.shape, 4))
4845
self.visited_plot = self.ax.imshow(self.visited_overlay, interpolation='nearest')
4946

50-
# Path, solver, target, and breadcrumbs plots
47+
# Path, breadcrumbs, solver, target
5148
self.path_line, = self.ax.plot([], [], 'g-', linewidth=3, alpha=0.7, label='Path')
5249
self.breadcrumbs_plot = self.ax.scatter([], [], c=[], cmap='viridis_r', s=50, alpha=0.6, label='Trail')
53-
self.solver_plot, = self.ax.plot(self.solver_pos[1], self.solver_pos[0], 'o', markersize=15, color='#00ffdd', label='Solver')
54-
self.target_plot, = self.ax.plot(self.target_pos[1], self.target_pos[0], '*', markersize=20, color='#ff006a', label='Target')
55-
50+
self.solver_plot, = self.ax.plot(
51+
[self.solver_pos[1]], [self.solver_pos[0]],
52+
'o', markersize=15, color='#00ffdd', label='Solver'
53+
)
54+
self.target_plot, = self.ax.plot(
55+
[self.target_pos[1]], [self.target_pos[0]],
56+
'*', markersize=20, color='#ff006a', label='Target'
57+
)
58+
5659
self.ax.legend(facecolor='gray', framealpha=0.5, loc='upper right')
5760
self.title = self.ax.set_title("Initializing Maze...", color='white', fontsize=14)
5861

5962
def _bfs(self):
60-
"""Performs BFS to find the shortest path and returns path and visited nodes."""
63+
"""Performs BFS to find shortest path."""
6164
queue = deque([(self.solver_pos, [self.solver_pos])])
6265
visited = {self.solver_pos}
6366

@@ -70,16 +73,14 @@ def _bfs(self):
7073
for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
7174
nr, nc = r + dr, c + dc
7275
if 0 <= nr < self.rows and 0 <= nc < self.cols and \
73-
self.maze[nr][nc] == 0 and (nr, nc) not in visited:
76+
self.maze[nr][nc] == 0 and (nr, nc) not in visited:
7477
visited.add((nr, nc))
75-
new_path = list(path)
76-
new_path.append((nr, nc))
77-
queue.append(((nr, nc), new_path))
78-
79-
return None, visited # No path found
78+
queue.append(((nr, nc), path + [(nr, nc)]))
79+
80+
return None, visited
8081

8182
def _update_target(self):
82-
"""Moves the target to a random adjacent valid cell."""
83+
"""Moves the target randomly to an adjacent open cell."""
8384
tr, tc = self.target_pos
8485
moves = [(-1, 0), (1, 0), (0, -1), (0, 1)]
8586
random.shuffle(moves)
@@ -90,86 +91,86 @@ def _update_target(self):
9091
break
9192

9293
def _update_obstacles(self):
93-
"""Randomly toggles a few obstacle cells."""
94+
"""Randomly toggle a few obstacles."""
9495
for r in range(self.rows):
9596
for c in range(self.cols):
96-
# Avoid changing start/target positions
97-
if (r,c) == self.solver_pos or (r,c) == self.target_pos:
97+
if (r, c) in [self.solver_pos, self.target_pos]:
9898
continue
9999
if random.random() < self.obstacle_change_prob:
100-
self.maze[r, c] = 1 - self.maze[r, c] # Toggle 0 to 1 or 1 to 0
100+
self.maze[r, c] = 1 - self.maze[r, c]
101101

102102
def _update_frame(self, frame):
103-
"""Main animation loop function."""
103+
"""Main animation loop."""
104104
self.frame_count += 1
105-
106-
# --- Update Game State ---
105+
106+
# --- State ---
107107
if self.frame_count % self.target_move_interval == 0:
108108
self._update_target()
109-
110109
self._update_obstacles()
111-
110+
112111
self.path, self.visited_nodes = self._bfs()
113112

113+
# Move solver one step
114114
if self.path and len(self.path) > 1:
115-
self.solver_pos = self.path[1] # Move solver one step
115+
self.solver_pos = self.path[1]
116116
self.breadcrumb_trail.append(self.solver_pos)
117117

118-
# --- Update Visuals ---
119-
# Update maze and visited nodes overlay
118+
# --- Visuals ---
120119
self.maze_plot.set_data(self.maze)
121-
self.visited_overlay.fill(0) # Reset overlay
120+
121+
# Visited overlay
122+
self.visited_overlay.fill(0)
122123
visited_color = mcolors.to_rgba('#0077b6', alpha=0.3)
123124
for r, c in self.visited_nodes:
124125
self.visited_overlay[r, c] = visited_color
125126
self.visited_plot.set_data(self.visited_overlay)
126-
127-
# Update path line
127+
128+
# Path line
128129
if self.path:
129-
path_y, path_x = zip(*self.path)
130-
self.path_line.set_data(path_x, path_y)
130+
y, x = zip(*self.path)
131+
self.path_line.set_data(x, y)
131132
else:
132133
self.path_line.set_data([], [])
133134

134-
# Update solver and target positions
135-
self.solver_plot.set_data(self.solver_pos[1], self.solver_pos[0])
136-
self.target_plot.set_data(self.target_pos[1], self.target_pos[0])
135+
# set_data() now receives sequences
136+
self.solver_plot.set_data([self.solver_pos[1]], [self.solver_pos[0]])
137+
self.target_plot.set_data([self.target_pos[1]], [self.target_pos[0]])
137138

138-
# Update breadcrumbs
139+
# Breadcrumbs
139140
if self.breadcrumb_trail:
140-
trail_y, trail_x = zip(*self.breadcrumb_trail)
141-
colors = np.linspace(0.1, 1.0, len(trail_y))
142-
self.breadcrumbs_plot.set_offsets(np.c_[trail_x, trail_y])
141+
y, x = zip(*self.breadcrumb_trail)
142+
colors = np.linspace(0.1, 1.0, len(y))
143+
self.breadcrumbs_plot.set_offsets(np.c_[x, y])
143144
self.breadcrumbs_plot.set_array(colors)
144145

145-
# Update title and check for win condition
146+
# Title update
146147
if self.solver_pos == self.target_pos:
147-
self.title.set_text("Target Reached! 🎉")
148+
self.title.set_text("Dynamic Maze Solver")
148149
self.title.set_color('lightgreen')
149-
self.anim.event_source.stop() # Stop animation
150+
self.anim.event_source.stop()
150151
else:
151-
path_len_str = len(self.path) if self.path else "N/A"
152-
self.title.set_text(f"Frame: {self.frame_count} | Path Length: {path_len_str}")
153-
if not self.path:
154-
self.title.set_color('coral')
155-
else:
156-
self.title.set_color('white')
157-
158-
return [self.maze_plot, self.visited_plot, self.path_line, self.solver_plot,
159-
self.target_plot, self.breadcrumbs_plot, self.title]
152+
path_len = len(self.path) if self.path else "N/A"
153+
self.title.set_text(f"Frame: {self.frame_count} | Path Length: {path_len}")
154+
self.title.set_color('white' if self.path else 'coral')
155+
156+
return [
157+
self.maze_plot, self.visited_plot, self.path_line,
158+
self.solver_plot, self.target_plot, self.breadcrumbs_plot, self.title
159+
]
160160

161161
def run(self):
162162
"""Starts the animation."""
163163
self.anim = animation.FuncAnimation(
164-
self.fig,
165-
self._update_frame,
166-
frames=200, # Can be increased for longer animation
167-
interval=self.step_delay_ms,
168-
blit=True,
164+
self.fig,
165+
self._update_frame,
166+
frames=500,
167+
interval=self.step_delay_ms,
168+
blit=False,
169169
repeat=False
170170
)
171171
plt.show()
172172

173+
173174
if __name__ == "__main__":
174175
initial_maze = [
175176
[0, 1, 0, 0, 0, 0, 0, 0, 1, 0],
@@ -182,9 +183,9 @@ def run(self):
182183
[1, 1, 1, 1, 0, 1, 1, 1, 1, 0],
183184
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
184185
]
185-
186+
186187
start_point = (0, 0)
187188
end_point = (8, 9)
188189

189-
visualizer = MazeVisualizer(maze=initial_maze, start=start_point, target=end_point)
190-
visualizer.run()
190+
visualizer = MazeVisualizer(initial_maze, start_point, end_point)
191+
visualizer.run()

0 commit comments

Comments
 (0)