brackt/app/services/simulations/race-points.ts
Claude adceda3aca
Fix auto racing sims ignoring championship standings
The IndyCar simulator gave a near-clinched championship leader ~60% to win
the title. It was reporting the futures odds and nothing else.

`event_type` has no race value, so a season_standings calendar (F1, IndyCar)
is stored as `schedule_event` rows — the admin default for that scoring
pattern. The simulator skipped exactly those rows when counting races, so it
saw zero remaining races, took the branch labelled "pre-season", and never
looked at `participant_season_results`. Since `sourceOdds` is an admin input
that is never auto-refreshed, the output was whatever the market said months
ago. With a realistic late-season field (leader on 601 pts vs 480, 2 races
left, stale futures at -300) the old path returns ~55%; counting the races
returns 100.0%.

- Add `countSeasonRaces` in a new leaf model. A race is every event except
  `final_standings`, and completion is inferred from the event date, since
  nobody marks rows labelled "Non-Scoring" complete. Its own module because
  `scoring-event.ts` reaches back into `simulator.ts` through
  `scoring-calculator`.
- Split "season over" from "pre-season". Both had `remainingRaces === 0`, so
  a finished season reverted to an odds draw instead of reporting the final
  standings.
- Warn when a season has championship points but no calendar, in the
  simulator and as a non-blocking readiness warning on the setup page.
- Replace proportional vig removal with a power devig. Dividing every runner
  by the same book sum guts the favourite in a 27-driver market: a 75%
  implied favourite came out at 55%, a -20000 near-lock at 95%. Power devig
  gives 69.5% and 99.3%. Unpriced drivers now floor at the bottom of the
  market instead of being handed 1/N.
- Move the race points tables to their own module so tests can read them
  without tripping the manifest/registry import cycle.

The existing tests missed all of this because their event fixtures used
`eventType: "race"`, which is not a value the enum has. Rebuilt on real enum
values, plus a regression test for the reported case.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TUcV7KenckF893zQ46EDXt
2026-08-17 00:36:20 +00:00

23 lines
1,009 B
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Championship points tables for auto racing series.
*
* Stable series rules, not refreshable season data, so they live in code (see
* the hardcoding rules in docs/agents/simulators.md). Kept out of `registry.ts`
* so tests and callers can read a table without pulling in every simulator —
* importing the registry first also trips the manifest/registry import cycle.
*
* Each table must be contiguous from P1 and monotonically decreasing: the
* simulator's award loop stops at the first unscored position.
*/
/** F1 points: positions 110. */
export 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 126. */
export 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,
};