brackt/app/services/simulations
Claude cf817cc9ff
Make the AFL simulator read the bracket that was actually drawn
An AFL club seeded into an Elimination Final is awarded 15 fantasy points the
moment the bracket is generated — afl_10's entryFloor: 7 — and cannot finish
worse than the 7th-8th tier. Its EV still read 13.

AFLSimulator was stateless with respect to the live bracket. It read only
participants, sourceElo and the regular-season standings, then re-projected the
whole ladder and re-seeded 1-10 from Elo on every one of its 10,000 iterations.
So a team with a locked Elimination Final berth was re-drawn into the Wildcard
Round, or out of the finals entirely, in a slice of them — and there it scores 0.
Even with the ladder complete the Math.random() tiebreaker reshuffled every club
tied on ladder points, which in the AFL is most of the middle of the table.
Games already played were re-played the same way, so a completed Wildcard win was
worth 50% of nothing. Mixing zeroes into a distribution whose floor is 15 is what
produced 13.

afl_10 is the only template that defines entryFloor at all, which is why this
surfaced here and not on LLWS, whose floors only exist once a team has won
something.

The simulator now mirrors llws-simulator's bracket-aware mode:

  - readAflBracketSeeds reads the 10 seeds from the slots generateAFL10Bracket
    writes them into. The two Elimination Final participant2 slots are TBD by
    design and are never read as seeds, leaving exactly 10 named slots. No draw
    at all falls back to the ladder projection; a partially seeded, duplicated or
    unknown draw throws rather than silently discarding the draw and every
    recorded result with it.
  - makePlayGame replays a completed match from its recorded result whenever both
    recorded teams are the two the simulation routed into that game, so an
    already-played result sticks across all iterations.
  - simAFLFinals labels each game with the round and match number
    generateAFL10Bracket and advanceAFLWinner use, so a result is looked up
    against the game it was played in. Its routing was already correct.

EV >= the banked floor now holds by construction, with no clamping: a team seeded
into an Elimination Final is in that game in every iteration. Column sums stay
exactly 1.0 and the 340-point total-EV invariant is unchanged, since teams
outside the bracket simply score nothing.

Fixing the simulator alone would not have held. processMatchResult calls
updateProbabilitiesAfterResult on every result, and its ICM branch re-derives
each still-alive participant's whole distribution from P(1st) alone, knowing
nothing about the bracket — so the next finals result would have put the EV
straight back under the floor. That branch was built for futures-odds seasons.
It now runs only when the season's EVs did not come from a bracket-aware
simulator; when they did, that simulator is re-run instead, since it already
knows the completed matches. Both conditions matter: re-running a bracket-blind
simulator would re-draw the field and hand equity back to knocked-out teams, so
a new manifest `bracketAware` flag limits this to AFL and LLWS. A failed re-run
leaves probabilities untouched rather than falling back to the ICM path that is
being replaced.

The finalized-participant pinning loop now runs after that recalculation rather
than before. A simulation run rewrites every participant in the season, the
finalized ones included; a finalized placement is a fact, not a projection, so it
is written last and wins.

Tests: seeds clear the entry floors their seeding banked, a Qualifying Final
entrant is structurally absent from the 7th-8th tier, the bracket's draw beats
Elo (the weakest club seeded 1, the strongest seeded 10), completed Wildcard and
Qualifying Finals are replayed with the winner banking its floor, teams outside
the bracket are zeroed, and the column sums and 340 total survive. The bracket
fixtures deliberately seed the ten weakest clubs, because seeding the strongest
ten lets the ladder projection reproduce much the same field by accident. Six of
the seven were confirmed to fail against the previous behavior. Plus the
probability-updater branch in each direction, its failure path, and the pin
ordering.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VPxDnSEKVoKx9HFcQ6gmcS
2026-08-29 02:42:31 +00:00
..
__tests__ Make the AFL simulator read the bracket that was actually drawn 2026-08-29 02:42:31 +00:00
afl-simulator.ts Make the AFL simulator read the bracket that was actually drawn 2026-08-29 02:42:31 +00:00
auto-racing-simulator.ts claude/indycar-ev-sims-probability-fpzfx6 (#139) 2026-08-17 22:50:35 +00:00
bracket-simulator.ts Canonical tournament layer: schema + backfill (1/2) (#365) 2026-05-01 20:13:18 -07:00
brackt-simulator.ts Add Brackt as a league-private meta-sport (#200) (#377) 2026-05-04 20:31:44 -07:00
college-hockey-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
config-access.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
cs-major-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
darts-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
epl-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
golf-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
input-policy.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
llws-simulator.ts Address review findings in LLWS simulator 2026-08-21 22:08:04 +00:00
manifest.ts Make the AFL simulator read the bracket that was actually drawn 2026-08-29 02:42:31 +00:00
mlb-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
mls-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
nba-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
ncaa-basketball-simulator.ts Upgrade NCAA preseason simulators (#419) 2026-05-12 22:26:25 -07:00
ncaa-football-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
ncaam-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
ncaaw-simulator.ts Fix NCAAW futures odds simulation and admin import UX (#420) 2026-05-13 01:06:16 -07:00
nfl-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
nhl-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
nll-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
race-points.ts claude/indycar-ev-sims-probability-fpzfx6 (#139) 2026-08-17 22:50:35 +00:00
registry.ts Make LLWS simulator bracket-aware and calibrate its futures model 2026-08-21 21:09:47 +00:00
runner.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
shared-major.ts Unify majors: score once, fan out across windows + tennis bracket EV 2026-06-22 20:32:22 -07:00
simulation-probabilities.ts Deduplicate soccer simulator helpers (#391) 2026-05-07 11:48:57 -07:00
simulator-config.ts Add MLS sport simulator (mls_bracket) (#422) 2026-05-14 12:45:53 -07:00
snooker-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
soccer-helpers.ts Deduplicate soccer simulator helpers (#391) 2026-05-07 11:48:57 -07:00
tennis-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
types.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
ucl-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
wnba-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00
world-cup-simulator.ts claude/probability-config-review-1tdolw (#119) 2026-06-30 23:24:48 +00:00