| title | 04 · Simulation Kernel |
|---|
The Simulation Kernel is the most important technical asset of the entire project.
World
├── Clock macro time (days) and event timestamps
├── RNG keyed RNG derivation (see Deterministic RNG)
├── Entity Store entity store (ordered containers guarantee deterministic iteration)
├── State runtime state
├── Systems system registry
└── Event Queue discrete event queue
The kernel provides mechanisms only, not policies: it knows nothing about "how combat is calculated" or "how gold is paid out" — those are implemented by the Systems registered into it. In the MVP phase, Systems are provided in hardcoded form (see MVP); configuration-driven Systems will be extracted afterward.
The macro timeline advances by discrete events (see Why):
flowchart LR
EQ["Event queue<br>(time-ordered)"] --> EV["Dequeue earliest event"]
EV --> SYS["Dispatch to the corresponding System"]
SYS --> ST["State change"]
ST --> NEW["Enqueue follow-up events"]
NEW --> EQ
ST --> AGG["Online metrics aggregation"]
Two key points:
- Time only jumps between events. No computation happens between two events, so within a day only the few dozen to few hundred events that actually occur are processed — instead of spinning at a fixed step.
- Metrics are updated incrementally as events are processed, not by replaying per-entity detail afterward (see Online Aggregation).
The full MVP event→system subscription map for the kernel (generated from source, kept in sync with the code):
The graph is generated by an example probing the real dispatch surface (system_registry shares the same system construction as the simulation execution, so the subscription matrix cannot drift from reality). Regeneration command (from the repository root):
cargo run -p sandtable-core --example system_graphThe player_day system internally selects an action by behavior band, settles combat, and advances progression; the gacha_roll system internally runs its pity state machine (see MVP).
The copy committed to the repo is reconciled edge by edge by tests: add a system or change a subscription but forget to re-run the example, and CI goes red (the generated-artifact freshness guard, see Testing Strategy).
Initialization order for one run:
Config file → validation → compiled into Model (immutable)
→ World built from Scenario (entities, event queue, RNG, aggregators)
→ event-by-event advancement → metrics output
Model compilation happens before a run: formulas are parsed into executable form at this stage (see Formula Engine), and parameter paths are resolved to indices. No configuration string lookup happens at runtime.
- Experiment-level parallelism: different candidates (parameter combinations) are independent of each other and run in parallel with rayon (see Parameter Sweep). This is the primary parallelism in the MVP.
- Player-level parallelism: when the player independence assumption holds, player states are independent and parallelism can be split per player. Where floating-point aggregation is involved, the parallel-determinism rules must be followed (see Deterministic RNG).
The kernel's target platforms are native (CLI / desktop) and wasm32 (browser). From Phase 1 onward, a CI gate guarantees that cargo check --target wasm32-unknown-unknown passes — compilation capability is not "to be filled in later" but an architectural commitment:
| Rule | Rationale |
|---|---|
| Core hot paths are serially deterministic: algorithms are written serially, and parallelism is a capability injected at the experiment layer (feature flag) | Parallel execution must already obey the determinism rules; under single-threaded WASM it automatically degrades to serial, with unchanged behavior |
Core forbids file / network I/O, the Instant::now wall clock, and platform libraries (duckdb / web-sys / tokio) |
Simulation time comes only from the simulated clock; platform adaptation lives in the shell layer (sandtable-cli / sandtable-wasm / desktop shell) |
| All random consumption goes through keyed derivation; integer / fixed-point arithmetic is preferred cross-platform | See the cross-platform notes in Deterministic RNG |
::: warning One red line
The analysis engine must not enter core. use duckdb::* or use web_sys::* appearing in core is an architectural regression — core's dependency tree contains only pure Rust.
:::
- MVP target: 10,000 players × 30 days, completed in minutes on a developer machine (compute rationale).
- Follow-up target: 100,000 players × 30–90 days. This requires metric collection to be online aggregation from day one; "store the details, aggregate afterward" is not an option — detail storage is infeasible at this scale (see Online Aggregation).
- The kernel stays headless and I/O-independent: it takes configuration and a seed as input, produces metrics as output, and does not depend on network, database, or a graphical interface.