Skip to content

Instantly share code, notes, and snippets.

@hilnius
Last active December 5, 2019 21:41
Show Gist options
  • Save hilnius/9347c6fd0f84dc3bb6d5acedad631b39 to your computer and use it in GitHub Desktop.
Save hilnius/9347c6fd0f84dc3bb6d5acedad631b39 to your computer and use it in GitHub Desktop.
A Google Sheets function to compute ELO for Mario-Kart games
var _ = LodashGS.load();
/*
NEXT_ELO Google Sheets function
This method computes the ELO for our Mario Kart tournament.
Usage: =NEXT_ELO(results_range, elo_range)
- results_range is an horizontal range that contains the results of a game.
- elo_range is an horizontal range that contains the ELOs before that game.
Returns: an horizontal range containing the new elo of all players after
considering the game results.
The formula is based on https://en.wikipedia.org/wiki/Elo_rating_system
Considering one results line, we consider the rank of all players that
participated in that race, and consider wins & losses depending on other
participant's scores. If a line contains 4 players' scores, we consider
3 wins/0 losses for the first, 2w/1l for the second, 1w/2l for the third
and 0w/3l for the fourth. There can be draws if players have the same score.
Example usage:
Sheet1:
[P1, P2, P3, P4, P5, P6]
[12, 35, 60, , 45, ] # You can use race or tourney points !
[12, 35, 60, , 45, ]
[ , , 60, 22, 45, 17]
[ ... ]
Sheet2:
[ P1 , P2 , P3 , P4 , P5 , P6 ]
[ 1000, 1000, 1000, 1000, 1000, 1000 ]
[=NEXT_ELO(Sheet1!A2:F2; Shee2!A2:F2)] ## only need to put it in the first cell !
[=NEXT_ELO(Sheet1!A3:F3; Shee2!A3:F3)]
[ ... ]
© Robin Nicollet
*/
function NEXT_ELO(marioKartTourneyResultsRange, currentEloLine) {
var results = [];
for (var i = 0; i < marioKartTourneyResultsRange[0].length; i++) {
if (marioKartTourneyResultsRange[0][i]) {
results.push({
playerColumn: i,
tourneyPoints: marioKartTourneyResultsRange[0][i],
currentElo: currentEloLine[0][i],
});
}
}
var p = function (D) {
return 1/(1 + Math.exp(-D / 400 * Math.LN10));
};
_.forEach(results, function (currentPlayerResult) {
var K = 40;
var currentPlayerNewElo = currentPlayerResult.currentElo;
_.forEach(results, function (otherPlayerResult) {
if (otherPlayerResult.playerColumn != currentPlayerResult.playerColumn) {
var D = currentPlayerResult.currentElo - otherPlayerResult.currentElo;
var W = 0;
if (currentPlayerResult.tourneyPoints > otherPlayerResult.tourneyPoints) {
W = 1;
} else if (currentPlayerResult.tourneyPoints == otherPlayerResult.tourneyPoints) {
W = 0.5;
}
currentPlayerNewElo += K * (W - p(D));
}
});
currentPlayerResult.newElo = currentPlayerNewElo;
});
return [_.map(currentEloLine[0], function (currentElo, column) {
var result = _.find(results, _.matches({ playerColumn: column }));
if (result) {
return result.newElo;
}
return currentElo;
})];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment