@inventure71/paddockjs 0.3.0 → 2.0.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.
Files changed (202) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/INSTALL_AND_UPDATE.md +5 -2
  3. package/README.md +328 -13
  4. package/docs/architecture.md +150 -41
  5. package/docs/concepts.md +22 -3
  6. package/docs/custom_model_controller.md +329 -0
  7. package/docs/data_contract.md +474 -34
  8. package/docs/data_model.md +1 -1
  9. package/docs/index.md +1 -0
  10. package/docs/learnings.md +23 -1
  11. package/docs/rules.md +169 -21
  12. package/docs/system_specs.md +56 -20
  13. package/docs/training.md +369 -25
  14. package/examples/python/README.md +62 -0
  15. package/examples/python/base_policy_server.py +191 -0
  16. package/examples/python/requirements.txt +2 -0
  17. package/examples/train-basic-policy.mjs +5 -6
  18. package/examples/trainingData.mjs +133 -0
  19. package/package.json +15 -4
  20. package/src/api/PaddockSimulatorController.js +36 -2
  21. package/src/app/BrowserExpertAdapter.js +367 -9
  22. package/src/app/F1SimulatorApp.js +615 -940
  23. package/src/app/banners/bannerFormatters.js +55 -0
  24. package/src/app/banners/raceDataBannerController.js +141 -0
  25. package/src/app/banners/stewardMessageController.js +99 -0
  26. package/src/app/camera/cameraConstants.js +21 -0
  27. package/src/app/camera/cameraController.js +265 -0
  28. package/src/app/camera/cameraGeometry.js +176 -0
  29. package/src/app/domBindings.js +14 -1
  30. package/src/app/readouts/carOverviewRenderer.js +109 -0
  31. package/src/app/readouts/controlStateRenderer.js +37 -0
  32. package/src/app/readouts/raceStatusRenderer.js +133 -0
  33. package/src/app/readouts/readoutFormatters.js +58 -0
  34. package/src/app/readouts/telemetryRenderer.js +113 -0
  35. package/src/app/readouts/timingTowerRenderer.js +177 -0
  36. package/src/app/rendering/appAssets.js +38 -0
  37. package/src/app/rendering/carRenderer.js +286 -0
  38. package/src/app/rendering/displayUtils.js +22 -0
  39. package/src/app/rendering/drsTrailRenderer.js +90 -0
  40. package/src/app/rendering/expertSensorRenderer.js +126 -0
  41. package/src/app/rendering/pitLaneStatusRenderer.js +50 -0
  42. package/src/app/rendering/replayGhostRenderer.js +124 -0
  43. package/src/app/rendering/trackRenderer.js +25 -0
  44. package/src/app/runtime/frameLoop.js +66 -0
  45. package/src/app/runtime/runtimeTiming.js +34 -0
  46. package/src/app/runtime/runtimeVisibility.js +45 -0
  47. package/src/config/defaultOptions.js +24 -4
  48. package/src/environment/actions.js +38 -7
  49. package/src/environment/controllerLoop.js +246 -0
  50. package/src/environment/episode.js +86 -2
  51. package/src/environment/evaluation.js +206 -0
  52. package/src/environment/index.d.ts +638 -6
  53. package/src/environment/index.js +14 -0
  54. package/src/environment/metrics.js +159 -0
  55. package/src/environment/observationOptions.js +33 -0
  56. package/src/environment/observations.js +500 -43
  57. package/src/environment/options.js +41 -2
  58. package/src/environment/recorder.js +28 -0
  59. package/src/environment/runtime.js +260 -24
  60. package/src/environment/scenarios.js +249 -0
  61. package/src/environment/sensors/bodySenses.js +23 -0
  62. package/src/environment/sensors/boundarySenses.js +17 -0
  63. package/src/environment/sensors/carRays.js +75 -0
  64. package/src/environment/sensors/contactSenses.js +32 -0
  65. package/src/environment/sensors/index.js +157 -0
  66. package/src/environment/sensors/indexedRayBands.js +103 -0
  67. package/src/environment/sensors/nearbyCars.js +52 -0
  68. package/src/environment/sensors/opponentRadar.js +65 -0
  69. package/src/environment/sensors/rayConfig.js +110 -0
  70. package/src/environment/sensors/rayDefaults.js +6 -0
  71. package/src/environment/sensors/rayGeometry.js +60 -0
  72. package/src/environment/sensors/rayGuards.js +19 -0
  73. package/src/environment/sensors/sensorTargets.js +60 -0
  74. package/src/environment/sensors/surfaceRays.js +296 -0
  75. package/src/environment/sensors/trackRays.js +261 -0
  76. package/src/environment/sensors.js +1 -257
  77. package/src/environment/specs.js +188 -9
  78. package/src/environment/workerProtocol.js +64 -0
  79. package/src/index.d.ts +533 -7
  80. package/src/index.js +26 -0
  81. package/src/rendering/proceduralTrackAsset.js +20 -510
  82. package/src/rendering/renderSnapshot.js +38 -12
  83. package/src/rendering/track/finishLineRenderer.js +34 -0
  84. package/src/rendering/track/gridRenderer.js +47 -0
  85. package/src/rendering/track/offsetStrokeSafety.js +136 -0
  86. package/src/rendering/track/pitLaneRenderer.js +248 -0
  87. package/src/rendering/track/trackMaterialRenderer.js +326 -0
  88. package/src/rendering/track/trackRenderConstants.js +38 -0
  89. package/src/rendering/track/trackRenderGeometry.js +94 -0
  90. package/src/simulation/collisionGeometry.js +214 -0
  91. package/src/simulation/driver/driverControlConstants.js +28 -0
  92. package/src/simulation/driver/driverController.js +47 -0
  93. package/src/simulation/driver/driverInput.js +28 -0
  94. package/src/simulation/driver/driverMath.js +6 -0
  95. package/src/simulation/driver/driverPersonality.js +25 -0
  96. package/src/simulation/driver/edgeRecovery.js +48 -0
  97. package/src/simulation/driver/racingControls.js +399 -0
  98. package/src/simulation/driver/racingLinePlan.js +166 -0
  99. package/src/simulation/driver/recoveryDynamics.js +38 -0
  100. package/src/simulation/driver/rejoinControls.js +213 -0
  101. package/src/simulation/driver/safetyCarControls.js +35 -0
  102. package/src/simulation/driver/trafficScan.js +51 -0
  103. package/src/simulation/driverController.js +4 -253
  104. package/src/simulation/participants/participantInteractions.js +120 -0
  105. package/src/simulation/pit/pitFlow.js +234 -0
  106. package/src/simulation/pit/pitIntent.js +92 -0
  107. package/src/simulation/pit/pitOccupancy.js +47 -0
  108. package/src/simulation/pit/pitPenaltyService.js +71 -0
  109. package/src/simulation/pit/pitQueue.js +55 -0
  110. package/src/simulation/pit/pitRouteMovement.js +160 -0
  111. package/src/simulation/pit/pitRouting.js +319 -0
  112. package/src/simulation/pit/pitService.js +41 -0
  113. package/src/simulation/pit/pitServiceConstants.js +21 -0
  114. package/src/simulation/pit/pitServiceProfile.js +45 -0
  115. package/src/simulation/pit/pitSnapshots.js +61 -0
  116. package/src/simulation/pit/pitState.js +113 -0
  117. package/src/simulation/pit/pitTireService.js +92 -0
  118. package/src/simulation/race/classification.js +104 -0
  119. package/src/simulation/race/gridPenalties.js +37 -0
  120. package/src/simulation/race/raceControlState.js +38 -0
  121. package/src/simulation/race/raceDistance.js +32 -0
  122. package/src/simulation/race/raceFinish.js +65 -0
  123. package/src/simulation/race/raceLifecycle.js +71 -0
  124. package/src/simulation/race/raceOrder.js +113 -0
  125. package/src/simulation/race/raceProgress.js +124 -0
  126. package/src/simulation/race/raceSetup.js +93 -0
  127. package/src/simulation/race/raceStep.js +80 -0
  128. package/src/simulation/race/redFlag.js +15 -0
  129. package/src/simulation/race/retirements.js +44 -0
  130. package/src/simulation/race/safetyCar.js +86 -0
  131. package/src/simulation/race/stalledDnf.js +86 -0
  132. package/src/simulation/raceSimulation.js +386 -1012
  133. package/src/simulation/replay/replayGhosts.js +128 -0
  134. package/src/simulation/rules/collisionSteward.js +69 -0
  135. package/src/simulation/rules/moduleConfig.js +72 -0
  136. package/src/simulation/rules/penaltyConfig.js +99 -0
  137. package/src/simulation/rules/penaltyEvents.js +18 -0
  138. package/src/simulation/rules/penaltyLedger.js +153 -0
  139. package/src/simulation/rules/penaltyStats.js +26 -0
  140. package/src/simulation/rules/pitLaneSpeedingSteward.js +71 -0
  141. package/src/simulation/rules/ruleConfigMerge.js +41 -0
  142. package/src/simulation/rules/ruleDefaults.js +85 -0
  143. package/src/simulation/rules/rulesReview.js +141 -0
  144. package/src/simulation/rules/rulesetPresets.js +47 -0
  145. package/src/simulation/rules/tireRequirementSteward.js +29 -0
  146. package/src/simulation/rules/trackLimitsSteward.js +85 -0
  147. package/src/simulation/rulesConfig.js +34 -0
  148. package/src/simulation/snapshots/raceSnapshots.js +192 -0
  149. package/src/simulation/timing/drsTiming.js +87 -0
  150. package/src/simulation/timing/gapEstimation.js +38 -0
  151. package/src/simulation/timing/lapTelemetry.js +182 -0
  152. package/src/simulation/timing/raceTiming.js +15 -0
  153. package/src/simulation/timing/sectorPerformance.js +60 -0
  154. package/src/simulation/timing/timingConstants.js +5 -0
  155. package/src/simulation/timing/timingHistory.js +46 -0
  156. package/src/simulation/timing/timingLines.js +79 -0
  157. package/src/simulation/track/centerlineSpline.js +59 -0
  158. package/src/simulation/track/drsZones.js +67 -0
  159. package/src/simulation/track/pitLaneAccess.js +103 -0
  160. package/src/simulation/track/pitLaneLayout.js +395 -0
  161. package/src/simulation/track/pitLaneState.js +160 -0
  162. package/src/simulation/track/proceduralCenterline.js +237 -0
  163. package/src/simulation/track/sampleGeometry.js +49 -0
  164. package/src/simulation/track/spatialQueries.js +154 -0
  165. package/src/simulation/track/startStraight.js +141 -0
  166. package/src/simulation/track/trackConstants.js +109 -0
  167. package/src/simulation/track/trackGenerationOptions.js +283 -0
  168. package/src/simulation/track/trackMath.js +241 -0
  169. package/src/simulation/track/trackModel.js +240 -0
  170. package/src/simulation/track/trackQueryIndex.js +945 -0
  171. package/src/simulation/track/trackSectors.js +19 -0
  172. package/src/simulation/track/trackStatePolicy.js +24 -0
  173. package/src/simulation/track/trackValidation.js +98 -0
  174. package/src/simulation/trackModel.js +1 -628
  175. package/src/simulation/units.js +6 -4
  176. package/src/simulation/vehicle/contactResolution.js +116 -0
  177. package/src/simulation/vehicle/mainTrackWheelSurface.js +82 -0
  178. package/src/simulation/vehicle/pitWheelSurface.js +69 -0
  179. package/src/simulation/vehicle/runoffResponse.js +88 -0
  180. package/src/simulation/vehicle/surfacePriority.js +28 -0
  181. package/src/simulation/vehicle/vehicleGeometry.js +247 -0
  182. package/src/simulation/vehicle/vehiclePhysics.js +363 -0
  183. package/src/simulation/vehicle/vehicleSnapshots.js +316 -0
  184. package/src/simulation/vehicle/vehicleState.js +186 -0
  185. package/src/simulation/vehicle/wheelSurface.js +150 -0
  186. package/src/simulation/vehicleGeometry.js +1 -0
  187. package/src/simulation/vehiclePhysics.js +1 -104
  188. package/src/simulation/warmup/runtimeWarmup.js +224 -0
  189. package/src/simulation/wheelSurface.js +1 -0
  190. package/src/styles.css +342 -42
  191. package/src/ui/bannerTemplates.js +69 -0
  192. package/src/ui/cameraControlsTemplate.js +20 -0
  193. package/src/ui/carOverviewTemplate.js +33 -0
  194. package/src/ui/componentTemplates.js +20 -426
  195. package/src/ui/raceCanvasTemplate.js +69 -0
  196. package/src/ui/raceControlStatusBanner.js +39 -0
  197. package/src/ui/raceControlsTemplate.js +36 -0
  198. package/src/ui/shellTemplate.js +6 -5
  199. package/src/ui/telemetryDrawerTemplate.js +55 -0
  200. package/src/ui/telemetryTemplates.js +136 -0
  201. package/src/ui/templateUtils.js +23 -0
  202. package/src/ui/timingTowerTemplate.js +34 -0
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env python3
2
+ """Neutral PaddockJS policy server base example.
3
+
4
+ This file is intentionally minimal and transport-focused:
5
+ - HTTP policy endpoints:
6
+ - POST /policy/reset
7
+ - POST /policy/reset-state
8
+ - POST /policy/decide-batch
9
+ - WebSocket preview endpoint:
10
+ - /preview (broadcasts {snapshot, observation, meta})
11
+
12
+ Extend `BasePolicyServer` and override hook methods to plug your own model/runtime.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import asyncio
18
+ import time
19
+ from dataclasses import dataclass, field
20
+ from typing import Any, Dict, Iterable, List, Mapping, MutableMapping, Optional
21
+
22
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect
23
+ from fastapi.middleware.cors import CORSMiddleware
24
+ from fastapi.responses import JSONResponse
25
+ import uvicorn
26
+
27
+
28
+ def _zero_action() -> Dict[str, float]:
29
+ return {
30
+ "steering": 0.0,
31
+ "throttle": 0.0,
32
+ "brake": 0.0,
33
+ }
34
+
35
+
36
+ @dataclass
37
+ class ServerState:
38
+ session_id: int = 0
39
+ controlled_drivers: List[str] = field(default_factory=list)
40
+ connections: List[WebSocket] = field(default_factory=list)
41
+ latest_preview_frame: Optional[Dict[str, Any]] = None
42
+
43
+
44
+ class BasePolicyServer:
45
+ """Base extension surface for custom policy/runtime integrations."""
46
+
47
+ def __init__(self) -> None:
48
+ self.app = FastAPI(title="PaddockJS Base Policy Server")
49
+ self.app.add_middleware(
50
+ CORSMiddleware,
51
+ allow_origins=["*"],
52
+ allow_methods=["*"],
53
+ allow_headers=["*"],
54
+ )
55
+ self.state = ServerState()
56
+ self._broadcast_lock = asyncio.Lock()
57
+ self._register_routes()
58
+
59
+ # ----- Extension hooks -------------------------------------------------
60
+
61
+ async def init_policy(self, context: Mapping[str, Any]) -> None:
62
+ """Initialize policy/runtime resources on full reset."""
63
+
64
+ async def reset_policy(self, context: Mapping[str, Any]) -> None:
65
+ """Reset all policy runtime state for a new run/session."""
66
+
67
+ async def reset_policy_state(self, driver_ids: Iterable[str]) -> None:
68
+ """Reset only selected driver-local model state."""
69
+
70
+ async def decide_batch(self, context: Mapping[str, Any]) -> Dict[str, Dict[str, float]]:
71
+ """Return per-driver controls for one batched policy step."""
72
+ driver_ids = list(context.get("driverIds") or [])
73
+ return {driver_id: _zero_action() for driver_id in driver_ids}
74
+
75
+ async def publish_preview_frame(self, frame: Mapping[str, Any]) -> None:
76
+ """Optional extension hook called before broadcasting preview frames."""
77
+
78
+ # ----- Public helpers --------------------------------------------------
79
+
80
+ async def broadcast_preview_frame(
81
+ self,
82
+ snapshot: Mapping[str, Any],
83
+ observation: Mapping[str, Any],
84
+ meta: Optional[Mapping[str, Any]] = None,
85
+ ) -> None:
86
+ """Broadcast one authoritative preview frame to websocket clients."""
87
+ frame = {
88
+ "type": "preview:snapshot",
89
+ "snapshot": dict(snapshot),
90
+ "observation": dict(observation),
91
+ "meta": dict(meta or {}),
92
+ }
93
+ self.state.latest_preview_frame = frame
94
+ await self.publish_preview_frame(frame)
95
+ payload = frame
96
+ async with self._broadcast_lock:
97
+ stale: List[WebSocket] = []
98
+ for ws in self.state.connections:
99
+ try:
100
+ await ws.send_json(payload)
101
+ except Exception:
102
+ stale.append(ws)
103
+ if stale:
104
+ self.state.connections = [ws for ws in self.state.connections if ws not in stale]
105
+
106
+ # ----- Internal routing ------------------------------------------------
107
+
108
+ def _register_routes(self) -> None:
109
+ @self.app.get("/preview/frame")
110
+ async def preview_frame() -> JSONResponse:
111
+ if self.state.latest_preview_frame:
112
+ return JSONResponse(self.state.latest_preview_frame)
113
+ return JSONResponse(
114
+ {
115
+ "type": "preview:status",
116
+ "status": "waiting-for-frame",
117
+ "session": {"id": self.state.session_id},
118
+ }
119
+ )
120
+
121
+ @self.app.post("/policy/reset")
122
+ async def policy_reset(body: MutableMapping[str, Any]) -> JSONResponse:
123
+ self.state.session_id += 1
124
+ self.state.controlled_drivers = list(body.get("driverIds") or [])
125
+ await self.init_policy(body)
126
+ await self.reset_policy(body)
127
+ return JSONResponse(
128
+ {
129
+ "ok": True,
130
+ "session": {
131
+ "id": self.state.session_id,
132
+ "controlledDrivers": self.state.controlled_drivers,
133
+ },
134
+ }
135
+ )
136
+
137
+ @self.app.post("/policy/reset-state")
138
+ async def policy_reset_state(body: MutableMapping[str, Any]) -> JSONResponse:
139
+ driver_ids = list(body.get("driverIds") or [])
140
+ await self.reset_policy_state(driver_ids)
141
+ return JSONResponse(
142
+ {
143
+ "ok": True,
144
+ "session": {"id": self.state.session_id},
145
+ "resetDrivers": driver_ids,
146
+ }
147
+ )
148
+
149
+ @self.app.post("/policy/decide-batch")
150
+ async def policy_decide_batch(body: MutableMapping[str, Any]) -> JSONResponse:
151
+ actions = await self.decide_batch(body)
152
+ if not isinstance(actions, dict):
153
+ actions = {}
154
+ now_ms = int(time.time() * 1000)
155
+ return JSONResponse(
156
+ {
157
+ "ok": True,
158
+ "session": {"id": self.state.session_id},
159
+ "actions": actions,
160
+ "meta": {"serverTsMs": now_ms},
161
+ }
162
+ )
163
+
164
+ @self.app.websocket("/preview")
165
+ async def preview_stream(ws: WebSocket) -> None:
166
+ await ws.accept()
167
+ self.state.connections.append(ws)
168
+ try:
169
+ await ws.send_json(
170
+ {
171
+ "type": "preview:status",
172
+ "status": "connected",
173
+ "session": {"id": self.state.session_id},
174
+ }
175
+ )
176
+ while True:
177
+ # Keep connection alive; ignore client messages.
178
+ await ws.receive_text()
179
+ except WebSocketDisconnect:
180
+ pass
181
+ finally:
182
+ self.state.connections = [conn for conn in self.state.connections if conn is not ws]
183
+
184
+
185
+ def run(host: str = "127.0.0.1", port: int = 8787) -> None:
186
+ server = BasePolicyServer()
187
+ uvicorn.run(server.app, host=host, port=port, log_level="info")
188
+
189
+
190
+ if __name__ == "__main__":
191
+ run()
@@ -0,0 +1,2 @@
1
+ fastapi
2
+ uvicorn
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { createPaddockEnvironment, createProgressReward } from '../src/environment/index.js';
4
- import { CHAMPIONSHIP_ENTRY_BLUEPRINTS } from '../src/data/championship.js';
5
- import { DEMO_PROJECT_DRIVERS } from '../src/data/demoDrivers.js';
3
+ import { createPaddockEnvironment, createProgressReward } from '@inventure71/paddockjs/environment';
4
+ import { TRAINING_DEMO_DRIVERS, TRAINING_DEMO_ENTRIES } from './trainingData.mjs';
6
5
 
7
6
  const DEFAULT_OPTIONS = Object.freeze({
8
7
  generations: 4,
@@ -38,7 +37,7 @@ function main() {
38
37
  return;
39
38
  }
40
39
 
41
- const driverId = DEMO_PROJECT_DRIVERS[0].id;
40
+ const driverId = TRAINING_DEMO_DRIVERS[0].id;
42
41
  const rng = createSeededRandom(options.seed);
43
42
  const reward = createProgressReward();
44
43
  const evaluate = (policy, seedOffset = 0) => evaluatePolicy({
@@ -122,10 +121,10 @@ function evaluatePolicy({ policy, driverId, reward, options, seedOffset = 0 }) {
122
121
  }
123
122
 
124
123
  function createTrainingEnvironment({ driverId, reward, options, episode, seedOffset }) {
125
- const drivers = options.traffic ? DEMO_PROJECT_DRIVERS.slice(0, 8) : DEMO_PROJECT_DRIVERS.slice(0, 1);
124
+ const drivers = options.traffic ? TRAINING_DEMO_DRIVERS.slice(0, 8) : TRAINING_DEMO_DRIVERS.slice(0, 1);
126
125
  return createPaddockEnvironment({
127
126
  drivers,
128
- entries: CHAMPIONSHIP_ENTRY_BLUEPRINTS,
127
+ entries: TRAINING_DEMO_ENTRIES,
129
128
  controlledDrivers: [driverId],
130
129
  seed: options.seed + seedOffset + episode,
131
130
  trackSeed: options.seed + 2026 + seedOffset + episode,
@@ -0,0 +1,133 @@
1
+ export const TRAINING_DEMO_DRIVERS = [
2
+ {
3
+ id: 'budget',
4
+ code: 'BUD',
5
+ icon: 'BB',
6
+ name: 'Budget Buddy',
7
+ color: '#ff2d55',
8
+ tire: 'M',
9
+ raceData: ['AI finance coach', 'Python + LLM', 'Budget guardrails'],
10
+ },
11
+ {
12
+ id: 'noir',
13
+ code: 'NOI',
14
+ icon: 'NN',
15
+ name: 'Neural Noir',
16
+ color: '#ffb000',
17
+ tire: 'M',
18
+ raceData: ['Procedural mystery', 'Gemini storytelling', 'Unreal loop'],
19
+ },
20
+ {
21
+ id: 'vinyl',
22
+ code: 'HOL',
23
+ icon: 'HV',
24
+ name: 'HoloVinyl',
25
+ color: '#00c2a8',
26
+ tire: 'H',
27
+ raceData: ['Touchless DJ control', 'Computer vision', 'Hand tracking'],
28
+ },
29
+ {
30
+ id: 'drsorriso',
31
+ code: 'DRS',
32
+ icon: 'DR',
33
+ name: 'DrSorrisoDonations',
34
+ color: '#e10600',
35
+ tire: 'S',
36
+ raceData: ['Donation intake', 'Database workflow', 'Automation tool'],
37
+ },
38
+ {
39
+ id: 'victoria',
40
+ code: 'VIC',
41
+ icon: 'VI',
42
+ name: 'VictorIA',
43
+ color: '#00a3ff',
44
+ tire: 'M',
45
+ raceData: ['Game AI sandbox', 'Robotics ideas', 'Vision systems'],
46
+ },
47
+ {
48
+ id: 'reminderz',
49
+ code: 'REM',
50
+ icon: 'RZ',
51
+ name: 'ReminderZ',
52
+ color: '#a3e635',
53
+ tire: 'H',
54
+ raceData: ['Knowledge workflow', 'Personal graph', 'Capture tool'],
55
+ },
56
+ {
57
+ id: 'clipclop',
58
+ code: 'CLP',
59
+ icon: 'CC',
60
+ name: 'ClipClop',
61
+ color: '#f97316',
62
+ tire: 'S',
63
+ raceData: ['Mac + Android sync', 'Local clipboard', 'Productivity app'],
64
+ },
65
+ {
66
+ id: 'core',
67
+ code: 'COR',
68
+ icon: 'AI',
69
+ name: 'Core Conflict',
70
+ color: '#38bdf8',
71
+ tire: 'M',
72
+ raceData: ['Runtime AI agents', 'Multiplayer state', 'Game systems'],
73
+ },
74
+ ];
75
+
76
+ export const TRAINING_DEMO_ENTRIES = [
77
+ {
78
+ driverId: 'budget',
79
+ driverNumber: 71,
80
+ timingName: 'Budget',
81
+ driver: { pace: 52, racecraft: 74, aggression: 38, riskTolerance: 47, patience: 81, consistency: 86 },
82
+ vehicle: { id: 'budget-bb01', name: 'BB-01 Ledger', power: 48, braking: 72, aero: 55, dragEfficiency: 66, mechanicalGrip: 63, weightControl: 58, tireCare: 82 },
83
+ },
84
+ {
85
+ driverId: 'noir',
86
+ driverNumber: 13,
87
+ timingName: 'Noir',
88
+ driver: { pace: 68, racecraft: 83, aggression: 61, riskTolerance: 72, patience: 57, consistency: 64 },
89
+ vehicle: { id: 'noir-nn13', name: 'NN-13 Shadow', power: 64, braking: 60, aero: 73, dragEfficiency: 57, mechanicalGrip: 69, weightControl: 54, tireCare: 51 },
90
+ },
91
+ {
92
+ driverId: 'vinyl',
93
+ driverNumber: 33,
94
+ timingName: 'HoloVinyl',
95
+ driver: { pace: 71, racecraft: 66, aggression: 52, riskTolerance: 59, patience: 62, consistency: 58 },
96
+ vehicle: { id: 'vinyl-hv33', name: 'HV-33 Groove', power: 70, braking: 49, aero: 58, dragEfficiency: 74, mechanicalGrip: 52, weightControl: 68, tireCare: 46 },
97
+ },
98
+ {
99
+ driverId: 'drsorriso',
100
+ driverNumber: 55,
101
+ timingName: 'DrSorriso',
102
+ driver: { pace: 46, racecraft: 69, aggression: 44, riskTolerance: 43, patience: 76, consistency: 73 },
103
+ vehicle: { id: 'sorriso-ds55', name: 'DS-55 Intake', power: 54, braking: 67, aero: 62, dragEfficiency: 49, mechanicalGrip: 71, weightControl: 45, tireCare: 69 },
104
+ },
105
+ {
106
+ driverId: 'victoria',
107
+ driverNumber: 11,
108
+ timingName: 'VictorIA',
109
+ driver: { pace: 78, racecraft: 79, aggression: 58, riskTolerance: 65, patience: 55, consistency: 68 },
110
+ vehicle: { id: 'victoria-vi11', name: 'VI-11 Vector', power: 76, braking: 58, aero: 65, dragEfficiency: 63, mechanicalGrip: 61, weightControl: 60, tireCare: 48 },
111
+ },
112
+ {
113
+ driverId: 'reminderz',
114
+ driverNumber: 7,
115
+ timingName: 'ReminderZ',
116
+ driver: { pace: 41, racecraft: 61, aggression: 34, riskTolerance: 39, patience: 85, consistency: 80 },
117
+ vehicle: { id: 'reminderz-rz07', name: 'RZ-07 Recall', power: 42, braking: 76, aero: 50, dragEfficiency: 72, mechanicalGrip: 58, weightControl: 63, tireCare: 88 },
118
+ },
119
+ {
120
+ driverId: 'clipclop',
121
+ driverNumber: 14,
122
+ timingName: 'ClipClop',
123
+ driver: { pace: 73, racecraft: 88, aggression: 76, riskTolerance: 78, patience: 48, consistency: 61 },
124
+ vehicle: { id: 'clipclop-cc14', name: 'CC-14 Sync', power: 68, braking: 63, aero: 59, dragEfficiency: 61, mechanicalGrip: 78, weightControl: 56, tireCare: 44 },
125
+ },
126
+ {
127
+ driverId: 'core',
128
+ driverNumber: 91,
129
+ timingName: 'Core',
130
+ driver: { pace: 82, racecraft: 85, aggression: 67, riskTolerance: 74, patience: 53, consistency: 72 },
131
+ vehicle: { id: 'core-ai91', name: 'AI-91 Core', power: 80, braking: 61, aero: 72, dragEfficiency: 59, mechanicalGrip: 75, weightControl: 51, tireCare: 50 },
132
+ },
133
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inventure71/paddockjs",
3
- "version": "0.3.0",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "description": "A data-driven browser F1 simulator component for host websites.",
6
6
  "license": "Apache-2.0",
@@ -53,7 +53,11 @@
53
53
  "LICENSE",
54
54
  "assets",
55
55
  "docs/*.md",
56
- "examples",
56
+ "examples/python/README.md",
57
+ "examples/python/base_policy_server.py",
58
+ "examples/python/requirements.txt",
59
+ "examples/train-basic-policy.mjs",
60
+ "examples/trainingData.mjs",
57
61
  "src/api",
58
62
  "src/app",
59
63
  "src/config",
@@ -68,10 +72,16 @@
68
72
  "README.md"
69
73
  ],
70
74
  "scripts": {
71
- "test": "vitest run src",
75
+ "test": "node scripts/run-vitest.mjs --slow",
76
+ "test:fast": "node scripts/run-vitest.mjs",
72
77
  "types:check": "tsc -p tsconfig.types.json --noEmit",
73
- "check": "npm test && npm run types:check && npm run pack:dry && npm run showcase:ci",
78
+ "check": "npm run test:fast && npm run types:check && npm run pack:dry && npm run consumer:smoke && npm run showcase:ci && npm run browser:smoke:quick -- --skip-build",
79
+ "check:release": "npm test && npm run types:check && npm run pack:dry && npm run consumer:smoke && npm run showcase:ci && npm run browser:smoke:full -- --skip-build",
74
80
  "pack:dry": "npm pack --dry-run",
81
+ "consumer:smoke": "node scripts/consumer-package-smoke.mjs",
82
+ "browser:smoke": "node smoke/browser-smoke.mjs",
83
+ "browser:smoke:quick": "node smoke/browser-smoke.mjs --quick",
84
+ "browser:smoke:full": "node smoke/browser-smoke.mjs --full",
75
85
  "showcase:install": "npm --prefix local-preview ci",
76
86
  "showcase:dev": "npm --prefix local-preview run dev",
77
87
  "showcase:build": "npm --prefix local-preview run build",
@@ -86,6 +96,7 @@
86
96
  },
87
97
  "devDependencies": {
88
98
  "@changesets/cli": "2.29.7",
99
+ "playwright": "^1.57.0",
89
100
  "typescript": "5.9.3",
90
101
  "vitest": "4.1.5"
91
102
  }
@@ -1,16 +1,18 @@
1
1
  import { F1SimulatorApp } from '../app/F1SimulatorApp.js';
2
+ import {
3
+ createRaceDataPanelMarkup,
4
+ createTelemetrySectorBannerMarkup,
5
+ } from '../ui/bannerTemplates.js';
2
6
  import {
3
7
  createCarDriverOverviewMarkup,
4
8
  createCameraControlsMarkup,
5
9
  createRaceCanvasMarkup,
6
10
  createRaceControlsMarkup,
7
- createRaceDataPanelMarkup,
8
11
  createRaceTelemetryDrawerMarkup,
9
12
  createSafetyCarControlMarkup,
10
13
  createTelemetryCoreMarkup,
11
14
  createTelemetryLapTimesMarkup,
12
15
  createTelemetryPanelMarkup,
13
- createTelemetrySectorBannerMarkup,
14
16
  createTelemetrySectorTimesMarkup,
15
17
  createTelemetrySectorsMarkup,
16
18
  createTimingTowerMarkup,
@@ -215,6 +217,14 @@ export class PaddockSimulatorController {
215
217
  this.app?.setSafetyCarDeployed(deployed);
216
218
  }
217
219
 
220
+ setRedFlagDeployed(deployed) {
221
+ this.app?.setRedFlagDeployed?.(deployed);
222
+ }
223
+
224
+ setPitLaneOpen(open) {
225
+ this.app?.setPitLaneOpen?.(open);
226
+ }
227
+
218
228
  callSafetyCar() {
219
229
  this.setSafetyCarDeployed(true);
220
230
  }
@@ -228,6 +238,30 @@ export class PaddockSimulatorController {
228
238
  this.setSafetyCarDeployed(!active);
229
239
  }
230
240
 
241
+ setPitIntent(driverId, intent, targetCompound) {
242
+ return this.app?.setPitIntent(driverId, intent, targetCompound) ?? false;
243
+ }
244
+
245
+ getPitIntent(driverId) {
246
+ return this.app?.getPitIntent(driverId) ?? 0;
247
+ }
248
+
249
+ getPitTargetCompound(driverId) {
250
+ return this.app?.getPitTargetCompound?.(driverId) ?? null;
251
+ }
252
+
253
+ getSimulationSpeed() {
254
+ return this.app?.simulationSpeed ?? 1;
255
+ }
256
+
257
+ servePenalty(penaltyId) {
258
+ return this.app?.servePenalty(penaltyId) ?? null;
259
+ }
260
+
261
+ cancelPenalty(penaltyId) {
262
+ return this.app?.cancelPenalty(penaltyId) ?? null;
263
+ }
264
+
231
265
  getSnapshot() {
232
266
  return this.app?.getSnapshot() ?? null;
233
267
  }