Mini Project: Build Your Own Pathfinding Toy
Let’s build a small pathfinding application. You can run it in a browser or as a console app.
Project Overview
Goal: Create a pathfinding tool where users can:
- Define grid size
- Place obstacles
- Choose algorithm (Dijkstra or A*)
- Run the algorithm
- See the path and statistics
Basic Implementation
Here’s a complete HTML/JavaScript version you can run in a browser:
<!DOCTYPE html>
<html>
<head>
<title>Pathfinding Toy</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
.controls { margin-bottom: 20px; }
.grid { display: inline-grid; gap: 2px; background: #e2e8f0; padding: 10px; }
.cell { width: 30px; height: 30px; border: 1px solid #cbd5e1; cursor: pointer; }
.cell.empty { background: white; }
.cell.wall { background: #1e293b; }
.cell.start { background: #10b981; }
.cell.goal { background: #ef4444; }
.cell.visited { background: #94a3b8; }
.cell.path { background: #fbbf24; }
.stats { margin-top: 20px; padding: 10px; background: #f8fafc; border-radius: 5px; }
</style>
</head>
<body>
<h1>Pathfinding Toy</h1>
<div class="controls">
<label>Grid Size: <input type="number" id="grid-size" value="15" min="5" max="30" /></label>
<label>Algorithm:
<select id="algorithm">
<option value="dijkstra">Dijkstra</option>
<option value="astar">A*</option>
</select>
</label>
<button id="run-btn">Run Algorithm</button>
<button id="clear-btn">Clear</button>
</div>
<div id="grid-container"></div>
<div class="stats" id="stats"></div>
<script>
// Implementation code here (from previous page)
// ... (include dijkstra and astar functions)
</script>
</body>
</html>
Extension Ideas
Once you have the basic version working, try these extensions:
1. Diagonal Movement
Add 8-direction movement with diagonal costs:
directions = [
(-1, 0), (1, 0), (0, -1), (0, 1), # 4-direction
(-1, -1), (-1, 1), (1, -1), (1, 1) # Diagonals
]
# Diagonal moves cost √2 ≈ 1.41
cost = 1.41 if abs(dr) + abs(dc) == 2 else 1.0
Challenge: Make sure A* heuristic accounts for diagonal movement (use Euclidean distance).
2. Variable Terrain Costs
Add different terrain types with different costs:
terrain_costs = {
0: 1, # Grass
2: 5, # Mud
3: 0.5, # Road
4: 10, # Water
}
Challenge: Update the grid to support multiple terrain types, and modify the algorithm to use variable costs.
3. Multiple Heuristics
Let users choose between different heuristics:
- Manhattan distance (4-direction)
- Euclidean distance (8-direction)
- Chebyshev distance (max of row/col differences)
- Zero (Dijkstra mode)
Challenge: Implement all heuristics and add a selector in the UI.
4. Save and Share Grids
Allow users to save grid configurations and share them:
// Save grid as JSON
function saveGrid() {
const config = {
grid: grid,
start: start,
goal: goal,
size: gridSize
};
localStorage.setItem('savedGrid', JSON.stringify(config));
}
// Load grid
function loadGrid() {
const saved = localStorage.getItem('savedGrid');
if (saved) {
const config = JSON.parse(saved);
// Restore grid
}
}
Challenge: Add import/export functionality and URL sharing.
5. Animated Visualization
Show the algorithm running step by step with animation:
async function visualizeStep(step) {
// Update grid visualization
renderGrid();
// Wait before next step
await new Promise(resolve => setTimeout(resolve, 100));
}
Challenge: Add play/pause controls and speed adjustment.
6. Performance Comparison
Show side-by-side comparison of algorithms:
- Nodes expanded
- Time taken
- Path length
- Memory usage
Challenge: Run both algorithms on the same grid and display comparison metrics.
Challenge Panel
Here are some specific challenges to try:
Challenge 1: Diagonal Movement with Optimality
Goal: Add diagonal movement to A* while maintaining optimality.
Hint: Use Euclidean distance as the heuristic, and make sure diagonal moves cost √2.
Difficulty: Intermediate
Show Hint
Euclidean distance is admissible for 8-direction movement. Make sure your cost calculation matches: diagonal = √2, straight = 1.
Challenge 2: Weighted A*
Goal: Implement weighted A* (wA*) that trades optimality for speed.
Hint: Multiply the heuristic by a weight factor (e.g., 1.5). This makes A* explore fewer nodes but may find suboptimal paths.
Difficulty: Advanced
Show Hint
Instead of f(n) = g(n) + h(n), use f(n) = g(n) + w × h(n) where w > 1. This biases the search more toward the goal.
Challenge 3: Jump Point Search
Goal: Implement Jump Point Search, an optimization of A* for uniform-cost grids.
Hint: Skip nodes that don’t change the direction of the optimal path. This reduces the number of nodes explored.
Difficulty: Advanced
Show Hint
JPS “jumps” over nodes in straight lines and diagonals when there are no obstacles. It’s much faster than A* on uniform grids.
Testing Your Implementation
Test with these scenarios:
Simple case:
- 10×10 grid
- No obstacles
- Start: (9, 0), Goal: (0, 9)
- Expected: Straight diagonal path
Maze:
- 20×20 grid
- Many obstacles creating a maze
- Both algorithms should find the same path length
No path:
- Start and goal separated by walls
- Should handle gracefully (return null, show message)
Performance test:
- Large grid (50×50)
- Compare nodes expanded between Dijkstra and A*
Key Takeaways
Before finishing:
- Start simple - Get basic version working first
- Add features incrementally - One extension at a time
- Test thoroughly - Try edge cases and different scenarios
- Visualize - Seeing the algorithm run helps understanding
- Experiment - Try different heuristics, costs, and optimizations
Final Knowledge Check
What’s Next?
Congratulations! You’ve completed the tutorial. In the completion page, we’ll summarize what you’ve learned and suggest next steps for further learning.