@inventure71/paddockjs 0.3.0 → 1.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 (54) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/INSTALL_AND_UPDATE.md +3 -1
  3. package/README.md +142 -9
  4. package/docs/architecture.md +49 -15
  5. package/docs/concepts.md +20 -3
  6. package/docs/data_contract.md +185 -27
  7. package/docs/data_model.md +1 -1
  8. package/docs/learnings.md +1 -1
  9. package/docs/rules.md +140 -19
  10. package/docs/system_specs.md +39 -16
  11. package/docs/training.md +103 -1
  12. package/examples/train-basic-policy.mjs +5 -6
  13. package/examples/trainingData.mjs +133 -0
  14. package/package.json +5 -2
  15. package/src/api/PaddockSimulatorController.js +32 -2
  16. package/src/app/F1SimulatorApp.js +1101 -128
  17. package/src/app/domBindings.js +10 -1
  18. package/src/config/defaultOptions.js +4 -4
  19. package/src/environment/actions.js +25 -2
  20. package/src/environment/evaluation.js +184 -0
  21. package/src/environment/index.d.ts +281 -1
  22. package/src/environment/index.js +13 -0
  23. package/src/environment/observationOptions.js +12 -0
  24. package/src/environment/observations.js +189 -17
  25. package/src/environment/options.js +7 -0
  26. package/src/environment/recorder.js +28 -0
  27. package/src/environment/runtime.js +41 -6
  28. package/src/environment/scenarios.js +220 -0
  29. package/src/environment/sensors.js +145 -35
  30. package/src/environment/specs.js +92 -8
  31. package/src/environment/workerProtocol.js +52 -0
  32. package/src/index.d.ts +366 -3
  33. package/src/index.js +21 -0
  34. package/src/rendering/proceduralTrackAsset.js +336 -23
  35. package/src/simulation/collisionGeometry.js +214 -0
  36. package/src/simulation/driverController.js +431 -65
  37. package/src/simulation/raceSimulation.js +2132 -171
  38. package/src/simulation/rules/collisionSteward.js +69 -0
  39. package/src/simulation/rules/penaltyLedger.js +153 -0
  40. package/src/simulation/rules/pitLaneSpeedingSteward.js +71 -0
  41. package/src/simulation/rules/tireRequirementSteward.js +29 -0
  42. package/src/simulation/rules/trackLimitsSteward.js +85 -0
  43. package/src/simulation/rulesConfig.js +352 -0
  44. package/src/simulation/trackModel.js +1154 -76
  45. package/src/simulation/units.js +6 -4
  46. package/src/simulation/vehicleGeometry.js +247 -0
  47. package/src/simulation/vehiclePhysics.js +82 -50
  48. package/src/simulation/wheelSurface.js +338 -0
  49. package/src/styles.css +300 -42
  50. package/src/ui/bannerTemplates.js +69 -0
  51. package/src/ui/componentTemplates.js +36 -96
  52. package/src/ui/raceControlStatusBanner.js +39 -0
  53. package/src/ui/shellTemplate.js +1 -1
  54. package/src/ui/templateUtils.js +23 -0
package/src/index.d.ts CHANGED
@@ -3,7 +3,17 @@ import type { PaddockActionSpec, PaddockObservationSpec } from './environment/in
3
3
  export type { PaddockActionSpec, PaddockObservationSpec } from './environment/index.js';
4
4
 
5
5
  export type TireCompound = 'S' | 'M' | 'H';
6
- export type CameraMode = 'overview' | 'leader' | 'selected' | 'show-all';
6
+ export type PaddockPitIntent = 0 | 1 | 2;
7
+ export type PaddockPitIntentRequest = PaddockPitIntent | {
8
+ intent?: PaddockPitIntent;
9
+ pitIntent?: PaddockPitIntent;
10
+ targetCompound?: TireCompound | string;
11
+ compound?: TireCompound | string;
12
+ pitCompound?: TireCompound | string;
13
+ pitTargetCompound?: TireCompound | string;
14
+ targetTire?: TireCompound | string;
15
+ };
16
+ export type CameraMode = 'overview' | 'leader' | 'selected' | 'show-all' | 'pit';
7
17
  export type RaceBannerMode = 'project' | 'radio' | 'hidden';
8
18
  export type RaceBannerEnabledMode = 'project' | 'radio';
9
19
  export type RaceDataBannerSize = 'auto' | 'custom';
@@ -26,6 +36,14 @@ export interface TeamData {
26
36
  name?: string;
27
37
  color?: string;
28
38
  icon?: string;
39
+ pitCrew?: PaddockPitCrewStats;
40
+ pitCrewStats?: PaddockPitCrewStats;
41
+ }
42
+
43
+ export interface PaddockPitCrewStats {
44
+ speed?: number;
45
+ consistency?: number;
46
+ reliability?: number;
29
47
  }
30
48
 
31
49
  export interface SimulatorDriver {
@@ -184,6 +202,9 @@ export interface F1SimulatorUiOptions {
184
202
  };
185
203
  raceDataBannerSize?: RaceDataBannerSize;
186
204
  raceDataTelemetryDetail?: boolean;
205
+ penaltyBanners?: boolean;
206
+ timingPenaltyBadges?: boolean;
207
+ simulationSpeedControl?: boolean;
187
208
  timingTowerVerticalFit?: TimingTowerVerticalFit;
188
209
  }
189
210
 
@@ -206,9 +227,113 @@ export interface TrackSnapshot {
206
227
  width?: number;
207
228
  length?: number;
208
229
  sectors?: TrackSectorSnapshot[];
230
+ pitLane?: PitLaneSnapshot;
209
231
  [key: string]: unknown;
210
232
  }
211
233
 
234
+ export interface TrackPointSnapshot {
235
+ x: number;
236
+ y: number;
237
+ heading?: number;
238
+ distance?: number;
239
+ }
240
+
241
+ export interface PitLaneSnapshot {
242
+ enabled: boolean;
243
+ side: -1 | 1;
244
+ width: number;
245
+ offset?: number;
246
+ boxCount: number;
247
+ teamCount: number;
248
+ boxesPerTeam: number;
249
+ entry: {
250
+ trackDistance: number;
251
+ distanceFromStart: number;
252
+ trackPoint: TrackPointSnapshot;
253
+ edgePoint: TrackPointSnapshot;
254
+ trackConnectPoint: TrackPointSnapshot;
255
+ lanePoint: TrackPointSnapshot;
256
+ roadCenterline: TrackPointSnapshot[];
257
+ connector: TrackPointSnapshot[];
258
+ };
259
+ exit: {
260
+ trackDistance: number;
261
+ distanceFromStart: number;
262
+ trackPoint: TrackPointSnapshot;
263
+ edgePoint: TrackPointSnapshot;
264
+ trackConnectPoint: TrackPointSnapshot;
265
+ lanePoint: TrackPointSnapshot;
266
+ roadCenterline: TrackPointSnapshot[];
267
+ connector: TrackPointSnapshot[];
268
+ };
269
+ mainLane: {
270
+ start: TrackPointSnapshot;
271
+ end: TrackPointSnapshot;
272
+ points: TrackPointSnapshot[];
273
+ length: number;
274
+ heading: number;
275
+ };
276
+ fastLane?: {
277
+ offset: number;
278
+ width: number;
279
+ };
280
+ workingLane?: {
281
+ start: TrackPointSnapshot;
282
+ end: TrackPointSnapshot;
283
+ points: TrackPointSnapshot[];
284
+ offset: number;
285
+ width: number;
286
+ };
287
+ serviceNormal: { x: number; y: number };
288
+ teams?: PitTeamSnapshot[];
289
+ boxes: PitBoxSnapshot[];
290
+ serviceAreas?: PitServiceAreaSnapshot[];
291
+ }
292
+
293
+ export interface PitTeamSnapshot {
294
+ id: string;
295
+ name: string;
296
+ color: string;
297
+ index: number;
298
+ boxIds: string[];
299
+ serviceAreaId?: string | null;
300
+ }
301
+
302
+ export interface PitBoxSnapshot {
303
+ id: string;
304
+ index: number;
305
+ teamIndex: number;
306
+ teamBoxIndex: number;
307
+ teamId?: string;
308
+ teamName?: string;
309
+ teamColor?: string;
310
+ distanceAlongLane: number;
311
+ laneTarget: TrackPointSnapshot;
312
+ center: TrackPointSnapshot;
313
+ length: number;
314
+ depth: number;
315
+ corners: TrackPointSnapshot[];
316
+ }
317
+
318
+ export interface PitServiceAreaSnapshot {
319
+ id: string;
320
+ index: number;
321
+ teamIndex: number;
322
+ teamId?: string;
323
+ teamName?: string;
324
+ teamColor?: string;
325
+ distanceAlongLane: number;
326
+ queueDistanceAlongLane: number;
327
+ laneTarget: TrackPointSnapshot;
328
+ center: TrackPointSnapshot;
329
+ queuePoint: TrackPointSnapshot;
330
+ length: number;
331
+ depth: number;
332
+ corners: TrackPointSnapshot[];
333
+ queueCorners: TrackPointSnapshot[];
334
+ garageBoxIds: string[];
335
+ }
336
+
212
337
  export interface TrackSectorSnapshot {
213
338
  index: number;
214
339
  id: string;
@@ -227,6 +352,8 @@ export interface LapTelemetrySnapshot {
227
352
  currentSectorElapsed: number | null;
228
353
  currentSectorProgress: number | null;
229
354
  currentSectors: Array<number | null>;
355
+ sectorProgress: Array<number | null>;
356
+ liveSectors: Array<number | null>;
230
357
  sectorPerformance: {
231
358
  current: Array<SectorPerformanceStatus | null>;
232
359
  last: Array<SectorPerformanceStatus | null>;
@@ -239,12 +366,187 @@ export interface LapTelemetrySnapshot {
239
366
  completedLaps: number;
240
367
  }
241
368
 
369
+ export interface PitStopSnapshot {
370
+ status: 'pending' | 'entering' | 'queued' | 'servicing' | 'exiting' | 'completed';
371
+ intent: PaddockPitIntent;
372
+ phase: 'entry' | 'queue' | 'queue-release' | 'penalty' | 'service' | 'exit' | null;
373
+ boxIndex: number;
374
+ boxId: string;
375
+ garageBoxIndex?: number | null;
376
+ garageBoxId?: string | null;
377
+ teamId?: string | null;
378
+ teamColor?: string | null;
379
+ stopsCompleted: number;
380
+ queueingForService?: boolean;
381
+ plannedRaceDistance: number | null;
382
+ entryRaceDistance: number | null;
383
+ serviceRemainingSeconds: number | null;
384
+ penaltyServiceRemainingSeconds: number | null;
385
+ penaltyServiceTotalSeconds: number | null;
386
+ servingPenaltyIds: string[];
387
+ targetTire?: TireCompound | string | null;
388
+ serviceProfile?: {
389
+ baseSeconds: number;
390
+ seconds: number;
391
+ perfect: boolean;
392
+ variabilityEnabled: boolean;
393
+ teamId?: string | null;
394
+ pitCrew: Required<PaddockPitCrewStats>;
395
+ speedDeltaSeconds: number;
396
+ consistencyDeltaSeconds: number;
397
+ issueDelaySeconds: number;
398
+ issue?: string | null;
399
+ } | null;
400
+ }
401
+
242
402
  export interface RaceEvent {
243
403
  type: string;
244
404
  at?: number;
405
+ firstShapeId?: string;
406
+ secondShapeId?: string;
407
+ contactType?: 'body-body' | 'body-wheel' | 'wheel-body' | 'wheel-wheel' | string;
408
+ depth?: number;
409
+ timeOfImpact?: number;
410
+ [key: string]: unknown;
411
+ }
412
+
413
+ export type PaddockRulesetName = 'paddock' | 'custom' | 'grandPrix2025' | 'fia2025';
414
+
415
+ export interface PaddockPitStopRules {
416
+ enabled?: boolean;
417
+ pitLaneSpeedLimitKph?: number;
418
+ defaultStopSeconds?: number;
419
+ variability?: {
420
+ enabled?: boolean;
421
+ perfect?: boolean;
422
+ speedImpactSeconds?: number;
423
+ consistencyJitterSeconds?: number;
424
+ issueChance?: number;
425
+ issueMaxDelaySeconds?: number;
426
+ };
427
+ maxConcurrentPitLaneCars?: number;
428
+ minimumPitLaneGapMeters?: number;
429
+ doubleStacking?: boolean;
430
+ tirePitRequestThresholdPercent?: number;
431
+ tirePitCommitThresholdPercent?: number;
432
+ }
433
+
434
+ export interface PaddockTireStrategyRules {
435
+ enabled?: boolean;
436
+ compounds?: TireCompound[];
437
+ mandatoryDistinctDryCompounds?: number | null;
438
+ }
439
+
440
+ export interface PaddockPenaltySubsectionRules {
441
+ strictness?: number;
442
+ timePenaltySeconds?: number;
443
+ consequences?: PaddockPenaltyConsequence[];
444
+ }
445
+
446
+ export type PaddockPenaltyConsequence =
447
+ | { type: 'warning' }
448
+ | { type: 'time'; seconds: number }
449
+ | { type: 'driveThrough'; conversionSeconds?: number }
450
+ | { type: 'stopGo'; seconds?: number; conversionSeconds?: number }
451
+ | { type: 'positionDrop'; positions: number }
452
+ | { type: 'gridDrop'; positions: number }
453
+ | { type: 'disqualification' };
454
+
455
+ export type PaddockPenaltyStatus = 'issued' | 'served' | 'applied' | 'cancelled';
456
+
457
+ export interface PaddockTrackLimitPenaltyRules extends PaddockPenaltySubsectionRules {
458
+ warningsBeforePenalty?: number;
459
+ relaxedMarginMeters?: number;
460
+ }
461
+
462
+ export interface PaddockCollisionPenaltyRules extends PaddockPenaltySubsectionRules {
463
+ minimumSeverity?: number;
464
+ relaxedSeverityMargin?: number;
465
+ minimumImpactSpeedKph?: number;
466
+ relaxedImpactSpeedKph?: number;
467
+ }
468
+
469
+ export interface PaddockPitLaneSpeedingPenaltyRules extends PaddockPenaltySubsectionRules {
470
+ speedLimitKph?: number;
471
+ marginKph?: number;
472
+ relaxedMarginKph?: number;
473
+ }
474
+
475
+ export interface PaddockPenaltyRules {
476
+ enabled?: boolean;
477
+ stewardStrictness?: number;
478
+ trackLimits?: PaddockTrackLimitPenaltyRules;
479
+ collision?: PaddockCollisionPenaltyRules;
480
+ tireRequirement?: PaddockPenaltySubsectionRules;
481
+ pitLaneSpeeding?: PaddockPitLaneSpeedingPenaltyRules;
482
+ }
483
+
484
+ export interface PaddockRaceModules {
485
+ pitStops?: PaddockPitStopRules;
486
+ tireStrategy?: PaddockTireStrategyRules;
487
+ penalties?: PaddockPenaltyRules;
488
+ weather?: { enabled?: boolean };
489
+ reliability?: { enabled?: boolean };
490
+ fuelLoad?: { enabled?: boolean };
491
+ }
492
+
493
+ export interface PaddockRaceRules {
494
+ ruleset?: PaddockRulesetName;
495
+ profile?: PaddockRulesetName;
496
+ modules?: PaddockRaceModules;
497
+ drsDetectionSeconds?: number;
498
+ safetyCarSpeed?: number;
499
+ safetyCarLeadDistance?: number;
500
+ safetyCarGap?: number;
501
+ collisionRestitution?: number;
502
+ standingStart?: boolean;
503
+ startLightCount?: number;
504
+ startLightInterval?: number;
505
+ startLightsOutHold?: number;
506
+ }
507
+
508
+ export interface PaddockPenaltyEntry {
509
+ id: string;
510
+ type: string;
511
+ at: number;
512
+ lap: number;
513
+ driverId: string;
514
+ strictness: number;
515
+ status: PaddockPenaltyStatus;
516
+ penaltySeconds: number;
517
+ pendingPenaltySeconds?: number;
518
+ consequences: PaddockPenaltyConsequence[];
519
+ serviceType?: 'driveThrough' | 'stopGo' | null;
520
+ serviceRequired?: boolean;
521
+ serviceServedAt?: number | null;
522
+ appliedAt?: number;
523
+ cancelledAt?: number;
524
+ unserved?: boolean;
525
+ positionDrop?: number;
526
+ gridDrop?: number;
527
+ disqualified?: boolean;
528
+ reason?: string;
529
+ otherCarId?: string;
530
+ aheadDriverId?: string;
531
+ atFaultDriverId?: string;
532
+ sharedFault?: boolean;
533
+ impactSpeedKph?: number;
534
+ impactSpeedThresholdKph?: number;
245
535
  [key: string]: unknown;
246
536
  }
247
537
 
538
+ export interface WheelSurfaceSnapshot {
539
+ id: string;
540
+ x: number;
541
+ y: number;
542
+ signedOffset: number;
543
+ crossTrackError: number;
544
+ surface: string;
545
+ onTrack: boolean;
546
+ inPitLane: boolean;
547
+ fullyOutsideWhiteLine: boolean;
548
+ }
549
+
248
550
  export interface CarSnapshot {
249
551
  id: string;
250
552
  rank: number;
@@ -255,12 +557,32 @@ export interface CarSnapshot {
255
557
  tire: TireCompound;
256
558
  lap: number;
257
559
  speedKph: number;
560
+ finishRank?: number | null;
561
+ status?: 'racing' | 'waved-flag' | string;
562
+ raceStatus?: 'racing' | 'waved-flag' | string;
563
+ wavedFlag?: boolean;
258
564
  finished?: boolean;
259
565
  finishTime?: number | null;
566
+ penaltySeconds?: number;
567
+ adjustedFinishTime?: number | null;
260
568
  classifiedRank?: number | null;
261
569
  intervalAheadSeconds?: number | null;
262
570
  leaderGapSeconds?: number | null;
571
+ gapAheadLaps?: number;
572
+ intervalAheadLaps?: number;
573
+ leaderGapLaps?: number;
263
574
  lapTelemetry?: LapTelemetrySnapshot;
575
+ surface?: string;
576
+ signedOffset?: number;
577
+ crossTrackError?: number;
578
+ inPitLane?: boolean;
579
+ wheels?: WheelSurfaceSnapshot[];
580
+ pitLanePart?: 'entry' | 'fast-lane' | 'working-lane' | 'exit' | 'service-box' | 'garage-box' | null;
581
+ pitBoxId?: string | null;
582
+ pitLaneCrossTrackError?: number | null;
583
+ usedTireCompounds?: Array<TireCompound | string>;
584
+ pitIntent?: PaddockPitIntent;
585
+ pitStop?: PitStopSnapshot | null;
264
586
  [key: string]: unknown;
265
587
  }
266
588
 
@@ -271,6 +593,12 @@ export interface RaceClassificationEntry {
271
593
  lap: number;
272
594
  finished: boolean;
273
595
  finishTime?: number | null;
596
+ penaltySeconds?: number;
597
+ adjustedFinishTime?: number | null;
598
+ gapLaps?: number;
599
+ intervalLaps?: number;
600
+ positionDrop?: number;
601
+ disqualified?: boolean;
274
602
  [key: string]: unknown;
275
603
  }
276
604
 
@@ -285,6 +613,15 @@ export interface RaceSnapshot {
285
613
  totalLaps: number;
286
614
  raceControl: {
287
615
  mode: string;
616
+ redFlag: boolean;
617
+ pitLaneOpen: boolean;
618
+ pitLaneStatus?: {
619
+ enabled: boolean;
620
+ open: boolean;
621
+ reason: string;
622
+ color: string;
623
+ light: string;
624
+ };
288
625
  finished: boolean;
289
626
  finishedAt: number | null;
290
627
  winner: CarSnapshot | null;
@@ -294,12 +631,20 @@ export interface RaceSnapshot {
294
631
  [key: string]: unknown;
295
632
  };
296
633
  };
634
+ pitLaneStatus?: {
635
+ enabled: boolean;
636
+ open: boolean;
637
+ reason: string;
638
+ color: string;
639
+ light: string;
640
+ };
297
641
  safetyCar: {
298
642
  deployed: boolean;
299
643
  [key: string]: unknown;
300
644
  };
301
- rules: Record<string, unknown>;
645
+ rules: PaddockRaceRules;
302
646
  events: RaceEvent[];
647
+ penalties: PaddockPenaltyEntry[];
303
648
  cars: CarSnapshot[];
304
649
  }
305
650
 
@@ -313,7 +658,7 @@ export interface F1SimulatorCallbacks {
313
658
  onLoadingChange?: (state: { loading: boolean; phase: string }) => void;
314
659
  onReady?: (payload: { snapshot: RaceSnapshot }) => void;
315
660
  onError?: (error: unknown, context: LifecycleErrorContext) => void;
316
- onDriverSelect?: (driver: CarSnapshot, snapshot: RaceSnapshot) => void;
661
+ onDriverSelect?: (driver: NormalizedSimulatorDriver, snapshot: RaceSnapshot) => void;
317
662
  onRaceEvent?: (event: RaceEvent, snapshot: RaceSnapshot) => void;
318
663
  onLapChange?: (payload: {
319
664
  previousLeaderLap: number | null;
@@ -341,6 +686,9 @@ export interface F1SimulatorExpertAction {
341
686
  steering: number;
342
687
  throttle: number;
343
688
  brake: number;
689
+ pitIntent?: PaddockPitIntent;
690
+ pitCompound?: TireCompound | string;
691
+ pitTargetCompound?: TireCompound | string;
344
692
  }
345
693
 
346
694
  export interface F1SimulatorExpertApi {
@@ -360,6 +708,7 @@ export interface F1SimulatorOptions extends F1SimulatorCallbacks {
360
708
  seed?: number;
361
709
  trackSeed?: number;
362
710
  totalLaps?: number;
711
+ rules?: PaddockRaceRules;
363
712
  initialCameraMode?: CameraMode;
364
713
  theme?: F1SimulatorTheme;
365
714
  title?: string;
@@ -397,9 +746,16 @@ export interface F1MountedSimulator {
397
746
  restart(nextOptions?: F1SimulatorRestartOptions): void;
398
747
  selectDriver(driverId: string): void;
399
748
  setSafetyCarDeployed(deployed: boolean): void;
749
+ setRedFlagDeployed(deployed: boolean): void;
750
+ setPitLaneOpen(open: boolean): void;
400
751
  callSafetyCar(): void;
401
752
  clearSafetyCar(): void;
402
753
  toggleSafetyCar(): void;
754
+ setPitIntent(driverId: string, intent: PaddockPitIntentRequest, targetCompound?: TireCompound | string): boolean;
755
+ getPitIntent(driverId: string): PaddockPitIntent;
756
+ getPitTargetCompound(driverId: string): TireCompound | string | null;
757
+ servePenalty(penaltyId: string): PaddockPenaltyEntry | null;
758
+ cancelPenalty(penaltyId: string): PaddockPenaltyEntry | null;
403
759
  getSnapshot(): RaceSnapshot | null;
404
760
  }
405
761
 
@@ -426,9 +782,16 @@ export interface PaddockSimulatorController {
426
782
  restart(nextOptions?: F1SimulatorRestartOptions): void;
427
783
  selectDriver(driverId: string): void;
428
784
  setSafetyCarDeployed(deployed: boolean): void;
785
+ setRedFlagDeployed(deployed: boolean): void;
786
+ setPitLaneOpen(open: boolean): void;
429
787
  callSafetyCar(): void;
430
788
  clearSafetyCar(): void;
431
789
  toggleSafetyCar(): void;
790
+ setPitIntent(driverId: string, intent: PaddockPitIntentRequest, targetCompound?: TireCompound | string): boolean;
791
+ getPitIntent(driverId: string): PaddockPitIntent;
792
+ getPitTargetCompound(driverId: string): TireCompound | string | null;
793
+ servePenalty(penaltyId: string): PaddockPenaltyEntry | null;
794
+ cancelPenalty(penaltyId: string): PaddockPenaltyEntry | null;
432
795
  getSnapshot(): RaceSnapshot | null;
433
796
  }
434
797
 
package/src/index.js CHANGED
@@ -109,6 +109,12 @@ export async function mountF1Simulator(root, options = {}) {
109
109
  setSafetyCarDeployed(deployed) {
110
110
  app.setSafetyCarDeployed(deployed);
111
111
  },
112
+ setRedFlagDeployed(deployed) {
113
+ app.setRedFlagDeployed(deployed);
114
+ },
115
+ setPitLaneOpen(open) {
116
+ app.setPitLaneOpen(open);
117
+ },
112
118
  callSafetyCar() {
113
119
  app.setSafetyCarDeployed(true);
114
120
  },
@@ -119,6 +125,21 @@ export async function mountF1Simulator(root, options = {}) {
119
125
  const active = app.getSnapshot()?.raceControl.mode === 'safety-car';
120
126
  app.setSafetyCarDeployed(!active);
121
127
  },
128
+ setPitIntent(driverId, intent, targetCompound) {
129
+ return app.setPitIntent(driverId, intent, targetCompound);
130
+ },
131
+ getPitIntent(driverId) {
132
+ return app.getPitIntent(driverId);
133
+ },
134
+ getPitTargetCompound(driverId) {
135
+ return app.getPitTargetCompound(driverId);
136
+ },
137
+ servePenalty(penaltyId) {
138
+ return app.servePenalty(penaltyId);
139
+ },
140
+ cancelPenalty(penaltyId) {
141
+ return app.cancelPenalty(penaltyId);
142
+ },
122
143
  getSnapshot() {
123
144
  return app.getSnapshot();
124
145
  },