Skip to content

Support multiple bracket formats: groundwork + round-robin implementation #146

Closed

Description

@Dejmenek

Summary

Lay the groundwork for supporting multiple tournament/bracket formats and implement the first new format, round-robin, alongside the existing single-elimination support. Double-elimination and Swiss system remain out of scope beyond reserving room for them.

Current State

  • Bracket generation/progression logic lives in TournamentAPI/Brackets/BracketService.cs (static CreateBracket + CreateNextRoundMatches), called directly from GenerateBracket/UpdateRound in BracketMutations.cs. There is no abstraction point — it's single-elimination logic only, no strategy/interface.
  • No TournamentFormat/BracketType concept exists anywhere in the domain model.
  • Bracket has a 1:1 FK relationship to Tournament (ApplicationDbContext configuration) — sufficient for round-robin, but double-elimination will likely need schema changes later (separate ticket/ADR).
  • UpdateRound's progression model assumes elimination: all matches in a round must have a winner, winners get paired into the next round, and having fewer than 2 remaining winners implies the bracket is complete. This doesn't generalize to round-robin, where everyone keeps playing across rounds regardless of individual match outcomes.
  • Match.WinnerId is a single nullable FK — there's no way to represent a draw distinctly from "not yet played."

Goals

  1. Introduce a way to add new bracket formats going forward without reworking GenerateBracket/UpdateRound for each new format — exact approach TBD, see Foundation tasks below.
  2. Move existing single-elimination logic to fit that approach with no behavior change.
  3. Implement round-robin as the first new format, using proper round-robin scheduling (circle method: each participant plays one match per round, no repeat pairings).
  4. Support draws/ties in match results, since round-robin makes them a real, common case.

Non-Goals (explicitly deferred)

  • Double-elimination and Swiss system implementations — only reserve room for them, no working implementation.
  • A standings/leaderboard query for ranking round-robin participants — separate follow-up ticket.
  • Any schema change to support multiple Bracket rows per Tournament (needed later for double-elimination's winners/losers brackets) — out of scope here.
  • Frontend/UI changes (API-only).

Tasks

Foundation

  • Decide and document the approach for representing tournament/bracket format in the domain model and how format-specific generation/progression logic will be organized
  • Represent tournament format in the domain model, set at tournament creation, defaulting to single-elimination
  • Adapt existing single-elimination logic to the chosen approach with no behavior change (existing tests must keep passing)
  • Add draw support to match results, distinguishing "not yet played" from "played, drawn"
  • Write an ADR under docs/adr/ documenting the chosen approach and schema decisions

Round-robin implementation

  • Implement round-robin schedule generation via the circle method (N or N-1 rounds depending on odd/even participant count, with byes for odd counts)
  • Define round-robin's "completion" semantics (all scheduled rounds/matches played) as distinct from single-elimination's winner-advancement-based completion, and adjust round-progression validation (BracketMutationValidations) accordingly
  • Review/adjust participant-count validation for round-robin (e.g. minimum participants) as needed
  • Unit/integration tests: schedule generation (even and odd participant counts), recording wins/losses/draws, reaching full-schedule completion
  • Update GraphQL schema docs and any published API docs/Postman collection to reflect the new format field and draw support

Acceptance Criteria

  • Existing single-elimination tournaments behave exactly as before, with existing tests green
  • createTournament accepts an optional format, defaulting to single-elimination
  • A round-robin tournament can generate a full round-robin schedule, have match results (including draws) recorded, and reach a completed state without any elimination logic being invoked
  • Adding a future format (e.g. double-elimination) is a scoped, isolated change, not a rework of GenerateBracket/UpdateRound
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions