quantum-forge 2.0.2 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +18 -0
- package/QUANTUM_FORGE.md +528 -0
- package/README.md +115 -0
- package/dist/lib/quantum.js +1 -1
- package/dist/lib/quantum.js.map +1 -1
- package/dist/lib/vite-plugin.js +36 -26
- package/dist/lib/vite-plugin.js.map +1 -1
- package/dist/quantum-forge-qubit/quantum-forge-web-api.d.mts +61 -0
- package/dist/quantum-forge-qubit/quantum-forge-web-api.mjs +202 -0
- package/dist/quantum-forge-qubit/quantum-forge-web-esm.mjs +14 -0
- package/dist/quantum-forge-qubit/quantum-forge-web-esm.wasm +0 -0
- package/dist/quantum-forge-web-esm.mjs +1 -1
- package/dist/quantum-forge-web-esm.wasm +0 -0
- package/package.json +3 -4
- package/LICENSING.md +0 -33
- package/dist/quantum-forge-web-0.3.0.tgz +0 -0
- /package/{LICENSE-BINARY.md → dist/LICENSE-BINARY.md} +0 -0
package/LICENSE.md
CHANGED
|
@@ -19,3 +19,21 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Split Licensing
|
|
26
|
+
|
|
27
|
+
This package uses a split licensing model:
|
|
28
|
+
|
|
29
|
+
- **TypeScript source code** is MIT licensed (this file).
|
|
30
|
+
- **Compiled binaries** (WebAssembly `.wasm` files in `dist/`) are proprietary.
|
|
31
|
+
See [`dist/LICENSE-BINARY.md`](dist/LICENSE-BINARY.md) for the full terms.
|
|
32
|
+
|
|
33
|
+
The short version: the binaries are **free** for applications under $100K/year
|
|
34
|
+
in revenue, with a "Powered by Quantum Forge" attribution requirement. Above
|
|
35
|
+
$100K, contact hello@quantum.dev for a commercial license.
|
|
36
|
+
|
|
37
|
+
The source code is the API you build with — we want it open so you can read,
|
|
38
|
+
debug, and contribute. The compiled quantum simulator is how we sustain the
|
|
39
|
+
project.
|
package/QUANTUM_FORGE.md
ADDED
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: quantum-forge
|
|
3
|
+
description: Quantum Forge framework reference — quantum operations, gates, entanglement, rendering, input. Use when writing game code, debugging WASM/imports, or understanding quantum mechanics in games.
|
|
4
|
+
allowed-tools: Read Glob Grep
|
|
5
|
+
user-invocable: true
|
|
6
|
+
argument-hint: [topic]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Quantum Forge Reference
|
|
10
|
+
|
|
11
|
+
## Project Configuration
|
|
12
|
+
|
|
13
|
+
```!
|
|
14
|
+
node -e "
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
try {
|
|
17
|
+
const p = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
|
|
18
|
+
const deps = { ...p.dependencies, ...p.devDependencies };
|
|
19
|
+
const core = deps['quantum-forge'] || deps['@quantum-native/quantum-forge'] || 'not installed';
|
|
20
|
+
const engine = deps['quantum-forge-engine'] || deps['@quantum-native/quantum-forge-engine'] || 'not installed';
|
|
21
|
+
const corePkg = deps['quantum-forge'] ? 'quantum-forge' : '@quantum-native/quantum-forge';
|
|
22
|
+
const enginePkg = deps['quantum-forge-engine'] ? 'quantum-forge-engine' : '@quantum-native/quantum-forge-engine';
|
|
23
|
+
console.log('Core: ' + corePkg + '@' + core);
|
|
24
|
+
if (engine !== 'not installed') console.log('Engine: ' + enginePkg + '@' + engine);
|
|
25
|
+
console.log('Type: ' + (p.type || 'commonjs'));
|
|
26
|
+
} catch { console.log('No package.json found'); }
|
|
27
|
+
" 2>/dev/null
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Initialization
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { ensureLoaded } from "quantum-forge/quantum";
|
|
34
|
+
await ensureLoaded(); // MUST be called before any quantum operations
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For the Qubit Edition (d2n20):
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { useQuantumForgeBuild, ensureLoaded } from "quantum-forge/quantum";
|
|
41
|
+
useQuantumForgeBuild("qubit"); // before ensureLoaded()
|
|
42
|
+
await ensureLoaded();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Vite Plugin
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { quantumForgeVitePlugin } from "quantum-forge/vite-plugin";
|
|
49
|
+
export default defineConfig({
|
|
50
|
+
plugins: [quantumForgeVitePlugin()],
|
|
51
|
+
build: { rollupOptions: { external: [/quantum-forge-web-api/] } },
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
ESM note: if using `vite.config.js` (not `.ts`/`.mjs`), add `"type": "module"` to `package.json`.
|
|
56
|
+
|
|
57
|
+
## Core Concepts
|
|
58
|
+
|
|
59
|
+
Quantum Forge gives game objects real quantum state. The usage loop:
|
|
60
|
+
|
|
61
|
+
1. **Attach quantum properties** to things — each property is a qudit (d-dimensional quantum digit)
|
|
62
|
+
2. **Apply gates** to transform state without collapsing it
|
|
63
|
+
3. **Use predicated gates** to create entanglement — correlations between properties
|
|
64
|
+
4. **Measure** to collapse quantum state to a classical value (the game moment)
|
|
65
|
+
|
|
66
|
+
### Programmer's Mental Model
|
|
67
|
+
|
|
68
|
+
**Superposition** is a weighted collection of values. A qubit in equal superposition is `{|0⟩: 0.707, |1⟩: 0.707}`.
|
|
69
|
+
|
|
70
|
+
**Gates** are collection algorithms that transform every entry at once. `cycle` increments each value. `hadamard` creates equal superposition.
|
|
71
|
+
|
|
72
|
+
**Predicated gates** apply a filter: "if property A is |1⟩, flip property B." This creates entanglement — correlated entries in the collection.
|
|
73
|
+
|
|
74
|
+
The weights are **complex amplitudes**, not probabilities. When two entries with the same value meet, their amplitudes add. In phase = constructive interference (probability increases). Out of phase = destructive interference (probability cancels). Probability of a value = squared magnitude of its amplitude.
|
|
75
|
+
|
|
76
|
+
## QuantumPropertyManager
|
|
77
|
+
|
|
78
|
+
Extend this class to build your game's quantum system. It manages property lifecycle, pooling, and WASM access.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { QuantumPropertyManager, ensureLoaded } from "quantum-forge/quantum";
|
|
82
|
+
|
|
83
|
+
await ensureLoaded();
|
|
84
|
+
|
|
85
|
+
class QuantumRegistry extends QuantumPropertyManager {
|
|
86
|
+
constructor(logger?: any) { super({ dimension: 2, logger }); }
|
|
87
|
+
|
|
88
|
+
makeQuantum(id: string): void {
|
|
89
|
+
const prop = this.acquireProperty();
|
|
90
|
+
const m = this.getModule();
|
|
91
|
+
m.cycle(prop); // |0⟩ → |1⟩
|
|
92
|
+
m.hadamard(prop); // → 50/50 superposition
|
|
93
|
+
this.setProperty(id, prop);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
entangle(id1: string, id2: string): void {
|
|
97
|
+
const p1 = this.getProperty(id1);
|
|
98
|
+
const p2 = this.getProperty(id2);
|
|
99
|
+
if (!p1 || !p2) return;
|
|
100
|
+
this.getModule().i_swap(p1, p2, 0.5);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getProbability(id: string): number {
|
|
104
|
+
const prop = this.getProperty(id);
|
|
105
|
+
if (!prop) return 1.0;
|
|
106
|
+
const results = this.getModule().probabilities([prop]);
|
|
107
|
+
for (const r of results) {
|
|
108
|
+
if (r.qudit_values[0] === 1) return r.probability;
|
|
109
|
+
}
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
measure(id: string): number {
|
|
114
|
+
const prop = this.getProperty(id);
|
|
115
|
+
if (!prop) return 1;
|
|
116
|
+
const [value] = this.getModule().measure_properties([prop]);
|
|
117
|
+
this.deleteProperty(id);
|
|
118
|
+
this.releaseProperty(prop, value); // reset to |0⟩, return to pool
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Dimension
|
|
125
|
+
|
|
126
|
+
| Dimension | States | Use Case |
|
|
127
|
+
|-----------|--------|----------|
|
|
128
|
+
| 2 (qubit) | `\|0⟩`, `\|1⟩` | Binary: exists/doesn't, alive/dead |
|
|
129
|
+
| 3 (qutrit) | `\|0⟩` – `\|2⟩` | Three-way: rock/paper/scissors |
|
|
130
|
+
|
|
131
|
+
Start with dimension 2 unless your design needs more. Shipped package supports up to 3.
|
|
132
|
+
|
|
133
|
+
## Gates
|
|
134
|
+
|
|
135
|
+
All gates are called via `getModule()`. Every gate accepts optional `predicates` for conditional execution (creates entanglement).
|
|
136
|
+
|
|
137
|
+
| Gate | Method | Dim | Description |
|
|
138
|
+
|------|--------|-----|-------------|
|
|
139
|
+
| Cycle | `m.cycle(prop, fraction?, preds?)` | Any | Cyclic permutation. `\|0⟩→\|1⟩→\|0⟩` for dim=2 (NOT gate) |
|
|
140
|
+
| Shift / X | `m.shift(prop, fraction?, preds?)` | Any | Inverse of cycle. Same as cycle for dim=2 |
|
|
141
|
+
| Hadamard | `m.hadamard(prop, fraction?, preds?)` | Any | Equal superposition. The main "make it quantum" gate |
|
|
142
|
+
| Inv. Hadamard | `m.inverse_hadamard(prop, preds?)` | Any | Reverse of hadamard |
|
|
143
|
+
| Clock / Z | `m.clock(prop, fraction, preds?)` | Any | Phase rotation. Invisible until interaction |
|
|
144
|
+
| Y | `m.y(prop, fraction?, preds?)` | **2 only** | Pauli Y. Throws if dimension != 2 |
|
|
145
|
+
| iSwap | `m.i_swap(p1, p2, fraction, preds?)` | Any | Anti-correlated entanglement |
|
|
146
|
+
| Swap | `m.swap(p1, p2, preds?)` | Any | Direct state exchange, no entanglement |
|
|
147
|
+
| Phase Rotate | `m.phase_rotate(preds, angle)` | Any | Phase rotation conditioned on predicates (required) |
|
|
148
|
+
|
|
149
|
+
**Fractional gates**: `fraction=1` is the full gate, `0.5` is the square root, `0.1` barely moves state. Key for gradual quantum effects.
|
|
150
|
+
|
|
151
|
+
### Predicates (Conditional Gates)
|
|
152
|
+
|
|
153
|
+
Every gate accepts predicates that condition it on other properties' states. **This is how entanglement works** — a gate that depends on another property's state correlates them.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// CNOT: flip target only when control is |1⟩
|
|
157
|
+
m.shift(target, 1, [control.is(1)]);
|
|
158
|
+
// Result: (|00⟩ + |11⟩)/√2 — positively correlated
|
|
159
|
+
|
|
160
|
+
// Controlled Hadamard: target enters superposition conditionally
|
|
161
|
+
m.hadamard(target, 1, [control.is(1)]);
|
|
162
|
+
|
|
163
|
+
// Predicate types:
|
|
164
|
+
prop.is(value) // true when property is |value⟩
|
|
165
|
+
prop.is_not(value) // true when property is NOT |value⟩
|
|
166
|
+
|
|
167
|
+
// Multiple predicates are AND'd
|
|
168
|
+
m.shift(target, 1, [controlA.is(1), controlB.is(1)]);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
For `PredicateSpec` objects (used in some APIs):
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import type { PredicateSpec } from "quantum-forge/quantum";
|
|
175
|
+
const specs: PredicateSpec[] = [
|
|
176
|
+
{ property: controlProp, value: 1, isEqual: true },
|
|
177
|
+
];
|
|
178
|
+
const wasmPreds = specs.map(s =>
|
|
179
|
+
s.isEqual ? s.property.is(s.value) : s.property.is_not(s.value)
|
|
180
|
+
);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Entanglement Patterns
|
|
184
|
+
|
|
185
|
+
### Entangle-Split (iSwap)
|
|
186
|
+
|
|
187
|
+
Object splits into anti-correlated pair. Exactly one is real.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
entangleSplit(originalId: string, newId: string): void {
|
|
191
|
+
const prop1 = this.acquireProperty();
|
|
192
|
+
const prop2 = this.acquireProperty();
|
|
193
|
+
const m = this.getModule();
|
|
194
|
+
m.cycle(prop1); // |1⟩ (exists)
|
|
195
|
+
m.i_swap(prop1, prop2, 0.5); // entangle
|
|
196
|
+
this.setProperty(originalId, prop1);
|
|
197
|
+
this.setProperty(newId, prop2);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Correlated Pair (CNOT)
|
|
202
|
+
|
|
203
|
+
Both match — both alive or both dead.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
createLinkedPair(id1: string, id2: string): void {
|
|
207
|
+
const prop1 = this.acquireProperty();
|
|
208
|
+
const prop2 = this.acquireProperty();
|
|
209
|
+
const m = this.getModule();
|
|
210
|
+
m.cycle(prop1);
|
|
211
|
+
m.hadamard(prop1);
|
|
212
|
+
m.shift(prop2, 1, [prop1.is(1)]); // CNOT
|
|
213
|
+
this.setProperty(id1, prop1);
|
|
214
|
+
this.setProperty(id2, prop2);
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Quantum-Split
|
|
219
|
+
|
|
220
|
+
Split an already-quantum object. Pooled properties preferred (no tensor product growth).
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
quantumSplit(originalId: string, newId: string): boolean {
|
|
224
|
+
const prop1 = this.getProperty(originalId);
|
|
225
|
+
if (!prop1) return false;
|
|
226
|
+
const prop2 = this.acquireProperty();
|
|
227
|
+
try {
|
|
228
|
+
this.getModule().i_swap(prop1, prop2, 0.5);
|
|
229
|
+
} catch {
|
|
230
|
+
this.releaseProperty(prop2, 0); // qudit limit
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
this.setProperty(newId, prop2);
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Overlap Entanglement
|
|
239
|
+
|
|
240
|
+
Objects that spatially overlap become entangled:
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
this.getModule().i_swap(propA, propB, 0.5);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Conditional Superposition
|
|
247
|
+
|
|
248
|
+
A control property determines whether a target enters superposition:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
m.hadamard(target, 1, [control.is(1)]);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Choosing Between Mechanisms
|
|
255
|
+
|
|
256
|
+
| Mechanism | Correlation | Use |
|
|
257
|
+
|-----------|-------------|-----|
|
|
258
|
+
| Predicated shift (CNOT) | Positive — both match | Linked states: both alive or both dead |
|
|
259
|
+
| Predicated hadamard | Conditional superposition | One object's quantumness depends on another |
|
|
260
|
+
| `i_swap(0.5)` | Anti-correlated — exactly one | Object splits into two ghosts |
|
|
261
|
+
| `i_swap` + phase | Tunable bias | Player-influenced split probability |
|
|
262
|
+
|
|
263
|
+
## Measurement
|
|
264
|
+
|
|
265
|
+
Measurement collapses superposition to a definite value. Entangled partners collapse instantly too.
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
const [value] = m.measure_properties([prop]); // probabilistic collapse
|
|
269
|
+
// Always pool after:
|
|
270
|
+
this.deleteProperty(id);
|
|
271
|
+
this.releaseProperty(prop, value);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Batch Measurement
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
const [va, vb, vc] = m.measure_properties([propA, propB, propC]);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Reading State (No Collapse)
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
// Probabilities — read-only, no state change
|
|
284
|
+
const results = m.probabilities([prop]);
|
|
285
|
+
// [{ probability: 0.5, qudit_values: [0] }, { probability: 0.5, qudit_values: [1] }]
|
|
286
|
+
|
|
287
|
+
// Reduced density matrix — phase and correlation info
|
|
288
|
+
const rdm = m.reduced_density_matrix([prop1, prop2]);
|
|
289
|
+
// Off-diagonal entries carry relative phase
|
|
290
|
+
|
|
291
|
+
// Measure predicate — projective measurement
|
|
292
|
+
const outcome = m.measure_predicate([prop1.is(1), prop2.is(0)]);
|
|
293
|
+
// 1 = satisfied, 0 = not
|
|
294
|
+
|
|
295
|
+
// Forced measurement — replay/save-load only
|
|
296
|
+
const [value] = m.forced_measure_properties([prop], [1]);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Phase & Interference
|
|
300
|
+
|
|
301
|
+
Phase is invisible to measurement but changes how properties interact. Phase + iSwap = probability redistribution through interference.
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
// Apply phase bias
|
|
305
|
+
m.clock(prop, 0.5); // π/2 phase on |1⟩
|
|
306
|
+
|
|
307
|
+
// Later, entangle → phase redistributes probability
|
|
308
|
+
m.i_swap(propA, propB, 0.5);
|
|
309
|
+
// Same phase = constructive interference (probability concentrates)
|
|
310
|
+
// Opposite phase = destructive interference (probability cancels)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Grover oracle** (walls/barriers): mark states with π phase, then diffuse with fractional Hadamard. Probability "bounces off" marked states.
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
m.phase_rotate([hexProp.is(wallState)], Math.PI); // mark
|
|
317
|
+
m.hadamard(hexProp, 0.1); // diffuse
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**Key insight:** Phase is the control knob that lets players influence quantum outcomes without directly choosing them. The player can't pick which ball exists, but they can bias the odds by applying phase before the next entanglement interaction.
|
|
321
|
+
|
|
322
|
+
### Visualizing Phase
|
|
323
|
+
|
|
324
|
+
Extract from the reduced density matrix:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
const rdm = m.reduced_density_matrix([propRef, propTarget]);
|
|
328
|
+
for (const entry of rdm) {
|
|
329
|
+
if (entry.row_values[0] === 1 && entry.row_values[1] === 0 &&
|
|
330
|
+
entry.col_values[0] === 0 && entry.col_values[1] === 1) {
|
|
331
|
+
const phase = Math.atan2(entry.value.imag, entry.value.real);
|
|
332
|
+
// Map to dial rotation, color hue, force magnitude, compass direction
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Recording & Replay
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
import { QuantumRecorder } from "quantum-forge/quantum";
|
|
341
|
+
|
|
342
|
+
const recorder = new QuantumRecorder(qpm);
|
|
343
|
+
recorder.startRecording();
|
|
344
|
+
// ... operations ...
|
|
345
|
+
const log = recorder.getLog(); // serializable JSON
|
|
346
|
+
// Save: localStorage.setItem("quantum-save", JSON.stringify(log));
|
|
347
|
+
// Load: recorder.replayLog(JSON.parse(saved)); // forced measurements
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## Performance
|
|
351
|
+
|
|
352
|
+
**The one rule: pool your properties.** Measure → delete → release. Every time.
|
|
353
|
+
|
|
354
|
+
Shipped limits: Qutrit Edition has dimension 3, 12 max qudits. Qubit Edition has dimension 2, 20 max qudits.
|
|
355
|
+
|
|
356
|
+
| Active Properties | Performance |
|
|
357
|
+
|-------------------|-------------|
|
|
358
|
+
| 1–4 | No issues |
|
|
359
|
+
| 4–8 | Good with pooling |
|
|
360
|
+
| 8–12 | At the limit, aggressive pooling required |
|
|
361
|
+
|
|
362
|
+
**Most expensive operation**: tensor product (triggered by iSwap between properties in different shared states). Pooled properties avoid this — they're already in the shared state.
|
|
363
|
+
|
|
364
|
+
**Strategies**: aggressive pooling, limit concurrent quantum objects, use dimension 2 unless needed, separate registries for independent systems, batch measurements, throttle probability queries.
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
// Monitor WASM memory
|
|
368
|
+
import { getWasmMemoryBytes } from "quantum-forge/quantum";
|
|
369
|
+
const mem = getWasmMemoryBytes(); // bytes, or null
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Engine
|
|
373
|
+
|
|
374
|
+
`Engine<TState>` is the state coordinator. Define your state type, expose mutations through `getHelpers()`.
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
import { Engine } from "quantum-forge-engine/engine";
|
|
378
|
+
|
|
379
|
+
class MyEngine extends Engine<GameState> {
|
|
380
|
+
constructor() { super(initialState); }
|
|
381
|
+
getHelpers() {
|
|
382
|
+
return {
|
|
383
|
+
movePlayer: (dx: number, dy: number) => {
|
|
384
|
+
const state = this.getState();
|
|
385
|
+
this.setState({ ...state, player: { ...state.player, x: state.player.x + dx } });
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
reset() { this.setState(initialState); }
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
| Method | Description |
|
|
394
|
+
|--------|-------------|
|
|
395
|
+
| `getState()` | Current game state |
|
|
396
|
+
| `setState(state)` | Replace game state |
|
|
397
|
+
| `getHelpers()` | Object of state-mutating functions (override) |
|
|
398
|
+
| `reset()` | Reset to initial state (override) |
|
|
399
|
+
|
|
400
|
+
## Rendering
|
|
401
|
+
|
|
402
|
+
### PixiRenderer (WebGL/WebGPU)
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
import { PixiRenderer, GameLoop } from "quantum-forge-engine/rendering";
|
|
406
|
+
|
|
407
|
+
class MyRenderer extends PixiRenderer {
|
|
408
|
+
protected draw(state: GameState) {
|
|
409
|
+
this.graphics.circle(state.player.x, state.player.y, 10).fill("#fff");
|
|
410
|
+
this.drawText("score", `Score: ${state.score}`, 10, 10, { fill: "#fff", fontSize: 16 });
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
await renderer.init(); // required before game loop
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
- `this.graphics` — PixiJS Graphics, cleared each frame
|
|
418
|
+
- `this.drawText(key, content, x, y, style?)` — managed text by key, no texture churn
|
|
419
|
+
- `this.stage` — PixiJS Container root
|
|
420
|
+
- `this.app` — PixiJS Application for sprites/containers
|
|
421
|
+
|
|
422
|
+
### CanvasRenderer (Canvas 2D)
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
import { CanvasRenderer } from "quantum-forge-engine/rendering";
|
|
426
|
+
|
|
427
|
+
class MyRenderer extends CanvasRenderer {
|
|
428
|
+
render(state: GameState) {
|
|
429
|
+
this.clear("#000");
|
|
430
|
+
// draw with this.ctx (CanvasRenderingContext2D)
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### GameLoop
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
const loop = new GameLoop({
|
|
439
|
+
update: (dt) => { input.poll(); /* update state */ },
|
|
440
|
+
render: () => renderer.render(engine.getState()),
|
|
441
|
+
targetFps: 60,
|
|
442
|
+
});
|
|
443
|
+
loop.start();
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
`dt` is in seconds (~0.0167 at 60 FPS).
|
|
447
|
+
|
|
448
|
+
**Rule:** renderers derive visuals from state. Never compute, cache, or decide in the renderer.
|
|
449
|
+
|
|
450
|
+
### Quantum Visualization
|
|
451
|
+
|
|
452
|
+
Map probability to visual properties (opacity, size, color):
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
protected draw(state: GameState) {
|
|
456
|
+
for (const ball of state.balls) {
|
|
457
|
+
const alpha = ball.isQuantum ? ball.existenceProbability : 1.0;
|
|
458
|
+
this.graphics.circle(ball.x, ball.y, ball.radius).fill({ color: ball.color, alpha });
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## Input
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
import { InputManager, GamepadButtons, GamepadAxes } from "quantum-forge-engine/input";
|
|
467
|
+
|
|
468
|
+
const input = new InputManager({ logger });
|
|
469
|
+
input.bind("jump", { type: "key", code: "Space" }, { type: "gamepad-button", index: GamepadButtons.A });
|
|
470
|
+
input.bind("move-up", { type: "key", code: "KeyW" }, { type: "gamepad-axis", index: GamepadAxes.LeftStickY, direction: -1 });
|
|
471
|
+
|
|
472
|
+
// In update loop:
|
|
473
|
+
input.poll();
|
|
474
|
+
if (input.isActionJustPressed("jump")) { /* edge */ }
|
|
475
|
+
if (input.isActionDown("move-up")) { /* held */ }
|
|
476
|
+
const speed = input.getActionValue("move-up") * MAX_SPEED; // analog 0-1
|
|
477
|
+
|
|
478
|
+
// Event handlers
|
|
479
|
+
const unsub = input.on("pause", () => togglePause());
|
|
480
|
+
|
|
481
|
+
// Active device detection
|
|
482
|
+
const device = input.getActiveDevice(); // "keyboard" | "mouse" | "touch" | "gamepad"
|
|
483
|
+
|
|
484
|
+
// Always clean up
|
|
485
|
+
input.destroy();
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
**Source types:** `key`, `mouse-button`, `gamepad-button`, `gamepad-axis` (with direction), `touch-zone`, `touch-joystick`, `touch-gesture`
|
|
489
|
+
|
|
490
|
+
**Touch controls:** `input.addTouchZone({ name, x, y, w, h })` and `input.addJoystick({ name, zone, radius, deadZone, dynamic })`
|
|
491
|
+
|
|
492
|
+
**Local multiplayer:** `LocalMultiplayerManager` wraps multiple `InputManager` instances with automatic gamepad assignment.
|
|
493
|
+
|
|
494
|
+
## Package Exports
|
|
495
|
+
|
|
496
|
+
### Core (`quantum-forge`)
|
|
497
|
+
|
|
498
|
+
| Export | Key APIs |
|
|
499
|
+
|--------|----------|
|
|
500
|
+
| `./quantum` | `ensureLoaded`, `QuantumPropertyManager`, `QuantumRecorder`, `useQuantumForgeBuild` |
|
|
501
|
+
| `./logging` | `Logger` |
|
|
502
|
+
| `./vite-plugin` | `quantumForgeVitePlugin` |
|
|
503
|
+
|
|
504
|
+
### Engine (`quantum-forge-engine`)
|
|
505
|
+
|
|
506
|
+
| Export | Key APIs |
|
|
507
|
+
|--------|----------|
|
|
508
|
+
| `./engine` | `Engine<TState>` |
|
|
509
|
+
| `./rendering` | `PixiRenderer`, `CanvasRenderer`, `GameLoop`, `Camera` |
|
|
510
|
+
| `./input` | `InputManager`, `LocalMultiplayerManager`, `GamepadButtons`, `GamepadAxes` |
|
|
511
|
+
| `./events` | `EventBus` |
|
|
512
|
+
| `./collision` | `SpatialGrid`, AABB/circle/point |
|
|
513
|
+
| `./audio` | `AudioManager` (Howler.js) |
|
|
514
|
+
| `./particles` | `ParticleSystem` |
|
|
515
|
+
| `./animation` | `AnimationSystem` |
|
|
516
|
+
| `./entities` | `EntityManager` |
|
|
517
|
+
| `./state-machine` | `StateMachine` |
|
|
518
|
+
| `./timer` | `TimerManager` |
|
|
519
|
+
| `./save` | `SaveManager` |
|
|
520
|
+
| `./scenes` | `SceneManager` |
|
|
521
|
+
|
|
522
|
+
Optional packages can be added via `npx quantum-forge-engine add-system`.
|
|
523
|
+
|
|
524
|
+
## Links
|
|
525
|
+
|
|
526
|
+
- Documentation: https://docs.quantum.dev
|
|
527
|
+
- Developer site: https://quantum.dev
|
|
528
|
+
- Discord: https://discord.gg/quantumforge
|
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# quantum-forge
|
|
2
|
+
|
|
3
|
+
Real quantum mechanics for game developers. Superposition, entanglement, and interference powered by a compiled C++ quantum simulator running via WebAssembly.
|
|
4
|
+
|
|
5
|
+
This is the core package — WASM loader, quantum property manager, and Vite plugin. For the full game framework (engine, rendering, input, audio, etc.), install [`quantum-forge-engine`](https://www.npmjs.com/package/quantum-forge-engine) instead.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install quantum-forge
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### 1. Configure Vite
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
// vite.config.ts
|
|
19
|
+
import { defineConfig } from "vite";
|
|
20
|
+
import { quantumForgeVitePlugin } from "quantum-forge/vite-plugin";
|
|
21
|
+
|
|
22
|
+
export default defineConfig({
|
|
23
|
+
plugins: [quantumForgeVitePlugin()],
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> **ESM note:** If using `vite.config.js` (not `.ts` or `.mjs`), add `"type": "module"` to your `package.json`.
|
|
28
|
+
|
|
29
|
+
### 2. Use Quantum Mechanics
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { ensureLoaded, QuantumPropertyManager } from "quantum-forge/quantum";
|
|
33
|
+
|
|
34
|
+
// Initialize WASM (call once at startup)
|
|
35
|
+
await ensureLoaded();
|
|
36
|
+
|
|
37
|
+
// Create a property manager
|
|
38
|
+
const qpm = new QuantumPropertyManager({ dimension: 2 });
|
|
39
|
+
|
|
40
|
+
// Create a qubit and put it in superposition
|
|
41
|
+
const qubit = qpm.acquireProperty();
|
|
42
|
+
qpm.hadamard(qubit);
|
|
43
|
+
|
|
44
|
+
// Read probabilities (no collapse)
|
|
45
|
+
const probs = qpm.probabilities(qubit); // [~0.5, ~0.5]
|
|
46
|
+
|
|
47
|
+
// Measure (collapses to 0 or 1)
|
|
48
|
+
const [value] = qpm.measureProperties([qubit]);
|
|
49
|
+
|
|
50
|
+
// Recycle for reuse
|
|
51
|
+
qpm.releaseProperty(qubit, value);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Entanglement
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
const a = qpm.acquireProperty();
|
|
58
|
+
const b = qpm.acquireProperty();
|
|
59
|
+
|
|
60
|
+
qpm.hadamard(a);
|
|
61
|
+
qpm.iSwap(a, b); // Entangle — measuring one determines the other
|
|
62
|
+
|
|
63
|
+
const [va, vb] = qpm.measureProperties([a, b]);
|
|
64
|
+
// va and vb are always anti-correlated
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Editions
|
|
68
|
+
|
|
69
|
+
Two WASM builds are included:
|
|
70
|
+
|
|
71
|
+
| Edition | Dimensions | Max Qudits | Use Case |
|
|
72
|
+
|---------|-----------|------------|----------|
|
|
73
|
+
| **Qutrit** (default) | 2–3 | 12 | Games using qutrits (3-state quantum digits) |
|
|
74
|
+
| **Qubit** | 2 | 20 | Games needing more qubits at dimension 2 |
|
|
75
|
+
|
|
76
|
+
To use the Qubit Edition:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { useQuantumForgeBuild, ensureLoaded } from "quantum-forge/quantum";
|
|
80
|
+
|
|
81
|
+
useQuantumForgeBuild("qubit"); // Call before ensureLoaded()
|
|
82
|
+
await ensureLoaded();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Package Exports
|
|
86
|
+
|
|
87
|
+
| Export | Contents |
|
|
88
|
+
|--------|----------|
|
|
89
|
+
| `quantum-forge/quantum` | `ensureLoaded`, `QuantumPropertyManager`, `QuantumRecorder`, `useQuantumForgeBuild` |
|
|
90
|
+
| `quantum-forge/logging` | `Logger` |
|
|
91
|
+
| `quantum-forge/vite-plugin` | `quantumForgeVitePlugin` |
|
|
92
|
+
|
|
93
|
+
## Gates
|
|
94
|
+
|
|
95
|
+
| Gate | Qudits | Description |
|
|
96
|
+
|------|--------|-------------|
|
|
97
|
+
| `hadamard` | 1 | Equal superposition |
|
|
98
|
+
| `cycle` | 1 | Deterministic rotation: \|0⟩→\|1⟩→\|2⟩→\|0⟩ |
|
|
99
|
+
| `clock` (Z) | 1 | Phase rotation |
|
|
100
|
+
| `shift` (X) | 1 | Value shift |
|
|
101
|
+
| `y` | 1 | Y gate (qubit-only) |
|
|
102
|
+
| `iSwap` | 2 | Entangling swap |
|
|
103
|
+
| `swap` | 2 | Value swap |
|
|
104
|
+
|
|
105
|
+
All gates support fractional application and conditional predicates.
|
|
106
|
+
|
|
107
|
+
## Documentation
|
|
108
|
+
|
|
109
|
+
- [Full documentation](https://docs.quantum.dev)
|
|
110
|
+
- [Developer site](https://quantum.dev)
|
|
111
|
+
- [Discord](https://discord.gg/quantumforge)
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
TypeScript source: MIT (see [LICENSE.md](./LICENSE.md)). WASM binaries: proprietary — free for apps under $100K annual revenue with attribution. See [dist/LICENSE-BINARY.md](./dist/LICENSE-BINARY.md) for binary terms.
|
package/dist/lib/quantum.js
CHANGED
|
@@ -106,7 +106,7 @@ function getWasmMemoryBytes() {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
function getAttribution() {
|
|
109
|
-
return "Powered by Quantum Forge \u2014 \xA9 Quantum
|
|
109
|
+
return "Powered by Quantum Forge \u2014 \xA9 Quantum Native \u2014 quantumnative.io";
|
|
110
110
|
}
|
|
111
111
|
async function registerServiceWorker(swPath = "/quantum-forge-sw.js") {
|
|
112
112
|
if (!("serviceWorker" in navigator)) return null;
|