UNPKG

brackets-manager

Version:

A simple library to manage tournament brackets (round-robin, single elimination, double elimination)

132 lines 6.19 kB
"use strict"; // https://web.archive.org/web/20200601102344/https://tl.net/forum/sc2-tournaments/202139-superior-double-elimination-losers-bracket-seeding Object.defineProperty(exports, "__esModule", { value: true }); exports.defaultMinorOrdering = exports.ordering = void 0; exports.ordering = { 'natural': (array) => [...array], 'reverse': (array) => [...array].reverse(), 'half_shift': (array) => [...array.slice(array.length / 2), ...array.slice(0, array.length / 2)], 'reverse_half_shift': (array) => [...array.slice(0, array.length / 2).reverse(), ...array.slice(array.length / 2).reverse()], 'pair_flip': (array) => { const result = []; for (let i = 0; i < array.length; i += 2) result.push(array[i + 1], array[i]); return result; }, 'inner_outer': (array) => { if (array.length === 2) return array; const participantCount = array.length; // Generate standard bracket seeding positions iteratively. let positions = [1, 2]; while (positions.length < participantCount) { const size = positions.length * 2; const next = []; for (const pos of positions) next.push(pos, size + 1 - pos); positions = next; } const result = []; for (const pos of positions) result.push(array[pos - 1]); return result; }, 'groups.effort_balanced': (array, groupCount) => { const result = []; let i = 0, j = 0; while (result.length < array.length) { result.push(array[i]); i += groupCount; if (i >= array.length) i = ++j; } return result; }, 'groups.seed_optimized': (array, groupCount) => { const groups = Array.from({ length: groupCount }, (_) => []); for (let run = 0; run < array.length / groupCount; run++) { if (run % 2 === 0) { for (let group = 0; group < groupCount; group++) groups[group].push(array[run * groupCount + group]); } else { for (let group = 0; group < groupCount; group++) groups[groupCount - group - 1].push(array[run * groupCount + group]); } } return groups.flat(); }, 'groups.bracket_optimized': (array, groupCount) => { if (groupCount < 2) return [...array]; // This method relies on pairing seeds as they would be matched in a // classic bracket (inner-outer). Then it distributes each pair across // two opposite groups so these opponents never end up in the same // round-robin group. // Require an even number of groups to strictly separate paired seeds. // If it's odd, fall back to the seed-optimized grouping which is the // closest sensible behavior. if (groupCount % 2 === 1) return exports.ordering['groups.seed_optimized'](array, groupCount); const participantCount = array.length; const halfGroupCount = groupCount / 2; // Generate standard bracket seeding positions iteratively (1-based). let positions = [1, 2]; while (positions.length < participantCount) { const size = positions.length * 2; const next = []; for (const pos of positions) next.push(pos, size + 1 - pos); positions = next; } // Helper that returns the base group index (0-based) for the first // element of the i-th pair in the bracket. The second element of the // pair goes to the opposite group (base + halfGroupCount). const baseGroupForPair = (i) => { const t = i % halfGroupCount; // index inside the current block const r = Math.floor(i / halfGroupCount); // block index // Toggle orientation every two blocks to create a balanced snake // pattern across left and right halves of the groups. const inverted = Math.floor(r / 2) % 2 === 1; if (r % 2 === 0) { // Left half of groups: [0 .. half-1] return inverted ? (halfGroupCount - 1 - t) : t; } // Right half of groups: [groupCount-1 .. half] return inverted ? (halfGroupCount + t) : (groupCount - 1 - t); }; const groups = Array.from({ length: groupCount }, () => []); const pairCount = Math.floor(positions.length / 2); // First, distribute the first element of each pair to its base group // to keep the strongest seeds spread nicely. for (let i = 0; i < pairCount; i++) { const base = baseGroupForPair(i); const aIndex = positions[2 * i] - 1; // convert to 0-based groups[base].push(array[aIndex]); } // Then, distribute the second element of each pair to the opposite // group to avoid having bracket-opponents in the same group. for (let i = 0; i < pairCount; i++) { const base = baseGroupForPair(i); const bIndex = positions[2 * i + 1] - 1; // convert to 0-based groups[(base + halfGroupCount) % groupCount].push(array[bIndex]); } // Sort each group by original seed order so the strongest seed of the // group appears first, matching common UI expectations and the // reference layout. const indexByItem = new Map(array.map((v, i) => [v, i])); for (const g of groups) g.sort((a, b) => (indexByItem.get(a) - indexByItem.get(b))); return groups.flat(); }, }; exports.defaultMinorOrdering = { // 1 or 2: Not possible. 4: ['natural', 'reverse'], 8: ['natural', 'reverse', 'natural'], 16: ['natural', 'reverse_half_shift', 'reverse', 'natural'], 32: ['natural', 'reverse', 'half_shift', 'natural', 'natural'], 64: ['natural', 'reverse', 'half_shift', 'reverse', 'natural', 'natural'], 128: ['natural', 'reverse', 'half_shift', 'pair_flip', 'pair_flip', 'pair_flip', 'natural'], }; //# sourceMappingURL=ordering.js.map