An interactive terminal-based pathfinding visualizer supporting Dijkstra's algorithm and A search with Manhattan distance heuristic on weighted grid graphs.*
This project demonstrates optimal pathfinding algorithms on randomly generated weighted 2D grids. It features an interactive CLI interface with real-time visualization using Unicode box-drawing characters, colored terrain costs, and highlighted solution paths.
Key Features:
- Two pathfinding algorithms: Dijkstra's algorithm and A* search
- Weighted grid graphs with configurable terrain costs (1-3)
- Random grid generation with seeded reproducibility
- Live terminal visualization with Unicode box-drawing
- Interactive CLI menu with safe input handling
- Performance benchmarking utilities
- Cross-platform terminal support (Windows, Linux, macOS)
No external dependencies required for the core application. Uses only Python standard library.
# Optional: For Benchmark visualization
pip install matplotlibpython main.pyOr as a module:
python -m mainThis launches an interactive CLI interface with a default 20 by 10 cells grid.
- Generate New Grid Enter dimensions, choose seed or use random, and wall probability (0.1 = 10% walls)
- Find Path Select algorithm (Dijkstra or A*) Enter start and goal coordinates
- View Results Visualized path with execution time and total cost displayed
class Graph:
"""Undirected, weighted 4-directional grid graph."""
- width, height: Grid dimensions
- weight: 1D adjacency list (optimized memory layout)
- Methods: node_at(), neighbours(), idx()Key Functions:
- \create_grid_graph(width, height, seed, wall_probability)\ - Generate random weighted grids
- \print_grid_graph(graph, path)\ - Render grid with Unicode visualization
- \cost_to_color(cost)\ - Map terrain costs to ANSI colors
Grid Representation:
- Internal: 1D list indexed as \y * width + x\
- Each cell stores [right_edge_cost, down_edge_cost]\
- Edge weight 0 = blocked path; 1-3 = terrain cost
Implements three algorithms:
1. Dijkstra's Algorithm
def dijkstra(graph, start, goal) -> (distances, previous)- Uses priority queue (heapq)
- Explores nodes in order of increasing distance from start
- Guaranteed optimal path for non-negative weights
- Time complexity: O((V + E) log V)
2. A Search*
def a_star(graph, start, goal) -> (previous, g_score)- Uses Manhattan distance heuristic: \1.5 * (|x_goal - x| + |y_goal - y|)\
- Heuristic weighted factor improves efficiency
- Guarantees optimal path when heuristic is admissible
- Time complexity: O((V + E) log V) with better practical performance
3. Breadth-First Search (Utility)
def bfs(graph, start) -> parent_map- For unweighted grids
- Used internally for various analyses
Wrapper Function:
def shortest_path(graph, start, goal, algorithm=\"dijkstra\")
(path_with_costs, total_cost)Returns list of (node_coordinates, cumulative_cost)\ tuples and total path cost.
Main Class: \App\
- Manages user interaction, grid generation, and pathfinding workflow
- Features colorized output with ANSI color codes
- Safe input handling with type conversion and validation
Key Methods:
- \generate_grid()\ - Interactive grid creation with parameters
- \find_path()\ - Algorithm selection and pathfinding execution
- \display_main_menu()\ - Main menu interface
- \print_header()\ - Formatted header with styling
Menu Options:
1. Generate New Grid - Create new grid with custom dimensions/seed
2. Find Path - Select algorithm and find path between points
3. Print Current Grid - Display current grid state
q. Quit - Exit application
from app import App
app = App()
app.run()Benchmarks pathfinding algorithms across multiple grid sizes with multiple trials.
benchmark_running_times()- Tests sizes: 50 by 50 to 1000 by 1000
- Runs 50 trials per test for statistical accuracy
- Outputs JSON with timing results for three path variants: horizontal, diagonal, center
- Saves results to \dijkstra_astar.json\
benchmark_pathfinding()- Profiles CPU time and memory usage
- Generates .prof\ files for cProfile analysis
- Tests three path variants: horizontal, diagonal, center
plot_pathfinding_benchmark(json_file=\"dijkstra_astar.json\")- Creates comparative performance plots
- Shows execution time vs. grid size for both algorithms
- Generates 3-subplot figure (one per path variant)
- Saves visualization as \pathfinding_benchmark_3_variants.png\
- \collections\ (deque)
- \heapq\ (priority queue)
- \typing\ (type hints)
- \random\ (grid generation)
- \os \time \typing (CLI utilities)
- \matplotlib\ - Benchmark result visualization
- \cProfile, \ tracemalloc, \pstats\ - Performance profiling (standard library)
- \json\ - Results serialization -\gc garbage collector
Suitable for: Weighted graphs where all weights are non-negative
1. Initialize distances to all nodes as infinity (except start = 0)
2. Use priority queue to always process nearest unvisited node
3. For each neighbor, calculate relaxation: new_dist = current_dist + edge_weight
4. Update if new_dist improves previous best
5. Continue until goal reached or queue empty
Complexity:
- Time: O((V + E) log V) with binary heap
- Space: O(V) for distances and visited set
Suitable for: Single-pair shortest path when good heuristic exists
1. Use f_score = g_score (actual cost) + h(node) (estimated cost to goal)
2. Manhattan heuristic: h = 1.5 * (|x_goal - x| + |y_goal - y|)
3. Heuristic weighting (1.5 factor) accelerates convergence
4. Expand nodes with lowest f_score first
5. Guarantees optimality if heuristic is admissible (never overestimates)
Why faster than Dijkstra:
- Heuristic guides search toward goal
- Explores fewer total nodes
- Especially effective for grid-based pathfinding
Complexity:
- Time: O((V + E) log V) best case, O(V log V) average with good heuristic
- Space: O(V) for priority queue and tracking sets
Node Symbols:
- \ (empty circle) - Unvisited node
- \ (filled circle) - Node on solution path
Edge Symbols:
(thin line) - Single edge (1-3 weight)
(bold line) - Path edge (solution path)
(thin pipe) - Vertical edge
(bold pipe) - Vertical path edge
Colors (ANSI):
- Green (#41) - Open terrain (cost 1)
- Blue (#63) - Forest/hills (cost 2)
- Purple (#198) - Mountain/rough (cost 3)
- Yellow (\93m) - Solution path
- Bright green - Menu text
Example Output:
○───○───○───○───○───○───○───○───○───○
│ │ │ │ │ │ │ │ │ │
○───○───○───○───○───○───○───○───○───○
│ │ │ │ │ │ │ │ │ |
○───○───○───○───○───○───○───○───○───○
│ │ │ │ │ │ | │ │ │
○───○───○───○───○───○───○───○───○───○
│ │ │ │ │ │ │ │ │ │
○───○───○───○───○───○───○───○───○───○
create_grid_graph(
width: int, # Grid width (recommend 50-60 for display)
height: int, # Grid height (recommend 100)
seed: int | str, # Random seed for reproducibility
wall_probability: float # Probability to block edge (0.0-1.0)
)Recommended Settings:
- Small grids: 10-20 cells for clear visualization
- Medium grids: 50-100 cells for performance testing
- Large grids: 500-1000 cells for benchmark comparison
COSTS = (1, 2, 3) # Randomly assigned edge weights- Cost 1 = Open terrain (fast)
- Cost 2 = Forest/hills (moderate)
- Cost 3 = Mountain/rough (slow)
Tested on grids from 5050 to 10001000 with 50 trials each:
Benchmarking 300x300 grid Variant: horizontal dijkstra : 1.8217s | 18.3 MB astar : 0.6007s | 2.8 MB -> A* speedup: 3.03x Variant: diagonal dijkstra : 1.9385s | 25.5 MB astar : 0.0596s | 0.8 MB -> A* speedup: 32.52x Variant: center dijkstra : 0.8105s | 8.0 MB astar : 0.0296s | 0.4 MB -> A* speedup: 27.35x
Key Observations:
- A* consistently outperforms Dijkstra by 25-75%
- Speedup increases with grid size
- Both scale approximately linearly with grid area
- Manhattan heuristic very effective for grid pathfinding
\$ python main.py
----------------------------------------------------------------------
GRID-BASED PATH-FINDING VISUALIZER
● Dijkstra ●
● A* ●
----------------------------------------------------------------------
Main Menu:
----------------------------------------------------------------------
1 → Generate New Grid
2 → Find Path
3 → Print Current Grid
----------------------------------------------------------------------
q → Quit
----------------------------------------------------------------------
Choose option:
Choose option: 1
Generate New Grid
Enter width: 15
Enter height: 10
Enter seed or press Enter for random: 42
Wall-removal probability (0.0-1.0, default 0.1): 0.15
Generating 15x10 grid (seed=42)
...
from Graph import create_grid_graph
from algorithms import shortest_path, dijkstra, a_star
# Create grid
graph = create_grid_graph(20, 15, seed=\'myseed999\', wall_probability=0.1)
# Find shortest path
path, total_cost = shortest_path(
graph,
start=(0, 0),
goal=(19, 14),
algorithm=\"astar\"
)
if path:
print(f\"Path length: {len(path)} nodes\")
print(f\"Total cost: {total_cost:.2f}\")
for node, cumulative_cost in path:
print(f\" {node}: {cumulative_cost:.2f}\")
else:
print(\"No path found!\")# Performance benchmarks with JSON output
python algo_benchmark.py
# Detailed profiling (outputs .prof files)
python performance_algos.py
# Visualize results
python visualize_tests.py- Display: Grid width limited to ~50-60 cells for proper terminal rendering (due to terminal width)
- Grid height: Should not exceed ~100 to avoid excessive scrolling
- Edge weights: Fixed to 1-3 (not configurable at runtime)
- Heuristic: Manhattan distance only (no option for Euclidean or other metrics)
- Movement: 4-directional (no diagonal movement)
- 8-directional movement (including diagonals)
- Configurable heuristics (Euclidean, custom)
- Bidirectional A* search
- Jump Point Search (JPS)
- Theta* (any-angle pathfinding)
- Dynamic obstacle handling
- Multiple goal search
- GUI visualization (matplotlib/pygame)
- A* with weights (Weighted A*)
- Persistent grid save/load
- Uses 1D list for grid storage instead of 2D for better cache locality
- Compact representation: 2 integers per cell (edge weights)
- Example: 10001000 grid = ~2MB (vs ~4MB for 2D arrays)
- Dijkstra: Priority queue prevents unnecessary relaxations
- A:* Heuristic reduces explored nodes by ~60-75%
- Tie-breaking: Counter in heap prevents comparison of non-comparable objects
- Pure Manhattan distance is admissible but underestimates in weighted grids
- Weighting factor 1.5 still admissible (weights max out at 3)
- Provides better guidance toward goal without sacrificing optimality
- Type Hints: Full typing annotations for better IDE support and documentation
- Docstrings: Comprehensive module and function documentation
- Comments: Inline comments for complex logic
- Error Handling: Input validation with try-except for type conversion
- Constants: Named constants for magic numbers and colors
Amit Patel. (2025) Amit’s A* Pages. Red Blob Games. [Online] [9th December 2025] https://theory.stanford.edu/~amitp/GameProgramming/index.html Goodrich, M. T, Tamassia, R., and Goldwasser M. H. (2013) Data Structures and Algorithms in Python. 1st ed., USA: John Wiley and sons.
Hart, P., Nilsson, N. & Raphael, B. (1968). A Formal Basis for the Heuristic Determination of Minimum Cost Paths. IEEE Transactions on Systems Science and Cybernetics, 4(2), pp.100–107. Python Software Foundation. (2025) heapq - Heap queue algorithm. Python Docs. [Online] [Accessed on 5th December 2025] https://docs.python.org/3/library/heapq.html#priority-queue-implementation-notes Rajesh Kumar. (2024) The A* Algorithm: A Complete Guide. Datacamp. [Online] [9th December 2025] https://www.datacamp.com/tutorial/a-star-algorithm Red Blob Games. (2023) Implementation of A*. Red Blob Games. [Online] [Accessed on 7th December] https://www.redblobgames.com/pathfinding/a-star/implementation.html
This project is open source and available under the MIT License.
kamScripts - Grid-Based Pathfinding Visualizer Project