/** * 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 { AutoRacingSimulator } from "./auto-racing-simulator"; import { GolfSimulator } from "./golf-simulator"; import { UCLSimulator } from "./ucl-simulator"; import { NCAAMSimulator } from "./ncaam-simulator"; import { NCAAWSimulator } from "./ncaaw-simulator"; import { NBASimulator } from "./nba-simulator"; import { NHLSimulator } from "./nhl-simulator"; import { AFLSimulator } from "./afl-simulator"; import { SnookerSimulator } from "./snooker-simulator"; import { TennisSimulator } from "./tennis-simulator"; import { DartsSimulator } from "./darts-simulator"; import { CSMajorSimulator } from "./cs-major-simulator"; import { MLBSimulator } from "./mlb-simulator"; import { NFLSimulator } from "./nfl-simulator"; import { WNBASimulator } from "./wnba-simulator"; import { WorldCupSimulator } from "./world-cup-simulator"; import { NCAAFootballSimulator } from "./ncaa-football-simulator"; import { LLWSSimulator } from "./llws-simulator"; import { BracktSimulator } from "./brackt-simulator"; export const SIMULATOR_TYPES = [ "f1_standings", "indycar_standings", "golf_qualifying_points", "playoff_bracket", "ucl_bracket", "ncaam_bracket", "ncaaw_bracket", "nba_bracket", "nhl_bracket", "nfl_bracket", "afl_bracket", "snooker_bracket", "tennis_qualifying_points", "mlb_bracket", "wnba_bracket", "world_cup", "darts_bracket", "cs2_major_qualifying_points", "ncaa_football_bracket", "llws_bracket", "brackt", ] as const; export type SimulatorType = typeof SIMULATOR_TYPES[number]; // ─── Race points tables ─────────────────────────────────────────────────────── /** F1 points: positions 1–10. */ const F1_RACE_POINTS: Record = { 1: 25, 2: 18, 3: 15, 4: 12, 5: 10, 6: 8, 7: 6, 8: 4, 9: 2, 10: 1, }; /** IndyCar standard race points: positions 1–26. */ const INDYCAR_RACE_POINTS: Record = { 1: 50, 2: 40, 3: 35, 4: 32, 5: 30, 6: 28, 7: 26, 8: 24, 9: 22, 10: 20, 11: 19, 12: 18, 13: 17, 14: 16, 15: 15, 16: 14, 17: 13, 18: 12, 19: 11, 20: 10, 21: 9, 22: 8, 23: 7, 24: 6, 25: 5, 26: 5, }; export interface SimulatorInfo { name: string; description: string; } const REGISTRY: Record Simulator }> = { f1_standings: { info: { name: "F1 Standings Model", description: "Simulates remaining races using current F1 standings and futures odds" }, create: () => new AutoRacingSimulator(F1_RACE_POINTS, "f1_standings_model"), }, indycar_standings: { info: { name: "IndyCar Standings Model", description: "Simulates remaining races using current IndyCar standings and futures odds" }, create: () => new AutoRacingSimulator(INDYCAR_RACE_POINTS, "indycar_standings_model"), }, golf_qualifying_points: { info: { name: "Golf Qualifying Points Monte Carlo", description: "Simulates remaining majors using a Plackett-Luce model with SG: Total ratings. Awards QP by finishing position; ranks players by total QP across all 4 majors." }, create: () => new GolfSimulator(), }, playoff_bracket: { info: { name: "Bracket Monte Carlo", description: "Simulates playoff bracket outcomes using Elo ratings" }, create: () => new BracketSimulator(), }, ucl_bracket: { info: { name: "UCL Bracket Monte Carlo", description: "Simulates the UEFA Champions League 16-team knockout bracket using blended Elo + futures odds" }, create: () => new UCLSimulator(), }, ncaam_bracket: { info: { name: "NCAAM Bracket Monte Carlo", description: "Simulates the NCAA Men's Basketball Tournament 64-team bracket using KenPom net ratings" }, create: () => new NCAAMSimulator(), }, ncaaw_bracket: { info: { name: "NCAAW Bracket Monte Carlo", description: "Simulates the NCAA Women's Basketball Tournament 64-team bracket using Barttorvik Barthag ratings" }, create: () => new NCAAWSimulator(), }, nba_bracket: { info: { name: "NBA Playoff Monte Carlo", description: "Simulates NBA playoff seedings (via seed probabilities) and full bracket (best-of-7 series) using Elo ratings" }, create: () => new NBASimulator(), }, nhl_bracket: { info: { name: "NHL Playoff Monte Carlo", description: "Simulates NHL playoff seedings (divisional format, via seed probabilities) and full bracket (best-of-7 series) using Elo ratings" }, create: () => new NHLSimulator(), }, nfl_bracket: { info: { name: "NFL Season + Playoffs Monte Carlo", description: "Projects NFL regular season standings (via nfelo Elo ratings) to determine the 14-team playoff field, then simulates the full bracket (Wild Card → Divisional → Conference Championship → Super Bowl). Elo ratings maintained via admin sourceElo field.", }, create: () => new NFLSimulator(), }, afl_bracket: { info: { name: "AFL Season + Finals Monte Carlo", description: "Projects AFL regular season standings via Elo, then simulates the 10-team finals series (Wildcard → QF/EF → SF → PF → Grand Final). Reads current standings from DB; falls back to full-season projection pre-season." }, create: () => new AFLSimulator(), }, snooker_bracket: { info: { name: "Snooker World Championship Monte Carlo", description: "Simulates the 32-player World Championship bracket using frame-by-frame Bernoulli win probability and direct Elo ratings. Pre-bracket path simulates qualifying (ranks 17-48) and randomly draws qualifiers vs top 16 seeds." }, create: () => new SnookerSimulator(), }, tennis_qualifying_points: { info: { name: "Tennis Grand Slam Monte Carlo", description: "Simulates all 4 Grand Slam majors (128-player seeded bracket) using surface-specific Elo ratings. Accumulates qualifying points per round (tie-split applied) and ranks players by total QP across the season." }, create: () => new TennisSimulator(), }, mlb_bracket: { info: { name: "MLB Playoff Monte Carlo", description: "Simulates MLB division races + full playoff bracket (WC best-of-3, DS best-of-5, LCS/WS best-of-7) using Elo ratings calibrated from FanGraphs projected wins" }, create: () => new MLBSimulator(), }, wnba_bracket: { info: { name: "WNBA Playoff Monte Carlo", description: "Projects WNBA regular season seedings and simulates full playoff bracket (R1 best-of-3, Semis best-of-5, Finals best-of-7) using SRS-derived Elo ratings. SRS values sourced from basketball-reference.com." }, create: () => new WNBASimulator(), }, world_cup: { info: { name: "FIFA World Cup 2026 Monte Carlo", description: "Simulates the full 48-team World Cup: round-robin group stage (12 groups), best-8 third-place selection, R32→R16→QF→SF→3rd place game→Final. Uses blended Elo + futures odds." }, create: () => new WorldCupSimulator(), }, darts_bracket: { info: { name: "PDC World Darts Championship Monte Carlo", description: "Simulates the 128-player World Darts Championship bracket using set-level Bernoulli win probability and Elo ratings. Top 32 seeds placed in fixed positions; remaining 96 players randomly drawn per simulation." }, create: () => new DartsSimulator(), }, cs2_major_qualifying_points: { info: { name: "CS2 Major Qualifying Points Monte Carlo", description: "Simulates 2 CS2 Majors per season (3 Swiss stages: Opening Bo1, Elimination Bo1, Decider Bo3, then Champions Stage single-elimination). Awards QP by final placement; ranks teams by total QP across both majors. Stage 3 exits (placements 9–16) are sub-ranked by W-L record." }, create: () => new CSMajorSimulator(), }, ncaa_football_bracket: { info: { name: "NCAA Football CFP Monte Carlo", description: "Simulates the 12-team College Football Playoff bracket using Elo/FPI ratings entered via Admin → Elo Ratings. Optionally blends with championship futures odds (60% Elo / 40% odds). Seeds 1–4 receive first-round byes; First Round losers score 0 points." }, create: () => new NCAAFootballSimulator(), }, llws_bracket: { info: { name: "LLWS Bracket Monte Carlo", description: "Simulates the 20-team Little League World Series: pool play round-robin (5 teams/pool, top 2 advance) → 4-team double-elimination bracket per side (US & International) → consolation game (3rd/4th) → World Series. Uses championship futures odds for all win probabilities. Set externalId to 'US'/'Intl' (randomized pools) or 'US:A'/'US:B'/'Intl:A'/'Intl:B' (fixed pools).", }, create: () => new LLWSSimulator(), }, brackt: { info: { name: "Brackt Harville Model", description: "Projects league manager standings from drafted non-Brackt EVs and simulates final league placement probabilities.", }, create: () => new BracktSimulator(), }, }; 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; }