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

ffa-tb

v1.2.1

Published

FFA tourney with automatic tiebreakers between rounds

Downloads

11

Readme

FFA-TB

npm status build status dependency status coverage status

A free-for-all style tournament, but with tiebreakers in between each round to ensure we can pick out the designated number of advancers unambiguously each round. It is an implementation of tourney.

It is a pointless abstraction if you are modelling a game/sport in which match level ties cannot occur, but for when that's not true - or if you are providing a tiebreaking resolution outside an actual match - then this will provide the glue needed.

Usage

Use like FFA, but watch for stages. What follows is a comprehensive stage-by-stage example.

Creation

All the constructor arguments are identical to FFA. This example will follow a three round tournament with 16 players in round one, 8 in round two, and 4 in round three. The top 2 from each match (match sizes always 4) will proceed to the next round, and we have additionally asked for an unambiguous top 2 from the final.

var FfaTb = require('ffa-tb');
var trn = new FfaTb(16, { sizes: [4, 4, 4], advancers: [2, 2], limit: 2 });

Scoring Stages

Each stage is scored in turn. First round is the quarter finals:

trn.inFFA(); // true
trn.matches
[ { id: { s: 1, r: 1, m: 1 }, p: [ 1, 5, 12, 16 ] },
  { id: { s: 1, r: 1, m: 2 }, p: [ 2, 6, 11, 15 ] },
  { id: { s: 1, r: 1, m: 3 }, p: [ 3, 7, 10, 14 ] },
  { id: { s: 1, r: 1, m: 4 }, p: [ 4, 8, 9, 13 ] } ]

trn.matches.forEach(m => {
  trn.score(m.id, m.id.m === 3 ? [4,3,3,1]: [4,3,2,1]); // tie match 3
});

This will complete the first stage, but not the entire tourney:

trn.stageDone(); // true
trn.isDone(); // false - two more ffa rounds to go
trn.createNextStage(); // true - must createNextStage when stageDone && !isDone

Creating next stage will update state, and now we are in a tiebreaker round to resolve match 3:

trn.inTieBreaker(); // true
trn.matches;
[ { id: { s: 3, r: 1, m: 1 }, p: [ 7, 10 ] } ] // 2nd placers from match 3 must be resolved
trn.score(trn.matches[0].id, [0,1]);

trn.stageDone() && !trn.isDone(); // true
trn.createNextStage(); // true - intermediate tiebreaker stage over

This made for a short stage (and typically tiebreakers are short), so we are now back to the FFA part, in round 2, the semifinals:

trn.inFFA(); // true
trn.inFinal(); // false - these are the semis
trn.matches;
[ { id: { s: 1, r: 1, m: 1 }, p: [ 1, 3, 6, 10 ] }, // player 10 won tiebreaker
  { id: { s: 1, r: 1, m: 2 }, p: [ 2, 4, 5, 8 ] } ]

trn.matches.forEach(m => {
  trn.score(m.id, [4,3,2,1]); // score without ties
});

trn.stageDone() && !trn.isDone(); // true
trn.createNextStage(); // true - advance to round 3

This round did not have any tiebreakers, so we skip any tiebreaker stages after round 2 and go straight to the final:

trn.inFFA() && trn.inFinal(); // true - in last round, and we're in FFA
trn.matches
[ { id: { s: 1, r: 1, m: 1 }, p: [ 1, 2, 3, 4 ] } ] // top seeds won their semis

trn.score(trn.matches[0].id, [1,1,1,1]); // complete tie in final

trn.stageDone() && !trn.isDone(); // true - need to tiebreak final (limit set to 2)
trn.createNextStage(); // true

Because we had set limit, we are now forced to replay the final in a tiebreaker:

trn.inTieBreaker() && trn.inFinal(); // true
// matches is complete replay of final - full tie
trn.matches
[ { id: { s: 1, r: 1, m: 1 }, p: [ 1, 2, 3, 4 ] } ]

trn.score(trn.matches[0].id, [1,1,1,0]); // tie slightly less
trn.stageDone() && !trn.isDone(); // true - need to tiebreak final better
trn.createNextStage(); // true

This goes on if things do not resolve, but we did at least knock out player 4:

trn.inTieBreaker() && trn.inFinal(); // true
trn.matches;
[ { id: { s: 1, r: 1, m: 1 }, p: [ 1, 2, 3 ] } ]
trn.score(trn.matches[0].id, [1,1,0])); // tied for 1st place
trn.stageDone() && trn.isDone(); // true - we don't need to break the top 2

This is actually sufficient. Note that we have a tie for 1st place, but we can pick out our top 2, which is all we asked of limit. If you wanted the top 2, then split these two, you would really have an extra FFA round with 2 players and have limit at 1.

At any rate, this example is over, let's lock it down so no one comes around later and messes with the final state:

trn.complete();

Tiebreaker algorithm

See tiebreaker module. We will break the smallest cluster necessary to determine the top n players where n is the current advancers number or the limit in the final round.

Since we have established that we can play matches with more than 2 players, tiebreakers are simple, meaning they will make a single multiple player match which is a subset of the previous match. This is different from default behaviour in groupstage tiebreakers which actually require subgroups when there are multi-way ties.

Modelling and tiebreaker matches

In most cases, tiebreaker matches resolve most conflicts with modelling, and maps nicely onto the physical world. However, sometimes this is not true:

Suppose a match tied, but external factors (not present in the map score) actually decided who broke. For such a situation you have two options:

  1. Use tiebreaker round as model only

I.e. fill in the tiebreaker match with enough external information (pretend it was an actual match) to indicate to tournament who clearly won.

  1. Lie to FFA and encapsulate external information in original map score

This way, since you never actually intend to play physical tiebreaker matches, you can just use FFA directly.

Both of these solutions are fine, and it comes to you how much data you want.

Modelling Examples

Football

Football/soccer never really presents a physical tiebreaker match, but it certainly does tiebreak. Overtime rounds and penalty shootouts can all be viewed as individual tiebreaker matches, or you can just look at the grand total goal sum as the final score.

Whichever you want to use for a football tournament would depend on the information you want. Since the goals are pretty much perfectly the match score, you could easily just not use tiebreaker since the game itself has a way of resolving ties.

However, if you want a way to model the individual parts of the tiebreaking resolution, it could be useful (but note that this can more easily be done with metadata outside tournament structures).

Quake FFA

Say you stick 10 players in a deatmatch server and want to take the pick the top 5. When the match is over, it is over. If there's a tie between 5th, 6th, and 7th, then extending the match would be unfair to the top 4 that clearly should proceed. In this case a tiebreaker match where these three players play another round of FFA is really the perfect fit.

Given the perfect fit of model and physical reality of the situation, this is were this module shines over plain FFA.

You could technically still choose not to model this inside the module, and present an amalgamated final score; i.e. if the scores were [10,9,8,7,6,6,6,3,2,1] and the tie resolver [3,2,1], you could just score the match as [10,9,8,7,6,5,4,3,2,1] and gloss over the details.

Doing so would be a little silly though. This module can formalize manual tiebreaking processes, provide transparency, and can limit the time needed for organizers to work out this kind of stuff.

Installation

Install from npm:

$ npm install ffa-tb

License

MIT-Licensed. See LICENSE file for details.