Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,18 @@ node test/run.js # (alias: npm test)
NODE_PATH=$(npm root -g) node test/smoke.js # (alias: npm run smoke)
# SMOKE_URL overrides the default http://localhost:8080/

# Headless CLI — simulate a saved diagram JSON from the terminal.
# Headless CLI — simulate a saved diagram (JSON or .econ text) from the terminal.
node cli.js diagram.json --steps 500 > trace.csv
node cli.js diagram.json --runs 1000 --steps 200 --seed 42 --param rate=3

# Economy-as-code (docs/ECONOMY_AS_CODE.md): assertions, format conversion,
# standalone-module codegen. Assertions exit 2 on failure (CI-friendly).
node cli.js economy.econ --assert "always gold < 500" --assert "at end: score >= 10"
node cli.js economy.econ --check # run assertions saved in the diagram
node cli.js diagram.json --to-dsl > economy.econ # and .econ --to-json back
node cli.js economy.econ --emit economy.module.js # dependency-free JS module
node cli.js diagram.json --loops # feedback-loop table (R/B/F/?)
node cli.js diagram.json --why "Gold@120" # attribute a node's change at a step
```

**Running a single unit test:** `test/run.js` has no filter/grep flag — every
Expand Down Expand Up @@ -60,6 +69,19 @@ it lets `test/run.js` load them into a bare `new Function` sandbox, lets `cli.js
run them under Node, and lets Monte Carlo clone a `Diagram` + `SimEngine` per trial.
Do not reach for `document`/`window` in these two files.

The economy-as-code layer lives under the same contract: `js/dsl.js` (the `.econ`
text format: `dslSerialize`/`dslParse`/`normalizeEconJSON`), `js/assertions.js`
(`parseAssertion`/`AssertionChecker`/`assertionScope`), `js/codegen.js`
(`buildEconomyModule`), `js/loops.js` (`detectLoops`: feedback-cycle
enumeration + R/B/F/? classification), and `js/attribution.js`
(`attributeChange`: per-step change breakdowns from the flow records the
engine attaches to history entries) are DOM-free, load after
`model.js`/`engine.js` in `index.html`, `cli.js`, and `test/run.js`, and are
documented in `docs/ECONOMY_AS_CODE.md`. When you add a serialized field to
the model, the DSL's generic key=value attrs pick it up automatically, but
extend the `kitchenSink()` fixture in `test/run.js` so the round-trip test
covers it.

Two engine invariants to preserve when editing the tick:
- **Conditions are synchronous.** Activator and connection-condition checks read a
start-of-step snapshot (`_tickSnap`, via `_condValueOf`), so a step's outcome does
Expand Down
682 changes: 661 additions & 21 deletions LICENSE

Large diffs are not rendered by default.

61 changes: 59 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,11 @@ the panel back to the selection. The left palette stays for tools/nodes and the
top bar for run/zoom/file controls.

### Analysis & data
- **Feedback-loop detection** — the Loops rail panel finds every feedback cycle in the causal graph (flows, triggers, activators, modifiers, and formula reads via published variables) and classifies it **reinforcing (R)**, **balancing (B)**, a pure **resource circulation (F)**, or **unclear (?)** from link polarities; formula links are probed numerically. Click a loop to spotlight it on the canvas; `cli.js --loops` prints the same table.
- **Spike attribution** — click any point on the timeline to see **why** that value changed there: per‑connection inflows and outflows, applied modifiers, and internal changes (conversions, queue losses), summing exactly to the delta even on stride‑sampled long runs. The contributing connections spotlight on the canvas while the breakdown is open; `cli.js --why "Gold@120"` prints the same from the terminal.
- **Scenario branching** — checkpoint the simulation mid-run (full state: resources, in-flight queues, variables, history), fork back to it with any tweaks, and run forward again. The previous run is kept automatically as a **ghost branch** — dashed, faded traces overlaid on the timeline chart (same colour = same node across timelines), toggleable from the legend or the Branch rail panel. Reset still returns to the true run start. Session-only — branches aren't saved with the diagram.
- **Global timeline chart** — every tracked node's value over time, with a legend.
- **On‑canvas charts** — live line charts placed in the diagram itself, tracking chosen nodes.
- **On‑canvas charts** — live charts placed in the diagram itself, tracking chosen nodes (line / area / bars / step), plus a **phase portrait** style that plots one node against another in state space — limit cycles draw themselves as orbits, equilibria as inward spirals.
- **Monte Carlo / batch runs** — run N isolated simulations for up to M steps and report per‑node distributions (mean / min / p10 / p50 / p90 / max) plus goal reach‑rate and end‑step stats — non‑destructive to the live diagram. Batches run **chunked off the hot path** with live progress, so the UI never freezes.
- **Seeded, reproducible randomness** — set a **run seed** in the Simulation panel to make a live run reproducible, or give a batch its own seed in the Monte Carlo modal; the same seed reproduces the exact same results, bit for bit. Every stochastic decision (dice, distributions, chance %, probabilistic gates, custom variables) draws from the seedable RNG, and the headless CLI's `--seed` flag does the same from the terminal.
- **Parameter sweeps** — vary one diagram parameter across a range (e.g. 5 values from 0.5× to 1.5×) and compare per‑node means and goal reach‑rates side by side, one column per value. Runs on clones; the live diagram is untouched.
Expand All @@ -143,6 +145,19 @@ top bar for run/zoom/file controls.
- **JSON** save/load, and **SVG / PNG** export of the diagram.
- **Shareable URL** — the whole diagram is base64‑encoded in the URL hash (`#d=…`); opening it restores the diagram. `?embed` (or `#embed`) hides the editing chrome for a clean, embeddable view.

### Economy as code
- **`.econ` text format** — save/open the diagram as readable, diff‑friendly text
that lives happily in git (one line per node/connection; full round‑trip with
the canvas). See **[docs/ECONOMY_AS_CODE.md](docs/ECONOMY_AS_CODE.md)**.
- **Design tests (assertions)** — checks like `always gold < 500` or
`eventually score >= 100`, run per step (and across Monte Carlo trials).
Edit and run them in the **Checks** rail panel; they save with the diagram
(`assert` lines in `.econ`), and `cli.js --check` runs the same suite with a
non‑zero exit on failure — balance regressions become CI failures.
- **Export as JS module** — compile the diagram + engine into one
dependency‑free `.js` file with a `createEconomy()` API (`step/run/get/set/
fire/onStep`), so the economy you designed is the economy your game ships.

For the **why** and **how** behind the model — the tick order, the fair‑allocation
algorithm, the one‑step variable lag, and more — see
**[docs/CONCEPTS.md](docs/CONCEPTS.md)**.
Expand All @@ -168,7 +183,40 @@ node cli.js my-diagram.json --runs 500 --csv --param mine_rate=3 > samples.csv
`--param name=value` (repeatable) overrides diagram parameters without editing
the file; `--seed` makes any run or batch bit‑for‑bit reproducible. Save a
diagram with **File → Save as JSON** (or take one from the library) to feed it
to the CLI.
to the CLI. Files ending in `.econ` are parsed as the text format automatically.

### Economy‑as‑code workflows

```bash
# design tests: fail CI when a balance change breaks the economy (exit code 2)
node cli.js economy.econ --steps 300 \
--assert "always gold < 500" \
--assert "eventually tutorial_done >= 1" \
--assert "at step 60: churn <= 3"

# or run the suite saved in the diagram itself (the app's Checks rail panel /
# `assert` lines in .econ files)
node cli.js economy.econ --check

# the same assertions across 1000 Monte Carlo trials; tolerate 5% unlucky runs
node cli.js economy.econ --runs 1000 --seed 42 --pass-rate 95 \
--assert "at end: gold >= 100"

# assertions can live in a file (one per line, // comments)
node cli.js economy.econ --assert-file economy.checks

# convert between formats (text ↔ JSON)
node cli.js economy.json --to-dsl > economy.econ
node cli.js economy.econ --to-json > economy.json

# emit a standalone, dependency-free JS module of the economy
node cli.js economy.econ --emit economy.module.js
node -e "const E=require('./economy.module.js');
console.log(E.createEconomy({seed:42}).run(200).values())"
```

The `.econ` grammar, assertion language, and generated‑module API are documented
in **[docs/ECONOMY_AS_CODE.md](docs/ECONOMY_AS_CODE.md)**.

---

Expand Down Expand Up @@ -312,3 +360,12 @@ Held until after the in‑progress UI/UX overhaul, so they don't immediately go
The simulation model, file architecture, and APIs documented here and in
`docs/CONCEPTS.md` are independent of the visual design and should remain accurate
across the redesign.

---

## License

This project is licensed under the **GNU Affero General Public License v3.0**
(AGPL‑3.0). See [LICENSE](LICENSE) for the full text. In short: you are free to
use, study, modify, and share it; if you run a modified version as a network
service, you must offer its source to your users under the same license.
Loading
Loading