// Main application logic const API_BASE = '/api'; let currentMazeId = null; let currentMazeData = null; let animationState = { isAnimating: false, isPaused: false, currentStep: 0, visitedCells: [], solutionPath: [], animationId: null, speed: 50 // milliseconds per step }; // DOM Elements const generateBtn = document.getElementById('generateBtn'); const visualizeBtn = document.getElementById('visualizeBtn'); const downloadBtn = document.getElementById('downloadBtn'); const saveBtn = document.getElementById('saveBtn'); const loadBtn = document.getElementById('loadBtn'); const solveDfsBtn = document.getElementById('solveDfsBtn'); const solveBfsBtn = document.getElementById('solveBfsBtn'); const analyzeBtn = document.getElementById('analyzeBtn'); const benchmarkBtn = document.getElementById('benchmarkBtn'); const pauseBtn = document.getElementById('pauseBtn'); const stopBtn = document.getElementById('stopBtn'); const speedSlider = document.getElementById('speedSlider'); const algorithmSelect = document.getElementById('algorithm'); const rowsInput = document.getElementById('rows'); const colsInput = document.getElementById('cols'); const seedInput = document.getElementById('seed'); const resultsContent = document.getElementById('resultsContent'); const mazeInfo = document.getElementById('mazeInfo'); const animationControls = document.getElementById('animationControls'); // Disable buttons initially function disableActionButtons() { visualizeBtn.disabled = true; downloadBtn.disabled = true; saveBtn.disabled = true; solveDfsBtn.disabled = true; solveBfsBtn.disabled = true; analyzeBtn.disabled = true; } function enableActionButtons() { visualizeBtn.disabled = false; downloadBtn.disabled = false; saveBtn.disabled = false; solveDfsBtn.disabled = false; solveBfsBtn.disabled = false; analyzeBtn.disabled = false; } disableActionButtons(); // Event Listeners generateBtn.addEventListener('click', generateMaze); visualizeBtn.addEventListener('click', visualizeMaze); downloadBtn.addEventListener('click', downloadMaze); saveBtn.addEventListener('click', saveMaze); loadBtn.addEventListener('click', showLoadModal); solveDfsBtn.addEventListener('click', () => solveMaze('dfs')); solveBfsBtn.addEventListener('click', () => solveMaze('bfs')); analyzeBtn.addEventListener('click', analyzeMaze); benchmarkBtn.addEventListener('click', runBenchmark); pauseBtn.addEventListener('click', togglePause); stopBtn.addEventListener('click', stopAnimation); speedSlider.addEventListener('input', updateSpeed); // Generate Maze async function generateMaze() { const algorithm = algorithmSelect.value; const rows = parseInt(rowsInput.value); const cols = parseInt(colsInput.value); const seed = seedInput.value ? parseInt(seedInput.value) : null; if (rows < 5 || rows > 50 || cols < 5 || cols > 50) { showError('Dimensions must be between 5 and 50'); return; } setLoading(generateBtn, true); try { const response = await fetch(`${API_BASE}/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ algorithm, rows, cols, seed }) }); const data = await response.json(); if (data.success) { currentMazeId = data.id; currentMazeData = data.maze; enableActionButtons(); showSuccess('Maze generated successfully!'); displayMazeInfo(data.maze); visualizeMaze(); } else { showError(data.error || 'Failed to generate maze'); } } catch (error) { showError('Network error: ' + error.message); } finally { setLoading(generateBtn, false); } } // Visualize Maze function visualizeMaze() { if (!currentMazeData) { showError('No maze to visualize'); return; } renderMaze(currentMazeData); showInfo('Maze visualized'); } // Download Maze async function downloadMaze() { if (currentMazeId === null) { showError('No maze to download'); return; } try { window.open(`${API_BASE}/download/${currentMazeId}?solution=false`, '_blank'); showSuccess('Downloading maze image...'); } catch (error) { showError('Failed to download: ' + error.message); } } // Save Maze async function saveMaze() { if (currentMazeId === null) { showError('No maze to save'); return; } const filename = prompt('Enter filename:', `maze_${currentMazeId}`); if (!filename) return; try { const response = await fetch(`${API_BASE}/save/${currentMazeId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ filename }) }); const data = await response.json(); if (data.success) { showSuccess(`Maze saved to: ${data.filepath}`); } else { showError(data.error || 'Failed to save maze'); } } catch (error) { showError('Network error: ' + error.message); } } // Load Maze Modal async function showLoadModal() { const modal = document.getElementById('loadModal'); const fileList = document.getElementById('fileList'); const closeBtn = document.getElementById('closeModal'); try { const response = await fetch(`${API_BASE}/saved-mazes`); const data = await response.json(); if (data.files && data.files.length > 0) { fileList.innerHTML = data.files.map(file => `
No saved mazes found
'; } modal.classList.add('active'); closeBtn.onclick = () => modal.classList.remove('active'); modal.onclick = (e) => { if (e.target === modal) modal.classList.remove('active'); }; } catch (error) { showError('Failed to load file list: ' + error.message); } } async function loadMazeFile(filename) { const modal = document.getElementById('loadModal'); modal.classList.remove('active'); try { const response = await fetch(`${API_BASE}/load`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ filename }) }); const data = await response.json(); if (data.success) { currentMazeId = data.id; currentMazeData = data.maze; enableActionButtons(); showSuccess(`Loaded: ${filename}`); displayMazeInfo(data.maze); visualizeMaze(); } else { showError(data.error || 'Failed to load maze'); } } catch (error) { showError('Network error: ' + error.message); } } // Solve Maze with Animation async function solveMaze(algorithm) { if (currentMazeId === null) { showError('No maze to solve'); return; } // Stop any ongoing animation stopAnimation(); const btn = algorithm === 'dfs' ? solveDfsBtn : solveBfsBtn; setLoading(btn, true); try { const response = await fetch(`${API_BASE}/solve`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ maze_id: currentMazeId, algorithm }) }); const data = await response.json(); setLoading(btn, false); if (data.success) { // Start animated solving startSolvingAnimation(data); displaySolutionInfo(data); } else { showError('Failed to solve maze'); } } catch (error) { showError('Network error: ' + error.message); setLoading(btn, false); } } // Animation Control Functions function startSolvingAnimation(solutionData) { animationState.isAnimating = true; animationState.isPaused = false; animationState.currentStep = 0; animationState.visitedCells = solutionData.visited; animationState.solutionPath = solutionData.path; // Show animation controls animationControls.style.display = 'block'; pauseBtn.textContent = 'PAUSE'; // Disable solve buttons during animation solveDfsBtn.disabled = true; solveBfsBtn.disabled = true; // Start animation animateStep(); } function animateStep() { if (!animationState.isAnimating || animationState.isPaused) { return; } const { currentStep, visitedCells, solutionPath } = animationState; if (currentStep < visitedCells.length) { // Show visited cells up to current step const visibleVisited = visitedCells.slice(0, currentStep + 1); // Don't show solution path yet, only when all cells are visited renderMaze(currentMazeData, visibleVisited, null); animationState.currentStep++; // Schedule next step animationState.animationId = setTimeout(animateStep, animationState.speed); } else if (currentStep < visitedCells.length + solutionPath.length) { // Now animate the solution path const pathStep = currentStep - visitedCells.length; const visiblePath = solutionPath.slice(0, pathStep + 1); renderMaze(currentMazeData, visitedCells, visiblePath); animationState.currentStep++; // Schedule next step (slower for solution path) animationState.animationId = setTimeout(animateStep, animationState.speed * 2); } else { // Animation complete finishAnimation(); } } function togglePause() { if (!animationState.isAnimating) return; animationState.isPaused = !animationState.isPaused; if (animationState.isPaused) { pauseBtn.textContent = 'RESUME'; if (animationState.animationId) { clearTimeout(animationState.animationId); } } else { pauseBtn.textContent = 'PAUSE'; animateStep(); } } function stopAnimation() { if (!animationState.isAnimating) return; animationState.isAnimating = false; animationState.isPaused = false; if (animationState.animationId) { clearTimeout(animationState.animationId); animationState.animationId = null; } // Hide animation controls animationControls.style.display = 'none'; // Re-enable solve buttons solveDfsBtn.disabled = false; solveBfsBtn.disabled = false; // Show final result if we have solution data if (animationState.visitedCells.length > 0 && animationState.solutionPath.length > 0) { renderMaze(currentMazeData, animationState.visitedCells, animationState.solutionPath); } else { renderMaze(currentMazeData); } } function finishAnimation() { animationState.isAnimating = false; // Hide animation controls animationControls.style.display = 'none'; // Re-enable solve buttons solveDfsBtn.disabled = false; solveBfsBtn.disabled = false; // Show final result renderMaze(currentMazeData, animationState.visitedCells, animationState.solutionPath); showSuccess('Solving animation complete!'); } function updateSpeed(event) { // Speed slider: 1 (slow) to 100 (fast) // Convert to delay: 200ms (slow) to 10ms (fast) const sliderValue = parseInt(event.target.value); animationState.speed = 210 - (sliderValue * 2); } // Analyze Maze async function analyzeMaze() { if (currentMazeId === null) { showError('No maze to analyze'); return; } setLoading(analyzeBtn, true); try { const response = await fetch(`${API_BASE}/analyze/${currentMazeId}`); const data = await response.json(); displayAnalysisResults(data); showSuccess('Analysis complete!'); } catch (error) { showError('Network error: ' + error.message); } finally { setLoading(analyzeBtn, false); } } // Run Benchmark async function runBenchmark() { setLoading(benchmarkBtn, true); showInfo('Running benchmarks... This may take a moment.'); try { const response = await fetch(`${API_BASE}/benchmark`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'quick' }) }); const data = await response.json(); displayBenchmarkResults(data); showSuccess('Benchmark complete!'); } catch (error) { showError('Network error: ' + error.message); } finally { setLoading(benchmarkBtn, false); } } // Display Functions function displayMazeInfo(maze) { mazeInfo.innerHTML = ` ALGORITHM: ${maze.algorithm}| Algorithm | Size | Avg Time (ms) |
|---|---|---|
| ${r.algorithm} | ${r.size} | ${r.avg_time_ms} |
| Algorithm | Size | Avg Time (ms) | Path Length |
|---|---|---|---|
| ${r.algorithm} | ${r.size} | ${r.avg_time_ms} | ${r.avg_path_length} |