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

roundpairings

v1.0.1

Published

A TypeScript library for generating round-robin tournament pairings for 5-20 players

Readme

Round Robin Pairings

A TypeScript library for generating fair round-robin tournament pairings for 5–20 players using the Berger circle method.

Installation

npm install roundpairings

Quick Start

import { RoundRobin } from 'roundpairings';

const rr = new RoundRobin(['Alice', 'Bob', 'Charlie', 'David', 'Eve']);

// Print schedule
rr.getRounds().forEach(round => {
    console.log(`Round ${round.roundNumber}:`);
    round.pairings.forEach(p => {
        console.log(`  Player ${p.player1} vs Player ${p.player2}`);
    });
});

// Submit results (after each round)
rr.submitRoundResult(1, ['1:0', '0:1', '1/2:1/2']);

// Display cross table
rr.displayCrossTable();

Valid Results

| Result | Meaning | | ----------- | ------------------------------ | | 1:0 | White wins | | 0:1 | Black wins | | 1/2:1/2 | Draw | | 0:0 | Both score 0 | | Empty | Game not played (0:0) | | 1F:0F | White wins by forfeit | | 0F:1F | Black wins by forfeit | | 0F:0F | Both forfeit (0:0) | | Adjn | Adjourned (0:0) | | 0:1/2 | White scores 0, Black scores ½ | | 1/2:0 | White scores ½, Black scores 0 | | 1U:0U | White wins (unrated) | | 1/2U:1/2U | Draw (unrated) | | 0U:1U | Black wins (unrated) |

API

new RoundRobin(players: string[])

Creates a tournament. players must have 5–20 entries.

getRounds(): Round[]

Returns all rounds. Each Round has roundNumber and pairings: Pairing[]. Each Pairing has player1 and player2 (1-based player numbers).

getRound(roundNumber: number): Round

Returns a specific round (1-based).

getRoundCount(): number

Returns total number of rounds.

getTournament(): Tournament

Returns the full tournament object ({ playerCount, rounds }).

getPlayerPairings(playerNumber: number): Pairing[]

Returns all pairings for a given player number.

getPlayerStats(playerNumber: number)

Returns { playerNumber, pairingCount, opponents[] }.

getPlayerNames(): string[]

Returns the array of player names.

getPlayerName(playerNumber: number): string

Returns the name of a player by number (1-based).

submitRoundResult(roundNumber: number, results: string[]): void

Submits results for a round. results must be one entry per pairing, using the valid result strings above.

getMatchResult(roundNumber: number, player1: number, player2: number): MatchResult | null

Returns the stored result for a specific match, or null if not submitted.

getPlayerScore(playerNumber: number): number

Returns the total score for a player across all submitted rounds.

isPlayoffNeeded(): boolean

Returns true if multiple players are tied for the highest score.

getPlayoffParticipants(): number[]

Returns the player numbers tied for the highest score.

getPlayoffPairings(): Pairing[]

Returns the head-to-head pairings for playoff participants.

submitPlayoffResult(results: string[]): void

Submits playoff results. Works the same as submitRoundResult but for the auto-generated playoff pairings.

getTotalScoreWithPlayoff(playerNumber: number): number

Returns a player's score including playoff round contributions.

displayCrossTable(options?): void

Prints the cross table to the console.

Options:

  • round?: number — show results up to this round
  • orderBy?: 'name' | 'score' — sort order (default: 'name')

displayEveryCrossTable(orderBy?): void

Prints a cross table after every round, with game results listed below each table. Includes playoff games if present.

displayPlayoffResults(): void

Prints the playoff round matches and results.

displayFinalStandings(): void

Prints final standings including playoff scores and the winner.

Round Counts

| Players | Rounds | | ------- | ------ | | 5–6 | 5 | | 7–8 | 7 | | 9–10 | 9 | | 11–12 | 11 | | 13–14 | 13 | | 15–16 | 15 | | 17–18 | 17 | | 19–20 | 19 |

Odd player counts use the next even number internally (the extra slot acts as a bye).

Berger Table Reference

5–6 Players

1:  1-6  2-5  3-4
2:  6-4  5-3  1-2
3:  2-6  3-1  4-5
4:  6-5  1-4  2-3
5:  3-6  4-2  5-1

7-8 Players 1: 1-8 2-7 3-6 4-5 2: 8-5 6-4 7-3 1-2 3: 2-8 3-1 4-7 5-6 4: 8-6 7-5 1-4 2-3 5: 3-8 4-2 5-1 6-7 6: 8-7 1-6 2-5 3-4 7: 4-8 5-3 6-2 7-1

9-10 Players 1: 1-10 2-9 3-8 4-7 5-6 2: 10-6 7-5 8-4 9-3 1-2 3: 2-10 3-1 4-9 5-8 6-7 4: 10-7 8-6 9-5 1-4 2-3 5: 3-10 4-2 5-1 6-9 7-8 6: 10-8 9-7 1-6 2-5 3-4 7: 4-10 5-3 6-2 7-1 8-9 8: 10-9 1-8 2-7 3-6 4-5 9: 5-10 6-4 7-3 8-2 9-1

11-12 Players 1: 1-12 2-11 3-10 4-9 5-8 6-7 2: 12-7 8-6 9-5 10-4 11-3 1-2 3: 2-12 3-1 4-11 5-10 6-9 7-8 4: 12-8 9-7 10-6 11-5 1-4 2-3 5: 3-12 4-2 5-1 6-11 7-10 8-9 6: 12-9 10-8 11-7 1-6 2-5 3-4 7: 4-12 5-3 6-2 7-1 8-11 9-10 8: 12-10 11-9 1-8 2-7 3-6 4-5 9: 5-12 6-4 7-3 8-2 9-1 10-11 10: 12-11 1-10 2-9 3-8 4-7 5-6 11: 6-12 7-5 8-4 9-3 10-2 11-1

13-14 Players 1: 1-14 2-13 3-12 4-11 5-10 6-9 7-8 2: 14-8 9-7 10-6 11-5 12-4 13-3 1-2 3: 2-14 3-1 4-13 5-12 6-11 7-10 8-9 4: 14-9 10-8 11-7 12-6 13-5 1-4 2-3 5: 3-14 4-2 5-1 6-13 7-12 8-11 9-10 6: 14-10 11-9 12-8 13-7 1-6 2-5 3-4 7: 4-14 5-3 6-2 7-1 8-13 9-12 10-11 8: 14-11 12-10 13-9 1-8 2-7 3-6 4-5 9: 5-14 6-4 7-3 8-2 9-1 10-13 11-12 10: 14-12 13-11 1-10 2-9 3-8 4-7 5-6 11: 6-14 7-5 8-4 9-3 10-2 11-1 12-13 12: 14-13 1-12 2-11 3-10 4-9 5-8 6-7 13: 7-14 8-6 9-5 10-4 11-3 12-2 13-1

15-16 Players 1: 1-16 2-15 3-14 4-13 5-12 6-11 7-10 8-9 2: 16-9 10-8 11-7 12-6 13-5 14-4 15-3 1-2 3: 2-16 3-1 4-15 5-14 6-13 7-12 8-11 9-10 4: 16-10  11-9 12-8 13-7 14-6 15-5 1-4 2-3 5: 3-16 4-2 5-1 6-15 7-14 8-13 9-12 10-11 6: 16-11 12-10 13-9 14-8 15-7 1-6 2-5 3-4 7: 4-16 5-3 6-2 7-1 8-15 9-14 10-13 11-12 8: 16-12 13-11 14-10 15-9 1-8 2-7 3-6 4-5 9: 5-16 6-4 7-3 8-2 9-1 10-15 11-14 12-13 10: 16-13 14-12 15-11 1-10 2-9 3-8 4-7 5-6 11: 6-16 7-5 8-4 9-3 10-2 11-1 12-15 13-14 12: 16-14 15-13 1-12 2-11 3-10 4-9 5-8 6-7 13: 7-16 8-6 9-5 10-4 11-3 12-2 13-1 14-15 14: 16-15 1-14 2-13 3-12 4-11 5-10 6-9 7-8 15: 8-16 9-7 10-6 11-5 12-4 13-3 14-2 15-1

17-18 Players 1: 1-18 2-17 3-16 4-15 5-14 6-13 7-12 8-11 9-10 2: 18-10 11-9 12-8 13-7 14-6 15-5 16-4 17-3 1-2 3: 2-18 3-1 4-17 5-16 6-15 7-14 8-13 9-12 10-11 4: 18-11 12-10 13-9 14-8 15-7 16-6 17-5 1-4 2-3 5: 3-18 4-2 5-1 6-17 7-16 8-15 9-14 10-13 11-12 6: 18-12 13-11 14-10 15-9 16-8 17-7 1-6 2-5 3-4 7: 4-18 5-3 6-2 7-1 8-17 9-16 10-15 11-14 12-13 8: 18-13 14-12 15-11 16-10 17-9 1-8 2-7 3-6 4-5 9: 5-18 6-4 7-3 8-2 9-1 10-17 11-16 12-15 13-14 10: 18-14 15-13 16-12 17-11 1-10 2-9 3-8 4-7 5-6 11: 6-18 7-5 8-4 9-3 10-2 11-1 12-17 13-16 14-15 12: 18-15 16-14 17-13 1-12 2-11 3-10 4-9 5-8 6-7 13: 7-18 8-6 9-5 10-4 11-3 12-2 13-1 14-17 15-16 14: 18-16 17-15 1-14 2-13 3-12 4-11 5-10 6-9 7-8 15: 8-18 9-7 10-6 11-5 12-4 13-3 14-2 15-1 16-17 16: 18-17 1-16 2-15 3-14 4-13 5-12 6-11 7-10 8-9 17: 9-18 10-8 11-7 12-6 13-5 14-4 15-3 16-2 17-1

19-20 Players 1: 1-20 2-19 3-18 4-17 5-16 6-15 7-14 8-13 9-12 10-11 2: 20-11 12-10 13-9 14-8 15-7 16-6 17-5 18-4 19-3 1-2 3: 2-20 3-1 4-19 5-18 6-17 7-16 8-15 9-14 10-13 11-12 4: 20-12 13-11 14-10 15-9 16-8 17-7 18-6 19-5 1-4 2-3 5: 3-20 4-2 5-1 6-19 7-18 8-17 9-16 10-15 11-14 12-13 6: 20-13 14-12 15-11 16-10 17-9 18-8 19-7 1-6 2-5 3-4 7: 4-20 5-3 6-2 7-1 8-19 9-18 10-17 11-16 12-15 13-14 8: 20-14 15-13 16-12 17-11 18-10 19-9 1-8 2-7 3-6 4-5 9: 5-20 6-4 7-3 8-2 9-1 10-19 11-18 12-17 13-16 14-15 10: 20-15 16-14 17-13 18-12 19-11 1-10 2-9 3-8 4-7 5-6 11: 6-20 7-5 8-4 9-3 10-2 11-1 12-19 13-18 14-17 15-16 12: 20-16 17-15 18-14 19-13 1-12 2-11 3-10 4-9 5-8 6-7 13: 7-20 8-6 9-5 10-4 11-3 12-2 13-1 14-19 15-18 16-17 14: 20-17 18-16 19-15 1-14 2-13 3-12 4-11 5-10 6-9 7-8 15: 8-20 9-7 10-6 11-5 12-4 13-3 14-2 15-1 16-19 17-18 16: 20-18 19-17 1-16 2-15 3-14 4-13 5-12 6-11 7-10 8-9 17: 9-20 10-8 11-7 12-6 13-5 14-4 15-3 16-2 17-1 18-19 18: 20-19 1-18 2-17 3-16 4-15 5-14 6-13 7-12 8-11 9-10 19: 10-20 11-9 12-8 13-7 14-6 15-5 16-4 17-3 18-2 19-1