brackets
Tournament bracket + competitive ranking pack for sports, esports and gaming leagues: generate single-elimination and double-elimination brackets (seeded, bye-padded, with...
brackets — tournament brackets + competitive ranking (domain adapter)
Pure-JavaScript capability pack for running competitions: generate tournament structures, seed and pair players, compute rankings (Elo + league standings), and simulate whole events. Zero npm dependencies — Node built-ins only, self-contained in one index.js.
Part of the Leumas2 adapter engine. It follows the one adapter contract (export default { metadata, adapters }), so the middleware registry auto-discovers it and it is callable via /api/adapters, the MCP bridge, and chatbot functioncalls.
Tools
| Tool | Input | Output |
|---|---|---|
singleElim | { players, simulate?, seed? } | Seeded, bye-padded single-elimination bracket (Round → Semifinals → Final). simulate:true plays it out to a champion. |
doubleElim | { players } | Double-elimination structure: winners + losers brackets + grand final; you must lose twice to be eliminated. |
roundRobin | { players, doubleRound? } | Round-robin schedule (circle method); everyone plays everyone. doubleRound:true adds a reversed home/away round. |
swiss | { players, round?, results? } | Swiss-system pairings for the next round: groups by score, avoids rematches, assigns a bye. |
elo | { ratingA, ratingB, score, k? } | New Elo ratings after one head-to-head (score: 1=A wins, 0.5=draw, 0=A loses). |
winProbability | { ratingA, ratingB } | Elo win probability for each side (no rating change). |
standings | { results, points? } | League table / leaderboard from results: points, W/D/L, goal diff, tie-broken sort. |
seed | { players, byRating? } | Ranked draw order + bracket slot per seed (by explicit seed, by rating, or as given). |
nextMatches | { schedule, results?, limit? } | The unplayed, currently-playable matches from a schedule/bracket given what's been played. |
simulate | { players, seed?, format? } | Play out a whole singleElim (with champion path) or roundRobin event; Elo-weighted, reproducible with seed. |
players input is flexible
Any of these work anywhere a tool takes players:
- a count:
8→ auto-namedPlayer 1..8 - a name string:
"Alice, Bob, Carol, Dave"(comma or newline separated) - a JSON array:
[{ "name": "Alice", "rating": 1600, "seed": 1 }, { "name": "Bob", "rating": 1500 }]
When ratings are present, simulate/singleElim weight match outcomes by Elo; when seeds are present they drive the bracket draw; otherwise input order is used.
Usage example
import brackets from './index.js';
// Seeded 6-player single-elimination bracket (2 byes, top seeds protected)
brackets.adapters.singleElim({ players: ['A','B','C','D','E','F'] });
// Elo update after A (1600) beats B (1500)
brackets.adapters.elo({ ratingA: 1600, ratingB: 1500, score: 1 });
// → { newRatingA: 1611.5, newRatingB: 1488.5, ... }
// League table from results
brackets.adapters.standings({ results: [
{ a: 'A', b: 'B', scoreA: 2, scoreB: 1 },
{ a: 'A', b: 'C', winner: 'draw' },
] });
// Reproducible full-tournament simulation
brackets.adapters.simulate({ players: 8, seed: 42 });
Via the adapter registry / API:
POST /api/adapters/brackets/simulate { "players": 16, "seed": 7 }
DRY boundary
This is a new capability — tournament structure + competitive ranking. It does not duplicate:
dice— generic randomness (dice notation, loot tables, shuffles).bracketsonly uses a tiny
inlined seeded RNG so simulate is reproducible; it is not a randomness toolkit.
statistics/numbers— descriptive stats and multi-criteriaweightedScoring. Elo and
standings here are head-to-head competitor ranking, which those packs do not cover.
Keep bracket/ranking logic here; keep general math in numbers/statistics and general randomness in dice.