Skip to content

Commit

Permalink
new virtual method: '_safe' for improved recoring handling - fixes #20
Browse files Browse the repository at this point in the history
…(now it's up to implementations)
  • Loading branch information
clux committed Oct 2, 2014
1 parent 82b5437 commit d8b86e6
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
2.2.0 / 2014-10-02
===================
* `SubClass.prototype._safe` will now be used to improve non-`allowPast` scoring

2.1.0 / 2014-10-01
===================
* Expose `Tournament.configure` the underlying helper used to create `Klass.configure`
Expand Down
3 changes: 2 additions & 1 deletion doc/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,12 +303,13 @@ else {

When guarding on `!trn.unscorable()` like this `tournament` will never log anything during a `trn.score()` call as they will always work.

The reasons are currently hardcoded inside the the base class and implementations internal `verify` functions. Typical faults include:
The unscorable reasons are hardcoded inside the the base class and implementations internal `_verify` functions. Typical faults include:

- parameters not integers, scores not an array of numbers
- scores not the same length as the player array
- scores cannot distinguish between the top X player that are advancing (if eliminating match)
- players are not ready in the match (dependent match not played)
- match so far back in the past it would change history (unless allowPast)

## Ensuring Constructibility
Certain parameter configurations to tournament constructors are logically impossible. Like group size > number of players etc. Whether or not a set of parameters will fail contruction can be tested for with a method taking the same parameters as its respective constructor.
Expand Down
33 changes: 33 additions & 0 deletions doc/implementors.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ SomeTournament.prototype._early = function () {
return false;
};

SomeTournament.prototype._safe = function (match) {
// TODO: return true if here this match in particular is safe to modify
// without leaving the tournament in an inconsistent state
return false;
};

SomeTournament.prototype._initResult = function (seed) {
// TODO: initialize extra result properties here
return {};
Expand Down Expand Up @@ -200,6 +206,7 @@ It's often useful to supply the following methods

- `_verify` - if extra scoring restrictions are necessary
- `_progress` - if player propagation is necessary (tournaments with stages)
- `_safe` - if a match is safe to re-score without corrupting the tournament
- `_early` - if a tournament can be done before all matches are played


Expand Down Expand Up @@ -234,6 +241,20 @@ Due to the way Tournaments are usually serialized (by recording successful score

If something goes wrong in this method, throw an error.

### _safe
Typically, `unscorable` without the extra `allowPast` parameter will not allow you to re-score any matches that already have a score associated with them. This is to ensure the tournament is never left in an inconsistent state.

This could happen in, Duel style playoffs (say), where the finals have been scored, but one of the semis are re-scored to change the outcome. This would render the match history for the final as questionable as a different finalist could have been moved to the final after the final has been scored.

If the scoring administrator knows what he is doing, then allowing re-scoring is fine, as long as the state is cleaned up afterwards. However, it is better to not `allowPast` rescoring and instead have `_safe` implemented so that re-scoring is only allowed when it does not affect the future.

```js
SomeTournament.prototype._safe = function (match) {
var next = this.findMatch({ s: 1, r: match.id.r + 1, m: 1 });
return next && !Array.isArray(next.m); // safe iff next NOT played
};
```

### _early
Called when `isDone` is called and there are still matches remaining. If you implement this, you can decide if the tournament is done early, even if there are more matches to be played.

Expand All @@ -249,6 +270,14 @@ SomeTournament.prototype._early = function () {
If you implement one of the above, and inherit from another tournament that implements the same method, then you SHOULD call the method you are inheriting from:

```js
var Inherited = SuperClass.sub('Inherited', function (opts, initParent) {
// own configuration here
initParent(superClassOpts);
});
Inherited.configure({
defaults: ownDefaultsFn, // only if necessary - SuperClass.defaults used automatically
invalid: ownInvalidReasons, // only if necessary - SuperClass.invalid used automatically
});
Inherited.prototype._verify = function (match, score) {
var reason = SuperClass.verify.call(this, match, score);
if (reason) return reason;
Expand All @@ -263,6 +292,10 @@ Inherited.prototype._early = function () {
SuperClass.prototype._early.call(this);
// specific check here
};
Inherited.prototype._safe = function (match) {
var superSafe = SuperClass.prototype._safe.call(this, match);
// specific check here
};
Inherited.prototype._initResult = function (seed) {
var res = SuperClass.prototype._initResult.call(this, seed);
// specific extensions here
Expand Down
4 changes: 3 additions & 1 deletion lib/tournament.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ Tournament.inherit = function (Klass, Initial) {
_verify: null,
_progress: undefined,
_early: false,
_safe: false,
_initResult: {}
};
Object.keys(methods).forEach(function (fn) {
Expand Down Expand Up @@ -295,7 +296,8 @@ Tournament.prototype.unscorable = function (id, score, allowPast) {
if (score.length !== m.p.length) {
return idString(id) + " scores must have length " + m.p.length;
}
if (!allowPast && Array.isArray(m.m)) {
// if allowPast - you can do anything - but if not, it has to be safe
if (!allowPast && Array.isArray(m.m) && !this._safe(m)) {
return idString(id) + " cannot be re-scored";
}
return this._verify(m, score);
Expand Down

0 comments on commit d8b86e6

Please sign in to comment.