Files
logic-gates/js/saveLoad.js
Jose Luis b2e367817c 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>
2026-03-20 02:06:57 +01:00

150 lines
4.2 KiB
JavaScript

// Save/Load system — export and import circuits and progress
import { state } from './state.js';
import { progress } from './levels.js';
/**
* Save complete application state to JSON
*/
export function saveState() {
return {
timestamp: new Date().toISOString(),
circuit: {
gates: state.gates,
connections: state.connections,
nextId: state.nextId
},
camera: {
camX: state.camX,
camY: state.camY,
zoom: state.zoom
},
components: state.customComponents || {},
progress: {
unlockedLevels: progress.unlockedLevels,
completedLevels: progress.completedLevels,
currentLevel: progress.currentLevel,
customComponents: progress.customComponents
}
};
}
/**
* Load application state from JSON
*/
export function loadState(data) {
if (!data || !data.circuit) {
return { success: false, error: 'Invalid save data' };
}
try {
// Load circuit
state.gates = JSON.parse(JSON.stringify(data.circuit.gates));
state.connections = JSON.parse(JSON.stringify(data.circuit.connections));
state.nextId = data.circuit.nextId;
// Load camera
if (data.camera) {
state.camX = data.camera.camX;
state.camY = data.camera.camY;
state.zoom = data.camera.zoom;
}
// Load components
if (data.components) {
state.customComponents = JSON.parse(JSON.stringify(data.components));
}
// Load progress
if (data.progress) {
progress.unlockedLevels = data.progress.unlockedLevels || ['buffer'];
progress.completedLevels = data.progress.completedLevels || [];
progress.currentLevel = data.progress.currentLevel || null;
progress.customComponents = data.progress.customComponents || {};
}
return { success: true };
} catch (e) {
return { success: false, error: e.message };
}
}
/**
* Export as JSON file (for download)
*/
export function exportAsFile(filename = 'logic-circuit.json') {
const data = saveState();
const json = JSON.stringify(data, null, 2);
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
/**
* Import from file (returns Promise)
*/
export function importFromFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
try {
const data = JSON.parse(e.target.result);
const result = loadState(data);
resolve(result);
} catch (err) {
reject(new Error('Failed to parse JSON'));
}
};
reader.onerror = () => reject(new Error('Failed to read file'));
reader.readAsText(file);
});
}
/**
* Export circuit as a simple netlist (text format)
*/
export function exportAsNetlist() {
let netlist = '# Logic Circuit Netlist\n\n';
netlist += '## Gates\n';
state.gates.forEach(gate => {
netlist += `${gate.type} ${gate.id} at (${gate.x.toFixed(0)}, ${gate.y.toFixed(0)})\n`;
});
netlist += '\n## Connections\n';
state.connections.forEach(conn => {
netlist += `${conn.from}:${conn.fromPort} -> ${conn.to}:${conn.toPort}\n`;
});
return netlist;
}
/**
* Copy state to clipboard as JSON (useful for sharing)
*/
export function copyToClipboard() {
const data = saveState();
const json = JSON.stringify(data);
navigator.clipboard.writeText(json).then(() => {
alert('State copied to clipboard');
}).catch(() => {
alert('Failed to copy to clipboard');
});
}
/**
* Paste state from clipboard
*/
export async function pasteFromClipboard() {
try {
const text = await navigator.clipboard.readText();
const data = JSON.parse(text);
return loadState(data);
} catch (e) {
return { success: false, error: e.message };
}
}