Compare commits
2 Commits
920a30ffa8
...
3bff1fd4b4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bff1fd4b4 | ||
|
|
3f1daa77bd |
@@ -1,5 +1,34 @@
|
|||||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
|
||||||
|
/* ==================== Custom Scrollbar ==================== */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #2a2a3a;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #00e59966;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-corner {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Firefox */
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #2a2a3a transparent;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Segoe UI', system-ui, sans-serif;
|
font-family: 'Segoe UI', system-ui, sans-serif;
|
||||||
background: #0a0a0f;
|
background: #0a0a0f;
|
||||||
|
|||||||
37
js/events.js
37
js/events.js
@@ -30,10 +30,20 @@ export function initEvents() {
|
|||||||
state.hoveredGate = state.hoveredPort ? state.hoveredPort.gate : findGateAt(world.x, world.y);
|
state.hoveredGate = state.hoveredPort ? state.hoveredPort.gate : findGateAt(world.x, world.y);
|
||||||
|
|
||||||
if (state.dragging) {
|
if (state.dragging) {
|
||||||
|
// Detect if mouse actually moved (threshold of 4px to distinguish click vs drag)
|
||||||
|
if (dragStartPos && !dragMoved) {
|
||||||
|
const dx = e.offsetX - dragStartPos.x;
|
||||||
|
const dy = e.offsetY - dragStartPos.y;
|
||||||
|
if (Math.abs(dx) > 4 || Math.abs(dy) > 4) {
|
||||||
|
dragMoved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dragMoved) {
|
||||||
state.dragging.x = world.x - state.dragOffset.x;
|
state.dragging.x = world.x - state.dragOffset.x;
|
||||||
state.dragging.y = world.y - state.dragOffset.y;
|
state.dragging.y = world.y - state.dragOffset.y;
|
||||||
evaluateAll();
|
evaluateAll();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
canvas.style.cursor = state.placingGate ? 'crosshair'
|
canvas.style.cursor = state.placingGate ? 'crosshair'
|
||||||
: state.hoveredPort ? 'pointer'
|
: state.hoveredPort ? 'pointer'
|
||||||
@@ -41,9 +51,14 @@ export function initEvents() {
|
|||||||
: 'default';
|
: 'default';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let dragStartPos = null;
|
||||||
|
let dragMoved = false;
|
||||||
|
|
||||||
canvas.addEventListener('mousedown', e => {
|
canvas.addEventListener('mousedown', e => {
|
||||||
if (e.button !== 0) return;
|
if (e.button !== 0) return;
|
||||||
const world = screenToWorld(e.offsetX, e.offsetY);
|
const world = screenToWorld(e.offsetX, e.offsetY);
|
||||||
|
dragStartPos = { x: e.offsetX, y: e.offsetY };
|
||||||
|
dragMoved = false;
|
||||||
|
|
||||||
// Placing a new gate
|
// Placing a new gate
|
||||||
if (state.placingGate) {
|
if (state.placingGate) {
|
||||||
@@ -81,15 +96,8 @@ export function initEvents() {
|
|||||||
|
|
||||||
if (state.connecting) { state.connecting = null; return; }
|
if (state.connecting) { state.connecting = null; return; }
|
||||||
|
|
||||||
// Toggle INPUT/CLOCK
|
// Drag any gate (including INPUT/CLOCK)
|
||||||
const gate = findGateAt(world.x, world.y);
|
const gate = findGateAt(world.x, world.y);
|
||||||
if (gate && (gate.type === 'INPUT' || gate.type === 'CLOCK')) {
|
|
||||||
gate.value = gate.value ? 0 : 1;
|
|
||||||
evaluateAll();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drag gate
|
|
||||||
if (gate) {
|
if (gate) {
|
||||||
state.dragging = gate;
|
state.dragging = gate;
|
||||||
state.dragOffset = { x: world.x - gate.x, y: world.y - gate.y };
|
state.dragOffset = { x: world.x - gate.x, y: world.y - gate.y };
|
||||||
@@ -97,7 +105,18 @@ export function initEvents() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
canvas.addEventListener('mouseup', () => { state.dragging = null; });
|
canvas.addEventListener('mouseup', e => {
|
||||||
|
// Toggle INPUT/CLOCK only on click (no drag movement)
|
||||||
|
if (state.dragging && !dragMoved) {
|
||||||
|
const gate = state.dragging;
|
||||||
|
if (gate.type === 'INPUT' || gate.type === 'CLOCK') {
|
||||||
|
gate.value = gate.value ? 0 : 1;
|
||||||
|
evaluateAll(true); // record waveform on intentional toggle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.dragging = null;
|
||||||
|
dragStartPos = null;
|
||||||
|
});
|
||||||
|
|
||||||
canvas.addEventListener('contextmenu', e => {
|
canvas.addEventListener('contextmenu', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -53,12 +53,12 @@ export function evaluate(gate, visited = new Set()) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function evaluateAll() {
|
export function evaluateAll(recordWave = false) {
|
||||||
state.gates.forEach(g => {
|
state.gates.forEach(g => {
|
||||||
if (g.type !== 'INPUT' && g.type !== 'CLOCK') g.value = 0;
|
if (g.type !== 'INPUT' && g.type !== 'CLOCK') g.value = 0;
|
||||||
});
|
});
|
||||||
state.gates.forEach(g => evaluate(g));
|
state.gates.forEach(g => evaluate(g));
|
||||||
if (state.recording && state.waveformVisible) recordSample();
|
if (recordWave && state.recording && state.waveformVisible) recordSample();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register evaluateAll in waveform to break circular dependency
|
// Register evaluateAll in waveform to break circular dependency
|
||||||
|
|||||||
240
js/levels.js
240
js/levels.js
@@ -75,6 +75,111 @@ export const LEVELS = [
|
|||||||
hints: ['XOR = (A NAND B) NAND (NOT(A NAND NOT B) NAND NOT(NOT A NAND B))', 'This requires 4-5 NAND gates.'],
|
hints: ['XOR = (A NAND B) NAND (NOT(A NAND NOT B) NAND NOT(NOT A NAND B))', 'This requires 4-5 NAND gates.'],
|
||||||
difficulty: 3
|
difficulty: 3
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'xnor_gate',
|
||||||
|
category: 'Logic Basics',
|
||||||
|
title: 'XNOR Gate',
|
||||||
|
description: 'Build an XNOR gate. True when both inputs are equal.',
|
||||||
|
availableGates: ['INPUT', 'OUTPUT', 'NAND'],
|
||||||
|
testCases: [
|
||||||
|
{ inputs: { 0: 0, 1: 0 }, outputs: { 0: 1 } },
|
||||||
|
{ inputs: { 0: 0, 1: 1 }, outputs: { 0: 0 } },
|
||||||
|
{ inputs: { 0: 1, 1: 0 }, outputs: { 0: 0 } },
|
||||||
|
{ inputs: { 0: 1, 1: 1 }, outputs: { 0: 1 } }
|
||||||
|
],
|
||||||
|
hints: ['XNOR is the inverse of XOR.', 'Build XOR first, then invert the output with another NAND-as-NOT.'],
|
||||||
|
difficulty: 3
|
||||||
|
},
|
||||||
|
// ============ Combinational Logic ============
|
||||||
|
{
|
||||||
|
id: 'mux_2to1',
|
||||||
|
category: 'Combinational Logic',
|
||||||
|
title: '2:1 Multiplexer',
|
||||||
|
description: 'Build a MUX. When SEL=0, output A. When SEL=1, output B. Three inputs: A (0), B (1), SEL (2).',
|
||||||
|
availableGates: ['INPUT', 'OUTPUT', 'AND', 'OR', 'NOT'],
|
||||||
|
testCases: [
|
||||||
|
{ inputs: { 0: 0, 1: 0, 2: 0 }, outputs: { 0: 0 } }, // SEL=0 → A=0
|
||||||
|
{ inputs: { 0: 1, 1: 0, 2: 0 }, outputs: { 0: 1 } }, // SEL=0 → A=1
|
||||||
|
{ inputs: { 0: 0, 1: 1, 2: 0 }, outputs: { 0: 0 } }, // SEL=0 → A=0
|
||||||
|
{ inputs: { 0: 1, 1: 1, 2: 0 }, outputs: { 0: 1 } }, // SEL=0 → A=1
|
||||||
|
{ inputs: { 0: 0, 1: 0, 2: 1 }, outputs: { 0: 0 } }, // SEL=1 → B=0
|
||||||
|
{ inputs: { 0: 0, 1: 1, 2: 1 }, outputs: { 0: 1 } }, // SEL=1 → B=1
|
||||||
|
{ inputs: { 0: 1, 1: 0, 2: 1 }, outputs: { 0: 0 } }, // SEL=1 → B=0
|
||||||
|
{ inputs: { 0: 1, 1: 1, 2: 1 }, outputs: { 0: 1 } } // SEL=1 → B=1
|
||||||
|
],
|
||||||
|
hints: ['MUX = (A AND NOT SEL) OR (B AND SEL).', 'You need 1 NOT, 2 AND, and 1 OR gate.'],
|
||||||
|
difficulty: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'demux_1to2',
|
||||||
|
category: 'Combinational Logic',
|
||||||
|
title: '1:2 Demultiplexer',
|
||||||
|
description: 'Route input to one of two outputs based on SEL. Inputs: DATA (0), SEL (1). Outputs: Q0, Q1.',
|
||||||
|
availableGates: ['INPUT', 'OUTPUT', 'AND', 'NOT'],
|
||||||
|
testCases: [
|
||||||
|
{ inputs: { 0: 0, 1: 0 }, outputs: { 0: 0, 1: 0 } }, // DATA=0, SEL=0 → Q0=0, Q1=0
|
||||||
|
{ inputs: { 0: 1, 1: 0 }, outputs: { 0: 1, 1: 0 } }, // DATA=1, SEL=0 → Q0=1, Q1=0
|
||||||
|
{ inputs: { 0: 0, 1: 1 }, outputs: { 0: 0, 1: 0 } }, // DATA=0, SEL=1 → Q0=0, Q1=0
|
||||||
|
{ inputs: { 0: 1, 1: 1 }, outputs: { 0: 0, 1: 1 } } // DATA=1, SEL=1 → Q0=0, Q1=1
|
||||||
|
],
|
||||||
|
hints: ['Q0 = DATA AND NOT SEL. Q1 = DATA AND SEL.', 'You need 1 NOT and 2 AND gates.'],
|
||||||
|
difficulty: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'and3',
|
||||||
|
category: 'Combinational Logic',
|
||||||
|
title: '3-input AND',
|
||||||
|
description: 'Build a 3-input AND gate. Output is 1 only when all three inputs are 1.',
|
||||||
|
availableGates: ['INPUT', 'OUTPUT', 'AND'],
|
||||||
|
testCases: [
|
||||||
|
{ inputs: { 0: 0, 1: 0, 2: 0 }, outputs: { 0: 0 } },
|
||||||
|
{ inputs: { 0: 1, 1: 0, 2: 0 }, outputs: { 0: 0 } },
|
||||||
|
{ inputs: { 0: 1, 1: 1, 2: 0 }, outputs: { 0: 0 } },
|
||||||
|
{ inputs: { 0: 0, 1: 1, 2: 1 }, outputs: { 0: 0 } },
|
||||||
|
{ inputs: { 0: 1, 1: 1, 2: 1 }, outputs: { 0: 1 } }
|
||||||
|
],
|
||||||
|
hints: ['Chain two AND gates: (A AND B) AND C.'],
|
||||||
|
difficulty: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'majority',
|
||||||
|
category: 'Combinational Logic',
|
||||||
|
title: 'Majority Gate',
|
||||||
|
description: 'Output 1 if at least two of the three inputs are 1.',
|
||||||
|
availableGates: ['INPUT', 'OUTPUT', 'AND', 'OR'],
|
||||||
|
testCases: [
|
||||||
|
{ inputs: { 0: 0, 1: 0, 2: 0 }, outputs: { 0: 0 } },
|
||||||
|
{ inputs: { 0: 1, 1: 0, 2: 0 }, outputs: { 0: 0 } },
|
||||||
|
{ inputs: { 0: 0, 1: 1, 2: 0 }, outputs: { 0: 0 } },
|
||||||
|
{ inputs: { 0: 0, 1: 0, 2: 1 }, outputs: { 0: 0 } },
|
||||||
|
{ inputs: { 0: 1, 1: 1, 2: 0 }, outputs: { 0: 1 } },
|
||||||
|
{ inputs: { 0: 1, 1: 0, 2: 1 }, outputs: { 0: 1 } },
|
||||||
|
{ inputs: { 0: 0, 1: 1, 2: 1 }, outputs: { 0: 1 } },
|
||||||
|
{ inputs: { 0: 1, 1: 1, 2: 1 }, outputs: { 0: 1 } }
|
||||||
|
],
|
||||||
|
hints: ['Majority = (A AND B) OR (A AND C) OR (B AND C).', 'You need 3 AND gates and 2 OR gates.'],
|
||||||
|
difficulty: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'parity',
|
||||||
|
category: 'Combinational Logic',
|
||||||
|
title: 'Even Parity',
|
||||||
|
description: 'Output 1 if an even number of inputs are 1 (0 counts as even). Three inputs.',
|
||||||
|
availableGates: ['INPUT', 'OUTPUT', 'XOR', 'NOT'],
|
||||||
|
testCases: [
|
||||||
|
{ inputs: { 0: 0, 1: 0, 2: 0 }, outputs: { 0: 1 } }, // 0 ones = even
|
||||||
|
{ inputs: { 0: 1, 1: 0, 2: 0 }, outputs: { 0: 0 } }, // 1 one = odd
|
||||||
|
{ inputs: { 0: 0, 1: 1, 2: 0 }, outputs: { 0: 0 } },
|
||||||
|
{ inputs: { 0: 0, 1: 0, 2: 1 }, outputs: { 0: 0 } },
|
||||||
|
{ inputs: { 0: 1, 1: 1, 2: 0 }, outputs: { 0: 1 } }, // 2 ones = even
|
||||||
|
{ inputs: { 0: 1, 1: 0, 2: 1 }, outputs: { 0: 1 } },
|
||||||
|
{ inputs: { 0: 0, 1: 1, 2: 1 }, outputs: { 0: 1 } },
|
||||||
|
{ inputs: { 0: 1, 1: 1, 2: 1 }, outputs: { 0: 0 } } // 3 ones = odd
|
||||||
|
],
|
||||||
|
hints: ['XOR gives odd parity. Invert it for even parity.', 'Chain: NOT(A XOR B XOR C).'],
|
||||||
|
difficulty: 2
|
||||||
|
},
|
||||||
|
// ============ Arithmetic ============
|
||||||
{
|
{
|
||||||
id: 'half_adder',
|
id: 'half_adder',
|
||||||
category: 'Arithmetic',
|
category: 'Arithmetic',
|
||||||
@@ -82,10 +187,10 @@ export const LEVELS = [
|
|||||||
description: 'Build a half adder. Two outputs: Sum (A XOR B) and Carry (A AND B).',
|
description: 'Build a half adder. Two outputs: Sum (A XOR B) and Carry (A AND B).',
|
||||||
availableGates: ['INPUT', 'OUTPUT', 'NAND'],
|
availableGates: ['INPUT', 'OUTPUT', 'NAND'],
|
||||||
testCases: [
|
testCases: [
|
||||||
{ inputs: { 0: 0, 1: 0 }, outputs: { 0: 0, 1: 0 } }, // 0+0: sum=0, carry=0
|
{ inputs: { 0: 0, 1: 0 }, outputs: { 0: 0, 1: 0 } },
|
||||||
{ inputs: { 0: 0, 1: 1 }, outputs: { 0: 1, 1: 0 } }, // 0+1: sum=1, carry=0
|
{ inputs: { 0: 0, 1: 1 }, outputs: { 0: 1, 1: 0 } },
|
||||||
{ inputs: { 0: 1, 1: 0 }, outputs: { 0: 1, 1: 0 } }, // 1+0: sum=1, carry=0
|
{ inputs: { 0: 1, 1: 0 }, outputs: { 0: 1, 1: 0 } },
|
||||||
{ inputs: { 0: 1, 1: 1 }, outputs: { 0: 0, 1: 1 } } // 1+1: sum=0, carry=1
|
{ inputs: { 0: 1, 1: 1 }, outputs: { 0: 0, 1: 1 } }
|
||||||
],
|
],
|
||||||
hints: ['Two independent functions: Sum=XOR, Carry=AND.', 'You need 2 OUTPUT gates.'],
|
hints: ['Two independent functions: Sum=XOR, Carry=AND.', 'You need 2 OUTPUT gates.'],
|
||||||
difficulty: 3
|
difficulty: 3
|
||||||
@@ -94,36 +199,131 @@ export const LEVELS = [
|
|||||||
id: 'full_adder',
|
id: 'full_adder',
|
||||||
category: 'Arithmetic',
|
category: 'Arithmetic',
|
||||||
title: 'Full Adder',
|
title: 'Full Adder',
|
||||||
description: 'Build a full adder with carry-in. Outputs: Sum (3-input XOR) and Carry.',
|
description: 'Build a full adder with carry-in. Outputs: Sum and Carry-out.',
|
||||||
availableGates: ['INPUT', 'OUTPUT', 'NAND'],
|
availableGates: ['INPUT', 'OUTPUT', 'NAND'],
|
||||||
testCases: [
|
testCases: [
|
||||||
{ inputs: { 0: 0, 1: 0, 2: 0 }, outputs: { 0: 0, 1: 0 } }, // 0+0+0
|
{ inputs: { 0: 0, 1: 0, 2: 0 }, outputs: { 0: 0, 1: 0 } },
|
||||||
{ inputs: { 0: 0, 1: 0, 2: 1 }, outputs: { 0: 1, 1: 0 } }, // 0+0+1
|
{ inputs: { 0: 0, 1: 0, 2: 1 }, outputs: { 0: 1, 1: 0 } },
|
||||||
{ inputs: { 0: 0, 1: 1, 2: 0 }, outputs: { 0: 1, 1: 0 } }, // 0+1+0
|
{ inputs: { 0: 0, 1: 1, 2: 0 }, outputs: { 0: 1, 1: 0 } },
|
||||||
{ inputs: { 0: 0, 1: 1, 2: 1 }, outputs: { 0: 0, 1: 1 } }, // 0+1+1
|
{ inputs: { 0: 0, 1: 1, 2: 1 }, outputs: { 0: 0, 1: 1 } },
|
||||||
{ inputs: { 0: 1, 1: 0, 2: 0 }, outputs: { 0: 1, 1: 0 } }, // 1+0+0
|
{ inputs: { 0: 1, 1: 0, 2: 0 }, outputs: { 0: 1, 1: 0 } },
|
||||||
{ inputs: { 0: 1, 1: 0, 2: 1 }, outputs: { 0: 0, 1: 1 } }, // 1+0+1
|
{ inputs: { 0: 1, 1: 0, 2: 1 }, outputs: { 0: 0, 1: 1 } },
|
||||||
{ inputs: { 0: 1, 1: 1, 2: 0 }, outputs: { 0: 0, 1: 1 } }, // 1+1+0
|
{ inputs: { 0: 1, 1: 1, 2: 0 }, outputs: { 0: 0, 1: 1 } },
|
||||||
{ inputs: { 0: 1, 1: 1, 2: 1 }, outputs: { 0: 1, 1: 1 } } // 1+1+1
|
{ inputs: { 0: 1, 1: 1, 2: 1 }, outputs: { 0: 1, 1: 1 } }
|
||||||
],
|
],
|
||||||
hints: ['Reuse your half adder logic. A full adder = half adder + half adder + OR.'],
|
hints: ['A full adder = two half adders + OR for the carry.', 'Sum = A XOR B XOR Cin. Cout = (A AND B) OR (Cin AND (A XOR B)).'],
|
||||||
difficulty: 4
|
difficulty: 4
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'half_subtractor',
|
||||||
|
category: 'Arithmetic',
|
||||||
|
title: 'Half Subtractor',
|
||||||
|
description: 'Build A minus B. Outputs: Difference (A XOR B) and Borrow (NOT A AND B).',
|
||||||
|
availableGates: ['INPUT', 'OUTPUT', 'AND', 'XOR', 'NOT'],
|
||||||
|
testCases: [
|
||||||
|
{ inputs: { 0: 0, 1: 0 }, outputs: { 0: 0, 1: 0 } }, // 0-0: diff=0, borrow=0
|
||||||
|
{ inputs: { 0: 0, 1: 1 }, outputs: { 0: 1, 1: 1 } }, // 0-1: diff=1, borrow=1
|
||||||
|
{ inputs: { 0: 1, 1: 0 }, outputs: { 0: 1, 1: 0 } }, // 1-0: diff=1, borrow=0
|
||||||
|
{ inputs: { 0: 1, 1: 1 }, outputs: { 0: 0, 1: 0 } } // 1-1: diff=0, borrow=0
|
||||||
|
],
|
||||||
|
hints: ['Difference = A XOR B (same as addition!).', 'Borrow = (NOT A) AND B.'],
|
||||||
|
difficulty: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'comparator',
|
||||||
|
category: 'Arithmetic',
|
||||||
|
title: '1-bit Comparator',
|
||||||
|
description: 'Compare A and B. Three outputs: A>B, A=B, A<B.',
|
||||||
|
availableGates: ['INPUT', 'OUTPUT', 'AND', 'OR', 'NOT', 'XOR'],
|
||||||
|
testCases: [
|
||||||
|
{ inputs: { 0: 0, 1: 0 }, outputs: { 0: 0, 1: 1, 2: 0 } }, // 0=0: GT=0, EQ=1, LT=0
|
||||||
|
{ inputs: { 0: 0, 1: 1 }, outputs: { 0: 0, 1: 0, 2: 1 } }, // 0<1: GT=0, EQ=0, LT=1
|
||||||
|
{ inputs: { 0: 1, 1: 0 }, outputs: { 0: 1, 1: 0, 2: 0 } }, // 1>0: GT=1, EQ=0, LT=0
|
||||||
|
{ inputs: { 0: 1, 1: 1 }, outputs: { 0: 0, 1: 1, 2: 0 } } // 1=1: GT=0, EQ=1, LT=0
|
||||||
|
],
|
||||||
|
hints: ['A>B = A AND NOT B.', 'A=B = NOT(A XOR B) = XNOR.', 'A<B = NOT A AND B.'],
|
||||||
|
difficulty: 3
|
||||||
|
},
|
||||||
|
// ============ Decoders & Encoders ============
|
||||||
|
{
|
||||||
|
id: 'decoder_2to4',
|
||||||
|
category: 'Decoders & Encoders',
|
||||||
|
title: '2-to-4 Decoder',
|
||||||
|
description: 'Decode 2 input bits into 4 output lines. Only one output is 1 at a time.',
|
||||||
|
availableGates: ['INPUT', 'OUTPUT', 'AND', 'NOT'],
|
||||||
|
testCases: [
|
||||||
|
{ inputs: { 0: 0, 1: 0 }, outputs: { 0: 1, 1: 0, 2: 0, 3: 0 } }, // 00 → line 0
|
||||||
|
{ inputs: { 0: 0, 1: 1 }, outputs: { 0: 0, 1: 1, 2: 0, 3: 0 } }, // 01 → line 1
|
||||||
|
{ inputs: { 0: 1, 1: 0 }, outputs: { 0: 0, 1: 0, 2: 1, 3: 0 } }, // 10 → line 2
|
||||||
|
{ inputs: { 0: 1, 1: 1 }, outputs: { 0: 0, 1: 0, 2: 0, 3: 1 } } // 11 → line 3
|
||||||
|
],
|
||||||
|
hints: ['Q0 = NOT A AND NOT B.', 'Q1 = NOT A AND B. Q2 = A AND NOT B. Q3 = A AND B.', 'You need 2 NOT gates and 4 AND gates.'],
|
||||||
|
difficulty: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'encoder_4to2',
|
||||||
|
category: 'Decoders & Encoders',
|
||||||
|
title: '4-to-2 Encoder',
|
||||||
|
description: 'Encode 4 input lines into 2-bit binary. Assume only one input is active at a time.',
|
||||||
|
availableGates: ['INPUT', 'OUTPUT', 'OR'],
|
||||||
|
testCases: [
|
||||||
|
{ inputs: { 0: 1, 1: 0, 2: 0, 3: 0 }, outputs: { 0: 0, 1: 0 } }, // line 0 → 00
|
||||||
|
{ inputs: { 0: 0, 1: 1, 2: 0, 3: 0 }, outputs: { 0: 0, 1: 1 } }, // line 1 → 01
|
||||||
|
{ inputs: { 0: 0, 1: 0, 2: 1, 3: 0 }, outputs: { 0: 1, 1: 0 } }, // line 2 → 10
|
||||||
|
{ inputs: { 0: 0, 1: 0, 2: 0, 3: 1 }, outputs: { 0: 1, 1: 1 } } // line 3 → 11
|
||||||
|
],
|
||||||
|
hints: ['Bit 1 (MSB) = Input2 OR Input3.', 'Bit 0 (LSB) = Input1 OR Input3.', 'Just 2 OR gates!'],
|
||||||
|
difficulty: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'seven_seg_a',
|
||||||
|
category: 'Decoders & Encoders',
|
||||||
|
title: '7-Segment: Segment A',
|
||||||
|
description: 'Drive segment A of a 7-segment display for digits 0-3. Segment A is ON for 0, 2, 3.',
|
||||||
|
availableGates: ['INPUT', 'OUTPUT', 'AND', 'OR', 'NOT'],
|
||||||
|
testCases: [
|
||||||
|
{ inputs: { 0: 0, 1: 0 }, outputs: { 0: 1 } }, // 0 → A on
|
||||||
|
{ inputs: { 0: 0, 1: 1 }, outputs: { 0: 0 } }, // 1 → A off
|
||||||
|
{ inputs: { 0: 1, 1: 0 }, outputs: { 0: 1 } }, // 2 → A on
|
||||||
|
{ inputs: { 0: 1, 1: 1 }, outputs: { 0: 1 } } // 3 → A on
|
||||||
|
],
|
||||||
|
hints: ['A = NOT(NOT A1 AND A0). Or equivalently: A = A1 OR NOT A0.'],
|
||||||
|
difficulty: 2
|
||||||
|
},
|
||||||
|
// ============ Components ============
|
||||||
{
|
{
|
||||||
id: 'two_bit_adder',
|
id: 'two_bit_adder',
|
||||||
category: 'Components',
|
category: 'Components',
|
||||||
title: '2-bit Adder',
|
title: '2-bit Adder',
|
||||||
description: 'Chain two full adders to add two 2-bit numbers.',
|
description: 'Chain two full adders to add two 2-bit numbers.',
|
||||||
availableGates: ['INPUT', 'OUTPUT', 'FULL_ADDER'], // FULL_ADDER is saved component
|
availableGates: ['INPUT', 'OUTPUT', 'FULL_ADDER'],
|
||||||
testCases: [
|
testCases: [
|
||||||
{ inputs: { 0: 0, 1: 0, 2: 0, 3: 0 }, outputs: { 0: 0, 1: 0, 2: 0 } }, // 00+00=000
|
{ inputs: { 0: 0, 1: 0, 2: 0, 3: 0 }, outputs: { 0: 0, 1: 0, 2: 0 } },
|
||||||
{ inputs: { 0: 0, 1: 0, 2: 0, 3: 1 }, outputs: { 0: 0, 1: 0, 2: 1 } }, // 00+01=001
|
{ inputs: { 0: 0, 1: 0, 2: 0, 3: 1 }, outputs: { 0: 0, 1: 0, 2: 1 } },
|
||||||
{ inputs: { 0: 0, 1: 1, 2: 0, 3: 1 }, outputs: { 0: 0, 1: 1, 2: 1 } }, // 01+01=010
|
{ inputs: { 0: 0, 1: 1, 2: 0, 3: 1 }, outputs: { 0: 0, 1: 1, 2: 1 } },
|
||||||
{ inputs: { 0: 1, 1: 1, 2: 0, 3: 1 }, outputs: { 0: 1, 1: 1, 2: 1 } }, // 11+01=100
|
{ inputs: { 0: 1, 1: 1, 2: 0, 3: 1 }, outputs: { 0: 1, 1: 1, 2: 1 } },
|
||||||
{ inputs: { 0: 1, 1: 1, 2: 1, 3: 1 }, outputs: { 0: 0, 1: 1, 2: 1 } } // 11+11=110
|
{ inputs: { 0: 1, 1: 1, 2: 1, 3: 1 }, outputs: { 0: 0, 1: 1, 2: 1 } }
|
||||||
],
|
],
|
||||||
hints: ['Use two FULL_ADDER components chained together.', 'First adder has no carry-in (set to 0).'],
|
hints: ['Use two FULL_ADDER components chained together.', 'First adder has no carry-in (set to 0).'],
|
||||||
difficulty: 3
|
difficulty: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'alu_1bit',
|
||||||
|
category: 'Components',
|
||||||
|
title: '1-bit ALU',
|
||||||
|
description: 'Build a 1-bit ALU. OP=0 → AND, OP=1 → OR. Inputs: A (0), B (1), OP (2).',
|
||||||
|
availableGates: ['INPUT', 'OUTPUT', 'AND', 'OR', 'NOT'],
|
||||||
|
testCases: [
|
||||||
|
{ inputs: { 0: 0, 1: 0, 2: 0 }, outputs: { 0: 0 } }, // 0 AND 0 = 0
|
||||||
|
{ inputs: { 0: 1, 1: 0, 2: 0 }, outputs: { 0: 0 } }, // 1 AND 0 = 0
|
||||||
|
{ inputs: { 0: 1, 1: 1, 2: 0 }, outputs: { 0: 1 } }, // 1 AND 1 = 1
|
||||||
|
{ inputs: { 0: 0, 1: 0, 2: 1 }, outputs: { 0: 0 } }, // 0 OR 0 = 0
|
||||||
|
{ inputs: { 0: 1, 1: 0, 2: 1 }, outputs: { 0: 1 } }, // 1 OR 0 = 1
|
||||||
|
{ inputs: { 0: 0, 1: 1, 2: 1 }, outputs: { 0: 1 } }, // 0 OR 1 = 1
|
||||||
|
{ inputs: { 0: 1, 1: 1, 2: 1 }, outputs: { 0: 1 } } // 1 OR 1 = 1
|
||||||
|
],
|
||||||
|
hints: ['Compute both A AND B and A OR B, then use a MUX to select based on OP.', 'MUX = (result0 AND NOT OP) OR (result1 AND OP).'],
|
||||||
|
difficulty: 3
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user