2026-03-09 15:34:31 -07:00
/ * *
* 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" ;
2026-03-16 12:22:08 -07:00
import { AutoRacingSimulator } from "./auto-racing-simulator" ;
2026-03-09 15:34:31 -07:00
import { GolfSimulator } from "./golf-simulator" ;
2026-03-10 23:04:51 -07:00
import { UCLSimulator } from "./ucl-simulator" ;
Add NCAAM and NCAAW bracket Monte Carlo simulators (#150)
- NCAAM: KenPom AEM logistic formula (1/(1+exp(-diff/7.5))), data through 2025-26 March 15
- NCAAW: Barttorvik Barthag Log5 formula (A*(1-B)/(A*(1-B)+B*(1-A))), same bracket structure
- Both simulators: 50,000-iteration Monte Carlo, First Four simulation, honors completed matches
- Track E8+ placements only: champion, finalist, FF losers (3rd/4th), E8 losers (5th–8th)
- Add bracket configuration validation: null R64 slots must exactly match First Four mapping
- Fix DEFAULT_SCORING_RULES to 100/70/45/45/20/20/20/20 (3rd/4th=45, 5th–8th=20)
- Align scoring constants across simulate route, expected-values display, and server action
- Zero out EVs for non-bracket participants on every simulation run (prevents EV inflation)
- Add EV total invariant warning (expected ~340) on expected-values admin page
- 98 unit tests across NCAAM, NCAAW, and UCL simulators — all passing
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:31:47 -07:00
import { NCAAMSimulator } from "./ncaam-simulator" ;
import { NCAAWSimulator } from "./ncaaw-simulator" ;
2026-03-16 21:43:39 -07:00
import { NBASimulator } from "./nba-simulator" ;
2026-03-18 01:23:53 -07:00
import { NHLSimulator } from "./nhl-simulator" ;
2026-03-09 15:34:31 -07:00
2026-03-16 12:22:08 -07:00
export const SIMULATOR_TYPES = [
"f1_standings" ,
"indycar_standings" ,
"golf_qualifying_points" ,
"playoff_bracket" ,
"ucl_bracket" ,
"ncaam_bracket" ,
"ncaaw_bracket" ,
2026-03-16 21:43:39 -07:00
"nba_bracket" ,
2026-03-18 01:23:53 -07:00
"nhl_bracket" ,
2026-03-16 12:22:08 -07:00
] as const ;
export type SimulatorType = typeof SIMULATOR_TYPES [ number ] ;
// ─── Race points tables ───────────────────────────────────────────────────────
/** F1 points: positions 1– 10. */
const F1_RACE_POINTS : Record < number , number > = {
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 < number , number > = {
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 ,
} ;
2026-03-09 15:34:31 -07:00
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" } ,
2026-03-16 12:22:08 -07:00
create : ( ) = > new AutoRacingSimulator ( F1_RACE_POINTS , "f1_standings_model" ) ,
2026-03-09 15:34:31 -07:00
} ,
indycar_standings : {
info : { name : "IndyCar Standings Model" , description : "Simulates remaining races using current IndyCar standings and futures odds" } ,
2026-03-16 12:22:08 -07:00
create : ( ) = > new AutoRacingSimulator ( INDYCAR_RACE_POINTS , "indycar_standings_model" ) ,
2026-03-09 15:34:31 -07:00
} ,
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 ( ) ,
} ,
2026-03-10 23:04:51 -07:00
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 ( ) ,
} ,
Add NCAAM and NCAAW bracket Monte Carlo simulators (#150)
- NCAAM: KenPom AEM logistic formula (1/(1+exp(-diff/7.5))), data through 2025-26 March 15
- NCAAW: Barttorvik Barthag Log5 formula (A*(1-B)/(A*(1-B)+B*(1-A))), same bracket structure
- Both simulators: 50,000-iteration Monte Carlo, First Four simulation, honors completed matches
- Track E8+ placements only: champion, finalist, FF losers (3rd/4th), E8 losers (5th–8th)
- Add bracket configuration validation: null R64 slots must exactly match First Four mapping
- Fix DEFAULT_SCORING_RULES to 100/70/45/45/20/20/20/20 (3rd/4th=45, 5th–8th=20)
- Align scoring constants across simulate route, expected-values display, and server action
- Zero out EVs for non-bracket participants on every simulation run (prevents EV inflation)
- Add EV total invariant warning (expected ~340) on expected-values admin page
- 98 unit tests across NCAAM, NCAAW, and UCL simulators — all passing
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:31:47 -07:00
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 ( ) ,
} ,
2026-03-16 21:43:39 -07:00
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 ( ) ,
} ,
2026-03-18 01:23:53 -07:00
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 ( ) ,
} ,
2026-03-09 15:34:31 -07:00
} ;
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 ;
}