Skip to content

Instantly share code, notes, and snippets.

@steren
Last active January 9, 2016 17:06
Show Gist options
  • Save steren/6636508 to your computer and use it in GitHub Desktop.
Save steren/6636508 to your computer and use it in GitHub Desktop.
Poker Tournament manager script to be used with Google Spreadsheet
// Poker Tournament script
// by Steren Giannini http://www.steren.fr
// # How to use
// Use "=computePlayerScoreAndRank()" to generate a tournament ladder, as first argument select a column of player name, as second argument, select the cells of game
/////////////
// Variables
/////////////
// When participating, how much goes for this party?
var contribPrice = 7;
// When participating, how much goes for the pot?
var potPrice = 1;
// How much does a bounty represent as a gain
var bountyGain = 2;
// How much total money should you put to go into a party
var buyInPrice = contribPrice + potPrice + bountyGain;
// Coefficients used in the point computation, for 3rd place, use pointCoefs[3]
var pointCoefs = [
0,
5,
4,
3,
2.5,
2.25,
2,
1.75,
1.5,
1.25
];
// coefficients used in the gain coefficient, for 3rd place, use gainCoefs[3]
var gainCoefs = [
0,
contribPrice / 2,
2 * contribPrice / 7,
2 * contribPrice / 13
];
// Number under which, every game will count in the final score, above this number of playes games, score is ajusted.
var playedGamesNonAjustedLimit = 8;
////////////
// Functions
////////////
/**
* From a player position, return this player's score.
* @param position: number of the position in the game
* @param totalPlayers: the total player number in the game
*/
function computePointsFromPosition(position, totalPlayers) {
var points;
if(position > pointCoefs.length - 1) {
points = Math.round(totalPlayers + 1 - position);
} else {
points = Math.round(pointCoefs[position] * (totalPlayers - position + 1));
}
return points;
}
/**
* Compute real gain for a player, considering money won (Ladder + bounties) and money spent
*/
function computeGainsFromPosition(position, totalPlayers) {
var gain = 0;
if (position < gainCoefs.length) {
gain = Math.round( totalPlayers * gainCoefs[position] );
} else if (position == gainCoefs.length) { // the last paid person wins the rest.
gain = totalPlayers * contribPrice - Math.round(totalPlayers * gainCoefs[1]) - Math.round(totalPlayers * gainCoefs[2]) - Math.round(totalPlayers * gainCoefs[3]);
}
return gain;
}
/**
* for every game, count the number of players
*/
function computePlayerPerGames(positions) {
// store the number of player for one game
var playerPerGames = [];
// for every game
for(var game = 0; game < positions[0].length; game++) {
playerPerGames[game] = 0;
// for every player
for(var player = 0; player < positions.length; player++) {
if( positions[player][game] !== "") {
playerPerGames[game]++;
}
}
}
return playerPerGames;
}
/**
* Based on number of player per games, compute the real number of games
*/
function computeGameNumber(playerPerGames) {
var gameNumber = 0;
for(var i = 0; i < playerPerGames.length; i++) {
if(playerPerGames[i]) {
gameNumber++;
}
}
return gameNumber;
}
/**
* Compute all results for the given player
* @param playerPositions: an array containing the positions of this player for each game
* @param playerPerGames: an array containing the total number of player per games (result of computePlayerPerGames() )
* @param playerBounties: bounties for this player.
* @param gameNumber: Total of games with more than 0 player (in order to remove empty game columns)
* @return object containing all results for the given player.
*/
function computeForPlayer(playerPositions, playerPerGames, playerBounties, gameNumber) {
var result = {}
var totalScore = 0;
var totalGain = 0;
// Number of time a player has win the game
var totalWin = 0;
// Number of time a player has had a gain > 0
var totalITM = 0;
// Number of played games for the given player
var totalPlayedGames = 0;
// Number of time a player has received a bounty from another player
var bountyNumberForPlayer = 0;
// Average position of this player
var averagePosition = 0;
// Number of time a player has finished last in the game
var lostNumberForPlayer = 0;
for(var i = 0; i < gameNumber; i++) {
if( playerPositions[i] !== "" ) {
totalScore += computePointsFromPosition(playerPositions[i], playerPerGames[i]);
var playerGameGain = computeGainsFromPosition(playerPositions[i], playerPerGames[i]);
totalGain += playerGameGain;
if (playerGameGain > 0) {
totalITM++;
}
totalPlayedGames++;
averagePosition += playerPositions[i];
if( playerPositions[i] === playerPerGames[i]) {
lostNumberForPlayer++;
}
}
if( playerPositions[i] === 1 ) {
totalWin++;
}
if( playerBounties[i] !== "" ) {
bountyNumberForPlayer = bountyNumberForPlayer + playerBounties[i];
}
}
// Now we potentially adjust the totalScore for players who played too many games.
// (Previously used formula)
//var maxCountedGames = Math.floor(gameNumber * 3 / 4);
//var averageScore = totalScore / totalPlayedGames;
//if(totalPlayedGames > playedGamesNonAjustedLimit && totalPlayedGames > maxCountedGames){
// totalScore = totalScore - (totalPlayedGames - maxCountedGames) * averageScore;
//}
var averageScore = totalScore / totalPlayedGames;
if(totalPlayedGames > playedGamesNonAjustedLimit){
totalScore = totalScore - (totalPlayedGames - playedGamesNonAjustedLimit) * averageScore / 3;
}
averagePosition = totalPlayedGames === 0 ? 0 : averagePosition / totalPlayedGames;
result.totalScore = totalScore;
result.totalGain = totalGain;
result.totalITM = totalITM;
result.totalWin = totalWin;
result.totalPlayedGames = totalPlayedGames;
result.bountyNumberForPlayer = bountyNumberForPlayer;
result.averagePosition = averagePosition;
result.lostNumberForPlayer = lostNumberForPlayer;
return result;
}
/**
* Main entry point
* Compute for the scores of every given players, based on all their positions
* @param playerNames: range of cell containing the player names.
* @param positions: range double array, one row per player, in columns: the positions and bounties for one game (one game = 2 consecutive columns).
* @return: a sorted multi columns array with: the player name as first column, other data as next columns
*/
function computePlayerScoreAndRank(playerNames, positionsAndBounties) {
// playerNames is a [][], we need to transform it to an array.
var playerNamesArray = [];
for(var p = 0; p < playerNames.length; p++) {
playerNamesArray.push(playerNames[p][0]);
}
// split the big game array in two smaller arrays, one containing only the player's positions and the other containing the player's bounties
var positions = [];
var bounties = [];
for(var player = 0; player < positionsAndBounties.length; player++) {
positions[player] = [];
bounties[player] = [];
for(var game = 0; game < positionsAndBounties[player].length / 2; game++) {
positions[player][game] = positionsAndBounties[player][2 * game];
bounties[player][game] = positionsAndBounties[player][2 * game + 1];
}
}
var playerPerGames = computePlayerPerGames(positions);
var gameNumber = computeGameNumber(playerPerGames);
var playerScores = [];
var playerAverage = [];
var playerITMs = [];
var playerWins = [];
var playerLasts = [];
var playerGains = [];
var playerBounties = [];
var playerPlayedGames = [];
var playerRes;
// for every player, compute his score and other data
for(var player = 0; player < positions.length; player++) {
playerRes = computeForPlayer(positions[player], playerPerGames, bounties[player], gameNumber);
playerScores[player] = playerRes.totalScore;
playerWins[player] = playerRes.totalWin;
playerITMs[player] = playerRes.totalITM;
playerLasts[player] = playerRes.lostNumberForPlayer;
playerBounties[player] = playerRes.bountyNumberForPlayer;
playerPlayedGames[player] = playerRes.totalPlayedGames;
//Total gain is gain + bounties - buy ins
playerGains[player] = playerRes.totalGain + playerBounties[player] * bountyGain - playerPlayedGames[player] * buyInPrice
}
var result = arrayTranspose([playerNamesArray, playerPlayedGames, playerScores, playerWins, playerITMs, playerLasts, playerGains, playerBounties]);
var sortFunction = function (a, b) {
if (a[2] > b[2])
return 1;
if (a[2] < b[2])
return -1;
// a must be equal to b
return 0;
};
// now time to sort everything, we write a custom sorting function that taks into account the score
return result.sort(sortFunction).reverse();
}
/**
* Given a JavaScript 2d Array, this function returns the transposed table.
* Example: arrayTranspose([[1,2,3],[4,5,6]]) returns [[1,4],[2,5],[3,6]].
* @param data: JavaScript 2d Array
* @return a JavaScript 2d Array
*/
function arrayTranspose(data) {
if (data.length == 0 || data[0].length == 0) {
return null;
}
var ret = [];
for (var i = 0; i < data[0].length; ++i) {
ret.push([]);
}
for (var i = 0; i < data.length; ++i) {
for (var j = 0; j < data[i].length; ++j) {
ret[j][i] = data[i][j];
}
}
return ret;
}
///////////
// TESTS //
///////////
/** Check tests results by displaying the Log view (View > Logs) */
function test() {
Logger.log("Running tests");
Logger.log("Test compute player per games:");
var scores = computePlayerPerGames([[1,3,2], [2,2,""], ["",1,1]]);
if( scores[0] !== 2
|| scores[1] !== 3
|| scores[2] !== 2 ) {
Logger.log("FAIL");
} else {
Logger.log("OK");
}
Logger.log("Test point per position");
if(computePointsFromPosition(1, 19) == 95) { Logger.log("OK"); } else { Logger.log("FAIL"); }
if(computePointsFromPosition(2, 14) == 52) { Logger.log("OK"); } else { Logger.log("FAIL"); }
if(computePointsFromPosition(6, 12) == 14) { Logger.log("OK"); } else { Logger.log("FAIL"); }
if(computePointsFromPosition(11, 15) == 5) { Logger.log("OK"); } else { Logger.log("FAIL"); }
if(computePointsFromPosition(4, 20) == 43) { Logger.log("OK"); } else { Logger.log("FAIL"); }
Logger.log("Test gain per position");
if(computeGainsFromPosition(1, 19) == 67) { Logger.log("OK"); } else { Logger.log("FAIL"); }
if(computeGainsFromPosition(2, 14) == 28) { Logger.log("OK"); } else { Logger.log("FAIL"); }
if(computeGainsFromPosition(6, 12) == 0) { Logger.log("OK"); } else { Logger.log("FAIL"); }
if(computeGainsFromPosition(11, 15) == 0) { Logger.log("OK"); } else { Logger.log("FAIL"); }
if(computeGainsFromPosition(4, 20) == 8) { Logger.log("OK"); } else { Logger.log("FAIL"); }
}
/** calls computePlayerScoreAndRank() on current active sheet */
function triggerMainFunctionOnRealSheet() {
var playerNamesRange = "A6:A40";
var positionsAndBountiesRange = "B6:AM40";
var testSheet = SpreadsheetApp.getActive();
Logger.log("testing on current sheet: " + testSheet.getSheetName());
var result = computePlayerScoreAndRank(testSheet.getRange(playerNamesRange).getValues(), testSheet.getRange(positionsAndBountiesRange).getValues());
Logger.log(result);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment