Skip to content

Commit 62481b0

Browse files
committed
update the rst and added the test file
1 parent 3849184 commit 62481b0

2 files changed

Lines changed: 203 additions & 0 deletions

File tree

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
Dynamic Maze Solver using Breadth-First Search (BFS)
2+
====================================================
3+
4+
.. contents:: Table of Contents
5+
:local:
6+
:depth: 2
7+
8+
Overview
9+
--------
10+
11+
This example demonstrates a **dynamic maze-solving algorithm** based on the
12+
**Breadth-First Search (BFS)** strategy. The visualizer dynamically updates a maze
13+
in real-time while the solver attempts to reach a moving target.
14+
15+
Unlike static pathfinding examples, this version introduces:
16+
17+
- **A moving target** that relocates periodically.
18+
- **Randomly evolving obstacles** that can appear or disappear.
19+
- **Animated BFS exploration**, showing visited cells, computed paths, and breadcrumbs.
20+
21+
This simulation provides intuition for dynamic pathfinding problems such as
22+
robot navigation in unpredictable environments.
23+
24+
25+
Algorithmic Background
26+
----------------------
27+
28+
### Breadth-First Search (BFS)
29+
30+
The BFS algorithm is a graph traversal method that explores nodes in layers,
31+
guaranteeing the shortest path in an unweighted grid.
32+
33+
Let the maze be represented as a grid:
34+
35+
.. math::
36+
37+
M = \{ (i, j) \mid 0 \leq i < R, 0 \leq j < C \}
38+
39+
where each cell is either *free (0)* or *obstacle (1)*.
40+
41+
The BFS frontier expands as:
42+
43+
.. math::
44+
45+
Q = [(s, [s])]
46+
47+
where *s* is the start position, and the second term is the path history.
48+
49+
At each iteration:
50+
51+
.. math::
52+
53+
(r, c), path = Q.pop(0)
54+
55+
\text{for each neighbor } (r', c') \text{ in } N(r, c):
56+
\text{if } (r', c') \text{ is free and unvisited:}
57+
Q.append((r', c'), path + [(r', c')])
58+
59+
The algorithm halts when the target node *t* is reached.
60+
61+
Because BFS explores all nodes in increasing distance order, the path returned
62+
is the shortest (in terms of number of moves).
63+
64+
65+
Dynamic Components
66+
------------------
67+
68+
### Moving Target
69+
70+
Every few frames, the target moves randomly to an adjacent open cell:
71+
72+
.. math::
73+
74+
T_{new} = T_{old} + \Delta
75+
76+
where :math:`\Delta \in \{ (-1,0), (1,0), (0,-1), (0,1) \}`.
77+
78+
This simulates dynamic goals or moving entities in robotic navigation.
79+
80+
### Evolving Obstacles
81+
82+
With a small probability :math:`p`, each cell toggles between *free* and *blocked*:
83+
84+
.. math::
85+
86+
M_{i,j}^{t+1} =
87+
\begin{cases}
88+
1 - M_{i,j}^{t} & \text{with probability } p \\
89+
M_{i,j}^{t} & \text{otherwise}
90+
\end{cases}
91+
92+
This reflects real-world conditions like temporary obstructions or environment changes.
93+
94+
95+
Visualization
96+
-------------
97+
98+
The maze, solver, target, and BFS layers are visualized using **Matplotlib**.
99+
100+
Elements include:
101+
102+
- **Maze cells** - magma colormap (black = wall, bright = open)
103+
- **Visited nodes** - blue overlay with transparency
104+
- **Path line** - green connecting line
105+
- **Solver (robot)** - cyan circle
106+
- **Target** - magenta star
107+
- **Breadcrumbs** - trail of previously visited solver positions
108+
109+
A sample animation frame:
110+
111+
.. image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathPlanning/dynamic_maze_solver/animation.gif
112+
:alt: Maze BFS dynamic visualizer frame
113+
:align: center
114+
:scale: 80 %
115+
116+
117+
Mathematical Insights
118+
---------------------
119+
120+
- **BFS guarantees optimality** in unweighted grids.
121+
- The evolving maze introduces **non-stationarity**, requiring recomputation per frame.
122+
- The path length :math:`L_t` fluctuates as the environment changes.
123+
124+
If :math:`E_t` is the set of explored nodes at frame :math:`t`, then:
125+
126+
.. math::
127+
128+
L_t = |P_t|, \quad E_t = |V_t|
129+
130+
where :math:`P_t` is the discovered path and :math:`V_t` is the visited node set.
131+
132+
The solver continually re-estimates the path to accommodate new maze configurations.
133+
134+
135+
Code Link
136+
++++++++
137+
138+
.. automodule:: PathPlanning.BreadthFirstSearch.dynamic_maze_solver
139+
:members:
140+
:undoc-members:
141+
:show-inheritance:
142+
143+
Usage Example
144+
++++++++++++
145+
146+
.. code-block:: python
147+
import matplotlib.pyplot as plt
148+
from PathPlanning.BreadthFirstSearch.dynamic_maze_solver import MazeVisualizer
149+
150+
initial_maze = [
151+
[0, 1, 0, 0, 0, 0, 0, 0, 1, 0],
152+
[0, 1, 0, 1, 1, 0, 1, 0, 1, 0],
153+
[0, 0, 0, 1, 0, 0, 1, 0, 0, 0],
154+
[0, 1, 0, 1, 0, 1, 1, 1, 1, 0],
155+
[0, 1, 0, 0, 0, 0, 0, 0, 1, 0],
156+
[0, 1, 1, 1, 1, 1, 1, 0, 1, 0],
157+
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
158+
[1, 1, 1, 1, 0, 1, 1, 1, 1, 0],
159+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
160+
]
161+
162+
start_point = (0, 0)
163+
end_point = (8, 9)
164+
165+
visualizer = MazeVisualizer(initial_maze, start_point, end_point)
166+
visualizer.run()
167+
168+
169+
References
170+
----------
171+
172+
- **Algorithm:** Breadth-First Search (BFS) :-`<https://en.wikipedia.org/wiki/Breadth-first_search>`_
173+
- **Visualization:** Matplotlib animation
174+
- **Maze Solver:**:-`<https://medium.com/@luthfisauqi17_68455/artificial-intelligence-search-problem-solve-maze-using-breadth-first-search-bfs-algorithm-255139c6e1a3>`__
175+
176+
177+

tests/test_dynamic_maze_solver.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import conftest
2+
from PathPlanning.BreadthFirstSearch.dynamic_maze_solver import MazeVisualizer
3+
4+
5+
def test_bfs_finds_path():
6+
# small maze: 0=open, 1=wall
7+
maze = [
8+
[0, 0, 0],
9+
[1, 1, 0],
10+
[0, 0, 0]
11+
]
12+
13+
start = (0, 0)
14+
target = (2, 2)
15+
16+
viz = MazeVisualizer(maze, start, target)
17+
18+
path, visited = viz._bfs()
19+
20+
assert path is not None
21+
assert path[0] == start
22+
assert path[-1] == target
23+
24+
25+
if __name__ == '__main__':
26+
conftest.run_this_test(__file__)

0 commit comments

Comments
 (0)