@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
@@ -1,10 +1,127 @@
1
1
  export type TireCompound = 'S' | 'M' | 'H';
2
+ export type PaddockPhysicsMode = 'arcade' | 'simulator';
3
+ export type PaddockProceduralTrackProfile = 'race' | 'training-short' | 'training-medium' | 'training-technical';
4
+
5
+ export interface PaddockProceduralTrackOptions {
6
+ profile?: PaddockProceduralTrackProfile;
7
+ minLengthMeters?: number;
8
+ maxLengthMeters?: number;
9
+ startStraightMeters?: number;
10
+ includePitLane?: boolean;
11
+ length?: {
12
+ minMeters?: number;
13
+ maxMeters?: number;
14
+ };
15
+ startStraight?: {
16
+ gridMeters?: number;
17
+ exitMeters?: number;
18
+ blendMeters?: number;
19
+ lockExtraMeters?: number;
20
+ };
21
+ pitLane?: {
22
+ enabled?: boolean;
23
+ };
24
+ shape?: {
25
+ scale?: number;
26
+ cornerDensity?: number;
27
+ variation?: number;
28
+ };
29
+ validation?: {
30
+ minClearanceMultiplier?: number;
31
+ minShapeVariation?: number;
32
+ minNonAdjacentArcMeters?: number;
33
+ maxLocalTurnRadians?: number;
34
+ maxSampleHeadingDeltaRadians?: number;
35
+ };
36
+ attempts?: {
37
+ primary?: number;
38
+ fallback?: number;
39
+ };
40
+ }
41
+
42
+ export type PaddockStabilityState = 'stable' | 'understeer' | 'oversteer' | 'spin-risk' | 'destroyed';
43
+ export type PaddockPitIntent = 0 | 1 | 2;
44
+ export type PaddockScenarioPreset = 'cornering' | 'off-track-recovery' | 'overtaking-pack' | 'pit-entry';
45
+ export type PaddockParticipantInteractionProfile =
46
+ | 'normal'
47
+ | 'isolated-training'
48
+ | 'batch-training'
49
+ | 'phantom-race'
50
+ | 'time-trial-overlay';
51
+
52
+ export interface PaddockParticipantInteraction {
53
+ profile: PaddockParticipantInteractionProfile;
54
+ collidable: boolean;
55
+ detectableByRays: boolean;
56
+ detectableAsNearby: boolean;
57
+ blocksPitLane: boolean;
58
+ affectsRaceOrder: boolean;
59
+ }
60
+
61
+ export type PaddockParticipantInteractionOverride =
62
+ Partial<PaddockParticipantInteraction> & { profile?: PaddockParticipantInteractionProfile };
63
+
64
+ export interface PaddockParticipantInteractionsOptions {
65
+ defaultProfile?: PaddockParticipantInteractionProfile;
66
+ drivers?: Record<string, PaddockParticipantInteractionOverride>;
67
+ }
68
+
69
+ export interface PaddockReplayGhostTrajectorySample {
70
+ timeSeconds: number;
71
+ x: number;
72
+ y: number;
73
+ headingRadians: number;
74
+ speedKph?: number;
75
+ progressMeters?: number;
76
+ }
77
+
78
+ export interface PaddockReplayGhostOptions {
79
+ id: string;
80
+ label?: string;
81
+ color?: string;
82
+ opacity?: number;
83
+ visible?: boolean;
84
+ trajectory: PaddockReplayGhostTrajectorySample[];
85
+ sensors?: {
86
+ detectableByRays?: boolean;
87
+ detectableAsNearby?: boolean;
88
+ };
89
+ }
90
+
91
+ export interface PaddockReplayGhostSnapshot {
92
+ id: string;
93
+ label: string;
94
+ color: string;
95
+ opacity: number;
96
+ visible: boolean;
97
+ previousX: number;
98
+ previousY: number;
99
+ x: number;
100
+ y: number;
101
+ previousHeading: number;
102
+ heading: number;
103
+ speedKph: number;
104
+ progressMeters: number;
105
+ timeSeconds: number;
106
+ sensors: {
107
+ detectableByRays: boolean;
108
+ detectableAsNearby: boolean;
109
+ };
110
+ }
2
111
 
3
112
  export interface TeamData {
4
113
  id?: string;
5
114
  name?: string;
6
115
  color?: string;
7
116
  icon?: string;
117
+ pitCrew?: PaddockPitCrewStats;
118
+ pitCrewStats?: PaddockPitCrewStats;
119
+ }
120
+
121
+ export interface PaddockPitCrewStats {
122
+ speed?: number;
123
+ consistency?: number;
124
+ reliability?: number;
8
125
  }
9
126
 
10
127
  export interface SimulatorDriver {
@@ -37,8 +154,52 @@ export interface RaceEvent {
37
154
  [key: string]: unknown;
38
155
  }
39
156
 
157
+ export type PaddockRulesetName = 'paddock' | 'custom' | 'grandPrix2025' | 'fia2025';
158
+
159
+ export interface PaddockPenaltyEntry {
160
+ id: string;
161
+ type: string;
162
+ at: number;
163
+ lap: number;
164
+ driverId: string;
165
+ strictness: number;
166
+ status: PaddockPenaltyStatus;
167
+ penaltySeconds: number;
168
+ pendingPenaltySeconds?: number;
169
+ consequences: PaddockPenaltyConsequence[];
170
+ serviceType?: 'driveThrough' | 'stopGo' | null;
171
+ serviceRequired?: boolean;
172
+ serviceServedAt?: number | null;
173
+ appliedAt?: number;
174
+ cancelledAt?: number;
175
+ unserved?: boolean;
176
+ positionDrop?: number;
177
+ gridDrop?: number;
178
+ disqualified?: boolean;
179
+ reason?: string;
180
+ otherCarId?: string;
181
+ aheadDriverId?: string;
182
+ atFaultDriverId?: string;
183
+ sharedFault?: boolean;
184
+ impactSpeedKph?: number;
185
+ impactSpeedThresholdKph?: number;
186
+ [key: string]: unknown;
187
+ }
188
+
189
+ export type PaddockPenaltyConsequence =
190
+ | { type: 'warning' }
191
+ | { type: 'time'; seconds: number }
192
+ | { type: 'driveThrough'; conversionSeconds?: number }
193
+ | { type: 'stopGo'; seconds?: number; conversionSeconds?: number }
194
+ | { type: 'positionDrop'; positions: number }
195
+ | { type: 'gridDrop'; positions: number }
196
+ | { type: 'disqualification' };
197
+
198
+ export type PaddockPenaltyStatus = 'issued' | 'served' | 'applied' | 'cancelled';
199
+
40
200
  export interface RaceSnapshot {
41
201
  time: number;
202
+ physicsMode: PaddockPhysicsMode;
42
203
  totalLaps: number;
43
204
  raceControl: {
44
205
  mode: string;
@@ -47,7 +208,9 @@ export interface RaceSnapshot {
47
208
  };
48
209
  track: Record<string, unknown>;
49
210
  cars: Array<Record<string, unknown>>;
211
+ replayGhosts: PaddockReplayGhostSnapshot[];
50
212
  events: RaceEvent[];
213
+ penalties: PaddockPenaltyEntry[];
51
214
  [key: string]: unknown;
52
215
  }
53
216
 
@@ -55,27 +218,129 @@ export interface PaddockAction {
55
218
  steering: number;
56
219
  throttle: number;
57
220
  brake: number;
221
+ pitIntent?: PaddockPitIntent;
222
+ pitCompound?: TireCompound | string;
223
+ pitTargetCompound?: TireCompound | string;
58
224
  }
59
225
 
60
226
  export type PaddockActionMap = Record<string, PaddockAction>;
61
227
 
228
+ export interface PaddockRaceRules {
229
+ ruleset?: PaddockRulesetName;
230
+ profile?: PaddockRulesetName;
231
+ modules?: {
232
+ pitStops?: {
233
+ enabled?: boolean;
234
+ pitLaneSpeedLimitKph?: number;
235
+ defaultStopSeconds?: number;
236
+ variability?: {
237
+ enabled?: boolean;
238
+ perfect?: boolean;
239
+ speedImpactSeconds?: number;
240
+ consistencyJitterSeconds?: number;
241
+ issueChance?: number;
242
+ issueMaxDelaySeconds?: number;
243
+ };
244
+ doubleStacking?: boolean;
245
+ maxConcurrentPitLaneCars?: number;
246
+ minimumPitLaneGapMeters?: number;
247
+ tirePitRequestThresholdPercent?: number;
248
+ tirePitCommitThresholdPercent?: number;
249
+ };
250
+ tireStrategy?: {
251
+ enabled?: boolean;
252
+ compounds?: TireCompound[];
253
+ mandatoryDistinctDryCompounds?: number | null;
254
+ };
255
+ tireDegradation?: {
256
+ enabled?: boolean;
257
+ };
258
+ stalledDnf?: {
259
+ enabled?: boolean;
260
+ maxStoppedSeconds?: number;
261
+ speedThresholdKph?: number;
262
+ };
263
+ penalties?: {
264
+ enabled?: boolean;
265
+ stewardStrictness?: number;
266
+ trackLimits?: {
267
+ strictness?: number;
268
+ warningsBeforePenalty?: number;
269
+ relaxedMarginMeters?: number;
270
+ timePenaltySeconds?: number;
271
+ consequences?: PaddockPenaltyConsequence[];
272
+ };
273
+ collision?: {
274
+ strictness?: number;
275
+ timePenaltySeconds?: number;
276
+ consequences?: PaddockPenaltyConsequence[];
277
+ minimumSeverity?: number;
278
+ relaxedSeverityMargin?: number;
279
+ minimumImpactSpeedKph?: number;
280
+ relaxedImpactSpeedKph?: number;
281
+ };
282
+ tireRequirement?: {
283
+ strictness?: number;
284
+ timePenaltySeconds?: number;
285
+ consequences?: PaddockPenaltyConsequence[];
286
+ };
287
+ pitLaneSpeeding?: {
288
+ strictness?: number;
289
+ speedLimitKph?: number;
290
+ marginKph?: number;
291
+ relaxedMarginKph?: number;
292
+ timePenaltySeconds?: number;
293
+ consequences?: PaddockPenaltyConsequence[];
294
+ };
295
+ };
296
+ weather?: { enabled?: boolean };
297
+ reliability?: { enabled?: boolean };
298
+ fuelLoad?: { enabled?: boolean };
299
+ };
300
+ drsDetectionSeconds?: number;
301
+ safetyCarSpeed?: number;
302
+ safetyCarLeadDistance?: number;
303
+ safetyCarGap?: number;
304
+ collisionRestitution?: number;
305
+ standingStart?: boolean;
306
+ startLightCount?: number;
307
+ startLightInterval?: number;
308
+ startLightsOutHold?: number;
309
+ }
310
+
62
311
  export interface PaddockSensorRayResult {
312
+ id?: string;
63
313
  angleDegrees: number;
64
314
  angleRadians: number;
65
315
  lengthMeters: number;
316
+ roadEdge: {
317
+ hit: boolean;
318
+ distanceMeters: number;
319
+ kind: 'exit' | 'entry' | null;
320
+ };
66
321
  track: {
67
322
  hit: boolean;
68
323
  distanceMeters: number;
69
324
  kind: 'exit' | 'entry' | null;
70
325
  };
326
+ kerb: PaddockSurfaceRayHit;
327
+ illegalSurface: PaddockSurfaceRayHit;
71
328
  car: {
72
329
  hit: boolean;
73
330
  distanceMeters: number;
74
331
  driverId: string | null;
332
+ targetId?: string | null;
333
+ targetType?: 'car' | 'replayGhost' | null;
75
334
  relativeSpeedKph: number;
76
335
  };
77
336
  }
78
337
 
338
+ export interface PaddockSurfaceRayHit {
339
+ hit: boolean;
340
+ distanceMeters: number;
341
+ surface: string | null;
342
+ }
343
+
79
344
  export interface PaddockNearbyCarObservation {
80
345
  id: string;
81
346
  relativeForwardMeters: number;
@@ -84,15 +349,29 @@ export interface PaddockNearbyCarObservation {
84
349
  relativeSpeedKph: number;
85
350
  relativeHeadingRadians: number;
86
351
  ahead: boolean;
352
+ behind: boolean;
87
353
  sameLap: boolean;
354
+ closingRateMetersPerSecond: number;
355
+ timeToContactSeconds: number | null;
356
+ leftOverlap: boolean;
357
+ rightOverlap: boolean;
358
+ entityType?: 'car' | 'replayGhost';
359
+ }
360
+
361
+ export interface PaddockTrackLookaheadObservation {
362
+ distanceMeters: number;
363
+ curvature: number;
364
+ headingDeltaRadians: number;
88
365
  }
89
366
 
90
367
  export interface PaddockDriverObservationObject {
368
+ profile: string;
91
369
  self: {
92
370
  id: string;
93
371
  speedKph: number;
94
372
  speedMetersPerSecond: number;
95
373
  headingRadians: number;
374
+ yawRateRadiansPerSecond: number;
96
375
  steeringAngleRadians: number;
97
376
  throttle: number;
98
377
  brake: number;
@@ -103,19 +382,66 @@ export interface PaddockDriverObservationObject {
103
382
  trackHeadingErrorRadians: number;
104
383
  onTrack: boolean;
105
384
  surface: string;
385
+ inPitLane: boolean;
386
+ pitLanePart: 'entry' | 'fast-lane' | 'working-lane' | 'exit' | 'service-box' | 'garage-box' | null;
387
+ pitBoxId: string | null;
106
388
  tireEnergy: number | null;
389
+ pitIntent: PaddockPitIntent;
390
+ pitTargetCompound: TireCompound | string | null;
391
+ pitStopStatus: 'pending' | 'entering' | 'queued' | 'servicing' | 'exiting' | 'completed' | null;
392
+ pitStopPhase: 'entry' | 'queue' | 'queue-release' | 'penalty' | 'service' | 'exit' | null;
393
+ pitStopServiceRemainingSeconds: number | null;
394
+ pitStopPenaltyServiceRemainingSeconds: number | null;
395
+ pitStopsCompleted: number;
396
+ lateralG: number;
397
+ longitudinalG: number;
398
+ gripUsage: number;
399
+ slipAngleRadians: number;
400
+ tractionLimited: boolean;
401
+ stabilityState: PaddockStabilityState;
402
+ destroyed: boolean;
403
+ destroyReason: string | null;
404
+ };
405
+ trackRelation: {
406
+ lateralOffsetMeters: number;
407
+ headingErrorRadians: number;
408
+ legalWidthMeters: number;
409
+ leftBoundaryMeters: number;
410
+ rightBoundaryMeters: number;
411
+ onLegalSurface: boolean;
412
+ surface: string;
107
413
  };
414
+ contactPatches: PaddockContactPatchObservation[];
108
415
  race: {
109
416
  position: number;
110
417
  totalCars: number;
111
418
  raceMode: string;
419
+ pitLaneOpen: boolean;
420
+ redFlag: boolean;
112
421
  totalLaps: number;
113
422
  };
423
+ track: {
424
+ lengthMeters: number;
425
+ widthMeters: number;
426
+ curvature: number;
427
+ lookahead: PaddockTrackLookaheadObservation[];
428
+ };
114
429
  rays: PaddockSensorRayResult[];
115
430
  nearbyCars: PaddockNearbyCarObservation[];
116
431
  events: RaceEvent[];
117
432
  }
118
433
 
434
+ export interface PaddockContactPatchObservation {
435
+ id: 'front-left' | 'front-right' | 'rear-left' | 'rear-right' | string;
436
+ present: boolean;
437
+ signedOffsetMeters: number;
438
+ crossTrackErrorMeters: number;
439
+ surface: string;
440
+ surfaceCode: number;
441
+ onLegalSurface: boolean;
442
+ inPitLane: boolean;
443
+ }
444
+
119
445
  export interface PaddockObservationSchemaEntry {
120
446
  name: string;
121
447
  unit?: string;
@@ -123,30 +449,56 @@ export interface PaddockObservationSchemaEntry {
123
449
  }
124
450
 
125
451
  export interface PaddockDriverObservation {
126
- object: PaddockDriverObservationObject;
127
- vector: number[];
128
- schema: PaddockObservationSchemaEntry[];
452
+ object?: PaddockDriverObservationObject;
453
+ vector?: number[] | Float32Array;
454
+ schema?: PaddockObservationSchemaEntry[];
129
455
  events: RaceEvent[];
130
456
  }
131
457
 
458
+ export type PaddockWarmupPolicy = 'config-change' | 'always' | 'never';
459
+
460
+ export interface PaddockWarmupOptions {
461
+ enabled?: boolean;
462
+ policy?: PaddockWarmupPolicy;
463
+ steps?: number;
464
+ }
465
+
132
466
  export interface PaddockEnvironmentOptions {
133
467
  drivers: SimulatorDriver[];
134
468
  entries?: ChampionshipEntryBlueprint[];
135
469
  controlledDrivers: string[];
136
470
  seed?: number;
137
471
  trackSeed?: number;
472
+ trackGeneration?: PaddockProceduralTrackOptions;
473
+ trackQueryIndex?: boolean;
138
474
  totalLaps?: number;
139
475
  frameSkip?: number;
476
+ physicsMode?: PaddockPhysicsMode;
477
+ rules?: PaddockRaceRules;
478
+ participantInteractions?: PaddockParticipantInteractionsOptions;
479
+ replayGhosts?: PaddockReplayGhostOptions[];
140
480
  actionPolicy?: 'strict' | 'report';
141
481
  scenario?: {
142
482
  participants?: 'all' | 'controlled-only' | string[];
143
483
  nonControlled?: 'ai';
484
+ preset?: PaddockScenarioPreset;
485
+ placements?: Record<string, PaddockScenarioPlacement>;
486
+ traffic?: PaddockScenarioTrafficPlacement[];
144
487
  };
145
488
  sensors?: {
146
489
  rays?: {
147
490
  enabled?: boolean;
148
491
  anglesDegrees?: number[];
149
492
  lengthMeters?: number;
493
+ defaultLengthMeters?: number;
494
+ layout?: 'compact' | 'driver-front-heavy' | 'lidar-lite' | string;
495
+ rays?: Array<number | {
496
+ id?: string;
497
+ angleDegrees: number;
498
+ lengthMeters?: number;
499
+ }>;
500
+ channels?: Array<'roadEdge' | 'kerb' | 'illegalSurface' | 'car'>;
501
+ precision?: 'driver' | 'debug';
150
502
  detectTrack?: boolean;
151
503
  detectCars?: boolean;
152
504
  };
@@ -157,11 +509,45 @@ export interface PaddockEnvironmentOptions {
157
509
  };
158
510
  };
159
511
  sensorsByDriver?: Record<string, PaddockEnvironmentOptions['sensors']>;
512
+ observation?: {
513
+ profile?: 'default' | 'physical-driver' | 'debug-map' | string;
514
+ output?: 'full' | 'vector' | 'object';
515
+ includeSchema?: boolean;
516
+ vectorType?: 'array' | 'float32';
517
+ lookaheadMeters?: number[];
518
+ };
519
+ result?: {
520
+ stateOutput?: 'full' | 'minimal' | 'none';
521
+ resetDriversObservationScope?: 'all' | 'reset';
522
+ };
523
+ warmup?: PaddockWarmupOptions | boolean;
160
524
  episode?: {
161
525
  maxSteps?: number;
162
526
  endOnRaceFinish?: boolean;
163
527
  };
164
528
  reward?: (payload: PaddockRewardContext) => number;
529
+ externalRenderer?: PaddockExternalRendererHook | ((frame: PaddockExternalRenderFrame) => void);
530
+ }
531
+
532
+ export interface PaddockExternalRendererHook {
533
+ onFrame(frame: PaddockExternalRenderFrame): void;
534
+ }
535
+
536
+ export interface PaddockScenarioPlacement {
537
+ distanceMeters?: number;
538
+ startDistanceMeters?: number;
539
+ offsetMeters?: number;
540
+ speedKph?: number;
541
+ headingErrorRadians?: number;
542
+ }
543
+
544
+ export interface PaddockScenarioTrafficPlacement {
545
+ driverId: string;
546
+ relativeTo: string;
547
+ deltaDistanceMeters?: number;
548
+ offsetMeters?: number;
549
+ speedKph?: number;
550
+ headingErrorRadians?: number;
165
551
  }
166
552
 
167
553
  export interface PaddockRewardContext {
@@ -171,6 +557,8 @@ export interface PaddockRewardContext {
171
557
  action: PaddockAction | undefined;
172
558
  events: RaceEvent[];
173
559
  state: { snapshot: RaceSnapshot };
560
+ metrics: PaddockEnvironmentDriverMetrics;
561
+ episode: PaddockDriverRuntimeState;
174
562
  }
175
563
 
176
564
  export interface PaddockProgressRewardWeights {
@@ -195,12 +583,14 @@ export interface PaddockActionSpec {
195
583
  steering: { min: -1; max: 1; unit: 'normalized' };
196
584
  throttle: { min: 0; max: 1; unit: 'normalized' };
197
585
  brake: { min: 0; max: 1; unit: 'normalized' };
586
+ pitIntent: { values: [0, 1, 2]; unit: 'request'; optional: true };
587
+ pitCompound: { values: string[]; unit: 'compound'; optional: true };
198
588
  };
199
589
  };
200
590
  }
201
591
 
202
592
  export interface PaddockObservationSpec {
203
- version: 1;
593
+ version: 2 | 4;
204
594
  controlledDrivers: string[];
205
595
  object: Record<string, unknown>;
206
596
  vector: {
@@ -208,14 +598,193 @@ export interface PaddockObservationSpec {
208
598
  };
209
599
  }
210
600
 
601
+ export interface PaddockRolloutTransition {
602
+ observation: Record<string, PaddockDriverObservation>;
603
+ action: PaddockActionMap;
604
+ reward: null | Record<string, number>;
605
+ nextObservation: Record<string, PaddockDriverObservation>;
606
+ terminated: boolean;
607
+ truncated: boolean;
608
+ info: PaddockEnvironmentResult['info'];
609
+ }
610
+
611
+ export interface PaddockRolloutRecorder {
612
+ recordStep(
613
+ previousResult: PaddockEnvironmentResult,
614
+ action: PaddockActionMap,
615
+ nextResult: PaddockEnvironmentResult
616
+ ): PaddockRolloutTransition;
617
+ clear(): void;
618
+ toJSON(): PaddockRolloutTransition[];
619
+ }
620
+
621
+ export interface PaddockEvaluationCase {
622
+ name: string;
623
+ seed?: number;
624
+ trackSeed?: number;
625
+ maxSteps?: number;
626
+ scenario?: PaddockEnvironmentOptions['scenario'];
627
+ }
628
+
629
+ export interface PaddockEvaluationDriverMetrics {
630
+ distanceMeters: number;
631
+ lapProgressMeters: number;
632
+ offTrackSteps: number;
633
+ contactCount: number;
634
+ recoverySuccess: boolean;
635
+ passCount: number;
636
+ lapTimeSeconds: number | null;
637
+ }
638
+
639
+ export interface PaddockEvaluationCaseReport {
640
+ name: string;
641
+ seed?: number;
642
+ trackSeed?: number;
643
+ steps: number;
644
+ done: boolean;
645
+ endReason: string | null;
646
+ metrics: Record<string, PaddockEvaluationDriverMetrics>;
647
+ }
648
+
649
+ export interface PaddockEvaluationReport {
650
+ cases: PaddockEvaluationCaseReport[];
651
+ }
652
+
653
+ export type PaddockPolicyLike =
654
+ | ((observation: PaddockDriverObservation, context: Record<string, unknown>) => PaddockAction)
655
+ | { predict(observation: PaddockDriverObservation, context: Record<string, unknown>): PaddockAction };
656
+
657
+ export interface PaddockDriverControllerContext {
658
+ runtime: PaddockControllerRuntime;
659
+ mode: string;
660
+ actionRepeat: number;
661
+ controlledDrivers: string[];
662
+ actionSpec: PaddockActionSpec;
663
+ observationSpec: PaddockObservationSpec;
664
+ result: PaddockEnvironmentResult | unknown;
665
+ previousResult: PaddockEnvironmentResult | unknown | null;
666
+ observation: Record<string, PaddockDriverObservation>;
667
+ orderedObservations: Array<{
668
+ driverId: string;
669
+ index: number;
670
+ observation: PaddockDriverObservation | null;
671
+ vector: number[] | Float32Array | null;
672
+ }>;
673
+ metrics: Record<string, PaddockEnvironmentDriverMetrics>;
674
+ events: RaceEvent[];
675
+ info: PaddockEnvironmentResult['info'] | unknown | null;
676
+ previousActions: PaddockActionMap;
677
+ orderedPreviousActions: Array<PaddockAction | null>;
678
+ actions: PaddockActionMap | null;
679
+ policyStep: number;
680
+ runtimeStep: number;
681
+ heldFramesRemaining: number;
682
+ actionIndex: number;
683
+ resetDriverIds: string[];
684
+ }
685
+
686
+ export interface PaddockExternalRenderFrame {
687
+ snapshot: RaceSnapshot;
688
+ observation: Record<string, unknown>;
689
+ meta?: Record<string, unknown> | null;
690
+ }
691
+
692
+ export interface PaddockExternalRendererSource {
693
+ subscribe(onFrame: (frame: PaddockExternalRenderFrame) => void): () => void;
694
+ }
695
+
696
+ export interface PaddockExternalRendererState {
697
+ attached: boolean;
698
+ lastMeta: Record<string, unknown> | null;
699
+ lastFrameAt: number | null;
700
+ lastError: string | null;
701
+ }
702
+
703
+ export interface PaddockDriverController {
704
+ init?(context: PaddockDriverControllerContext): void | Promise<void>;
705
+ reset?(context: PaddockDriverControllerContext): void | Promise<void>;
706
+ decideBatch(context: PaddockDriverControllerContext): PaddockActionMap | Promise<PaddockActionMap>;
707
+ onStep?(context: PaddockDriverControllerContext): void | Promise<void>;
708
+ }
709
+
710
+ export interface PaddockControllerRuntime {
711
+ reset(options?: unknown): PaddockEnvironmentResult | unknown;
712
+ step(actions: PaddockActionMap): PaddockEnvironmentResult | unknown;
713
+ resetDrivers?(
714
+ placements: Record<string, PaddockScenarioPlacement>,
715
+ resultOptions?: PaddockEnvironmentResultOptions
716
+ ): PaddockEnvironmentResult | unknown;
717
+ getObservation(): Record<string, PaddockDriverObservation>;
718
+ getState?(options?: { output?: 'full' | 'minimal' | 'none' }): PaddockEnvironmentResult['state'] | unknown;
719
+ getActionSpec(): PaddockActionSpec;
720
+ getObservationSpec(): PaddockObservationSpec;
721
+ destroy?(): void;
722
+ }
723
+
724
+ export interface PaddockDriverControllerLoopOptions {
725
+ runtime: PaddockControllerRuntime;
726
+ controller: PaddockDriverController;
727
+ actionRepeat?: number;
728
+ mode?: string;
729
+ scheduler?: null | ((callback: () => void | Promise<void>) => unknown);
730
+ }
731
+
732
+ export interface PaddockDriverControllerLoop {
733
+ readonly result: PaddockEnvironmentResult | unknown | null;
734
+ readonly actionSpec: PaddockActionSpec | null;
735
+ readonly observationSpec: PaddockObservationSpec | null;
736
+ readonly stats: {
737
+ policyStep: number;
738
+ runtimeStep: number;
739
+ heldFramesRemaining: number;
740
+ actions: PaddockActionMap | null;
741
+ actionRepeat: number;
742
+ lastDecisionMs: number;
743
+ running: boolean;
744
+ lastError: unknown | null;
745
+ };
746
+ reset(options?: unknown): Promise<PaddockEnvironmentResult | unknown>;
747
+ resetDrivers(
748
+ placements: Record<string, PaddockScenarioPlacement>,
749
+ resultOptions?: PaddockEnvironmentResultOptions
750
+ ): Promise<PaddockEnvironmentResult | unknown>;
751
+ step(): Promise<PaddockEnvironmentResult | unknown>;
752
+ stepFrame(): Promise<PaddockEnvironmentResult | unknown>;
753
+ start(): void;
754
+ stop(): void;
755
+ }
756
+
757
+ export interface PaddockEnvironmentWorkerMessage {
758
+ id?: string | number | null;
759
+ type: 'reset' | 'resetDrivers' | 'step' | 'getActionSpec' | 'getObservationSpec' | 'getObservation' | 'getState' | 'destroy' | string;
760
+ options?: Partial<PaddockEnvironmentOptions>;
761
+ actions?: PaddockActionMap;
762
+ placements?: Record<string, PaddockScenarioPlacement>;
763
+ resultOptions?: PaddockEnvironmentResultOptions;
764
+ stateOptions?: { output?: 'full' | 'minimal' | 'none' };
765
+ }
766
+
767
+ export interface PaddockEnvironmentWorkerResponse {
768
+ id: string | number | null;
769
+ ok: boolean;
770
+ type: string;
771
+ result?: unknown;
772
+ error?: string;
773
+ }
774
+
775
+ export interface PaddockEnvironmentWorkerProtocol {
776
+ handle(message: PaddockEnvironmentWorkerMessage): PaddockEnvironmentWorkerResponse;
777
+ }
778
+
211
779
  export interface PaddockEnvironmentResult {
212
780
  observation: Record<string, PaddockDriverObservation>;
213
781
  reward: null | Record<string, number>;
782
+ metrics: Record<string, PaddockEnvironmentDriverMetrics>;
214
783
  terminated: boolean;
215
784
  truncated: boolean;
216
785
  done: boolean;
217
786
  events: RaceEvent[];
218
- state: { snapshot: RaceSnapshot };
787
+ state: { snapshot: RaceSnapshot } | null;
219
788
  info: {
220
789
  step: number;
221
790
  elapsedSeconds: number;
@@ -224,18 +793,81 @@ export interface PaddockEnvironmentResult {
224
793
  controlledDrivers: string[];
225
794
  actionErrors: string[];
226
795
  endReason: string | null;
796
+ drivers: Record<string, PaddockDriverRuntimeState>;
227
797
  };
228
798
  }
229
799
 
800
+ export type PaddockEnvironmentSnapshotResult = PaddockEnvironmentResult & {
801
+ state: { snapshot: RaceSnapshot };
802
+ };
803
+
230
804
  export interface PaddockEnvironment {
231
805
  reset(options?: Partial<PaddockEnvironmentOptions>): PaddockEnvironmentResult;
806
+ resetDrivers(
807
+ placements: Record<string, PaddockScenarioPlacement>,
808
+ resultOptions?: PaddockEnvironmentResultOptions
809
+ ): PaddockEnvironmentResult;
232
810
  step(actions: PaddockActionMap): PaddockEnvironmentResult;
233
811
  getObservation(): PaddockEnvironmentResult['observation'];
234
- getState(): PaddockEnvironmentResult['state'];
812
+ getState(options?: { output?: 'full' | 'minimal' | 'none' }): PaddockEnvironmentResult['state'];
235
813
  getActionSpec(): PaddockActionSpec;
236
814
  getObservationSpec(): PaddockObservationSpec;
237
815
  destroy(): void;
238
816
  }
239
817
 
818
+ export interface PaddockEnvironmentResultOptions {
819
+ stateOutput?: 'full' | 'minimal' | 'none';
820
+ observationScope?: 'all' | 'reset';
821
+ resetDriversObservationScope?: 'all' | 'reset';
822
+ }
823
+
824
+ export interface PaddockDriverRuntimeState {
825
+ terminated: boolean;
826
+ truncated: boolean;
827
+ endReason: string | null;
828
+ episodeStep: number;
829
+ episodeId: number;
830
+ }
831
+
832
+ export interface PaddockEnvironmentDriverMetrics {
833
+ progressDeltaMeters: number;
834
+ legalProgressDeltaMeters: number;
835
+ offTrack: boolean;
836
+ kerb: boolean;
837
+ fullyOutsideWhiteLine: boolean;
838
+ severeCut: boolean;
839
+ destroyed: boolean;
840
+ destroyReason: string | null;
841
+ under30kph: boolean;
842
+ spinOrBackwards: boolean;
843
+ completedLap: boolean;
844
+ lapTimeSeconds: number | null;
845
+ contactCount: number;
846
+ }
847
+
240
848
  export function createPaddockEnvironment(options: PaddockEnvironmentOptions): PaddockEnvironment;
849
+ export function createPaddockDriverControllerLoop(options: PaddockDriverControllerLoopOptions): PaddockDriverControllerLoop;
241
850
  export function createProgressReward(options?: PaddockProgressRewardOptions): (context: PaddockRewardContext) => number;
851
+ export function createRolloutRecorder(): PaddockRolloutRecorder;
852
+ export function createRolloutTransition(
853
+ previousResult: PaddockEnvironmentResult,
854
+ action: PaddockActionMap,
855
+ nextResult: PaddockEnvironmentResult
856
+ ): PaddockRolloutTransition;
857
+ export const ENVIRONMENT_SCENARIO_PRESETS: readonly PaddockScenarioPreset[];
858
+ export const DEFAULT_EVALUATION_CASES: readonly PaddockEvaluationCase[];
859
+ export function createEvaluationTracker(initialResult: PaddockEnvironmentSnapshotResult): {
860
+ update(result: PaddockEnvironmentSnapshotResult): void;
861
+ finish(): Record<string, PaddockEvaluationDriverMetrics>;
862
+ };
863
+ export function runEnvironmentEvaluation(options: {
864
+ baseOptions: PaddockEnvironmentOptions;
865
+ policy: PaddockPolicyLike;
866
+ cases?: PaddockEvaluationCase[];
867
+ createEnvironment?: (options: PaddockEnvironmentOptions) => PaddockEnvironment;
868
+ }): PaddockEvaluationReport;
869
+ export function createEnvironmentWorkerProtocol(env: PaddockEnvironment): PaddockEnvironmentWorkerProtocol;
870
+ export function handleEnvironmentMessage(
871
+ env: PaddockEnvironment,
872
+ message: PaddockEnvironmentWorkerMessage
873
+ ): PaddockEnvironmentWorkerResponse;