From a05e708085edb8dbac9211ff2f148a4d1fd3274e Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:38:36 +0300 Subject: [PATCH] Fix division SPR comparing against tournament-wide seeds --- .../tournament/core/Standings.test.ts | 21 +++++++++++++++---- app/features/tournament/core/Standings.ts | 11 +++++++++- changelog/2026-10-16-division-spr.md | 5 +++++ 3 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 changelog/2026-10-16-division-spr.md diff --git a/app/features/tournament/core/Standings.test.ts b/app/features/tournament/core/Standings.test.ts index 994e7c53c..3ca4c1c12 100644 --- a/app/features/tournament/core/Standings.test.ts +++ b/app/features/tournament/core/Standings.test.ts @@ -312,16 +312,29 @@ describe("sprByTeamId", () => { expect(result.get(4)).toBe(0); }); - test("returns 0 for a team whose seed is outside the standings", () => { + test("returns 0 for a team without a seed", () => { const result = sprByTeamId( standings([ - { id: 1, seed: 1, placement: 1 }, - { id: 2, seed: 9, placement: 2 }, + { id: 2, seed: null, placement: 1 }, + { id: 1, seed: 1, placement: 2 }, ]), ); expect(result.get(2)).toBe(0); }); + + test("compares a division's teams by their seed order within that division", () => { + // division B of an A/B divisions tournament, seeds 1 & 3 went to division A + const result = sprByTeamId( + standings([ + { id: 4, seed: 4, placement: 1 }, + { id: 2, seed: 2, placement: 2 }, + ]), + ); + + expect(result.get(4)).toBe(1); + expect(result.get(2)).toBe(-1); + }); }); describe("winners", () => { @@ -976,7 +989,7 @@ function playOut( } function standings( - teams: Array<{ id: number; seed: number; placement: number }>, + teams: Array<{ id: number; seed: number | null; placement: number }>, ): Standing[] { return teams.map(({ id, seed, placement }) => ({ team: tournamentCtxTeam(id, { seed }), diff --git a/app/features/tournament/core/Standings.ts b/app/features/tournament/core/Standings.ts index 8e0dc2456..2ea05344a 100644 --- a/app/features/tournament/core/Standings.ts +++ b/app/features/tournament/core/Standings.ts @@ -66,6 +66,7 @@ export function reNumberPlacements( /** * SPR (Seed Performance Rating) of every team in the standings, keyed by tournament team id. + * Seeds are ranked among the given standings only, so a division's standings are compared to its own seed order. * See https://web.archive.org/web/20250513034545/https://www.pgstats.com/articles/introducing-spr-and-uf */ export function sprByTeamId(standings: Standing[]): Map { @@ -75,11 +76,19 @@ export function sprByTeamId(standings: Standing[]): Map { .map((placement, index) => [placement, index]), ); + const seedRankByTeamId = new Map( + R.sortBy( + standings.filter((standing) => typeof standing.team.seed === "number"), + (standing) => standing.team.seed ?? 0, + ).map((standing, rank) => [standing.team.id, rank]), + ); + const result = new Map(); for (const standing of standings) { + const seedRank = seedRankByTeamId.get(standing.team.id); const expectedPlacement = - standings[(standing.team.seed ?? 0) - 1]?.placement; + typeof seedRank === "number" ? standings[seedRank]?.placement : undefined; const expectedIndex = expectedPlacement ? indexByPlacement.get(expectedPlacement) : undefined; diff --git a/changelog/2026-10-16-division-spr.md b/changelog/2026-10-16-division-spr.md new file mode 100644 index 000000000..1afe1cc26 --- /dev/null +++ b/changelog/2026-10-16-division-spr.md @@ -0,0 +1,5 @@ +--- +navItem: medal +type: bug +--- +Fixed tournament results showing the wrong SPR for teams in A/B divisions