koesan's picture
Update templates/index.html
a9927b3 verified
<!DOCTYPE html>
<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>