@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.
- package/CHANGELOG.md +91 -0
- package/INSTALL_AND_UPDATE.md +5 -2
- package/README.md +328 -13
- package/docs/architecture.md +150 -41
- package/docs/concepts.md +22 -3
- package/docs/custom_model_controller.md +329 -0
- package/docs/data_contract.md +474 -34
- package/docs/data_model.md +1 -1
- package/docs/index.md +1 -0
- package/docs/learnings.md +23 -1
- package/docs/rules.md +169 -21
- package/docs/system_specs.md +56 -20
- package/docs/training.md +369 -25
- package/examples/python/README.md +62 -0
- package/examples/python/base_policy_server.py +191 -0
- package/examples/python/requirements.txt +2 -0
- package/examples/train-basic-policy.mjs +5 -6
- package/examples/trainingData.mjs +133 -0
- package/package.json +15 -4
- package/src/api/PaddockSimulatorController.js +36 -2
- package/src/app/BrowserExpertAdapter.js +367 -9
- package/src/app/F1SimulatorApp.js +615 -940
- package/src/app/banners/bannerFormatters.js +55 -0
- package/src/app/banners/raceDataBannerController.js +141 -0
- package/src/app/banners/stewardMessageController.js +99 -0
- package/src/app/camera/cameraConstants.js +21 -0
- package/src/app/camera/cameraController.js +265 -0
- package/src/app/camera/cameraGeometry.js +176 -0
- package/src/app/domBindings.js +14 -1
- package/src/app/readouts/carOverviewRenderer.js +109 -0
- package/src/app/readouts/controlStateRenderer.js +37 -0
- package/src/app/readouts/raceStatusRenderer.js +133 -0
- package/src/app/readouts/readoutFormatters.js +58 -0
- package/src/app/readouts/telemetryRenderer.js +113 -0
- package/src/app/readouts/timingTowerRenderer.js +177 -0
- package/src/app/rendering/appAssets.js +38 -0
- package/src/app/rendering/carRenderer.js +286 -0
- package/src/app/rendering/displayUtils.js +22 -0
- package/src/app/rendering/drsTrailRenderer.js +90 -0
- package/src/app/rendering/expertSensorRenderer.js +126 -0
- package/src/app/rendering/pitLaneStatusRenderer.js +50 -0
- package/src/app/rendering/replayGhostRenderer.js +124 -0
- package/src/app/rendering/trackRenderer.js +25 -0
- package/src/app/runtime/frameLoop.js +66 -0
- package/src/app/runtime/runtimeTiming.js +34 -0
- package/src/app/runtime/runtimeVisibility.js +45 -0
- package/src/config/defaultOptions.js +24 -4
- package/src/environment/actions.js +38 -7
- package/src/environment/controllerLoop.js +246 -0
- package/src/environment/episode.js +86 -2
- package/src/environment/evaluation.js +206 -0
- package/src/environment/index.d.ts +638 -6
- package/src/environment/index.js +14 -0
- package/src/environment/metrics.js +159 -0
- package/src/environment/observationOptions.js +33 -0
- package/src/environment/observations.js +500 -43
- package/src/environment/options.js +41 -2
- package/src/environment/recorder.js +28 -0
- package/src/environment/runtime.js +260 -24
- package/src/environment/scenarios.js +249 -0
- package/src/environment/sensors/bodySenses.js +23 -0
- package/src/environment/sensors/boundarySenses.js +17 -0
- package/src/environment/sensors/carRays.js +75 -0
- package/src/environment/sensors/contactSenses.js +32 -0
- package/src/environment/sensors/index.js +157 -0
- package/src/environment/sensors/indexedRayBands.js +103 -0
- package/src/environment/sensors/nearbyCars.js +52 -0
- package/src/environment/sensors/opponentRadar.js +65 -0
- package/src/environment/sensors/rayConfig.js +110 -0
- package/src/environment/sensors/rayDefaults.js +6 -0
- package/src/environment/sensors/rayGeometry.js +60 -0
- package/src/environment/sensors/rayGuards.js +19 -0
- package/src/environment/sensors/sensorTargets.js +60 -0
- package/src/environment/sensors/surfaceRays.js +296 -0
- package/src/environment/sensors/trackRays.js +261 -0
- package/src/environment/sensors.js +1 -257
- package/src/environment/specs.js +188 -9
- package/src/environment/workerProtocol.js +64 -0
- package/src/index.d.ts +533 -7
- package/src/index.js +26 -0
- package/src/rendering/proceduralTrackAsset.js +20 -510
- package/src/rendering/renderSnapshot.js +38 -12
- package/src/rendering/track/finishLineRenderer.js +34 -0
- package/src/rendering/track/gridRenderer.js +47 -0
- package/src/rendering/track/offsetStrokeSafety.js +136 -0
- package/src/rendering/track/pitLaneRenderer.js +248 -0
- package/src/rendering/track/trackMaterialRenderer.js +326 -0
- package/src/rendering/track/trackRenderConstants.js +38 -0
- package/src/rendering/track/trackRenderGeometry.js +94 -0
- package/src/simulation/collisionGeometry.js +214 -0
- package/src/simulation/driver/driverControlConstants.js +28 -0
- package/src/simulation/driver/driverController.js +47 -0
- package/src/simulation/driver/driverInput.js +28 -0
- package/src/simulation/driver/driverMath.js +6 -0
- package/src/simulation/driver/driverPersonality.js +25 -0
- package/src/simulation/driver/edgeRecovery.js +48 -0
- package/src/simulation/driver/racingControls.js +399 -0
- package/src/simulation/driver/racingLinePlan.js +166 -0
- package/src/simulation/driver/recoveryDynamics.js +38 -0
- package/src/simulation/driver/rejoinControls.js +213 -0
- package/src/simulation/driver/safetyCarControls.js +35 -0
- package/src/simulation/driver/trafficScan.js +51 -0
- package/src/simulation/driverController.js +4 -253
- package/src/simulation/participants/participantInteractions.js +120 -0
- package/src/simulation/pit/pitFlow.js +234 -0
- package/src/simulation/pit/pitIntent.js +92 -0
- package/src/simulation/pit/pitOccupancy.js +47 -0
- package/src/simulation/pit/pitPenaltyService.js +71 -0
- package/src/simulation/pit/pitQueue.js +55 -0
- package/src/simulation/pit/pitRouteMovement.js +160 -0
- package/src/simulation/pit/pitRouting.js +319 -0
- package/src/simulation/pit/pitService.js +41 -0
- package/src/simulation/pit/pitServiceConstants.js +21 -0
- package/src/simulation/pit/pitServiceProfile.js +45 -0
- package/src/simulation/pit/pitSnapshots.js +61 -0
- package/src/simulation/pit/pitState.js +113 -0
- package/src/simulation/pit/pitTireService.js +92 -0
- package/src/simulation/race/classification.js +104 -0
- package/src/simulation/race/gridPenalties.js +37 -0
- package/src/simulation/race/raceControlState.js +38 -0
- package/src/simulation/race/raceDistance.js +32 -0
- package/src/simulation/race/raceFinish.js +65 -0
- package/src/simulation/race/raceLifecycle.js +71 -0
- package/src/simulation/race/raceOrder.js +113 -0
- package/src/simulation/race/raceProgress.js +124 -0
- package/src/simulation/race/raceSetup.js +93 -0
- package/src/simulation/race/raceStep.js +80 -0
- package/src/simulation/race/redFlag.js +15 -0
- package/src/simulation/race/retirements.js +44 -0
- package/src/simulation/race/safetyCar.js +86 -0
- package/src/simulation/race/stalledDnf.js +86 -0
- package/src/simulation/raceSimulation.js +386 -1012
- package/src/simulation/replay/replayGhosts.js +128 -0
- package/src/simulation/rules/collisionSteward.js +69 -0
- package/src/simulation/rules/moduleConfig.js +72 -0
- package/src/simulation/rules/penaltyConfig.js +99 -0
- package/src/simulation/rules/penaltyEvents.js +18 -0
- package/src/simulation/rules/penaltyLedger.js +153 -0
- package/src/simulation/rules/penaltyStats.js +26 -0
- package/src/simulation/rules/pitLaneSpeedingSteward.js +71 -0
- package/src/simulation/rules/ruleConfigMerge.js +41 -0
- package/src/simulation/rules/ruleDefaults.js +85 -0
- package/src/simulation/rules/rulesReview.js +141 -0
- package/src/simulation/rules/rulesetPresets.js +47 -0
- package/src/simulation/rules/tireRequirementSteward.js +29 -0
- package/src/simulation/rules/trackLimitsSteward.js +85 -0
- package/src/simulation/rulesConfig.js +34 -0
- package/src/simulation/snapshots/raceSnapshots.js +192 -0
- package/src/simulation/timing/drsTiming.js +87 -0
- package/src/simulation/timing/gapEstimation.js +38 -0
- package/src/simulation/timing/lapTelemetry.js +182 -0
- package/src/simulation/timing/raceTiming.js +15 -0
- package/src/simulation/timing/sectorPerformance.js +60 -0
- package/src/simulation/timing/timingConstants.js +5 -0
- package/src/simulation/timing/timingHistory.js +46 -0
- package/src/simulation/timing/timingLines.js +79 -0
- package/src/simulation/track/centerlineSpline.js +59 -0
- package/src/simulation/track/drsZones.js +67 -0
- package/src/simulation/track/pitLaneAccess.js +103 -0
- package/src/simulation/track/pitLaneLayout.js +395 -0
- package/src/simulation/track/pitLaneState.js +160 -0
- package/src/simulation/track/proceduralCenterline.js +237 -0
- package/src/simulation/track/sampleGeometry.js +49 -0
- package/src/simulation/track/spatialQueries.js +154 -0
- package/src/simulation/track/startStraight.js +141 -0
- package/src/simulation/track/trackConstants.js +109 -0
- package/src/simulation/track/trackGenerationOptions.js +283 -0
- package/src/simulation/track/trackMath.js +241 -0
- package/src/simulation/track/trackModel.js +240 -0
- package/src/simulation/track/trackQueryIndex.js +945 -0
- package/src/simulation/track/trackSectors.js +19 -0
- package/src/simulation/track/trackStatePolicy.js +24 -0
- package/src/simulation/track/trackValidation.js +98 -0
- package/src/simulation/trackModel.js +1 -628
- package/src/simulation/units.js +6 -4
- package/src/simulation/vehicle/contactResolution.js +116 -0
- package/src/simulation/vehicle/mainTrackWheelSurface.js +82 -0
- package/src/simulation/vehicle/pitWheelSurface.js +69 -0
- package/src/simulation/vehicle/runoffResponse.js +88 -0
- package/src/simulation/vehicle/surfacePriority.js +28 -0
- package/src/simulation/vehicle/vehicleGeometry.js +247 -0
- package/src/simulation/vehicle/vehiclePhysics.js +363 -0
- package/src/simulation/vehicle/vehicleSnapshots.js +316 -0
- package/src/simulation/vehicle/vehicleState.js +186 -0
- package/src/simulation/vehicle/wheelSurface.js +150 -0
- package/src/simulation/vehicleGeometry.js +1 -0
- package/src/simulation/vehiclePhysics.js +1 -104
- package/src/simulation/warmup/runtimeWarmup.js +224 -0
- package/src/simulation/wheelSurface.js +1 -0
- package/src/styles.css +342 -42
- package/src/ui/bannerTemplates.js +69 -0
- package/src/ui/cameraControlsTemplate.js +20 -0
- package/src/ui/carOverviewTemplate.js +33 -0
- package/src/ui/componentTemplates.js +20 -426
- package/src/ui/raceCanvasTemplate.js +69 -0
- package/src/ui/raceControlStatusBanner.js +39 -0
- package/src/ui/raceControlsTemplate.js +36 -0
- package/src/ui/shellTemplate.js +6 -5
- package/src/ui/telemetryDrawerTemplate.js +55 -0
- package/src/ui/telemetryTemplates.js +136 -0
- package/src/ui/templateUtils.js +23 -0
- 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()
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { createPaddockEnvironment, createProgressReward } from '
|
|
4
|
-
import {
|
|
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 =
|
|
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 ?
|
|
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:
|
|
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
|
+
"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": "
|
|
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
|
}
|