Closed clux closed 11 years ago
Just a note: allowing overriding ties by having this pretend match would at any rate be an ultimately required feature. Especially considering the vast set of different rulesets people have for avoiding ties in group stages.
Just football, for instance, if teams are tied on points within a group, they will look at goals (equivalent to our mapScore), then they will look at a third silly factor that is ultimately worthless as a distinguisher, but will force no tiebreak matches. I think this happened when I was little (15 years ago - not watched football much since then) they used something weird like the number of freekicks/corners or ball possession time as the third measure. Then the same metric was applied between groups to pick a few extra "best losers". Edit: the list of statistics to sort on is now huge!
That's the kind of thing tournament
could never account for because of the custom rules and stats included in each match. We only allow the basics map scores in the match structure. The rest has to be acted on and included externally. Using a pretend match, this would allow an easy interface for parent modules to integrate their exceedingly overcomplicated scoring system by simply scoring a pretend match based on what comes out of results
.
Basic tie-handling is in master since: https://github.com/clux/tournament/commit/a9b4c38942c89a02ff08d4ad95e2789da426d341 and https://github.com/clux/tournament/commit/9a5e4a038938ece5d2c01c5cdc4a7f0fe59654e5
This at least sets pos
correctly, but it does not necessarily make it easy for potential tie-break tournament.
The easy example is a 9 player GroupStage
in groups of 3.
One group ties completely (all wins with 1 point) wheras the other groups decide a winner.
The 1st placers are collected to decide the top (because winning a group is ultimately more important than how many points you got). This is now problematic because there are 5 players tied for 1st: the 3 from group 1 + top in group 2 + top in group 3.
In a scenario where we want to determine the top 3, a poorly constructed tiebreaking scheme may decide to tiebreak the 5 players against each other. This is clearly wrong as the 3 from group 1 has to be tie-broken first inside the group. If this was not done, you may end up promoting all the players from group 1.
Now this could only possibly happen if the 3 tied in group 1 have the same points (and map scores if mapsBreak
) as the winners of group 2 and 3. Otherwise, they are not tied between groups (just within). In this case, by determining a winner, it's hard to get the winners to tie overall with all the group 1 players, but it's not impossible by counting map scores.
var T = require('./');
var gs = new T.GroupStage(9, 3);
var ms = gs.matches;
// tie up group 1 completely
gs.score(ms[0].id, [4,0]);
gs.score(ms[1].id, [0,4]);
gs.score(ms[2].id, [4,0]);
// tie up group 2 but let player 2 get more map points
gs.score(ms[3].id, [1,0]);
gs.score(ms[4].id, [0,1]);
gs.score(ms[5].id, [4,0]);
// tie up group 2 but let player 3 get more map points
gs.score(ms[6].id, [1,0]);
gs.score(ms[7].id, [0,1]);
gs.score(ms[8].id, [4,0]);
var res = gs.results({mapsBreak: true}); // new option
res.forEach(function(r){delete r.draws; delete r.losses}); // formatting nicer out of the box then
console.log(res);
produces:
[ { seed: 1, maps: 4, pts: 3, pos: 1, wins: 1, grp: 1 },
{ seed: 4, maps: 4, pts: 3, pos: 1, wins: 1, grp: 1 },
{ seed: 9, maps: 4, pts: 3, pos: 1, wins: 1, grp: 1 },
{ seed: 2, maps: 4, pts: 3, pos: 1, wins: 1, grp: 2 },
{ seed: 3, maps: 4, pts: 3, pos: 1, wins: 1, grp: 3 },
{ seed: 5, maps: 1, pts: 3, pos: 6, wins: 1, grp: 2 },
{ seed: 8, maps: 1, pts: 3, pos: 6, wins: 1, grp: 2 },
{ seed: 6, maps: 1, pts: 3, pos: 6, wins: 1, grp: 3 },
{ seed: 7, maps: 1, pts: 3, pos: 6, wins: 1, grp: 3 } ]
Thus we have tied the winners of group 2 and 3 with the entire group 1.
So, while this is technically correct, it makes it almost unavoidably hairy for anything that wants to pick a top 2, top 3, or even top 4. Fact of the matter is that group 1 has to be broken up first.
It is possible that by adding limit
to a GroupStage
it should figure this stuff out itself. But then this behaviour have go get baked in everywhere which feels wrong to me. Ahhh. More pondering.
0.10.1 published with at least pos
correct.
Because tie-handling is done well, it seems easier to add a TieBreaker tournament that can be generated from the return of an isDone gs.results()
. Construction of this tournament is already in master. Scoring and grabbing results from it is on the way.
I'd imagine that it can be extended to work on any tournament, but you'd only really need it in GroupStage
anyway, so not sure I'm going to put the effort into that.
0.11.0 is in and contains the first experimental version of TieBreaker
. Docs to follow.
documentation is up, and bundled with 0.11.1. All the test cases I've written for it so far works perfectly, so I can finally rest. This was actually really hard and off putting. Nevertheless, glad I did it.
First off, the
pos
attribute fromresults()
inGroupStage
tournaments are broken at the moment. This post is a response to the realizations that I had whilst working on that.Realization 1: Current position cannot be accurately computed before isDone
In the middle of a tournament it's theoretically impossible to provide a current position because each group can be super tight in different ways. One win does nothing to each estimate in large groups because the player can still come last. At current master, this is not done at all. Don't know what I was thinking when I wrote
GroupStage::results
, but it wasn't properly thought about, that much is clear.At any rate, we thus have to reduce the effectiveness of this feature for
GroupStage
, by just settingpos
tonumPlayers
on each result element until ALL matches are played. Regardless, the results list will contain the current number of points, and will be sorted between groups in order of points so at least the information is there should someone try to do something with it.Realization 2: Ties can happen in 2 difficult ways
Between groups is the simple case I was expecting and modelling simplified
limit
behaviour for in #6. I.e. say if you have 3 groups of 3 (9 player group stage) and want to determine/extract the top 3 you pick the winner of each group. If you want the top 4, then you have to consider all the second placers in the groups as candidates for the 4th place as well. This could be done in different ways based on options. Either pick based on points, then (optionally) by maps, but if these are tied, then tiebreaking has to be done between the top tieing players.Within groups presents an additional complication to the first case, by having the opportunity that all players in the group places 1st (say) meaning that even taking the top 3 involves tiebreaking within the group first.
Since a group stage should not enforce no tie situations by default, it's therefore possible that for certain annoying parameters, tiebreakers for progression have to be constructed twice! Once for within the group, and then once between the group for the xth placers (that we can now accurately pick because of the first tiebreaking) that we only pick some of.
This gets additionally hairy by the fact that the within tiebreaking only needs to occur if the ties extend to the xth place, where x is cutoff point for inclusion.
Discussion
Adding tiebreak-like functionality would be the solution to #6, but exactly how is a bit unclear. Due to the nature of it messing up history rewrites, it is my gut feeling that it should happen outside the main tournament structure.
My idea so far is to create these as a separate tournament type
TieBreak
. This would allow:results
at the end and manually piping to TieBreak, most tournament cases may simply work as if we didn't specifylimit
before/thought stream