brackt/app/services/simulations/registry.ts
Chris Parsons c6ba59b0e6
User/chris/ev f1 framework (#93)
* feat: EV simulation framework with F1 Monte Carlo simulator

- Add EV snapshot tables (participant_ev_snapshots, team_ev_snapshots) and simulation_status column on sports seasons
- Add ev-snapshot model with upsert and history query functions
- Add simulator framework: types, bracket/F1/golf simulators, registry
- F1 simulator: vig-removed ICM weighted draw (pre-season) + race-by-race Monte Carlo from current standings (in-season); per-position column normalization to prevent floating-point EV drift
- Add admin simulate route and Run Simulation button on sports season page
- Rework futures-odds admin page to save odds then run simulation in one action
- Remove recalculate-probabilities route (superseded by simulate route)
- Remove EV trend chart panel and associated DB queries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: map simulators to sports via simulatorType field

Adds a `simulator_type` enum column to the `sports` table so each sport
can be assigned a specific simulation algorithm rather than deriving it
from the sports season's scoring pattern.

- Add `simulatorTypeEnum` (f1_standings, indycar_standings,
  golf_qualifying_points, playoff_bracket) + `simulatorType` nullable
  column on `sports` table; migration 0037
- Rewrite simulator registry to key off `SimulatorType` instead of
  `ScoringPattern`; indycar_standings shares F1Simulator for now
- `findSportsSeasonById` now returns `SportsSeasonWithSport` so callers
  have typed access to `sport.simulatorType`
- Simulate and futures-odds actions read `sport.simulatorType`; guard
  fires before setting `simulationStatus: running`
- Admin sport edit page gains a Simulator Type dropdown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 15:34:31 -07:00

58 lines
1.9 KiB
TypeScript

/**
* Simulator Registry
*
* Maps a sport's simulatorType to the appropriate Simulator implementation.
* Set simulatorType on the sport record in the admin UI.
* Add new simulators here as new sports are supported.
*/
import type { Simulator } from "./types";
import { BracketSimulator } from "./bracket-simulator";
import { F1Simulator } from "./f1-simulator";
import { GolfSimulator } from "./golf-simulator";
export type SimulatorType =
| "f1_standings"
| "indycar_standings"
| "golf_qualifying_points"
| "playoff_bracket";
export interface SimulatorInfo {
name: string;
description: string;
}
const REGISTRY: Record<SimulatorType, { info: SimulatorInfo; create: () => Simulator }> = {
f1_standings: {
info: { name: "F1 Standings Model", description: "Simulates remaining races using current F1 standings and futures odds" },
create: () => new F1Simulator(),
},
indycar_standings: {
info: { name: "IndyCar Standings Model", description: "Simulates remaining races using current IndyCar standings and futures odds" },
create: () => new F1Simulator(),
},
golf_qualifying_points: {
info: { name: "Qualifying Points Model", description: "Simulates remaining majors using current QP standings and futures odds" },
create: () => new GolfSimulator(),
},
playoff_bracket: {
info: { name: "Bracket Monte Carlo", description: "Simulates playoff bracket outcomes using Elo ratings" },
create: () => new BracketSimulator(),
},
};
export function getSimulator(simulatorType: SimulatorType): Simulator {
const entry = REGISTRY[simulatorType];
if (!entry) {
throw new Error(
`No simulator registered for type: "${simulatorType}". ` +
`Add a simulator to app/services/simulations/registry.ts.`
);
}
return entry.create();
}
export function getSimulatorInfo(simulatorType: SimulatorType): SimulatorInfo | null {
return REGISTRY[simulatorType]?.info ?? null;
}