groupstage
v3.1.1
Published
Group stage tournament
Downloads
1,772
Maintainers
Readme
GroupStage
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.