Skip to content

Rank spirit scores on the average, not the rounded value - #568

Merged
Joe2k merged 1 commit into
mainfrom
fix-spirit-score-tie-break
Sep 3, 2026
Merged

Rank spirit scores on the average, not the rounded value#568
Joe2k merged 1 commit into
mainfrom
fix-spirit-score-tie-break

Conversation

@Joe2k

@Joe2k Joe2k commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Kerala's spirit captain reported a three-way tie at 10.4 between Bengal, Kerala and Karnataka that shouldn't have been one:

Karnataka  83/8 = 10.375
Kerala     73/7 = 10.4286

Both displayed as 10.4 — and both were being ranked as 10.4.

Cause

update_tournament_spirit_rankings rounded the average to one decimal before storing it, and rank_spirit_scores built its order from the set of distinct rounded values:

spirit_points = sorted({r["points"] for r in scores}, reverse=True)
score["rank"] = spirit_points.index(score["points"]) + 1

So any teams sharing a displayed figure necessarily shared a rank. Kerala and Karnataka are 0.054 apart — more than half a rounding step.

team    total   n     true avg    before          after
KL         73   7    10.428571    10.4  rank 1    10.43  rank 1
BT       83.2   8    10.400000    10.4  rank 1    10.40  rank 2
KA         83   8    10.375000    10.4  rank 1    10.38  rank 3

The rounding was correct — 10.375 → 10.4 is proper. Ranking on the rounded value was the defect.

Change

  • Ranks come from the unrounded average.
  • Ties break on final placement, read from current_seeding ({position: team_id}). Ranks are consequently sequential where equal scores used to share one — visible in the updated test_api.py expectation, where six teams on 0.0 move from all-rank-3 to ranks 3–8 in finishing order.
  • Scores are kept and shown to two decimals (backend rounds to 2; the table uses toFixed(2)), so these three now read 10.43 / 10.40 / 10.38 instead of three identical 10.4s.
  • Averages are compared at six decimals when ranking. Ten matches of 10.4 accumulate to 10.400000000000002, and raw float comparison would rank that above an exact 10.4 on representation alone, silently pre-empting the placement tie-break.

final_standings is optional because migration 0039 still calls this with one argument, on rankings recorded before self_points existed — covered by a test.

Testing

./scripts/lint passes. Five tests in server/tests/test_spirit_ranking.py use the real BT/KL/KA figures. Full suite: 489 tests, 3 errors, all pre-existing and unrelated (TOPSCORE_CLIENT_ID env var, two SeleniumBase UI tests).

Note

Standings won't change on merge. spirit_ranking is stored JSON, recomputed only when a spirit score in that tournament is next saved (api.py:2618, 2652). The affected tournament needs a recompute — happy to add a management command if that's easier than re-saving a score.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings September 2, 2026 16:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The change is narrowly scoped, matches the described defect, and is backed by targeted new tests plus updated API expectations.

Pull request overview

This PR fixes spirit ranking ties caused by ranking on a rounded average (1 decimal) instead of the full-precision average, ensuring teams with distinct underlying averages no longer get forced into the same rank when their displayed values match.

Changes:

  • Store full-precision per-team spirit averages in update_tournament_spirit_rankings, and defer rounding until after ranking is computed.
  • Update rank_spirit_scores to rank by full-precision average (rounded to 6 decimals for float-noise resistance) and break ties using tournament.current_seeding.
  • Add focused unit tests for the regression case (BT/KL/KA) and adjust API test expectations for sequential (tie-broken) ranks.
File summaries
File Description
server/tournament/utils.py Ranks spirit scores using full-precision averages and uses current_seeding as a deterministic tie-breaker before rounding for display/storage.
server/tests/test_spirit_ranking.py Adds regression and behavior tests covering full-precision ranking, tie-breaking by placement, float-noise handling, and backward-compat behavior.
server/tests/test_api.py Updates expected spirit ranking output to reflect sequential ranks after tie-breaking.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@Joe2k
Joe2k force-pushed the fix-spirit-score-tie-break branch from 7f7ceac to 1735751 Compare September 2, 2026 16:54
Kerala's spirit captain reported a three-way tie at 10.4 between Bengal,
Kerala and Karnataka that should not have been one:

    Karnataka  83/8 = 10.375
    Kerala     73/7 = 10.4286

The average was rounded to one decimal before being stored, and
rank_spirit_scores built its order from the set of distinct rounded values, so
teams sharing a displayed figure necessarily shared a rank. These two are more
than half a rounding step apart.

Ranks now come from the unrounded average. Teams genuinely level are separated
by their final placement, read from current_seeding, so ranks are sequential
where equal scores used to share one.

Scores are kept and shown to two decimals, which also tells these three apart
at a glance: 10.43, 10.40, 10.38.

Ranking compares averages at six decimals. Ten matches of 10.4 accumulate to
an average of 10.400000000000002, and comparing raw floats would rank that
above an exact 10.4 on representation alone.

Standings will not change until the rankings are recomputed, which happens
whenever a spirit score in the tournament is next saved.
@Joe2k
Joe2k force-pushed the fix-spirit-score-tie-break branch from 1735751 to bf603ba Compare September 3, 2026 00:31
@Joe2k
Joe2k merged commit 32ae9f8 into main Sep 3, 2026
5 checks passed
@Joe2k
Joe2k deleted the fix-spirit-score-tie-break branch September 3, 2026 01:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants