| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>N-Queens Problem Visualizer</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet"> |
| <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <style> |
| .chess-board { |
| display: grid; |
| aspect-ratio: 1/1; |
| border: 2px solid #333; |
| } |
| |
| .chess-cell { |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| position: relative; |
| } |
| |
| .queen { |
| color: #dc2626; |
| font-size: 80%; |
| } |
| |
| .conflict { |
| background-color: rgba(239, 68, 68, 0.3) !important; |
| } |
| |
| .solution-node { |
| transition: all 0.3s ease; |
| cursor: pointer; |
| } |
| |
| .solution-node:hover { |
| transform: scale(1.05); |
| } |
| |
| .tree-container { |
| max-height: 60vh; |
| overflow-y: auto; |
| } |
| |
| .active-solution { |
| border: 2px solid #3b82f6; |
| box-shadow: 0 0 10px rgba(59, 130, 246, 0.5); |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50 min-h-screen"> |
| <div class="container mx-auto px-4 py-8"> |
| <header class="text-center mb-8" data-aos="fade-down"> |
| <h1 class="text-3xl md:text-4xl font-bold text-gray-800 mb-2">N-Queens Problem Visualizer</h1> |
| <p class="text-gray-600">Place N queens on an N×N chessboard so that no two queens threaten each other</p> |
| </header> |
|
|
| <div class="grid grid-cols-1 lg:grid-cols-2 gap-8"> |
| |
| <div class="space-y-6"> |
| <div class="bg-white rounded-lg shadow-md p-6" data-aos="fade-right"> |
| <h2 class="text-xl font-semibold mb-4 flex items-center"> |
| <i data-feather="sliders" class="mr-2"></i> Configuration |
| </h2> |
| |
| <div class="mb-4"> |
| <label class="block text-sm font-medium text-gray-700 mb-2">Board Size (N)</label> |
| <div class="flex items-center space-x-4"> |
| <input type="range" min="1" max="8" value="4" class="w-full" id="boardSizeSlider"> |
| <span class="text-lg font-medium w-8 text-center" id="boardSizeValue">4</span> |
| </div> |
| </div> |
| |
| <div class="flex space-x-3"> |
| <button id="solveBtn" class="flex-1 bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-md flex items-center justify-center"> |
| <i data-feather="play" class="mr-2"></i> Solve |
| </button> |
| <button id="resetBtn" class="flex-1 bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-md flex items-center justify-center"> |
| <i data-feather="refresh-cw" class="mr-2"></i> Reset |
| </button> |
| </div> |
| </div> |
| |
| <div class="bg-white rounded-lg shadow-md p-6" data-aos="fade-right" data-aos-delay="100"> |
| <h2 class="text-xl font-semibold mb-4 flex items-center"> |
| <i data-feather="grid" class="mr-2"></i> Chessboard |
| </h2> |
| |
| <div class="flex justify-center"> |
| <div id="chessboardContainer" class="w-full max-w-md"></div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="space-y-6"> |
| <div class="bg-white rounded-lg shadow-md p-6" data-aos="fade-left"> |
| <h2 class="text-xl font-semibold mb-4 flex items-center"> |
| <i data-feather="list" class="mr-2"></i> Solutions |
| <span id="solutionsCount" class="ml-2 bg-blue-100 text-blue-800 text-xs font-medium px-2.5 py-0.5 rounded">0 found</span> |
| </h2> |
| |
| <div id="solutionsContainer" class="grid grid-cols-2 sm:grid-cols-3 gap-3 max-h-60 overflow-y-auto"></div> |
| </div> |
| |
| <div class="bg-white rounded-lg shadow-md p-6" data-aos="fade-left" data-aos-delay="100"> |
| <h2 class="text-xl font-semibold mb-4 flex items-center"> |
| <i data-feather="git-branch" class="mr-2"></i> Search Space Tree |
| </h2> |
| |
| <div id="treeContainer" class="tree-container bg-gray-100 p-4 rounded-md"> |
| <div id="treeVisualization" class="relative"></div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| AOS.init(); |
| feather.replace(); |
| |
| |
| const boardSizeSlider = document.getElementById('boardSizeSlider'); |
| const boardSizeValue = document.getElementById('boardSizeValue'); |
| const chessboardContainer = document.getElementById('chessboardContainer'); |
| const solutionsContainer = document.getElementById('solutionsContainer'); |
| const solutionsCount = document.getElementById('solutionsCount'); |
| const treeVisualization = document.getElementById('treeVisualization'); |
| const solveBtn = document.getElementById('solveBtn'); |
| const resetBtn = document.getElementById('resetBtn'); |
| |
| let currentSize = 4; |
| let solutions = []; |
| let currentSolutionIndex = -1; |
| |
| |
| createChessboard(currentSize); |
| |
| |
| boardSizeSlider.addEventListener('input', function() { |
| currentSize = parseInt(this.value); |
| boardSizeValue.textContent = currentSize; |
| createChessboard(currentSize); |
| clearSolutions(); |
| }); |
| |
| solveBtn.addEventListener('click', function() { |
| solveNQueens(currentSize); |
| }); |
| |
| resetBtn.addEventListener('click', function() { |
| createChessboard(currentSize); |
| clearSolutions(); |
| }); |
| |
| function createChessboard(size) { |
| chessboardContainer.innerHTML = ''; |
| const board = document.createElement('div'); |
| board.className = 'chess-board'; |
| board.style.gridTemplateColumns = `repeat(${size}, 1fr)`; |
| board.style.gridTemplateRows = `repeat(${size}, 1fr)`; |
| |
| for (let row = 0; row < size; row++) { |
| for (let col = 0; col < size; col++) { |
| const cell = document.createElement('div'); |
| cell.className = `chess-cell ${(row + col) % 2 === 0 ? 'bg-gray-100' : 'bg-gray-300'}`; |
| cell.dataset.row = row; |
| cell.dataset.col = col; |
| board.appendChild(cell); |
| } |
| } |
| |
| chessboardContainer.appendChild(board); |
| } |
| |
| function solveNQueens(n) { |
| solutions = []; |
| currentSolutionIndex = -1; |
| clearSolutions(); |
| |
| |
| const board = Array(n).fill().map(() => Array(n).fill(0)); |
| solveNQueensUtil(board, 0, n); |
| |
| |
| solutionsCount.textContent = `${solutions.length} found`; |
| |
| |
| if (solutions.length > 0) { |
| displaySolutions(); |
| displaySearchTree(n); |
| showSolution(0); |
| } else { |
| solutionsContainer.innerHTML = '<p class="text-gray-500 text-center col-span-full">No solutions found</p>'; |
| treeVisualization.innerHTML = '<p class="text-gray-500 text-center">No search tree to display</p>'; |
| } |
| } |
| |
| function solveNQueensUtil(board, col, n) { |
| if (col >= n) { |
| |
| const solution = []; |
| for (let i = 0; i < n; i++) { |
| for (let j = 0; j < n; j++) { |
| if (board[i][j] === 1) { |
| solution.push(j); |
| } |
| } |
| } |
| solutions.push(solution); |
| return; |
| } |
| |
| for (let i = 0; i < n; i++) { |
| if (isSafe(board, i, col, n)) { |
| board[i][col] = 1; |
| solveNQueensUtil(board, col + 1, n); |
| board[i][col] = 0; |
| } |
| } |
| } |
| |
| function isSafe(board, row, col, n) { |
| |
| for (let i = 0; i < col; i++) { |
| if (board[row][i] === 1) return false; |
| } |
| |
| |
| for (let i = row, j = col; i >= 0 && j >= 0; i--, j--) { |
| if (board[i][j] === 1) return false; |
| } |
| |
| |
| for (let i = row, j = col; i < n && j >= 0; i++, j--) { |
| if (board[i][j] === 1) return false; |
| } |
| |
| return true; |
| } |
| |
| function displaySolutions() { |
| solutionsContainer.innerHTML = ''; |
| |
| solutions.forEach((solution, index) => { |
| const solutionElement = document.createElement('div'); |
| solutionElement.className = 'solution-node bg-white border rounded-md p-2 text-center cursor-pointer'; |
| solutionElement.innerHTML = ` |
| <div class="text-sm font-medium">Solution ${index + 1}</div> |
| <div class="text-xs text-gray-500">[${solution.join(', ')}]</div> |
| `; |
| |
| solutionElement.addEventListener('click', () => { |
| showSolution(index); |
| }); |
| |
| solutionsContainer.appendChild(solutionElement); |
| }); |
| } |
| |
| function showSolution(index) { |
| |
| document.querySelectorAll('.solution-node').forEach(node => { |
| node.classList.remove('active-solution', 'bg-blue-50'); |
| }); |
| |
| |
| document.querySelectorAll('.solution-node')[index].classList.add('active-solution', 'bg-blue-50'); |
| |
| currentSolutionIndex = index; |
| renderBoardWithSolution(solutions[index]); |
| } |
| |
| function renderBoardWithSolution(solution) { |
| const cells = document.querySelectorAll('.chess-cell'); |
| const n = solution.length; |
| |
| |
| cells.forEach(cell => { |
| cell.innerHTML = ''; |
| cell.classList.remove('conflict'); |
| }); |
| |
| |
| for (let col = 0; col < n; col++) { |
| const row = solution[col]; |
| const cellIndex = row * n + col; |
| if (cells[cellIndex]) { |
| cells[cellIndex].innerHTML = '<i data-feather="circle" class="queen"></i>'; |
| } |
| } |
| |
| feather.replace(); |
| } |
| |
| function displaySearchTree(n) { |
| treeVisualization.innerHTML = ''; |
| |
| |
| const treeContainer = document.createElement('div'); |
| treeContainer.className = 'space-y-4'; |
| |
| |
| const title = document.createElement('div'); |
| title.className = 'text-sm font-medium text-gray-700'; |
| title.textContent = `Search space for ${n}-queens problem`; |
| treeContainer.appendChild(title); |
| |
| |
| const info = document.createElement('div'); |
| info.className = 'text-xs text-gray-500'; |
| info.innerHTML = ` |
| <p>• The algorithm uses backtracking to explore possible queen placements</p> |
| <p>• Each level represents a column where a queen is being placed</p> |
| <p>• Valid placements continue to the next level (column)</p> |
| <p>• Invalid placements cause backtracking to previous levels</p> |
| `; |
| treeContainer.appendChild(info); |
| |
| |
| const visual = document.createElement('div'); |
| visual.className = 'bg-blue-50 p-3 rounded-md mt-2'; |
| |
| const visualContent = document.createElement('div'); |
| visualContent.className = 'text-xs'; |
| |
| if (n <= 4) { |
| visualContent.innerHTML = ` |
| <p class="font-medium mb-1">Search Process:</p> |
| <p>1. Start with column 0, try all rows</p> |
| <p>2. For each valid placement, move to next column</p> |
| <p>3. When a solution is found, record it and backtrack</p> |
| <p>4. Continue until all possibilities are explored</p> |
| `; |
| } else { |
| visualContent.innerHTML = ` |
| <p class="font-medium mb-1">Search Process (simplified):</p> |
| <p>The search tree for ${n}-queens is too large to display in detail.</p> |
| <p>The algorithm explored ${Math.pow(n, n)} possible placements</p> |
| <p>but used constraints to prune invalid branches early.</p> |
| `; |
| } |
| |
| visual.appendChild(visualContent); |
| treeContainer.appendChild(visual); |
| |
| treeVisualization.appendChild(treeContainer); |
| } |
| |
| function clearSolutions() { |
| solutions = []; |
| currentSolutionIndex = -1; |
| solutionsContainer.innerHTML = ''; |
| solutionsCount.textContent = '0 found'; |
| treeVisualization.innerHTML = '<p class="text-gray-500 text-center">Solve to see search tree</p>'; |
| |
| |
| const cells = document.querySelectorAll('.chess-cell'); |
| cells.forEach(cell => { |
| cell.innerHTML = ''; |
| cell.classList.remove('conflict'); |
| }); |
| } |
| }); |
| </script> |
| </body> |
| </html> |
|
|