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 🙏

© 2024 – Pkg Stats / Ryan Hefner

groupstage

v3.1.1

Published

Group stage tournament

Downloads

1,772

Readme

GroupStage

npm status build status dependency status coverage status

Overview

GroupStage is a simple and customizable, early stage tournament. A group stage is designed to pick out the best players by first splitting them up in fair groups of requested size, then round robin schedule each group.

They are advantageous compared to eliminations because they guarantee groupSize-1 matches per player. By combining a group stage with a later elimination round, the best players get picked out for an exciting finale.

Construction

Specify the number of players and the group size, then use like a normal tournament instance.

// 5 players in a single group (league)
var gs = new GroupStage(5);
gs.matches;
[ { id: { s: 1, r: 1, m: 1 }, p: [ 2, 5 ] },
  { id: { s: 1, r: 1, m: 2 }, p: [ 3, 4 ] },
  { id: { s: 1, r: 2, m: 1 }, p: [ 1, 5 ] },
  { id: { s: 1, r: 2, m: 2 }, p: [ 2, 3 ] },
  { id: { s: 1, r: 3, m: 1 }, p: [ 1, 4 ] },
  { id: { s: 1, r: 3, m: 2 }, p: [ 5, 3 ] },
  { id: { s: 1, r: 4, m: 1 }, p: [ 1, 3 ] },
  { id: { s: 1, r: 4, m: 2 }, p: [ 4, 2 ] },
  { id: { s: 1, r: 5, m: 1 }, p: [ 1, 2 ] },
  { id: { s: 1, r: 5, m: 2 }, p: [ 4, 5 ] } ]


// 8 players in groups of 4
var gs = new GroupStage(8, { groupSize: 4 });
gs.matches;
[ { id: { s: 1, r: 1, m: 1 }, p: [ 1, 8 ] },
  { id: { s: 1, r: 1, m: 2 }, p: [ 3, 6 ] },
  { id: { s: 1, r: 2, m: 1 }, p: [ 1, 6 ] },
  { id: { s: 1, r: 2, m: 2 }, p: [ 8, 3 ] },
  { id: { s: 1, r: 3, m: 1 }, p: [ 1, 3 ] },
  { id: { s: 1, r: 3, m: 2 }, p: [ 6, 8 ] },
  { id: { s: 2, r: 1, m: 1 }, p: [ 2, 7 ] },
  { id: { s: 2, r: 1, m: 2 }, p: [ 4, 5 ] },
  { id: { s: 2, r: 2, m: 1 }, p: [ 2, 5 ] },
  { id: { s: 2, r: 2, m: 2 }, p: [ 7, 4 ] },
  { id: { s: 2, r: 3, m: 1 }, p: [ 2, 4 ] },
  { id: { s: 2, r: 3, m: 2 }, p: [ 5, 7 ] } ]
// NB: groups [ 1, 3, 6, 8 ], [ 2, 4, 5, 7 ]


// 9 players in groups of 3
var gs = new GroupStage(9, { groupSize: 3 });
gs.matches;
[ { id: { s: 1, r: 1, m: 1 }, p: [ 4, 9 ] },
  { id: { s: 1, r: 2, m: 1 }, p: [ 1, 9 ] },
  { id: { s: 1, r: 3, m: 1 }, p: [ 1, 4 ] },
  { id: { s: 2, r: 1, m: 1 }, p: [ 5, 8 ] },
  { id: { s: 2, r: 2, m: 1 }, p: [ 2, 8 ] },
  { id: { s: 2, r: 3, m: 1 }, p: [ 2, 5 ] },
  { id: { s: 3, r: 1, m: 1 }, p: [ 6, 7 ] },
  { id: { s: 3, r: 2, m: 1 }, p: [ 3, 7 ] },
  { id: { s: 3, r: 3, m: 1 }, p: [ 3, 6 ] } ]
// NB: groups: [ 1, 4, 9 ], [ 2, 5, 8 ], [ 3, 6, 7 ]

The GroupStage.invalid() will tell you whether the constructor arguments produce a valid tournament.

Relevant tournament entries

GroupStage is a tournament - to get the most out of this module you should read (at least) the following:

Special methods

GroupStage additionally feature the following methods, unique to this subclass:

groupFor(seedNumber) :: Number

Get the group number for the corresponding player.

Caveats

Ties allowed

Unlike most other tournaments, GroupStages allow for individual match ties. The results are simply tallied up by points (configurable) at the end. If match ties is not possible/ideal, just check for it externally:

var score = function (id, score) {
  // assumes you have guarded on `gs.unscorable` earlier
  if (score[0] !== score[1]) {
    // filtering out the ties - but should probably present an error reason
    gs.score(id, score);
  }
};

End results

Acting on end results in a group stage is sometimes problematic, for multiple reasons:

  • The .pos attribute is always increasing in any tournament, so we can never bump player positions upwards until it is guaranteed
  • We cannot make sufficiently general inferences between groups, so it is impossible unbreak ties at the between group level
  • Complex, unexpected results can cause multi-way ties (entire groups can tie), even if individual match ties have been disallowed

Thus, the following conventions are enforced:

  • Before .isDone(), all players have a tied .pos attribute at numPlayers
  • After .isDone(), we tie all the players who got 1st in each group at 1st, then all players who got 2nd at (numFirsts+1), then so on
  • Ties need to be broken outside this module to guarantee fair progression

Finally,

  • Within group positions are based entirely on points, then optionally on scores

The following options can be set to enforce how group winners are decided:

{
  winPoints: Number, // Number of points awarded per win - default 3
  tiePoints: Number, // Number of points awarded per tie - default 1
  scoresBreak: Boolean, // Look at the sum of scores in case of ties - default false
}

Tiebreaking

In some cases, breaking by scores is also insufficient. For this you need to forward to a TieBreaker tournament. This module can also be used as a test of whether or not tiebreaking is needed.

Seeding and groups

Like for most other tournaments, seeding is important. The initial player partitioning into groups is done in such a way so that there is a variety of differently skilled players:

  • If the number of players is divisible by the number of groups (ideal condition), then the sum of the seeds in each group differ by at most the number of groups.
require('group')(15, 5); // 15 players in groups of 5
[ [ 1, 4, 7, 12, 15 ],
  [ 2, 5, 8, 11, 14 ],
  [ 3, 6, 9, 10, 13 ] ]
  • If additionally, every group size is even, then the sum of seeds in each group is identical.

These conditions make standard group arrangements like 16 players in groups of 4 perfectly fair, provided the seeding is perfect.

require('group')(16, 4)
[ [ 1, 5, 12, 16 ],
  [ 2, 6, 11, 15 ],
  [ 3, 7, 10, 14 ],
  [ 4, 8, 9, 13 ] ]

This model ensures that unusual results are directly caused by upsets (a presumed bad player beats a higher ranked one), not the fact that the top 4 players was in one group, causing lower ranked players to advance from the group stage without merit.

Architechture

This whole library is essentially small tournament wrapper around the group library and the round robin library. These are worth looking into for UI helpers or understanding.

License

MIT-Licensed. See LICENSE file for details.