// Puzzle UI system — level selection, puzzle mode, verification import { state } from './state.js'; import { LEVELS, getLevel, isLevelUnlocked, isLevelCompleted, verifyLevel, completeLevel } from './levels.js'; import { evaluateAll } from './gates.js'; export let puzzleMode = false; export let currentLevel = null; /** * Initialize puzzle UI */ export function initPuzzleUI() { createLevelSelectionPanel(); createPuzzlePanel(); initModeToggle(); } /** * Create level selection panel */ function createLevelSelectionPanel() { const panel = document.createElement('div'); panel.id = 'level-panel'; panel.className = 'puzzle-panel'; panel.innerHTML = `

Levels

`; document.body.appendChild(panel); document.getElementById('close-levels').addEventListener('click', () => { panel.classList.remove('visible'); document.body.classList.remove('puzzle-sidebar-open'); window.dispatchEvent(new Event('resize')); }); } /** * Refresh level cards to reflect current progress */ function refreshLevelCards() { const container = document.getElementById('levels-container'); container.innerHTML = ''; const categories = new Map(); LEVELS.forEach(level => { if (!categories.has(level.category)) { categories.set(level.category, []); } categories.get(level.category).push(level); }); categories.forEach((levels, category) => { const categoryEl = document.createElement('div'); categoryEl.className = 'level-category'; categoryEl.innerHTML = `

${category}

`; levels.forEach(level => { const unlocked = isLevelUnlocked(level.id); const completed = isLevelCompleted(level.id); const card = document.createElement('div'); card.className = `level-card ${completed ? 'completed' : ''} ${!unlocked ? 'locked' : ''}`; card.innerHTML = `
${level.title}
${'●'.repeat(level.difficulty)}${'○'.repeat(5 - level.difficulty)}
${level.description}
${completed ? '✓ COMPLETED' : unlocked ? 'READY' : '🔒 LOCKED'}
`; if (unlocked) { card.addEventListener('click', () => selectLevel(level.id)); } categoryEl.appendChild(card); }); container.appendChild(categoryEl); }); } /** * Create puzzle info panel (shown when level is selected) */ function createPuzzlePanel() { const panel = document.createElement('div'); panel.id = 'puzzle-panel'; panel.className = 'puzzle-panel puzzle-info'; panel.innerHTML = `

Level Title

Level description

Truth Table

Available Gates

`; document.body.appendChild(panel); document.getElementById('close-puzzle').addEventListener('click', () => { exitPuzzleMode(); }); document.getElementById('verify-btn').addEventListener('click', () => { verifySolution(); }); document.getElementById('hint-btn').addEventListener('click', () => { showHint(); }); document.getElementById('reset-puzzle-btn').addEventListener('click', () => { resetPuzzle(); }); } /** * Initialize mode toggle in toolbar */ function initModeToggle() { const toolbar = document.getElementById('toolbar'); const separator = document.createElement('div'); separator.className = 'separator'; const modeToggle = document.createElement('div'); modeToggle.id = 'mode-toggle'; modeToggle.className = 'mode-toggle'; modeToggle.innerHTML = ` `; toolbar.appendChild(separator); toolbar.appendChild(modeToggle); document.getElementById('mode-sandbox').addEventListener('click', () => { setSandboxMode(); }); document.getElementById('mode-puzzle').addEventListener('click', () => { setPuzzleMode(); }); } /** * Set sandbox mode */ export function setSandboxMode() { puzzleMode = false; currentLevel = null; document.getElementById('mode-sandbox').classList.add('active'); document.getElementById('mode-puzzle').classList.remove('active'); document.getElementById('level-panel').classList.remove('visible'); document.getElementById('puzzle-panel').classList.remove('visible'); document.getElementById('toolbar').classList.remove('puzzle-mode'); document.body.classList.remove('puzzle-sidebar-open'); window.dispatchEvent(new Event('resize')); } /** * Set puzzle mode */ export function setPuzzleMode() { puzzleMode = true; document.getElementById('mode-puzzle').classList.add('active'); document.getElementById('mode-sandbox').classList.remove('active'); refreshLevelCards(); document.getElementById('level-panel').classList.add('visible'); document.body.classList.add('puzzle-sidebar-open'); window.dispatchEvent(new Event('resize')); } /** * Select a level */ function selectLevel(levelId) { const level = getLevel(levelId); if (!level || !isLevelUnlocked(levelId)) return; currentLevel = level; // Update puzzle panel document.getElementById('puzzle-title').textContent = level.title; document.getElementById('puzzle-description').textContent = level.description; // Render truth table renderTruthTable(level); // Show available gates renderAvailableGates(level); // Clear verification result document.getElementById('verification-result').innerHTML = ''; // Hide level panel, show puzzle panel document.getElementById('level-panel').classList.remove('visible'); document.getElementById('puzzle-panel').classList.add('visible'); // Clear circuit and setup for puzzle clearCircuitForPuzzle(level); } /** * Render truth table */ function renderTruthTable(level) { const table = document.getElementById('truth-table'); table.innerHTML = ''; // Get input/output counts const testCase = level.testCases[0]; const inputCount = Object.keys(testCase.inputs).length; const outputCount = Object.keys(testCase.outputs).length; // Create header const header = document.createElement('div'); header.className = 'truth-table-row header'; for (let i = 0; i < inputCount; i++) { const cell = document.createElement('div'); cell.className = 'truth-cell input-cell'; cell.textContent = `A${i}`; header.appendChild(cell); } for (let i = 0; i < outputCount; i++) { const cell = document.createElement('div'); cell.className = 'truth-cell output-cell'; cell.textContent = `Q${i}`; header.appendChild(cell); } table.appendChild(header); // Create rows level.testCases.forEach((tc, idx) => { const row = document.createElement('div'); row.className = 'truth-table-row'; for (let i = 0; i < inputCount; i++) { const cell = document.createElement('div'); cell.className = 'truth-cell input-cell'; cell.textContent = tc.inputs[i]; row.appendChild(cell); } for (let i = 0; i < outputCount; i++) { const cell = document.createElement('div'); cell.className = 'truth-cell output-cell'; cell.textContent = tc.outputs[i]; row.appendChild(cell); } table.appendChild(row); }); } /** * Render available gates */ function renderAvailableGates(level) { const container = document.getElementById('available-gates'); container.innerHTML = ''; level.availableGates.forEach(gateName => { const btn = document.createElement('button'); btn.className = 'available-gate-btn'; btn.textContent = gateName; btn.addEventListener('click', () => { state.placingGate = gateName; }); container.appendChild(btn); }); } /** * Clear circuit for puzzle mode */ function clearCircuitForPuzzle(level) { state.gates = []; state.connections = []; state.placingGate = null; state.connecting = null; evaluateAll(); } /** * Verify solution */ function verifySolution() { if (!currentLevel) return; const result = verifyLevel(currentLevel.id); const resultEl = document.getElementById('verification-result'); if (result.passed) { resultEl.className = 'verification-result success'; resultEl.innerHTML = `
✓ ${result.message}
`; completeLevel(currentLevel.id); document.getElementById('next-level-btn').addEventListener('click', () => { const currentIdx = LEVELS.findIndex(l => l.id === currentLevel.id); if (currentIdx < LEVELS.length - 1) { const nextLevel = LEVELS[currentIdx + 1]; selectLevel(nextLevel.id); } else { resultEl.innerHTML = '
🎉 All levels completed!
'; } }); } else { resultEl.className = 'verification-result failure'; resultEl.innerHTML = `
✗ ${result.message}
`; // Show which tests failed const failedTests = result.results.filter(r => !r.passed); if (failedTests.length > 0 && failedTests.length <= 3) { resultEl.innerHTML += '
'; failedTests.forEach(test => { resultEl.innerHTML += `
Input: ${JSON.stringify(test.inputs)} → Expected: ${JSON.stringify(test.expectedOutputs)}, Got: ${JSON.stringify(test.actualOutputs)}
`; }); resultEl.innerHTML += '
'; } } } /** * Show hint */ function showHint() { if (!currentLevel || !currentLevel.hints) return; const hints = currentLevel.hints; let hintIndex = parseInt(sessionStorage.getItem(`hint_${currentLevel.id}`) || '0'); if (hintIndex >= hints.length) { alert('No more hints available!'); return; } const hint = hints[hintIndex]; alert(`Hint: ${hint}`); sessionStorage.setItem(`hint_${currentLevel.id}`, String(hintIndex + 1)); } /** * Reset puzzle */ function resetPuzzle() { if (!currentLevel) return; clearCircuitForPuzzle(currentLevel); document.getElementById('verification-result').innerHTML = ''; } /** * Exit puzzle mode */ export function exitPuzzleMode() { setSandboxMode(); } /** * Show level panel */ export function showLevelPanel() { document.getElementById('level-panel').classList.add('visible'); }