Skip to content

perf: compute standings tiebreaker values once and fix Buchholz complexity#7652

Open
Rathoz wants to merge 1 commit into
standings-tests-aifrom
standings-tiebreaker-single-compute
Open

perf: compute standings tiebreaker values once and fix Buchholz complexity#7652
Rathoz wants to merge 1 commit into
standings-tests-aifrom
standings-tiebreaker-single-compute

Conversation

@Rathoz

@Rathoz Rathoz commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two related cuts to tiebreaker computation cost during standings parsing:

  • Every tiebreaker was computed twice per opponent per round. StandingsParser.calculateTiebreakerValues called both valueOf and display, and the default display calls valueOf again (and the diff/winrate overrides recomputed their stats). display(state, opponent, value) now receives the already-computed value; the default implementation and all overriding tiebreakers (Match/Diff, Match/WinRate, Game/Diff, Game/WinRate, Game/Rounds/Diff) use it instead of recomputing. resolveTieForGroup continues to use stored tiebreakerValues/valueOf as before.
  • Buchholz was O(N²·M) with Opponent.same in the inner loops. It now builds the enemy list as a name set via Opponent.toName (with an Opponent.same fallback for renamed-team cases coming from match2 records) and memoizes the result per opponent entry, mirroring the FnUtil.memoize pattern in Tiebreaker/Game/Util.

Stored tiebreakerValues and display strings are unchanged.

Based on standings-tests-ai (#7647), whose tiebreaker and parser specs lock the expected values and display output.

How did you test this change?

  • busted --run=ci: 605 successes / 0 failures, including standings_tiebreaker_spec.lua (buchholz, matchdiff/gamediff/rounddiff display cases) and standings_parser_spec.lua (tiebreakerValues assertions) unchanged
  • luacheck with lua/.luacheckrc: 0 warnings on all touched files

🤖 Generated with Claude Code

@Rathoz Rathoz requested review from a team as code owners June 12, 2026 15:53
…exity

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Rathoz Rathoz force-pushed the standings-tiebreaker-single-compute branch from ec216eb to a67a167 Compare June 12, 2026 16:10
return '-'
end
return MathUtil.formatPercentage(self:valueOf(state, opponent), 2)
return MathUtil.formatPercentage(value ~= nil and value or self:valueOf(state, opponent), 2)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

value ~= nil and is redundant

function TiebreakerMatchWinRate:display(state, opponent)
return MathUtil.formatPercentage(self:valueOf(state, opponent), 2)
function TiebreakerMatchWinRate:display(state, opponent, value)
return MathUtil.formatPercentage(value ~= nil and value or self:valueOf(state, opponent), 2)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

same

if value == nil then
value = self:valueOf(state, opponent)
end
return tostring(value)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

return tostring(value or self:valueOf(state, opponent))

plus kick the if above

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