# TournamentManager _(no submodules)_ ## Purpose `TournamentManager` orchestrates single-elimination and round-robin tournaments. Participants register during the `pending` phase, then a single `start()` locks the field, applies seeding (or RNG-driven shuffling), and constructs the bracket. From there the manager tracks match results, propagates winners through `nextMatchId` links, updates standings, and emits stable error codes whenever the caller would otherwise corrupt the bracket. The manager injects its `Clock`, `IdGenerator`, and `Rng` so deterministic test rigs and seeded replays produce byte-identical brackets each run. OTel counters record per-match creation and completion so production operators can monitor tournament throughput without bespoke instrumentation. ## Features - Two bracket types: single-elimination, round-robin. - Automatic first-round bye propagation for non-power-of-two fields. - Deterministic seeded shuffling via injectable `Rng`. - Standings table with stable points/wins/losses tie-break. - Stable `GAME_SERVER.*` error codes (`INVALID_BRACKET_SIZE`, `TOURNAMENT_INVALID_STATE`, `TOURNAMENT_FULL`, `TOURNAMENT_DUPLICATE_PARTICIPANT`, `TOURNAMENT_INSUFFICIENT_PARTICIPANTS`, `TOURNAMENT_MATCH_NOT_FOUND`, `TOURNAMENT_INVALID_RESULT`). - Zod-validated config. - OTel match counter tagged by `op` (`created`, `completed`). ## Usage: Basic ```ts import { TournamentManager } from "@suaveplan/game-server/enhanced/tournament/tournament-manager"; const tm = new TournamentManager({ id: "weekly-cup", name: "Weekly Cup", bracketType: "single-elimination", maxParticipants: 16, }); tm.addParticipant("alice"); tm.addParticipant("bob"); tm.start(); const firstMatch = tm.getTournament().rounds[0]!.matches[0]!; tm.recordResult(firstMatch.id, "alice"); ``` ## Usage: Advanced ```ts import { TournamentManager } from "@suaveplan/game-server/enhanced/tournament/tournament-manager"; const tm = new TournamentManager( { id: "monthly", name: "Monthly Championship", bracketType: "round-robin", maxParticipants: 8, shuffleSeeds: true, winPoints: 3, drawPoints: 1, lossPoints: 0, }, { clock: () => now, rng: seededRng, idGenerator: () => `m-${++counter}`, } ); for (const player of players) { tm.addParticipant(player.id, player.profile); } tm.start(); for (const round of tm.getTournament().rounds) { for (const match of round.matches) { const winnerId = await playMatch(match); tm.recordResult(match.id, winnerId, { latencyMs: 27 }); } } const champion = tm.getWinner(); const standings = tm.getStandings(); const stats = tm.getStats(); ``` ## API Reference - `new TournamentManager(config, deps?)` - `addParticipant(id, data?, seed?): Participant` - `start(): void` - `getMatch(id): Match` - `recordResult(matchId, winnerId, data?): Match` - `cancel(): void` - `getTournament(): Tournament` - `getWinner(): Participant | null` - `getStandings(): readonly Standing[]` - `getStats(): TournamentStats` ## Implementation Notes Bracket generation is split into `tournament-bracket.internal.ts` and standings/match utilities into `tournament-standings.internal.ts` to keep this file under the project's 300-line hard limit while preserving full behaviour and 100% coverage through the public surface. Internal modules are not exported through the package barrel — their tests run via the manager. Single-elimination brackets pad participants to the next power of two with `null` slots that become first-round byes. Byes auto-advance their opponent into the next round during `start()`, so the bracket is immediately consistent before the first `recordResult()` call. Tournament completion is detected when exactly one participant remains active (single-elim) or every round has completed (round-robin). Seeding has two paths: explicit `useSeeding=true` sorts by the caller-supplied `seed` value, while `shuffleSeeds=true` overrides that ordering by Fisher-Yates shuffling with the injected `Rng`. An `Rng` implementation that does not provide `nextIntInRange` is treated as a no-op shuffle — the conservative default for tournament harnesses that prefer explicit ordering errors over silent non-determinism.