npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@echecs/tournament

v2.0.0

Published

Stateful chess tournament orchestrator for any FIDE pairing system. Supports Swiss, round-robin, and accelerated formats. Zero dependencies.

Readme

Tournament

npm Test Coverage License: MIT

Tournament is a TypeScript library for orchestrating chess tournaments using any FIDE pairing system. It provides a stateful Tournament class that manages the full lifecycle — pairing rounds, recording results, computing standings — and a bakuAcceleration() function implementing FIDE C.04.7. Zero runtime dependencies.

The pairing system is injected as a function parameter. Bring your own from @echecs/swiss, @echecs/round-robin, or a custom implementation.

Installation

npm install @echecs/tournament

Quick Start

import { Tournament } from '@echecs/tournament';
import { dutch } from '@echecs/swiss';
import type { Game, GameKind, Player, Tiebreak } from '@echecs/tournament';

const players: Player[] = [
  { id: 'alice', rating: 2100 },
  { id: 'bob', rating: 1950 },
  { id: 'carol', rating: 1870 },
  { id: 'dave', rating: 1820 },
];

const tournament = new Tournament({
  pairingSystem: dutch,
  players,
  rounds: 3,
});

// Round 1
const round1 = tournament.pairRound();
// round1.pairings = [{ black: 'carol', white: 'alice' }, ...]
// round1.byes    = [{ player: 'dave' }]

tournament.recordResult({ black: 'carol', result: 1, white: 'alice' });
tournament.recordResult({ black: 'dave', result: 0.5, white: 'bob' });

// Round 2
const round2 = tournament.pairRound();
// ...record results...

// Standings (no tiebreaks)
const table = tournament.standings();
// [{ player: 'alice', rank: 1, score: 2, tiebreaks: [] }, ...]

API

Tournament

class Tournament {
  constructor(options: TournamentOptions);

  pairRound(): PairingResult;
  recordResult(game: Game): void;
  standings(tiebreaks?: Tiebreak[]): Standing[];

  get currentRound(): number;
  get games(): Game[][];
  get isComplete(): boolean;
  get players(): Player[];
  get rounds(): number;

  toJSON(): TournamentSnapshot;
  static fromJSON(
    snapshot: TournamentSnapshot,
    pairingSystem: PairingSystem,
    acceleration?: AccelerationMethod,
  ): Tournament;
}

constructor(options)

Creates a new tournament.

interface TournamentOptions {
  acceleration?: AccelerationMethod; // e.g. bakuAcceleration(players)
  pairingSystem: PairingSystem; // e.g. dutch, roundRobin
  players: Player[]; // all participants
  rounds: number; // total number of rounds
}

Throws RangeError if fewer than 2 players or fewer than 1 round.

pairRound()

Generates pairings for the next round using the injected pairing system. Returns a PairingResult with pairings and byes.

Throws RangeError if the tournament is complete or the current round has unrecorded results.

recordResult(game)

Records a game result for the current round.

tournament.recordResult({
  black: 'carol',
  result: 1, // 1 = white wins, 0.5 = draw, 0 = black wins
  white: 'alice',
});

The optional kind?: GameKind field classifies the game type:

type GameKind = 'forfeit' | 'normal' | 'rated' | 'unrated';

Throws RangeError if the players don't match any pairing in the current round.

standings(tiebreaks?)

Returns players ranked by score, with optional tiebreaks applied in order. Each tiebreak function receives (playerId, games, players) and returns a number.

import { buchholz } from '@echecs/buchholz';
import { sonnebornBerger } from '@echecs/sonneborn-berger';

const table = tournament.standings([buchholz, sonnebornBerger]);
// [{ player: 'alice', rank: 1, score: 2.5, tiebreaks: [7.5, 6.25] }, ...]

Tiebreak functions conform to:

type Tiebreak = (
  playerId: string,
  games: Game[][],
  players: Player[],
) => number;

toJSON() / fromJSON()

Serialize and restore tournament state. The pairing system function must be re-provided when restoring, since functions aren't JSON-serializable.

const snapshot = tournament.toJSON();
const json = JSON.stringify(snapshot);

// Later...
const restored = Tournament.fromJSON(JSON.parse(json), dutch);
const nextRound = restored.pairRound();

bakuAcceleration(players)

function bakuAcceleration(players: Player[]): AccelerationMethod;

Returns an AccelerationMethod implementing FIDE C.04.7 Baku Acceleration.

Splits players into two groups (GA = top half, GB = rest) and adds virtual points to GA players' scores in the first rounds, causing stronger players to face each other earlier.

import { Tournament, bakuAcceleration } from '@echecs/tournament';
import { dutch } from '@echecs/swiss';

const tournament = new Tournament({
  acceleration: bakuAcceleration(players),
  pairingSystem: dutch,
  players,
  rounds: 9,
});

Virtual points:

  • First half of accelerated rounds: GA players get 1 point
  • Second half of accelerated rounds: GA players get 0.5 points
  • After accelerated rounds: 0 points
  • GB players: always 0 points

Virtual points affect pairing only — they are never stored in the game history or reflected in standings.

Compatible Pairing Systems

Any function matching the PairingSystem signature works:

type PairingSystem = (players: Player[], games: Game[][]) => PairingResult;

| Package | Functions | FIDE Rules | | -------------------------------------------------------------------------- | --------------------------------------------------------------- | ---------------------------------- | | @echecs/swiss | dutch, dubov, burstein, lim, doubleSwiss, swissTeam | C.04.3, C.04.4.1-3, C.04.5, C.04.6 | | @echecs/round-robin | roundRobin | C.05 |

Types

interface Player {
  id: string;
  rating?: number;
}

interface Game {
  black: string;
  kind?: GameKind; // optional: classifies unplayed rounds
  result: Result;
  white: string;
}

type GameKind = 'forfeit' | 'normal' | 'rated' | 'unrated';

type Result = 0 | 0.5 | 1;

interface Pairing {
  black: string;
  white: string;
}

interface Bye {
  player: string;
}

interface PairingResult {
  byes: Bye[];
  pairings: Pairing[];
}

interface Standing {
  player: string;
  rank: number;
  score: number;
  tiebreaks: number[];
}

type Tiebreak = (
  playerId: string,
  games: Game[][],
  players: Player[],
) => number;

FIDE References

License

MIT