162 lines
7.8 KiB
Markdown
162 lines
7.8 KiB
Markdown
# Probability — design spec
|
||
|
||
A single-player space trading simulation in the browser. Inspired by Space Rangers.
|
||
The core conceit: NPC ships in systems the player cannot see exist as probability
|
||
clouds, not concrete positions. Entering a system collapses the clouds in it.
|
||
Probability is visible gameplay, not just an engine optimization: the player sees
|
||
clouds, stale prices, and aging intel, and trades on imperfect information — exactly
|
||
like every NPC does.
|
||
|
||
## Goals
|
||
|
||
- A complete, playable trading loop: fly, dock, buy low, sell high, reach a credit target.
|
||
- The probability mechanic is visible and legible: clouds on the map, intel that ages.
|
||
- A living galaxy of 100–200 NPC traders at near-zero simulation cost while unobserved.
|
||
- Deterministic, headless-testable simulation core.
|
||
|
||
## Non-goals (v1)
|
||
|
||
- Combat, named pirate units, missions, factions, sound, save/load.
|
||
(Pirates exist only as hazard fields. localStorage save is a v1.5 candidate.)
|
||
- Free 2D flight. All movement is graph-based, including inside systems.
|
||
- Multiplayer, server, persistence beyond a session.
|
||
|
||
## World model
|
||
|
||
- **Galaxy**: an undirected graph of ~25 systems connected by jump lanes, generated
|
||
from a seed with a roughly planar layout.
|
||
- **System contents**: one star, 1–3 planets, one jump gate per lane. Inside a system,
|
||
ships move along straight legs between gate(s) and planets — still a graph
|
||
(nodes: gates, planets; edges: legs).
|
||
- **Commodities**: 6 goods. Each planet produces some and consumes others at fixed
|
||
rates. Price is a deterministic curve over current stock (low stock → high price).
|
||
- **Hazard fields**: each lane has a pirate danger rating in [0, 1]. Danger feeds the
|
||
probabilities of bad branches for ships using that lane: delay, cargo loss, or
|
||
destruction. No individual pirate units exist in v1.
|
||
|
||
## NPC model
|
||
|
||
NPC traders (100–200) alternate between two states:
|
||
|
||
- **Docked (concrete decision point)**: the NPC picks the best arbitrage opportunity
|
||
it knows about and plans a route. Decisions use *stale news*, not live state:
|
||
price snapshots propagate through the galaxy at a fixed speed (a few lanes per
|
||
game-day). This bounds decision cascades — an NPC's plan depends only on committed
|
||
history at planning time — and makes NPCs fallible the same way the player is.
|
||
- **In transit (branch tree)**: the plan is a tree of possible histories. Branch
|
||
points: route choice (weighted by NPC caution vs. lane danger), hazard outcomes
|
||
per lane (safe / delayed / cargo lost / destroyed), and fuzzy arrival times
|
||
(a time distribution per leg whose spread grows with legs traveled since the NPC
|
||
was last observed).
|
||
|
||
**Hidden-variable determinism**: every NPC has a seed. Every branch outcome is drawn
|
||
deterministically from (seed, branch id). Outcomes are therefore fixed-but-unknown
|
||
from the moment a plan is made; observation merely reveals them. This guarantees
|
||
observation-order consistency (seeing a market first or the ship first always yields
|
||
the same history) and makes the sim reproducible for tests.
|
||
|
||
## Probability clouds
|
||
|
||
Because movement is graph-based, a cloud is one-dimensional: a weighted set of
|
||
lane/leg segments, derived analytically from the plan and elapsed time (no per-tick
|
||
integration). Properties:
|
||
|
||
- Total mass is 1 across all branches/segments (invariant, asserted).
|
||
- Spread grows with time/legs since last observation.
|
||
- Observation that excludes branches (e.g., "the ore never arrived at B, so the
|
||
safe-and-on-time branch is dead") renormalizes the remainder — observing a market
|
||
sharpens the cloud of every ship entangled with it.
|
||
|
||
## Markets and probabilistic trades
|
||
|
||
Markets in unobserved systems hold **pending probabilistic transactions**
|
||
("+50 ore, 85% likely, arriving t=10–18") instead of applying trades on a schedule.
|
||
Anything needing a price meanwhile (NPC planning, news snapshots) uses the
|
||
expected (probability-weighted) market state. The concrete price exists only after
|
||
resolution.
|
||
|
||
## Observation and collapse
|
||
|
||
The player's current system is a **classical bubble** — fully concrete and simulated
|
||
in real time. All conversion happens at its boundary:
|
||
|
||
- **Player enters a system**: every cloud with mass inside is sampled (from its seed):
|
||
the ship spawns concrete, or is excluded and its remaining cloud renormalized
|
||
outside.
|
||
- **While the player stays**: an **arrival queue** holds potential arrival events for
|
||
this system — every branch in every NPC tree that routes through it, plus new
|
||
plans made elsewhere as they are created. When an event comes due, roll it: ship
|
||
materializes at the gate, or the branch silently didn't happen. Traffic volume
|
||
emerges from trade-route topology; nothing is scripted.
|
||
- **Docking at a planet**: resolve that market's pending transactions in
|
||
chronological order → concrete stock and price.
|
||
- **A concrete ship jumps out / the player leaves**: concrete state re-fuzzes into a
|
||
tight cloud anchored at the last known position and time, spreading thereafter.
|
||
|
||
## Time
|
||
|
||
Real-time with pause and ×1 / ×4 / ×16 speed. Internally: a global event queue
|
||
(arrivals, plan completions, news propagation) plus a fixed sim tick (~10 Hz,
|
||
scaled by game speed) for in-system ship motion. Clouds cost nothing between
|
||
observations.
|
||
|
||
## Player gameplay (v1)
|
||
|
||
- Fly between systems via gates, dock at planets, buy/sell on a market screen,
|
||
manage credits and cargo capacity. Win: reach a credit target (e.g., 100,000 cr).
|
||
- Player ship is subject to the same lane hazards (cargo loss / delay; destruction
|
||
= game over).
|
||
- **Galaxy map intel layer**:
|
||
- Lanes tinted by hazard rating.
|
||
- Last-known prices per planet, displayed with their age ("ore 12 cr — 3 days old").
|
||
The player's price knowledge updates by visiting or as news reaches their location.
|
||
- Probability clouds rendered as glowing smears along lanes — only for ships the
|
||
player has personally observed at least once.
|
||
|
||
## Rendering
|
||
|
||
- **PixiJS v8** on a single canvas, WebGL backend.
|
||
- **Two views**: galaxy map (systems = bright circles, lanes = dim lines, clouds =
|
||
bloomed smears) and system view (star = large bright circle, planets = circles,
|
||
ships = triangles sliding along legs, gates marked at the rim).
|
||
- **Neon look**: `AdvancedBloomFilter` (pixi-filters) over the world container;
|
||
simple flat-color geometry underneath.
|
||
- **UI as DOM overlay** (not canvas): market table, cargo panel, time controls,
|
||
hover tooltips.
|
||
|
||
## Architecture
|
||
|
||
```
|
||
src/
|
||
sim/ pure JS, zero rendering imports; deterministic given a seed
|
||
galaxy generation, economy, news propagation, NPC planning,
|
||
branch trees, clouds, observation/collapse, event queue, seeded RNG
|
||
render/ Pixi scene graph, two views, bloom, motion interpolation
|
||
ui/ DOM panels and tooltips
|
||
main.js game loop: advance sim, project state into render/ui
|
||
```
|
||
|
||
The sim exposes a small read API (system contents, cloud segments, market views,
|
||
player state) and a small command API (depart, dock, buy, sell, set speed). The
|
||
renderer and UI never mutate sim state directly.
|
||
|
||
Plain JavaScript (ES modules), Vite for dev server and bundling, Vitest for tests.
|
||
|
||
## Testing
|
||
|
||
Headless tests against `sim/`:
|
||
|
||
- Cloud invariants: mass sums to 1; renormalization after exclusion is correct.
|
||
- **Observation-order independence**: observing market-then-ship produces the same
|
||
committed history as ship-then-market (the per-seed hidden-variable guarantee).
|
||
- Economy sanity: prices respond to stock; NPC arbitrage narrows price gaps over time.
|
||
- Event queue: chronological resolution; arrival events match branch probabilities
|
||
over many seeds (statistical test).
|
||
- Determinism: same galaxy seed + same player inputs → identical end state.
|
||
|
||
## Error handling
|
||
|
||
Single-player browser game: invariant assertions in `sim/` (probability mass,
|
||
non-negative stock, queue ordering) that throw in dev builds. No network, no I/O
|
||
beyond the page.
|