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" ;
import { F1Simulator } from "./f1-simulator" ;
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-09 15:34:31 -07:00
export type SimulatorType =
| "f1_standings"
| "indycar_standings"
| "golf_qualifying_points"
2026-03-10 23:04:51 -07:00
| "playoff_bracket"
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
| "ucl_bracket"
| "ncaam_bracket"
| "ncaaw_bracket" ;
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" } ,
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 ( ) ,
} ,
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-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 ;
}