feat: add Turing Complete-style puzzle system
Add progressive puzzle mode alongside the existing sandbox: - 8 levels from basic gates to 2-bit adder - Truth table verification with pass/fail feedback - Gate restrictions per level - Custom components system (save circuits as reusable chips) - Save/load circuits as JSON - Level selection sidebar with difficulty ratings - Mode toggle: Sandbox (free play) vs Puzzle (guided levels) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
43
js/events.js
43
js/events.js
@@ -5,6 +5,9 @@ import { evaluateAll, findGateAt, findPortAt } from './gates.js';
|
||||
import { manualStep, clearWaveData } from './waveform.js';
|
||||
import { startSim, stopSim, adjustSpeed } from './simulation.js';
|
||||
import { resize, screenToWorld } from './renderer.js';
|
||||
import { puzzleMode, currentLevel, showLevelPanel } from './puzzleUI.js';
|
||||
import { getLevel } from './levels.js';
|
||||
import { saveState, loadState, exportAsFile, importFromFile } from './saveLoad.js';
|
||||
|
||||
const PAN_SPEED = 40;
|
||||
|
||||
@@ -176,7 +179,18 @@ export function initEvents() {
|
||||
|
||||
// ==================== TOOLBAR ====================
|
||||
document.querySelectorAll('.gate-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => { state.placingGate = btn.dataset.gate; });
|
||||
btn.addEventListener('click', () => {
|
||||
const gateName = btn.dataset.gate;
|
||||
// In puzzle mode, check if gate is allowed
|
||||
if (puzzleMode && currentLevel) {
|
||||
const level = getLevel(currentLevel.id);
|
||||
if (!level.availableGates.includes(gateName)) {
|
||||
alert(`${gateName} is not available in this level.`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
state.placingGate = gateName;
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('clear-btn').addEventListener('click', () => {
|
||||
@@ -189,6 +203,33 @@ export function initEvents() {
|
||||
}
|
||||
});
|
||||
|
||||
// Export/Import
|
||||
document.getElementById('export-btn').addEventListener('click', () => {
|
||||
exportAsFile(`circuit-${new Date().toISOString().slice(0, 10)}.json`);
|
||||
});
|
||||
|
||||
document.getElementById('import-btn').addEventListener('click', () => {
|
||||
document.getElementById('import-file').click();
|
||||
});
|
||||
|
||||
document.getElementById('import-file').addEventListener('change', async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
try {
|
||||
const result = await importFromFile(file);
|
||||
if (result.success) {
|
||||
alert('Circuit imported successfully!');
|
||||
evaluateAll();
|
||||
} else {
|
||||
alert(`Import failed: ${result.error}`);
|
||||
}
|
||||
} catch (err) {
|
||||
alert(`Import error: ${err.message}`);
|
||||
}
|
||||
// Reset file input
|
||||
e.target.value = '';
|
||||
});
|
||||
|
||||
// Help modal
|
||||
document.getElementById('help-btn').addEventListener('click', () => {
|
||||
document.getElementById('help-modal').classList.add('visible');
|
||||
|
||||
Reference in New Issue
Block a user