bridge-scorer
Version:
collection of duplicate bridge scoring algorithms.
437 lines (390 loc) • 12.5 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.scorer = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
;
var scorer = module.exports = {};
scorer.contractMade = require('./lib/contract').made;
scorer.contractTricks = require('./lib/contract').tricks;
scorer.contractResult = require('./lib/contract').result;
scorer.matchpoints = require('./lib/matchpoints').standard;
scorer.matchpointsACBL = require('./lib/matchpoints').ACBL;
scorer.crossImps = require('./lib/imps').crossImps;
scorer.butler = require('./lib/imps').butler;
scorer.rank = require('./lib/rank');
},{"./lib/contract":2,"./lib/imps":3,"./lib/matchpoints":4,"./lib/rank":5}],2:[function(require,module,exports){
;
var doubledNotVulnerable = [
0, -100, -300, -500,
-800, -1100, -1400, -1700,
-2000, -2300, -2600, -2900,
-3200, -3500
];
var doubledVulnerable = [
0, -200, -500, -800,
-1100, -1400, -1700, -2000,
-2300, -2600, -2900, -3200,
-3500, -3800
];
/**
* Scores a bridge contract.
*/
function contractMade(contract, vulnerable, made) {
// Passed in?
if (contract.level === 0)
return 0;
var
overTricks = made - contract.level,
doubled = contract.risk === 'X',
redoubled = contract.risk === 'XX',
strain = contract.denomination;
// Undertricks?
if (made < 0) {
if (contract.risk === '')
return made * (vulnerable ? 100 : 50);
var penalty = vulnerable ? doubledVulnerable[-made] : doubledNotVulnerable[-made];
if (redoubled)
penalty *= 2;
return penalty;
}
var score = 0;
// Contract Points
switch (strain) {
case 'S':
case 'H':
score = contract.level * 30;
break;
case 'NT':
score = contract.level * 30 + 10;
break;
case 'C':
case 'D':
score = contract.level * 20;
break;
}
if (doubled)
score *= 2;
else if (redoubled)
score *= 4;
// Level Bonus
if (score < 100) // Part score?
score += 50;
else {
score += vulnerable ? 500 : 300; // game bonus
if (contract.level === 7) // Grand slam?
score += vulnerable ? 1500 : 1000;
else if (contract.level === 6) // small slam?
score += vulnerable ? 750 : 500;
}
// Insult bonus
if (doubled)
score += 50;
else if (redoubled)
score += 100;
// Overtrick bonus
if (overTricks > 0) {
if (doubled)
score += overTricks * (vulnerable ? 200 : 100);
else if (redoubled)
score += overTricks * (vulnerable ? 400 : 200);
else {
switch (strain) {
case 'S':
case 'H':
case 'NT':
score += overTricks * 30;
break;
case 'C':
case 'D':
score += overTricks * 20;
break;
}
}
}
return score;
}
function contractTricks(contract, vulnerable, tricks) {
var need = contract.level + 6;
var made = tricks < need ? tricks - need : tricks - 6;
return contractMade(
contract,
vulnerable,
made
);
}
function contractResult(contract, vulnerable, relativeTricks) {
return contractTricks(
contract,
vulnerable,
relativeTricks + contract.level + 6
);
}
module.exports = {
made: contractMade,
tricks: contractTricks,
result: contractResult
};
},{}],3:[function(require,module,exports){
;
var imps = [
{ diff: 0, imps: 0},
{ diff: 20, imps: 1},
{ diff: 50, imps: 2},
{ diff: 90, imps: 3},
{ diff: 130, imps: 4},
{ diff: 170, imps: 5},
{ diff: 220, imps: 6},
{ diff: 270, imps: 7},
{ diff: 320, imps: 8},
{ diff: 370, imps: 9},
{ diff: 430, imps: 10},
{ diff: 500, imps: 11},
{ diff: 600, imps: 12},
{ diff: 750, imps: 13},
{ diff: 900, imps: 14},
{ diff: 1100, imps: 15},
{ diff: 1300, imps: 16},
{ diff: 1500, imps: 17},
{ diff: 1750, imps: 18},
{ diff: 2000, imps: 19},
{ diff: 2250, imps: 20},
{ diff: 2500, imps: 21},
{ diff: 3000, imps: 22},
{ diff: 3500, imps: 23},
{ diff: 4000, imps: 24},
];
function toImps(diff) {
var n = imps.length - 1;
var sign = 1;
if (diff < 0) {
sign = -1;
diff = 0 - diff;
}
for (var i = 0; i < n; ++i) {
if (imps[i].diff <= diff && diff < imps[i+1].diff) {
return imps[i].imps * sign;
}
}
return 24 * sign;
}
function crossImps(games) {
var i, j, game, score, imps;
// Get the played games
var played = [];
for (i = 0; i < games.length; ++i) {
game = games[i];
if (game.score === 0) {
game.impsNS = { value: -1};
game.impsEW = { value: -1};
played.push({
game: game,
scoreNS: 0
});
}
else if (game.contract && game.contract.declaror && game.score) {
game.impsNS = { value: -1};
game.impsEW = { value: -1};
var playedGame = {
game: game,
scoreNS: (game.contract.declaror === 'N' || game.contract.declaror === 'S') ? game.score : -game.score
};
played.push(playedGame);
}
}
// Assign imps
for (i = 0; i < played.length; ++i) {
score = played[i].scoreNS;
imps = 0;
for (j = 0; j < played.length; ++j) {
if (i !== j){
imps += toImps(score - played[j].scoreNS);
}
}
played[i].game.impsNS.value = imps / (played.length - 1);
played[i].game.impsEW.value = -played[i].game.impsNS.value;
}
}
function pairsDatum(games, f) {
var i, game, score, imps;
// Get the played games
var played = [];
for (i = 0; i < games.length; ++i) {
game = games[i];
if (game.score === 0) {
game.impsNS = { value: -1};
game.impsEW = { value: -1};
played.push({
game: game,
scoreNS: 0
});
}
else if (game.contract && game.contract.declaror && game.score) {
game.impsNS = { value: -1};
game.impsEW = { value: -1};
var playedGame = {
game: game,
scoreNS: (game.contract.declaror === 'N' || game.contract.declaror === 'S') ? game.score : -game.score
};
played.push(playedGame);
}
}
// Determine the datum
var datum = f(played);
// Assign imps
for (i = 0; i < played.length; ++i) {
score = played[i].scoreNS;
imps = toImps(score - datum);
played[i].game.impsNS.value = imps;
played[i].game.impsEW.value = -imps;
}
return datum;
}
function butler (games) {
var datum = function(g) {
if (g.length > 3) {
g.sort(function (a,b) { return b.scoreNS - a.scoreNS; });
var drops = Math.ceil((g.length - 10) / 15) + 1;
g = g.slice(drops, -drops);
}
var sum = g.reduce(function (a, e) { return a + e.scoreNS; }, 0);
return Math.round((sum / g.length) / 10) * 10;
};
return pairsDatum(games, datum);
}
module.exports = {
toImps: toImps,
crossImps: crossImps,
butler: butler
};
},{}],4:[function(require,module,exports){
;
function matchpoints (games, scoring) {
var i, game;
// Get the played games
var played = [];
for (i = 0; i < games.length; ++i) {
game = games[i];
if (game.score === 0) {
game.matchpointsNS = { value: -1};
game.matchpointsEW = { value: -1};
played.push({
game: game,
scoreNS: 0
});
}
else if (game.contract && game.contract.declaror && game.score) {
game.matchpointsNS = { value: -1};
game.matchpointsEW = { value: -1};
var playedGame = {
game: game,
scoreNS: (game.contract.declaror === 'N' || game.contract.declaror === 'S') ? game.score : -game.score
};
played.push(playedGame);
}
}
var totalMatchpoints = played.length * scoring.win - scoring.win;
// Order the played games by descending NS score (highest score first)
played.sort(function (a,b) { return b.scoreNS - a.scoreNS; });
// Assign the matchpoints to played hands.
i = 0;
var n = played.length - 1;
var p, ties, j;
while (i <= n)
{
p = played[i];
game = p.game;
var matchpoints = 0;
var score = p.scoreNS;
ties = 0;
for (j = i + 1; j <= n && score === played[j].scoreNS; ++j)
{
++ties;
matchpoints += scoring.tie;
}
matchpoints += scoring.win * (n - i - ties);
game.matchpointsNS.value = matchpoints;
game.matchpointsNS.rank = (i + 1).toString();
game.matchpointsNS.percentage = (matchpoints / totalMatchpoints) * 100;
game.matchpointsEW.value = totalMatchpoints - matchpoints;
game.matchpointsEW.percentage = (game.matchpointsEW.value / totalMatchpoints) * 100;
if (ties > 0) {
game.matchpointsNS.rank += '=';
}
for (j = 0; j < ties; ++j)
{
++i;
played[i].game.matchpointsNS = game.matchpointsNS;
played[i].game.matchpointsEW = game.matchpointsEW;
}
++i;
}
// rank EW
played.sort(function (a,b) { return b.game.matchpointsEW.value - a.game.matchpointsEW.value; });
i = 0;
while (i <= n)
{
p = played[i];
game = p.game;
ties = 0;
for (j = i + 1; j <= n && game.matchpointsEW.value === played[j].game.matchpointsEW.value; ++j)
{
++ties;
}
game.matchpointsEW.rank = (i + 1).toString();
if (ties > 0) {
game.matchpointsEW.rank += '=';
}
for (j = 0; j < ties; ++j)
{
++i;
played[i].game.matchpointsEW = game.matchpointsEW;
}
++i;
}
return games;
}
module.exports = {
standard: function(games) { return matchpoints(games, { win: 2, tie: 1}); },
ACBL: function(games) { return matchpoints(games, { win: 1.0, tie: 0.5}); }
};
},{}],5:[function(require,module,exports){
;
function rank(results) {
// Order the results by descending score (highest score first)
results = results.slice().sort(function (a,b) { return b.score - a.score; });
// Assign the rank
var i = 0, j;
var n = results.length - 1;
var maxRank = 0;
var r;
while (i <= n)
{
r = results[i];
var score = r.score;
var ties = 0;
for (j = i + 1; j <= n && score === results[j].score; ++j)
{
++ties;
}
maxRank = i + 1;
r.rank = maxRank.toString();
if (ties > 0) {
r.rank += '=';
}
for (j = 0; j < ties; ++j)
{
results[++i].rank = r.rank;
}
++i;
}
for (i = 0; i <= n; ++i) {
r = results[i];
var rank = parseInt(r.rank, 10);
if (rank === 1) {
r.scale = 1;
} else {
r.scale = (maxRank - rank) / (maxRank);
}
}
return results;
}
module.exports = rank;
},{}]},{},[1])(1)
});