Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Path Finding Algorithms Visualizer</title> | |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> | |
| <style> | |
| body { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| padding: 20px; | |
| } | |
| .container { | |
| background: white; | |
| border-radius: 15px; | |
| padding: 30px; | |
| box-shadow: 0 10px 40px rgba(0,0,0,0.2); | |
| } | |
| .header { | |
| text-align: center; | |
| margin-bottom: 30px; | |
| } | |
| .header h1 { | |
| color: #667eea; | |
| font-weight: bold; | |
| } | |
| .controls-panel { | |
| background: #f8f9fa; | |
| padding: 20px; | |
| border-radius: 10px; | |
| margin-bottom: 20px; | |
| } | |
| .btn-find { | |
| width: 100%; | |
| padding: 12px; | |
| font-size: 18px; | |
| font-weight: bold; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| border: none; | |
| margin-top: 10px; | |
| } | |
| .btn-find:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); | |
| } | |
| .btn-clear { | |
| width: 100%; | |
| padding: 12px; | |
| font-size: 18px; | |
| font-weight: bold; | |
| margin-top: 10px; | |
| } | |
| #mapCanvas { | |
| border: 3px solid #667eea; | |
| border-radius: 10px; | |
| cursor: crosshair; | |
| max-width: 100%; | |
| box-shadow: 0 5px 20px rgba(0,0,0,0.1); | |
| } | |
| .info-box { | |
| background: #e7f3ff; | |
| padding: 15px; | |
| border-radius: 8px; | |
| border-left: 4px solid #667eea; | |
| margin-top: 20px; | |
| } | |
| .info-box h6 { | |
| color: #667eea; | |
| font-weight: bold; | |
| } | |
| .algorithm-btn { | |
| margin: 5px 0; | |
| } | |
| .loading { | |
| display: none; | |
| text-align: center; | |
| padding: 40px; | |
| } | |
| .spinner-border { | |
| color: #667eea; | |
| } | |
| .point-indicator { | |
| padding: 10px; | |
| border-radius: 5px; | |
| margin: 5px 0; | |
| font-weight: bold; | |
| } | |
| .start-indicator { | |
| background: #d4edda; | |
| color: #155724; | |
| } | |
| .goal-indicator { | |
| background: #f8d7da; | |
| color: #721c24; | |
| } | |
| .github-link { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 8px; | |
| color: #667eea; | |
| text-decoration: none; | |
| font-weight: 600; | |
| font-size: 1.em; | |
| padding: 10px 20px; | |
| background: white; | |
| border: 2px solid #667eea; | |
| border-radius: 8px; | |
| transition: all 0.3s; | |
| margin: 10px 0; | |
| } | |
| .github-link:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); | |
| } | |
| .github-link:not([style*="background"]):hover { | |
| background: #667eea; | |
| color: white; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>🗺️ Path Finding Algorithms Visualizer</h1> | |
| <p class="text-muted">Click on the map to set start (green) and goal (red) points!</p> | |
| <div style="display: flex; gap: 10px; justify-content: center; flex-wrap: wrap;"> | |
| <a href="https://github.com/koesan/Path_Finding" target="_blank" class="github-link"> | |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> | |
| <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/> | |
| </svg> | |
| Path Finding | |
| </a> | |
| <a href="https://github.com/koesan/q-learning" target="_blank" class="github-link" style="background: #28a745; color: white; border-color: #28a745;"> | |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"> | |
| <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/> | |
| </svg> | |
| Q-Learning | |
| </a> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <div class="col-lg-4"> | |
| <div class="controls-panel"> | |
| <h5 class="mb-3">⚙️ Settings</h5> | |
| <!-- Algorithm Selection --> | |
| <div class="mb-3"> | |
| <label class="form-label fw-bold">🎯 Algorithm</label> | |
| <div class="btn-group-vertical w-100" role="group"> | |
| <input type="radio" class="btn-check" name="algorithm" id="astar" value="A*" checked> | |
| <label class="btn btn-outline-primary algorithm-btn" for="astar"> | |
| A* (Heuristic) | |
| </label> | |
| <input type="radio" class="btn-check" name="algorithm" id="dijkstra" value="Dijkstra"> | |
| <label class="btn btn-outline-warning algorithm-btn" for="dijkstra"> | |
| Dijkstra (Classic) | |
| </label> | |
| <input type="radio" class="btn-check" name="algorithm" id="bellman" value="Bellman-Ford"> | |
| <label class="btn btn-outline-danger algorithm-btn" for="bellman"> | |
| Bellman-Ford (Robust) | |
| </label> | |
| <input type="radio" class="btn-check" name="algorithm" id="qlearning" value="Q-Learning"> | |
| <label class="btn btn-outline-success algorithm-btn" for="qlearning"> | |
| Q-Learning (ML) | |
| </label> | |
| </div> | |
| </div> | |
| <!-- Selected Points --> | |
| <div class="mb-3"> | |
| <label class="form-label fw-bold">📍 Selected Points</label> | |
| <div id="startPoint" class="point-indicator start-indicator"> | |
| 🟢 Start: Click on map | |
| </div> | |
| <div id="goalPoint" class="point-indicator goal-indicator"> | |
| 🔴 Goal: Click on map | |
| </div> | |
| </div> | |
| <!-- Buttons --> | |
| <button class="btn btn-primary btn-find" onclick="findPath()" id="findBtn" disabled> | |
| 🔍 Find Path | |
| </button> | |
| <button class="btn btn-secondary btn-clear" onclick="clearPoints()"> | |
| 🗑️ Clear Points | |
| </button> | |
| <!-- Info Box --> | |
| <div class="info-box"> | |
| <h6>📝 How to Use</h6> | |
| <ul class="small mb-0"> | |
| <li><strong>First Click:</strong> Set Start (Green)</li> | |
| <li><strong>Second Click:</strong> Set Goal (Red)</li> | |
| <li><strong>Path:</strong> Blue line</li> | |
| <li><strong>Grid Size:</strong> 12x8 cells</li> | |
| <li><strong>Cost Range:</strong> 1-9</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="col-lg-8"> | |
| <div class="visualization-panel"> | |
| <h5 class="mb-3">🗺️ Map (Click to set points)</h5> | |
| <div id="loading" class="loading"> | |
| <div class="spinner-border" role="status"> | |
| <span class="visually-hidden">Loading...</span> | |
| </div> | |
| <p class="mt-3">Calculating path...</p> | |
| </div> | |
| <div id="canvasContainer" style="position: relative;"> | |
| <canvas id="mapCanvas"></canvas> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> | |
| <script> | |
| const COLS = 12; | |
| const ROWS = 8; | |
| let canvas, ctx; | |
| let startPoint = null; | |
| let goalPoint = null; | |
| let mapImage = null; | |
| let clickCount = 0; | |
| // Initialize canvas | |
| window.onload = function() { | |
| canvas = document.getElementById('mapCanvas'); | |
| ctx = canvas.getContext('2d'); | |
| // Load background image | |
| mapImage = new Image(); | |
| mapImage.crossOrigin = 'anonymous'; | |
| mapImage.onload = function() { | |
| canvas.width = mapImage.width; | |
| canvas.height = mapImage.height; | |
| drawCanvas(); | |
| }; | |
| mapImage.src = '/static/images/map.jpg'; | |
| // Add click listener | |
| canvas.addEventListener('click', handleCanvasClick); | |
| }; | |
| function handleCanvasClick(event) { | |
| const rect = canvas.getBoundingClientRect(); | |
| const scaleX = canvas.width / rect.width; | |
| const scaleY = canvas.height / rect.height; | |
| const clickX = (event.clientX - rect.left) * scaleX; | |
| const clickY = (event.clientY - rect.top) * scaleY; | |
| // Calculate grid cell | |
| const cellX = Math.floor(clickX / (canvas.width / COLS)); | |
| const cellY = Math.floor(clickY / (canvas.height / ROWS)); | |
| if (cellX < 0 || cellX >= COLS || cellY < 0 || cellY >= ROWS) return; | |
| if (clickCount === 0) { | |
| // First click - set start | |
| startPoint = { x: cellX, y: cellY }; | |
| document.getElementById('startPoint').textContent = `🟢 Start: (${cellX}, ${cellY})`; | |
| clickCount = 1; | |
| } else { | |
| // Second click - set goal | |
| goalPoint = { x: cellX, y: cellY }; | |
| document.getElementById('goalPoint').textContent = `🔴 Goal: (${cellX}, ${cellY})`; | |
| clickCount = 0; | |
| document.getElementById('findBtn').disabled = false; | |
| } | |
| drawCanvas(); | |
| } | |
| function drawCanvas() { | |
| if (!mapImage || !mapImage.complete) return; | |
| // Clear canvas | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Draw background image | |
| ctx.drawImage(mapImage, 0, 0, canvas.width, canvas.height); | |
| const cellWidth = canvas.width / COLS; | |
| const cellHeight = canvas.height / ROWS; | |
| // Draw start point (green) | |
| if (startPoint) { | |
| ctx.fillStyle = '#00FF00'; | |
| ctx.beginPath(); | |
| ctx.arc( | |
| (startPoint.x + 0.5) * cellWidth, | |
| (startPoint.y + 0.5) * cellHeight, | |
| cellWidth * 0.4, | |
| 0, 2 * Math.PI | |
| ); | |
| ctx.fill(); | |
| ctx.strokeStyle = 'white'; | |
| ctx.lineWidth = 3; | |
| ctx.stroke(); | |
| // Draw 'S' | |
| ctx.fillStyle = 'white'; | |
| ctx.font = 'bold ' + (cellWidth * 0.6) + 'px Arial'; | |
| ctx.textAlign = 'center'; | |
| ctx.textBaseline = 'middle'; | |
| ctx.fillText('S', (startPoint.x + 0.5) * cellWidth, (startPoint.y + 0.5) * cellHeight); | |
| } | |
| // Draw goal point (red) | |
| if (goalPoint) { | |
| ctx.fillStyle = '#FF0000'; | |
| ctx.beginPath(); | |
| ctx.arc( | |
| (goalPoint.x + 0.5) * cellWidth, | |
| (goalPoint.y + 0.5) * cellHeight, | |
| cellWidth * 0.4, | |
| 0, 2 * Math.PI | |
| ); | |
| ctx.fill(); | |
| ctx.strokeStyle = 'white'; | |
| ctx.lineWidth = 3; | |
| ctx.stroke(); | |
| // Draw 'G' | |
| ctx.fillStyle = 'white'; | |
| ctx.font = 'bold ' + (cellWidth * 0.6) + 'px Arial'; | |
| ctx.textAlign = 'center'; | |
| ctx.textBaseline = 'middle'; | |
| ctx.fillText('G', (goalPoint.x + 0.5) * cellWidth, (goalPoint.y + 0.5) * cellHeight); | |
| } | |
| } | |
| function clearPoints() { | |
| startPoint = null; | |
| goalPoint = null; | |
| clickCount = 0; | |
| document.getElementById('startPoint').textContent = '🟢 Start: Click on map'; | |
| document.getElementById('goalPoint').textContent = '🔴 Goal: Click on map'; | |
| document.getElementById('findBtn').disabled = true; | |
| drawCanvas(); | |
| } | |
| async function findPath() { | |
| if (!startPoint || !goalPoint) { | |
| alert('Please select both start and goal points!'); | |
| return; | |
| } | |
| const algorithm = document.querySelector('input[name="algorithm"]:checked').value; | |
| // Show loading | |
| document.getElementById('loading').style.display = 'block'; | |
| document.getElementById('canvasContainer').style.display = 'none'; | |
| try { | |
| const response = await fetch('/find_path', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| start_x: startPoint.x, | |
| start_y: startPoint.y, | |
| goal_x: goalPoint.x, | |
| goal_y: goalPoint.y, | |
| algorithm: algorithm | |
| }) | |
| }); | |
| const data = await response.json(); | |
| // Hide loading | |
| document.getElementById('loading').style.display = 'none'; | |
| document.getElementById('canvasContainer').style.display = 'block'; | |
| if (data.error) { | |
| alert('Error: ' + data.error); | |
| } else { | |
| // Load result image | |
| const resultImg = new Image(); | |
| resultImg.onload = function() { | |
| canvas.width = resultImg.width; | |
| canvas.height = resultImg.height; | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| ctx.drawImage(resultImg, 0, 0); | |
| }; | |
| resultImg.src = data.image; | |
| } | |
| } catch (error) { | |
| console.error('Error:', error); | |
| alert('An error occurred: ' + error.message); | |
| document.getElementById('loading').style.display = 'none'; | |
| document.getElementById('canvasContainer').style.display = 'block'; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |