From c0f674f648b4f3ef96e86e13e7cff0dca07b1773 Mon Sep 17 00:00:00 2001 From: Chris Parsons Date: Sun, 31 May 2026 10:34:47 -0700 Subject: [PATCH] Fix darts simulator timeout and world-cup bracket edge case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add numSimulations constructor param to DartsSimulator (same pattern as WorldCupSimulator); drop default from 50k→10k; update manifest to match - Pass 200 iterations in the slow darts column-sum test to prevent CI timeout - Fix world-cup "SF losers" test to use 48 participants — with only 8, the R32 pool has 6 teams and SF never runs, leaving all placement counts at 0 and normalization producing probFirst=probThird=1 for one team - Add manifest iterations: 10_000 override for darts_bracket Co-Authored-By: Claude Sonnet 4.6 --- .../__tests__/darts-simulator.test.ts | 2 +- .../__tests__/world-cup-simulator.test.ts | 3 +-- app/services/simulations/darts-simulator.ts | 16 +++++++++++----- app/services/simulations/manifest.ts | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/app/services/simulations/__tests__/darts-simulator.test.ts b/app/services/simulations/__tests__/darts-simulator.test.ts index 8a45803..6f0c842 100644 --- a/app/services/simulations/__tests__/darts-simulator.test.ts +++ b/app/services/simulations/__tests__/darts-simulator.test.ts @@ -325,7 +325,7 @@ describe("DartsSimulator.simulate() — Path A (bracket populated)", () => { it("each probability column sums to 1.0 across all 128 participants", async () => { mockDb.query.playoffMatches.findMany.mockResolvedValue(makeFullBracket()); - const sim = new DartsSimulator(); + const sim = new DartsSimulator(200); const results = await sim.simulate("season-1"); const keys = [ diff --git a/app/services/simulations/__tests__/world-cup-simulator.test.ts b/app/services/simulations/__tests__/world-cup-simulator.test.ts index bf2e9e4..0ea67b6 100644 --- a/app/services/simulations/__tests__/world-cup-simulator.test.ts +++ b/app/services/simulations/__tests__/world-cup-simulator.test.ts @@ -156,8 +156,7 @@ describe("WorldCupSimulator", () => { }); it("SF losers land in 3rd or 4th, never 1st or 2nd", async () => { - // Set up 8 participants (small bracket, 2 groups of 4) - const participants = makeParticipants(8); + const participants = makeParticipants(48); mockDb.query.seasonParticipants.findMany.mockResolvedValue(participants); mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); diff --git a/app/services/simulations/darts-simulator.ts b/app/services/simulations/darts-simulator.ts index 673b37c..1671259 100644 --- a/app/services/simulations/darts-simulator.ts +++ b/app/services/simulations/darts-simulator.ts @@ -51,7 +51,7 @@ import type { Simulator, SimulationResult } from "./types"; // ─── Simulation parameters ──────────────────────────────────────────────────── -const NUM_SIMULATIONS = 50000; + /** * Controls how much Elo gaps affect per-set win probability. @@ -198,6 +198,12 @@ function shuffle(arr: T[]): T[] { // ─── Simulator ──────────────────────────────────────────────────────────────── export class DartsSimulator implements Simulator { + private readonly numSimulations: number; + + constructor(numSimulations = 10_000) { + this.numSimulations = numSimulations; + } + async simulate(sportsSeasonId: string): Promise { const db = database(); @@ -325,7 +331,7 @@ export class DartsSimulator implements Simulator { const sfLoserCounts = new Map(participantIds.map((id) => [id, 0])); const qfLoserCounts = new Map(participantIds.map((id) => [id, 0])); - for (let s = 0; s < NUM_SIMULATIONS; s++) { + for (let s = 0; s < this.numSimulations; s++) { // R1 (64 matches) const r1Winners: string[] = []; for (let i = 1; i <= 64; i++) { @@ -433,7 +439,7 @@ export class DartsSimulator implements Simulator { finalistCounts.set(finalist, (finalistCounts.get(finalist) ?? 0) + 1); } - return buildResults(participantIds, NUM_SIMULATIONS, { + return buildResults(participantIds, this.numSimulations, { championCounts, finalistCounts, sfLoserCounts, @@ -514,7 +520,7 @@ export class DartsSimulator implements Simulator { unseededPool.push(`__bye_${unseededPool.length}`); } - for (let s = 0; s < NUM_SIMULATIONS; s++) { + for (let s = 0; s < this.numSimulations; s++) { // Draw: shuffle the unseeded pool — seeded positions are pre-computed. const drawnUnseeded = shuffle([...unseededPool]); @@ -582,7 +588,7 @@ export class DartsSimulator implements Simulator { finalistCounts.set(finalist, (finalistCounts.get(finalist) ?? 0) + 1); } - return buildResults(allParticipantIds, NUM_SIMULATIONS, { + return buildResults(allParticipantIds, this.numSimulations, { championCounts, finalistCounts, sfLoserCounts, diff --git a/app/services/simulations/manifest.ts b/app/services/simulations/manifest.ts index 7558cda..e2e7f9f 100644 --- a/app/services/simulations/manifest.ts +++ b/app/services/simulations/manifest.ts @@ -163,7 +163,7 @@ const PROFILES: Record