add engine

This commit is contained in:
sfja 2026-05-05 20:59:32 +02:00
parent 67c7cf242a
commit 5552949d71
3 changed files with 245 additions and 0 deletions

5
engine/deno.json Normal file
View File

@ -0,0 +1,5 @@
{
"fmt": {
"indentWidth": 4
}
}

210
engine/src/circuit.ts Normal file
View File

@ -0,0 +1,210 @@
export class Circuit {
constructor(
private comps: Component[],
private inputComps: Component[],
private pins: Pin[],
private pinConsumers: Map<Pin, Component[]>,
private pinInCount: number,
private pinOutCount: number,
) {}
createState(): State {
return new State(this.pins, this.pinInCount, this.pinOutCount);
}
simulate(state: State) {
const queue: Component[] = [];
for (const comp of this.inputComps) {
queue.push(comp);
}
while (queue.length) {
const comp = queue.shift()!;
comp.simulate(state);
for (const pin of comp.outputs) {
queue.push(...this.pinConsumers.get(pin)!);
}
}
}
}
export class Component {
constructor(
public kind: ComponentKind,
public inputs: (Pin | null)[],
public outputs: Pin[],
) {}
simulate(state: State) {
const k = this.kind;
switch (k.tag) {
case "PinIn": {
state.setPin(this.outputs[0], state.getIn(k.idx));
break;
}
case "PinOut": {
state.setOut(k.idx, state.getPin(this.inputs[0]));
break;
}
case "Nand": {
const lhs = state.getPin(this.inputs[0]);
const rhs = state.getPin(this.inputs[1]);
state.setPin(
this.outputs[0],
!(lhs && rhs),
);
break;
}
default:
throw new Error(`not handled (${k})`);
}
}
}
export type ComponentKind =
| { tag: "PinIn" | "PinOut"; idx: number }
| { tag: "Nand" };
export class Pin {
constructor() {}
}
export class State {
private pinsHigh = new Set<Pin>();
private pinsInHigh = new Set<number>();
private pinsOutHigh = new Set<number>();
constructor(
private pins: Pin[],
private pinInCount: number,
private pinOutCount: number,
) {}
getPin(pin: Pin | null): boolean {
return pin !== null && this.pinsHigh.has(pin);
}
setPin(pin: Pin, value: boolean) {
if (value) {
this.pinsHigh.add(pin);
} else {
this.pinsHigh.delete(pin);
}
}
getIn(idx: number): boolean {
return this.pinsInHigh.has(idx);
}
setIn(idx: number, value: boolean) {
if (value) {
this.pinsInHigh.add(idx);
} else {
this.pinsInHigh.delete(idx);
}
}
getOut(idx: number): boolean {
return this.pinsOutHigh.has(idx);
}
setOut(idx: number, value: boolean) {
if (value) {
this.pinsOutHigh.add(idx);
} else {
this.pinsOutHigh.delete(idx);
}
}
prettyPrint() {
console.log(
`inputs: [${
new Array(this.pinInCount)
.fill(0)
.map((_, i) => this.pinsInHigh.has(i) ? 1 : 0)
.map((v) => v.toString())
.join(", ")
}]`,
);
console.log(
`outputs: [${
new Array(this.pinOutCount)
.fill(0)
.map((_, i) => this.pinsOutHigh.has(i) ? 1 : 0)
.map((v) => v.toString())
.join(", ")
}]`,
);
console.log(
`state: [${
new Array(this.pins.length)
.fill(0)
.map((_, i) => this.pinsHigh.has(this.pins[i]) ? 1 : 0)
.map((v) => v.toString())
.join(", ")
}]`,
);
}
}
export class CircuitBuilder {
private comps: Component[] = [];
private inputComps: Component[] = [];
private pins: Pin[] = [];
private pinInCount = 0;
private pinOutCount = 0;
build(): Circuit {
const pinConsumers = new Map<Pin, Component[]>();
for (const pin of this.pins) {
pinConsumers.set(pin, []);
}
for (const comp of this.comps) {
for (const pin of comp.inputs) {
if (!pin) {
continue;
}
pinConsumers.get(pin)!.push(comp);
}
}
return new Circuit(
this.comps,
this.inputComps,
this.pins,
pinConsumers,
this.pinInCount,
this.pinOutCount,
);
}
addPinIn(): Component {
const idx = this.pinInCount;
this.pinInCount += 1;
const comp = this.addComponent({ tag: "PinIn", idx }, 0, 1);
this.inputComps.push(comp);
return comp;
}
addPinOut(): Component {
const idx = this.pinOutCount;
this.pinOutCount += 1;
return this.addComponent({ tag: "PinOut", idx }, 1, 0);
}
addNand(): Component {
return this.addComponent({ tag: "Nand" }, 2, 1);
}
private addComponent(
kind: ComponentKind,
inputCount: number,
outputCount: number,
) {
const inputs = new Array(inputCount).fill(null);
const outputs = new Array(outputCount).fill(0).map(() => new Pin());
const comp = new Component(kind, inputs, outputs);
this.comps.push(comp);
this.pins.push(...outputs);
return comp;
}
}

30
engine/src/main.ts Normal file
View File

@ -0,0 +1,30 @@
import { CircuitBuilder } from "./circuit.ts";
const builder = new CircuitBuilder();
const i0 = builder.addPinIn();
const i1 = builder.addPinIn();
const o0 = builder.addPinOut();
const and = builder.addNand();
and.inputs[0] = i0.outputs[0];
and.inputs[1] = i1.outputs[0];
o0.inputs[0] = and.outputs[0];
const circuit = builder.build();
const state = circuit.createState();
state.setIn(0, true);
state.setIn(1, true);
console.log("-- before --");
state.prettyPrint();
circuit.simulate(state);
console.log("-- after --");
state.prettyPrint();