brackets-manager.js

repository·main·Indexed 43 days ago

https://github.com/drarig29/brackets-manager.js

A logic-only library for managing tournament brackets, including round-robin, single elimination, and double elimination. Version 1.11.0 handles complex tournament state, seeding, and match progression while remaining storage-agnostic, supporting implementations like JSON, SQL, and Redis. It provides submodules for creating, retrieving, updating, and deleting tournament stages, matches, and participants without providing a built-in UI.

Tokens
8.5K
Snippets
42
Records
46
Agent score
55%

What's inside brackets-manager

  1. Overview of BracketsManager submodules

    main

    The BracketsManager API is organized into functional submodules. Use these modules to perform specific operations on your tournament data:

    • create: Methods for initializing new entities (e.g., manager.create.stage()).
    • get: Methods for retrieving data (e.g., manager.get.seeding(), manager.get.finalStandings()).
    • update: Methods for modifying existing data (e.g., manager.update.match(), manager.update.confirmSeeding()).
    • reset: Methods for resetting tournament progress (e.g., manager.reset.seeding(), manager.reset.matchGame()).
    • delete: Methods for removing entities (e.g., manager.delete.stage()).
    • find: Methods for searching/querying (e.g., manager.find.match(), manager.find.nextMatches()).
    • storage: Provides direct access to the abstract storage interface (use as a last resort).
  2. Initialize BracketsManager with storage

    main

    To use BracketsManager, you must provide a storage implementation. The library is designed to be storage-agnostic and works with JSON, in-memory, SQL, Redis, and more. You can use existing implementations like brackets-json-db or implement the abstract storage interface yourself.

    Once initialized, the BracketsManager instance provides access to several submodules (create, get, update, reset, delete, find, and storage) to manage tournament logic.

    const { JsonDatabase } = require('brackets-json-db');
    const { BracketsManager } = require('brackets-manager');
    
    const storage = new JsonDatabase();
    const manager = new BracketsManager(storage);
  3. Represent Duel and ParticipantSlot data

    main

    The library models matches as Duels, which consist of two ParticipantSlots.

    • A ParticipantSlot represents a position in a match. It contains an id (which is null if the slot is a BYE) and an optional position.
    • A Duel is a tuple of two ParticipantSlot objects: [ParticipantSlot, ParticipantSlot].
    // Example of a Duel between two participants
    const duel: Duel = [
      { id: 'participant-1', position: 1 },
      { id: 'participant-2', position: 2 }
    ];
    
    // Example of a BYE slot
    const byeSlot: ParticipantSlot = null;
  4. How to handle forfeits for matches with child games

    main

    When dealing with matches that contain child games, there are two distinct patterns for handling forfeits and resets:

    Pattern 1: Forfeiting the Parent Match Directly

    If you set forfeits directly on the parent match:

    • The child games will remain in their current state (not locked, not finished, no forfeit) and will effectively be ignored/never played.
    • To reset: You must use reset.matchResults() on the parent match. Note that matchResults() will only work to reset the forfeit of the parent match; otherwise, it may throw an error if the parent is controlled by children.

    Pattern 2: Forfeiting Individual Child Games

    If you set forfeits for each individual child game:

    • The parent match will not automatically show a forfeit, but its score will be computed based on the forfeited child games.
    • To reset: You must call reset.matchGameResults() for each child game individually. In this scenario, calling reset.matchResults() on the parent match will throw an error.
  5. Initialize BracketsManager

    main

    To use BracketsManager, you must provide an implementation of the CrudInterface (passed as storageInterface). This interface handles the underlying database operations. You can optionally enable verbose mode to log CRUD operations to the console.

    BracketsManager manages tournament entities at several levels: stage, group, round, match, and match_game.

    import { BracketsManager } from 'brackets-manager';
    // storageInterface must implement CrudInterface
    const manager = new BracketsManager(storageInterface, true);
  6. Update match results and handle forfeits

    main

    Use manager.update.match() to record scores and results for a specific match.

    Key features during updates:

    • Forfeit support: Supported during match updates.
    • Match States: Matches can transition through states such as locked, waiting, ready, running, completed, and archived.
    • Result reporting: You can specify scores and the result (e.g., result: 'win') for an opponent.
    await manager.update.match({
      id: 0, // First match of winner bracket (round 1)
      opponent1: { score: 16, result: 'win' },
      opponent2: { score: 12 },
    });
  7. Listen for storage mutation events

    main

    The BracketsManager emits events whenever a storage mutation operation (insert, update, or delete) succeeds. You can listen for the entity.changed event to react to changes in the tournament data.

    manager.on('entity.changed', event => {
      // Triggered on any successful insert, update or delete operation.
    });
  8. Create a tournament stage

    main

    Use manager.create.stage() to initialize a new stage within a tournament. Supported tournament types include round-robin, single elimination, and double elimination.

    Key features during creation:

    • BYE support: Supported during creation for seeding and balancing.
    • Seeding: You can provide an initial list of participants via the seeding array.
    • Settings: You can pass stage-specific configurations (e.g., { grandFinal: 'double' } for double elimination).
    await manager.create.stage({
      tournamentId: 3,
      name: 'Elimination stage',
      type: 'double_elimination',
      seeding: ['Team 1', 'Team 2', 'Team 3', 'Team 4'],
      settings: { grandFinal: 'double' },
    });
  9. Retrieve the upper bracket using Find.upperBracket()

    main

    Use upperBracket(stageId) to retrieve the upper bracket (the winner bracket in double elimination or the only bracket in single elimination) for a specific stage.

    Note: This method will throw an error if the stage type is round_robin or if the stage ID is not found.

    const upperBracket = await findInstance.upperBracket(stageId);
  10. Retrieve tournament and stage data with the Get class

    main

    The Get class provides methods to retrieve structured tournament data from storage. You can fetch data for an entire tournament or for a specific stage.

    • tournamentData(tournamentId): Returns a Database object containing all stages, groups, rounds, matches, match games, and participants associated with the tournament.
    • stageData(stageId): Returns a Database object containing the specific stage, its groups, rounds, matches, match games, and the tournament's participants.
    // Example: Getting all data for a tournament
    const tournamentData = await manager.get.tournamentData(tournamentId);
    
    // Example: Getting all data for a specific stage
    const stageData = await manager.get.stageData(stageId);
  11. Query groups, rounds, and matches with filters

    main

    The Get class provides wrapper methods to query specific tournament entities using filters defined by DataTypes.

    • groups(filter): Returns an array of Group objects matching the filter.
    • rounds(filter): Returns an array of Round objects matching the filter.
    • matches(filter): Returns an array of Match objects matching the filter.

    These methods are useful for traversing the tournament structure (e.g., finding all rounds in a stage, or all matches in a round).

    // Example: Iterating through groups in a stage
    for (const group of await manager.get.groups({ stage_id: stageId })) {
        const rounds = await manager.get.rounds({ group_id: group.id });
        console.log(rounds.length);
    }
    
    // Example: Finding matches in a specific round
    for (const match of await manager.get.matches({ round_id: roundId })) {
        console.log(match.id);
    }