@inventure71/paddockjs 0.2.0 → 0.3.0
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/CHANGELOG.md +6 -0
- package/INSTALL_AND_UPDATE.md +163 -0
- package/README.md +80 -16
- package/docs/architecture.md +41 -6
- package/docs/data_contract.md +241 -7
- package/docs/data_model.md +1 -0
- package/docs/index.md +1 -0
- package/docs/system_specs.md +43 -12
- package/docs/training.md +104 -0
- package/examples/train-basic-policy.mjs +277 -0
- package/package.json +14 -7
- package/src/api/PaddockSimulatorController.js +7 -2
- package/src/app/BrowserExpertAdapter.js +32 -0
- package/src/app/F1SimulatorApp.js +152 -20
- package/src/app/domBindings.js +0 -1
- package/src/config/defaultOptions.js +8 -0
- package/src/data/championship.js +13 -0
- package/src/data/normalizeDrivers.js +7 -2
- package/src/environment/actions.js +45 -0
- package/src/environment/episode.js +17 -0
- package/src/environment/events.js +17 -0
- package/src/environment/index.d.ts +241 -0
- package/src/environment/index.js +2 -0
- package/src/environment/observations.js +112 -0
- package/src/environment/options.js +150 -0
- package/src/environment/rewards.js +69 -0
- package/src/environment/runtime.js +140 -0
- package/src/environment/sensors.js +257 -0
- package/src/environment/specs.js +79 -0
- package/src/index.d.ts +40 -4
- package/src/index.js +6 -2
- package/src/rendering/proceduralTrackAsset.js +7 -1
- package/src/simulation/raceSimulation.js +9 -1
- package/src/styles.css +71 -27
- package/src/ui/componentTemplates.js +31 -7
- package/AGENTS.md +0 -86
- package/src/standalone.js +0 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- eabe042: Prepare the major 0.3.0 feature release with published-package docs, runtime restart hardening, and the first expert environment API. Public install docs now target npm consumption, package dry-run contents exclude repo-agent files and unexported standalone code, lockfiles are synchronized to the package version, host driver/entry validation rejects duplicate IDs, invalid lap counts normalize to one-lap races, restart can rebuild deterministic tracks from a new `trackSeed`, asset URL changes are explicitly remount-only, and rerender paths destroy replaced PixiJS display children. The release also adds a browser-free JavaScript environment contract through the `@inventure71/paddockjs/environment` subpath and an opt-in browser expert wrapper that reuses the visual simulator's race state, including opt-in in-canvas ray sensor visualization for controlled expert drivers, center-origin geometry-based ray detection for track exit/re-entry transitions and car footprints, a starter `createProgressReward()` callback, and a dependency-free headless training/evaluation example. It also documents the bring-your-own-model boundary, adds environment action/observation specs, and includes a visual policy-runner example that demonstrates `policy.predict(observation)` driving browser expert mode without adding ML dependencies or model persistence.
|
|
8
|
+
|
|
3
9
|
## 0.2.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# PaddockJS Install and Update Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how host websites consume the published PaddockJS npm package.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Install the package from npm:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @inventure71/paddockjs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The host website imports PaddockJS by package name:
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { mountF1Simulator } from '@inventure71/paddockjs';
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Host applications should not copy simulator assets into their own source tree. PaddockJS owns its bundled car, safety-car, logo, panel, and track texture assets.
|
|
20
|
+
|
|
21
|
+
## Host Website Setup
|
|
22
|
+
|
|
23
|
+
Create a host entry file in the consuming website:
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
import {
|
|
27
|
+
CHAMPIONSHIP_ENTRY_BLUEPRINTS,
|
|
28
|
+
DEMO_PROJECT_DRIVERS,
|
|
29
|
+
mountF1Simulator,
|
|
30
|
+
} from '@inventure71/paddockjs';
|
|
31
|
+
|
|
32
|
+
const root = document.getElementById('f1-simulator-root');
|
|
33
|
+
|
|
34
|
+
if (root) {
|
|
35
|
+
await mountF1Simulator(root, {
|
|
36
|
+
drivers: DEMO_PROJECT_DRIVERS,
|
|
37
|
+
entries: CHAMPIONSHIP_ENTRY_BLUEPRINTS,
|
|
38
|
+
title: 'F1 Simulator Lab',
|
|
39
|
+
kicker: 'Race Control',
|
|
40
|
+
backLinkHref: '/projects',
|
|
41
|
+
backLinkLabel: 'Projects',
|
|
42
|
+
onDriverOpen(driver) {
|
|
43
|
+
if (driver.link) window.location.href = driver.link;
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The host page needs a root element and the bundled JavaScript/CSS produced by the host build:
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<div id="f1-simulator-root"></div>
|
|
53
|
+
<script type="module" src="/dist/f1-simulator.js"></script>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If the host bundler does not extract package CSS automatically, import the stylesheet explicitly:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
import '@inventure71/paddockjs/styles.css';
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Build Setup
|
|
63
|
+
|
|
64
|
+
Use a browser bundler that understands JavaScript module imports, CSS imports, and image imports. Vite, Rollup, Webpack, and similar bundlers are valid fits.
|
|
65
|
+
|
|
66
|
+
Use Node `20.19.0` or newer for local package development, showcase builds, and host builds based on the current Vite toolchain.
|
|
67
|
+
|
|
68
|
+
Example Vite entry config:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
import { defineConfig } from 'vite';
|
|
72
|
+
|
|
73
|
+
export default defineConfig({
|
|
74
|
+
build: {
|
|
75
|
+
outDir: 'dist',
|
|
76
|
+
rollupOptions: {
|
|
77
|
+
input: 'src/paddockjs-entry.js',
|
|
78
|
+
output: {
|
|
79
|
+
entryFileNames: 'f1-simulator.js',
|
|
80
|
+
chunkFileNames: 'chunks/[name]-[hash].js',
|
|
81
|
+
assetFileNames: 'assets/[name]-[hash][extname]',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Run the host's normal install and build checks:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npm install
|
|
92
|
+
npm run check
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Do not use raw Node as the main import test:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
node -e "import('@inventure71/paddockjs')"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
That can fail because PaddockJS imports CSS and image assets. Verify through a browser-oriented host build instead.
|
|
102
|
+
|
|
103
|
+
## Updating The Package
|
|
104
|
+
|
|
105
|
+
Install the latest published version:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npm install @inventure71/paddockjs@latest
|
|
109
|
+
npm run check
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Install a specific version when the host needs a controlled upgrade:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npm install @inventure71/paddockjs@0.2.0
|
|
116
|
+
npm run check
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
After updating, smoke-test the page that mounts the simulator. Browser behavior changes should be checked in the consuming host, because host CSS, container size, and route handling are outside the package.
|
|
120
|
+
|
|
121
|
+
## Package Release Workflow
|
|
122
|
+
|
|
123
|
+
The package repo owns its release process:
|
|
124
|
+
|
|
125
|
+
- `npm run check` runs runtime tests, public type verification, dry-pack verification, and the tracked showcase build.
|
|
126
|
+
- `npm run changeset` records the next version bump and changelog note.
|
|
127
|
+
- `npm run version-packages` applies pending Changesets locally.
|
|
128
|
+
- `.github/workflows/ci.yml` verifies the package on push and pull request.
|
|
129
|
+
- `.github/workflows/release.yml` opens a release PR from Changesets and publishes to npm after merge through npm trusted publishing.
|
|
130
|
+
|
|
131
|
+
For trusted publishing on npm, configure the package settings to trust:
|
|
132
|
+
|
|
133
|
+
- organization or user: `Inventure71`
|
|
134
|
+
- repository: `PaddockJS`
|
|
135
|
+
- workflow filename: `release.yml`
|
|
136
|
+
|
|
137
|
+
No long-lived `NPM_TOKEN` secret is required once trusted publishing is enabled.
|
|
138
|
+
|
|
139
|
+
The release PR must include synchronized `package.json`, `package-lock.json`, `CHANGELOG.md`, and Changesets output for the version being published.
|
|
140
|
+
|
|
141
|
+
## When Something Breaks
|
|
142
|
+
|
|
143
|
+
If the host cannot resolve the package, reinstall from npm:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npm install @inventure71/paddockjs@latest
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
If the host bundler cannot resolve an asset, verify the package contents from the package repo:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm run pack:dry
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Check that the asset is listed in the dry-pack output.
|
|
156
|
+
|
|
157
|
+
If simulator tests fail after a package change:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npm test
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Fix the package first, then update and rebuild the host.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# PaddockJS
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
PaddockJS is an installable F1-style simulator component for browser host websites. It owns the simulator source, bundled simulator assets, CSS, demo data, and public mount API.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -10,19 +10,13 @@ From npm:
|
|
|
10
10
|
npm install @inventure71/paddockjs
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
For local sibling-repo development:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install ../PaddockJS
|
|
17
|
-
```
|
|
18
|
-
|
|
19
13
|
## Documentation
|
|
20
14
|
|
|
21
15
|
Start with [docs/index.md](docs/index.md) for system specs, rules, concepts, data contracts, and architecture notes.
|
|
22
16
|
|
|
23
17
|
## Package Workflow
|
|
24
18
|
|
|
25
|
-
The repo
|
|
19
|
+
The repo includes the full package-release boundary:
|
|
26
20
|
|
|
27
21
|
- a tracked showcase host in `local-preview/`
|
|
28
22
|
- public TypeScript declarations in `src/index.d.ts`
|
|
@@ -41,6 +35,67 @@ npm run changeset
|
|
|
41
35
|
|
|
42
36
|
`npm run check` verifies the runtime tests, public declaration file, dry package contents, and showcase build.
|
|
43
37
|
|
|
38
|
+
Local development and showcase builds require Node `20.19.0` or newer. CI currently runs the package check on Node 22 and releases on Node 24.
|
|
39
|
+
|
|
40
|
+
## Expert Environment API
|
|
41
|
+
|
|
42
|
+
Headless training code imports the environment subpath:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
import { createPaddockEnvironment, createProgressReward } from '@inventure71/paddockjs/environment';
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The package root remains the browser component API. The environment subpath is intentionally browser-free and does not import DOM, PixiJS, or package CSS.
|
|
49
|
+
PaddockJS is a bring-your-own-model environment. It does not choose an ML framework, store model weights, or ship a trained driver. See [Bring Your Own Model](docs/training.md) for the policy shape and visual playback loop.
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
const env = createPaddockEnvironment({
|
|
53
|
+
drivers,
|
|
54
|
+
entries,
|
|
55
|
+
controlledDrivers: ['budget'],
|
|
56
|
+
frameSkip: 2,
|
|
57
|
+
reward: createProgressReward(),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
let result = env.reset();
|
|
61
|
+
result = env.step({
|
|
62
|
+
budget: { steering: 0, throttle: 1, brake: 0 },
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The repository includes a dependency-free starter training loop that uses the same environment contract:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
node examples/train-basic-policy.mjs --generations=4 --candidates=5 --episodes=1 --steps=240
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Each ray reports track-transition distance and car distance. A track hit uses `kind: 'exit'` when the ray leaves the road and `kind: 'entry'` when an off-track ray points back to the road.
|
|
73
|
+
|
|
74
|
+
External training code can inspect the environment contract without guessing field ranges:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
const actionSpec = env.getActionSpec();
|
|
78
|
+
const observationSpec = env.getObservationSpec();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Browser expert mode is opt-in through the normal mount API. When enabled, the visual simulator advances only when host code calls `simulator.expert.step(actions)`. Expert mode is a mount-time boundary; changing `expert` through `restart(nextOptions)` is rejected so ticker ownership cannot silently change under a mounted simulator.
|
|
82
|
+
Set `expert.visualizeSensors` to draw expert sensor rays inside the actual race canvas for visual debugging:
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
const simulator = await mountF1Simulator(root, {
|
|
86
|
+
drivers,
|
|
87
|
+
entries,
|
|
88
|
+
expert: {
|
|
89
|
+
enabled: true,
|
|
90
|
+
controlledDrivers: ['budget'],
|
|
91
|
+
frameSkip: 4,
|
|
92
|
+
visualizeSensors: {
|
|
93
|
+
rays: true,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
44
99
|
## API
|
|
45
100
|
|
|
46
101
|
All-in-one mount:
|
|
@@ -68,12 +123,14 @@ const simulator = await mountF1Simulator(document.getElementById('sim-root'), {
|
|
|
68
123
|
vehicle: { id: 'budget-bb01', name: 'BB-01 Ledger', power: 48, braking: 72, aero: 55, dragEfficiency: 66, mechanicalGrip: 63, weightControl: 58, tireCare: 82 },
|
|
69
124
|
},
|
|
70
125
|
],
|
|
126
|
+
initialCameraMode: 'show-all',
|
|
127
|
+
expert: {
|
|
128
|
+
enabled: true,
|
|
129
|
+
controlledDrivers: ['budget'],
|
|
130
|
+
},
|
|
71
131
|
onDriverOpen(driver) {
|
|
72
132
|
window.location.href = driver.link;
|
|
73
133
|
},
|
|
74
|
-
onRaceFinish({ winner, classification }) {
|
|
75
|
-
console.log('Race winner:', winner.name, classification);
|
|
76
|
-
},
|
|
77
134
|
});
|
|
78
135
|
```
|
|
79
136
|
|
|
@@ -96,7 +153,6 @@ simulator.mountSafetyCarControl(document.getElementById('sim-safety-car'));
|
|
|
96
153
|
simulator.mountTimingTower(document.getElementById('sim-timing'));
|
|
97
154
|
simulator.mountRaceCanvas(document.getElementById('sim-race'), {
|
|
98
155
|
includeRaceDataPanel: true,
|
|
99
|
-
includeTelemetrySectorBanner: true,
|
|
100
156
|
});
|
|
101
157
|
simulator.mountTelemetryCore(document.getElementById('sim-telemetry-core'));
|
|
102
158
|
simulator.mountTelemetrySectors(document.getElementById('sim-telemetry-sectors'));
|
|
@@ -114,7 +170,6 @@ The race canvas can also own the timing tower when a host wants a single reusabl
|
|
|
114
170
|
simulator.mountRaceCanvas(document.getElementById('sim-race'), {
|
|
115
171
|
includeTimingTower: true,
|
|
116
172
|
includeRaceDataPanel: true,
|
|
117
|
-
includeTelemetrySectorBanner: true,
|
|
118
173
|
timingTowerVerticalFit: 'scroll',
|
|
119
174
|
});
|
|
120
175
|
```
|
|
@@ -124,6 +179,7 @@ For a packaged race-window template with a right-side telemetry drawer:
|
|
|
124
179
|
```js
|
|
125
180
|
simulator.mountRaceTelemetryDrawer(document.getElementById('sim-race-workbench'), {
|
|
126
181
|
timingTowerVerticalFit: 'expand-race-view',
|
|
182
|
+
raceDataTelemetryDetail: true,
|
|
127
183
|
});
|
|
128
184
|
```
|
|
129
185
|
|
|
@@ -132,13 +188,14 @@ simulator.mountRaceTelemetryDrawer(document.getElementById('sim-race-workbench')
|
|
|
132
188
|
The returned object supports:
|
|
133
189
|
|
|
134
190
|
- `destroy()`
|
|
135
|
-
- `restart(nextOptions)`
|
|
191
|
+
- `restart(nextOptions)` for non-asset, non-expert race/data/seed changes
|
|
136
192
|
- `selectDriver(driverId)`
|
|
137
193
|
- `setSafetyCarDeployed(deployed)`
|
|
138
194
|
- `callSafetyCar()`
|
|
139
195
|
- `clearSafetyCar()`
|
|
140
196
|
- `toggleSafetyCar()`
|
|
141
197
|
- `getSnapshot()`
|
|
198
|
+
- `expert` when explicitly enabled, otherwise `null`
|
|
142
199
|
|
|
143
200
|
Useful UI options:
|
|
144
201
|
|
|
@@ -160,15 +217,22 @@ ui: {
|
|
|
160
217
|
enabled: ['project', 'radio'],
|
|
161
218
|
},
|
|
162
219
|
raceDataBannerSize: 'auto',
|
|
220
|
+
raceDataTelemetryDetail: true,
|
|
163
221
|
timingTowerVerticalFit: 'expand-race-view',
|
|
164
222
|
}
|
|
165
223
|
```
|
|
166
224
|
|
|
167
225
|
`preset` is resolved before explicit host options. Available presets are `dashboard`, `timing-overlay`, `compact-race`, and `full-dashboard`; hosts can start from a preset and override any `ui` or `theme` field. `theme` maps to package CSS variables for the stable sizing/color contract: `accentColor`, `greenColor`, `yellowColor`, `timingTowerMaxWidth`, and `raceViewMinHeight`.
|
|
168
226
|
|
|
169
|
-
If `trackSeed` is omitted, each mounted browser simulator creates a fresh procedural circuit. Passing `trackSeed` makes the track deterministic so multiple embeds can share the same generated circuit; repeated procedural seeds are cached within the page runtime.
|
|
227
|
+
If `trackSeed` is omitted, each mounted browser simulator creates a fresh procedural circuit. Passing `trackSeed` makes the track deterministic so multiple embeds can share the same generated circuit; repeated procedural seeds are cached within the page runtime. `restart({ trackSeed })` rebuilds the race on the deterministic circuit for the new seed. Asset URL changes are not restartable; destroy and mount a new simulator when changing assets.
|
|
228
|
+
|
|
229
|
+
`initialCameraMode` accepts `'overview'`, `'leader'`, `'selected'`, or `'show-all'`; invalid values fall back to `'leader'`. `layoutPreset: 'left-tower-overlay'` creates a left broadcast gutter inside the race view, frames the camera around the remaining race area, and places the timing tower in that gutter without covering camera controls. The project/radio lower-third stays inside the race window and can intentionally render over the timing sidebar instead of shrinking around it. Composable hosts should pass `{ includeRaceDataPanel: true }` to `mountRaceCanvas()` when they want that lower-third clipped and layered by the race window; `mountRaceDataPanel()` remains available for hosts that intentionally want the banner as a standalone surface. `raceDataTelemetryDetail: true` makes the project lower-third include a compact S1/S2/S3 sector strip; radio mode stays the same and the standalone `mountTelemetrySectorBanner()` surface remains available only when a host explicitly mounts it. `includeTimingTower: true` embeds the timing tower directly inside the race canvas and reserves camera space from the measured tower gutter when the tower is a side overlay. In narrow mobile hosts, package CSS stacks the embedded timing tower full-width and the camera stops reserving a fake left gutter. `mountRaceTelemetryDrawer()` is a higher-level template that mounts the race canvas, embedded timing tower, lower-third, safety-car control, and a right telemetry drawer together; pass `{ raceDataTelemetryDetail: true }` when the drawer lower-third should include compact sector detail. Opening the drawer reduces the race area instead of overlaying it, and closing it removes the drawer from interaction. `raceDataBanners.initial` selects the starting banner state (`'project'`, `'radio'`, or `'hidden'`), while `raceDataBanners.enabled` chooses which banner types may appear. `raceDataBannerSize: 'auto'` uses the race space to the right of the timing board when it is wide enough and falls back to the full lower-third overlap when it is not; `'custom'` preserves the default CSS-variable-driven lower-third size for hosts that want to tune their own banner geometry. `timingTowerVerticalFit: 'expand-race-view'` lets the race window grow tall enough for the tower; `'scroll'` keeps the race window height and scrolls the timing list inside the cropped tower. The same fit values can be passed to `mountRaceCanvas()` when `includeTimingTower` is enabled. Standalone timing towers are capped by `--timing-board-max-width` and fill their mount root height; placing the root in a fixed-height container makes only the timing entries scroll. Timing entries always stack from the top as fixed rows, so P1/P2 occupy the same vertical positions whether the race has 2 cars or 20. The timing tower includes an `Int`/`Gap` broadcast switch: `Int` shows the interval to the car ahead, while `Gap` shows total gap to the leader. `cameraControls: 'external'` moves view controls out of the canvas so hosts can mount them with `mountCameraControls()`. `showFps: false` hides the FPS readout. `telemetryIncludesOverview: false` keeps the car/driver overview out of the telemetry stack so hosts can mount `mountCarDriverOverview()` separately. `telemetryModules` controls optional package-owned telemetry surfaces: `core`, `sectors`, `lapTimes`, and `sectorTimes`; those same surfaces are also available as fully detached mount methods.
|
|
230
|
+
|
|
231
|
+
### Composable overlay layout contract
|
|
232
|
+
|
|
233
|
+
`ui.layoutPreset: 'left-tower-overlay'` is package-owned. Hosts should not recreate or depend on internal shell classes such as `.sim-shell--left-tower-overlay` or `.sim-grid` as their public integration contract.
|
|
170
234
|
|
|
171
|
-
|
|
235
|
+
For the all-in-one simulator, call `mountF1Simulator()` and let PaddockJS generate the shell. For composable layouts, hosts should provide mount roots and call package mount methods such as `mountRaceCanvas(root, { includeTimingTower: true, includeRaceDataPanel: true })` or `mountRaceTelemetryDrawer(root)`. Host CSS may size the outer container, but timing-tower placement, gutter measurement, camera safe area, and narrow-screen stacking are owned by PaddockJS.
|
|
172
236
|
|
|
173
237
|
Mounted package surfaces include a lightweight red start-light loading overlay. The runtime removes each overlay after PixiJS, assets, controls, and the initial readouts are ready.
|
|
174
238
|
|
package/docs/architecture.md
CHANGED
|
@@ -60,6 +60,24 @@ host page
|
|
|
60
60
|
-> PixiJS render loop + DOM readout updates
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
Headless environment flow:
|
|
64
|
+
|
|
65
|
+
```txt
|
|
66
|
+
training script
|
|
67
|
+
-> import from @inventure71/paddockjs/environment
|
|
68
|
+
-> createPaddockEnvironment(options)
|
|
69
|
+
-> resolveEnvironmentOptions(options)
|
|
70
|
+
-> createRaceSimulation(...)
|
|
71
|
+
-> env.reset()
|
|
72
|
+
-> env.step(actions)
|
|
73
|
+
-> resolveActionMap(actions)
|
|
74
|
+
-> RaceSimulation.step(...) repeated by frameSkip
|
|
75
|
+
-> buildEnvironmentObservation(...)
|
|
76
|
+
-> optional reward(context)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Browser expert mode reuses the same environment runtime around the visual app's existing `RaceSimulation` instance. When `expert.enabled` is true, the visual ticker does not advance simulation time automatically; `simulator.expert.reset()` and `simulator.expert.step(actions)` are the only simulation-advance entry points.
|
|
80
|
+
|
|
63
81
|
## Public Entry
|
|
64
82
|
|
|
65
83
|
`src/index.js` is the public package entry.
|
|
@@ -75,6 +93,18 @@ Responsibilities:
|
|
|
75
93
|
- Return the controller methods.
|
|
76
94
|
- Re-export the composable mount API.
|
|
77
95
|
|
|
96
|
+
`src/index.js` does not export the headless environment API. Training and automation code imports `@inventure71/paddockjs/environment`, which resolves to `src/environment/index.js`.
|
|
97
|
+
|
|
98
|
+
`src/environment/` owns the browser-free expert environment:
|
|
99
|
+
|
|
100
|
+
- `index.js` and `index.d.ts` expose the public subpath API.
|
|
101
|
+
- `runtime.js` owns environment-loop `reset()`, `step(actions)`, `getObservation()`, and `getState()` around an injected race-simulation host.
|
|
102
|
+
- `options.js` validates `controlledDrivers`, scenario participants, frame skip, action policy, sensor config, and episode limits.
|
|
103
|
+
- `actions.js` maps normalized public controls onto simulator steering/throttle/brake controls.
|
|
104
|
+
- `observations.js`, `sensors.js`, and `events.js` build sensor-style observations, fixed-schema vectors, ray/nearby-car readings, and global/per-driver events.
|
|
105
|
+
|
|
106
|
+
The environment subpath must not import `src/index.js`, package CSS, PixiJS, `F1SimulatorApp`, or DOM-specific code.
|
|
107
|
+
|
|
78
108
|
`src/api/PaddockSimulatorController.js` owns composable mounting.
|
|
79
109
|
|
|
80
110
|
Responsibilities:
|
|
@@ -104,8 +134,12 @@ Responsibilities:
|
|
|
104
134
|
- Race data panel rendering.
|
|
105
135
|
- Safety car button.
|
|
106
136
|
- Multiple package-rendered safety-car buttons bound to one simulation state.
|
|
107
|
-
- Restart behavior.
|
|
108
|
-
- Lifecycle cleanup.
|
|
137
|
+
- Restart behavior for race data, seed, and track changes. Asset URL changes and browser expert mode changes are intentionally outside restart because texture loading and ticker ownership are part of initialization.
|
|
138
|
+
- Lifecycle cleanup, including partial-init failure cleanup and destruction of replaced PixiJS display children without destroying shared textures.
|
|
139
|
+
|
|
140
|
+
When `options.expert.enabled` is true, `F1SimulatorApp` creates a narrow browser expert adapter around its existing `this.sim` race simulation. The adapter uses the same shared environment runtime as the headless API, but it must not create a second race simulation for the visual mount. Expert browser mounts disable automatic ticker-driven simulation advancement; the canvas updates only after `simulator.expert.reset()` or `simulator.expert.step(actions)` renders the new snapshot. Expert mode is a mount-time boundary; runtime restart rejects `expert` changes instead of attempting to rewire ticker ownership in place.
|
|
141
|
+
|
|
142
|
+
Expert sensor visualization belongs to the browser app layer, not the headless environment. The environment result owns the observation contract; `BrowserExpertAdapter` passes that observation into `F1SimulatorApp.renderExpertFrame()`, and `F1SimulatorApp` draws opt-in sensor rays in a Pixi world layer so they share the same camera transform as the track and cars.
|
|
109
143
|
|
|
110
144
|
This file is still large. When changing it substantially, prefer extracting cohesive modules rather than adding unrelated responsibilities.
|
|
111
145
|
|
|
@@ -147,9 +181,9 @@ The app runtime pauses its PixiJS ticker when the race canvas is outside the vie
|
|
|
147
181
|
|
|
148
182
|
`src/data/vehicleData.js` converts vehicle rating sheets into physical setup values.
|
|
149
183
|
|
|
150
|
-
`src/data/championship.js` pairs drivers with entries and generates timing codes, numbers, team metadata, and converted constructor data.
|
|
184
|
+
`src/data/championship.js` pairs drivers with entries and generates timing codes, numbers, team metadata, and converted constructor data. It rejects duplicate entry driver IDs, ignores omitted driver numbers during uniqueness checks, and falls back to stable grid-order numbers when host entries do not provide numbers.
|
|
151
185
|
|
|
152
|
-
`src/data/normalizeDrivers.js` validates host driver data and invokes championship pairing.
|
|
186
|
+
`src/data/normalizeDrivers.js` validates host driver data, rejects duplicate driver IDs before runtime maps are created, and invokes championship pairing.
|
|
153
187
|
|
|
154
188
|
`src/data/demoDrivers.js` is demo/portfolio-flavored sample data. It should not become the only supported data path.
|
|
155
189
|
|
|
@@ -162,14 +196,14 @@ The app runtime pauses its PixiJS ticker when the race canvas is outside the vie
|
|
|
162
196
|
- Safety-car control.
|
|
163
197
|
- Timing tower.
|
|
164
198
|
- Race canvas.
|
|
165
|
-
- Race-data panel.
|
|
199
|
+
- Race-data panel, including the optional project telemetry detail variant.
|
|
166
200
|
- Telemetry stack template.
|
|
167
201
|
- Detached telemetry core, sector graph, lap-time table, and sector-time table.
|
|
168
202
|
- Broadcast sector banner.
|
|
169
203
|
- Race telemetry drawer template.
|
|
170
204
|
- Car/driver overview panel.
|
|
171
205
|
|
|
172
|
-
`src/ui/shellTemplate.js` composes those component templates into the default all-in-one simulator DOM and package-owned layout presets such as `left-tower-overlay`. Telemetry graph/table surfaces are independent package-owned components controlled by `ui.telemetryModules` when used through stack/drawer templates; the app renders them from `car.lapTelemetry` without requiring host-owned DOM. The telemetry stack can embed the car/driver overview, but the overview is also a separately mountable package-owned component. The broadcast sector banner is a separate lower-third-style sector graph that can be mounted standalone or embedded inside the race canvas; `F1SimulatorApp` binds the selected car name/code/color to it through the same readout path as the other telemetry surfaces. The race telemetry drawer template composes a race canvas with embedded timing tower,
|
|
206
|
+
`src/ui/shellTemplate.js` composes those component templates into the default all-in-one simulator DOM and package-owned layout presets such as `left-tower-overlay`. Telemetry graph/table surfaces are independent package-owned components controlled by `ui.telemetryModules` when used through stack/drawer templates; the app renders them from `car.lapTelemetry` without requiring host-owned DOM. The telemetry stack can embed the car/driver overview, but the overview is also a separately mountable package-owned component. The broadcast sector banner is a separate lower-third-style sector graph that can be mounted standalone or embedded inside the race canvas only when a host explicitly asks for that independent popup; `F1SimulatorApp` binds the selected car name/code/color to it through the same readout path as the other telemetry surfaces. The race telemetry drawer template composes a race canvas with embedded timing tower, project/radio lower-third, safety-car control, and detached telemetry components in a right drawer. Its open/close state is owned by `F1SimulatorApp`; the safety-car control remains available while telemetry is open. The drawer reserves final race-view space with a stable margin while the sidebar itself animates with a compositor transform, avoiding grid-template reflow during the slide. The drawer race area inherits the workbench height so host embeds do not leave unused black space below the simulation. The left-tower overlay preset is responsible for internal component placement and package-owned proportions; the project/radio lower-third remains owned by the race canvas so it can either use the race space beside the timing tower in `auto` sizing mode or overlap the tower when space is constrained. Composable hosts can also ask the race-canvas template to embed the timing tower directly with `includeTimingTower`, using the same expand-vs-scroll vertical fit contract. `F1SimulatorApp` measures the resulting timing-tower gutter when framing the PixiJS camera, whether the tower comes from the prebuilt shell or from an embedded composable race canvas. On narrow hosts, CSS stacks timing boards full-width and the camera safe-area measurement treats those boards as stacked content instead of side gutters. Hosts should not provide the internal simulator markup or tune preset internals with raw sizing options.
|
|
173
207
|
|
|
174
208
|
`src/config/defaultOptions.js` owns preset resolution, telemetry module normalization, and the public theme contract. Presets are merged before host options; theme fields are applied as package CSS variables by the all-in-one app and composable controller. This keeps sizing/color customization explicit without turning internal layout ratios into public API.
|
|
175
209
|
|
|
@@ -200,6 +234,7 @@ Current coverage includes:
|
|
|
200
234
|
- Championship metadata.
|
|
201
235
|
- Component API and callback behavior.
|
|
202
236
|
- Driver controller behavior.
|
|
237
|
+
- Expert environment actions, observations, rewards, events, and browser adapter behavior.
|
|
203
238
|
- Procedural track rendering helpers.
|
|
204
239
|
- Race simulation rules.
|
|
205
240
|
- Render interpolation.
|