Created
March 9, 2020 10:51
-
-
Save mistahenry/ec77911fa24771962ce7c43490d9476b to your computer and use it in GitHub Desktop.
Terraforming Mars Solo Leaderboard Google Sheets generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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