UNPKG

bridge-scorer

Version:

collection of duplicate bridge scoring algorithms.

437 lines (390 loc) 12.5 kB
(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){ 'use strict'; 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){ 'use strict'; 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){ 'use strict'; 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){ 'use strict'; 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){ 'use strict'; 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) });