Skip to content

Instantly share code, notes, and snippets.

@mistahenry
Created March 9, 2020 10:51
Show Gist options
  • Save mistahenry/ec77911fa24771962ce7c43490d9476b to your computer and use it in GitHub Desktop.
Save mistahenry/ec77911fa24771962ce7c43490d9476b to your computer and use it in GitHub Desktop.
Terraforming Mars Solo Leaderboard Google Sheets generator
function CALCULATE_LEADERBOARD() {
var corps = [
"CrediCor",
"Ecoline",
"Interplanetary Cinematics",
"Saturn Systems",
"Helion",
"Mining Guild",
"Tharsis Replublic",
"Phobolog",
"Teractor",
"Inventrix",
"UNMI",
"Thorgate"
];
var users = [
"Will",
"Janine",
"Sandra",
"Dalton"
];
var misc = [
"Golf (Lowest Winning Score)",
"Most VPs",
"Most City Points",
"Most Greeneries",
"Most TR"
];
var overall = "Overall";
var miscBonus = 100;
// grab leaderboard sheet and clear everything including formatting
var leaderboard = getSheetByName("Leaderboard");
var rangeToReset = leaderboard.getRange(1,1,300,5);
rangeToReset.clearContent();
rangeToReset.setFontWeight("normal");
rangeToReset.setHorizontalAlignment("left");
/*
* collect each users corp stats, misc stats, and best score
*/
var statsPerUser = {};
users.forEach(function(userName){
var userStats = {};
var userSheet = getSheetByName(userName);
// get corps + score
var corps = userSheet.getRange(2, 1, 12, 2).getValues();
var highScore = { corp: "N/A", score: 0};
corps.forEach(function(corp){
var name = corp[0];
var score = corp[1];
if(score > highScore.score){
highScore = {
corp: corp,
score: score
};
}
userStats[name] = score || 0;
});
// get misc + score
var corps = userSheet.getRange(15, 1, 5, 2).getValues();
corps.forEach(function(corp){
var name = corp[0];
var score = corp[1];
userStats[name] = score;
});
userStats["highScore"] = highScore;
userStats[overall] = 0;
statsPerUser[userName] = userStats;
});
// extract the misc leaderboards
var statsPerMisc = buildCategoryLeaderboard(misc, users, statsPerUser);
// extract the corp leaderboards
var statsPerCorp = buildCategoryLeaderboard(corps, users, statsPerUser);
// write user highscore to spreadsheet
var sortedUserHighScores = users.map(function(user){
var highScore = statsPerUser[user].highScore;
return {
user: user,
score: highScore.score,
corp: highScore.corp
};
}).sort(scoreComparator);
var currentRow = 1;
var multiplier = 4;
var currentHighScore = 0;
for(var i = 0; i < sortedUserHighScores.length; i++){
var userStat = sortedUserHighScores[i];
var score = userStat.score;
if(score < currentHighScore){
multiplier = (4 - i);
}
statsPerUser[userStat.user][overall] += (multiplier * score);
currentHighScore = score;
}
// give the miscBonus (100) to each leader
misc.forEach(function(miscCategory){
statsPerMisc[miscCategory].leaders.forEach(function(leader){
statsPerUser[leader.user][overall] += miscBonus;
});
});
corps.forEach(function(corp){
currentHighScore = 0;
var place = 0;
var corpStats = statsPerCorp[corp].userStats;
for(var i = 0; i < corpStats.length; i++){
var userStat = corpStats[i];
var user = userStat.user;
var score = userStat.score || 0;
if(score < currentHighScore){
place = i;
}
var pointsToAdd = (score / Math.pow(2, i)); // divide score by 2 ^ (place - 1) aka 1 2 4 8 respectively
var allStatsForUser = statsPerUser[userStat.user]
allStatsForUser[overall] += pointsToAdd;
currentHighScore = score;
}
});
var sortedUserOverallScores = users.map(function(user){
var overallScore = statsPerUser[user][overall];
var corpSum = corps.reduce(function(acc,corp){
var corpScore = statsPerUser[user][corp];
return acc + corpScore;
}, 0);
return {
user: user,
score: overallScore,
corpSum: corpSum
};
}).sort(scoreComparator).map(function(stat){
return [stat.user, Math.round(stat.score), stat.corpSum];
});
leaderboard.getRange(currentRow,1, sortedUserOverallScores.length + 1, 3).setValues([["Overall Leaderboard", "Weighted", "All Corp total"]].concat(sortedUserOverallScores));
leaderboard.getRange(currentRow,1,1,3).setFontWeight("Bold");
currentRow += sortedUserOverallScores.length + 2;
var sortUserHighScoresForDisplay = sortedUserHighScores.map(function(stat){
return [stat.user, stat.corp, stat.score];
});
leaderboard.getRange(currentRow,1, sortUserHighScoresForDisplay.length + 1, 3).setValues([["User Leaderboard", "Corp", "Score"]].concat(sortUserHighScoresForDisplay));
leaderboard.getRange(currentRow,1,1,3).setFontWeight("Bold");
currentRow += sortUserHighScoresForDisplay.length + 2;
currentRow += writeLeaderboard({
leaderboard: leaderboard,
categories: misc,
stats: statsPerMisc,
currentRow: currentRow,
categoryType: "Misc"
});
currentRow++;
currentRow += writeLeaderboard({
leaderboard: leaderboard,
categories: corps,
stats: statsPerCorp,
currentRow: currentRow,
categoryType: "Corp"
});
currentRow++;
// write the leaderboards per corp
corps.map(function(corp){
var corpStats = statsPerCorp[corp];
var userStats = corpStats.userStats.filter(function(userStat){
return userStat.score > 0;
}).map(function(userStat){
return [userStat.user, userStat.score];
});
return [[corp + " Leaderboard", "Score"]].concat(userStats);
}).forEach(function(corpValues){
if(corpValues.length > 1){
leaderboard.getRange(currentRow,1, corpValues.length, 2).setValues(corpValues);
leaderboard.getRange(currentRow,1,1,2).setFontWeight("Bold");
currentRow += corpValues.length + 1; // add space
}
});
}
function getSheetByName(id) {
return SpreadsheetApp.getActive().getSheets().filter(
function(s) {return s.getSheetName() === id;}
)[0];
}
function golfComparator(a,b){
return a.score - b.score;
}
function scoreComparator(a,b){
return b.score - a.score;
}
function buildCategoryLeaderboard(categories, users, userStats){
// aggregate user stats around corporations
var agg = {};
categories.forEach(function(category){
// a few hacks necessary for Golf
var isGolf = category === "Golf (Lowest Winning Score)";
var comparator = isGolf ? golfComparator : scoreComparator;
var defaultVal = isGolf ? 1000 : 0;
var userStatsForCategory = users.map(function(user){
var categoryStatScore = userStats[user][category] || defaultVal;
return {
user: user,
score: categoryStatScore
};
});
userStatsForCategory.sort(comparator);
// allow for tied leaders
var leaders = [userStatsForCategory[0]];
for(var i = 1; i < userStatsForCategory.length; i++){
if(userStatsForCategory[i].score == leaders[0].score){
leaders.push(userStatsForCategory[i])
}else{
break;
}
}
agg[category] = {
leaders: leaders,
userStats: userStatsForCategory
};
});
return agg;
}
function writeLeaderboard(opts){
var categories = opts.categories;
var statsPerCategory = opts.stats;
var currentRow = opts.currentRow;
var categoryType = opts.categoryType;
var leaderboard = opts.leaderboard;
// aggregate the leaders per corp (more than one if tie)
let categoryLeaders = [];
categories.forEach(function(category){
var leaders = statsPerCategory[category].leaders;
leaders.forEach(function(leader){
if(leader.score > 0){
categoryLeaders.push({
category: category,
user: leader.user,
score: leader.score
});
}
});
});
var sortedCategoryWinners = categoryLeaders.sort(scoreComparator).map(function(stat){
return [stat.category, stat.user, stat.score];
});
// write the corp leaderboard
leaderboard.getRange(currentRow,1, sortedCategoryWinners.length + 1, 3).setValues([[categoryType + " Leaderboard", "User", "Score"]].concat(sortedCategoryWinners));
leaderboard.getRange(currentRow,1,1,3).setFontWeight("Bold");
return sortedCategoryWinners.length + 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment