Statistics - just wondering on the progress?
-
Id love to see:
- how many games I play with which faction
- how long players take (while actively on page)
- average score per game (with which race)
- lots more
-
I second statistics!! Just that I want about a zillion more options then Robert ;)
-
For now - we're both focused on other things. There's a UX change upcoming, with one page per boardgame, a sidebar, elo rankings. And we're focusing on adding docs for developers / adding new games.
We also plan to add tournaments, teams... after all this is done.
Maybe statistics after?
But we welcome any help - if people want to contribute (like with stats), we'll do our best to support them.
-
Are you planning to organize tournament within the site or on an external website like TM tour?
-
Although i guess statiscs would be a pretty important for the understanding of the game, do you think it will be possible to pull out statistics from games that have been played before the implementation of this feature?
-
@Babbuc49 said in Statistics - just wondering on the progress?:
Although i guess statiscs would be a pretty important for the understanding of the game, do you think it will be possible to pull out statistics from games that have been played before the implementation of this feature?
Things like how much time a player takes are not recorded, so no for those. For the rest, it depends.
@Babbuc49 said in Statistics - just wondering on the progress?:
Are you planning to organize tournament within the site or on an external website like TM tour?
We're planning to integrate tournaments into the site. For the league itself, probably, if someone else takes the initiative to do a separate site like TMTour, then maybe not.
-
Partially to understand better how those modern fancy JavaScript frameworks work and partially to be hopefully useful to other players, I wrote a JavaScript proof of concept that allows you to see some basic statistics of any player.
It works as follows: open the dev tools in your browser (usually this is done by pressing
F12
). Go to the console and copy and past all the code below. At the top of the page, a small form should show up where you can enter any username and see some basic info: how many times they chose each faction, what their average score is with that faction and how many times they won with that faction. I only consider finished non-cancelled Gaia Project games where nobody dropped out.If you've removed everything, run
statistics();
again in the console. If you ever refresh the page, you need to copy and paste the full code again.function statistics() { const factions = ['terrans', 'lantids', 'nevlas', 'itars', 'firaks', 'bescods', 'taklons', 'ambas', 'xenos', 'gleens', 'geodens', 'baltaks', 'ivits', 'hadsch-hallas'] let userId; let username; let showGlobalStats = false; function getRequest(url, callback, args) { fetch(url).then(response => response.json()) .then(data => callback(data, args)); } function loadGameData(userData) { userId = userData._id; if(!userId) return; getRequest('https://www.boardgamers.space/api/user/' + userId + '/games/count/closed', paginateThroughGames) } function paginateThroughGames(numberOfGames) { if(showGlobalStats) { getRequest('https://www.boardgamers.space/api/game/closed?count=100&skip=0', loadNextGames, {existingData: [], currentSkip: 0, totalGames: numberOfGames}); } else { getRequest('https://www.boardgamers.space/api/game/closed?user=' + userId + '&count=100&skip=0', loadNextGames, {existingData: [], currentSkip: 0, totalGames: numberOfGames}); } } function loadNextGames(newData, {existingData, currentSkip, totalGames}) { const mergedData = existingData.concat(newData); if(currentSkip + 100 >= totalGames) { filterAndSplitPlayerCount(mergedData); } else { newSkip = currentSkip + 100; if(showGlobalStats) { getRequest('https://www.boardgamers.space/api/game/closed?count=100&skip=' + newSkip, loadNextGames, {existingData: mergedData, currentSkip: newSkip, totalGames: totalGames}); } else { getRequest('https://www.boardgamers.space/api/game/closed?user=' + userId + '&count=100&skip=' + newSkip, loadNextGames, {existingData: mergedData, currentSkip: newSkip, totalGames: totalGames}); } } } function filterAndSplitPlayerCount(gameData) { const gaiaCompletedOnlyData = filterGameData(gameData); if(showGlobalStats) { analyseGameData(gaiaCompletedOnlyData, 'All players - all') } else { analyseGameData(gaiaCompletedOnlyData, username + ' - all') } for(let playerCount=2; playerCount <= 4; playerCount++) { if(showGlobalStats) { analyseGameData(gaiaCompletedOnlyData.filter(game => game.players.length === playerCount), 'All players - ' + playerCount + ' players'); } else { analyseGameData(gaiaCompletedOnlyData.filter(game => game.players.length === playerCount), username + ' - ' + playerCount + ' players'); } } } function analyseGameData(gameData, title) { const gaiaCompletedOnlyData = gameData; const factionDataToDisplay = {}; const countChosen = 'Times chosen'; const averageScore = 'Average score'; const wins = 'Wins'; const winPercent = 'Win %'; const total = 'total'; for(const faction of factions) { factionDataToDisplay[faction] = {}; factionDataToDisplay[faction][countChosen] = 0; factionDataToDisplay[faction][averageScore] = 0; factionDataToDisplay[faction][wins] = null; factionDataToDisplay[faction][winPercent] = null; } factionDataToDisplay[total] = {}; factionDataToDisplay[total][countChosen] = 0; factionDataToDisplay[total][averageScore] = null; factionDataToDisplay[total][wins] = null; factionDataToDisplay[total][winPercent] = null; for(const game of gaiaCompletedOnlyData) { for (const player of game.players) { if(showGlobalStats) { factionDataToDisplay[player.faction][countChosen] += 1; factionDataToDisplay[player.faction][averageScore] += player.score; factionDataToDisplay[player.faction][wins] += didPlayerWin(game, player.score); factionDataToDisplay[total][countChosen] += 1; factionDataToDisplay[total][averageScore] += player.score; } else if(player._id === userId) { factionDataToDisplay[player.faction][countChosen] += 1; factionDataToDisplay[player.faction][averageScore] += player.score; factionDataToDisplay[player.faction][wins] += didPlayerWin(game, player.score); factionDataToDisplay[total][countChosen] += 1; factionDataToDisplay[total][averageScore] += player.score; factionDataToDisplay[total][wins] += didPlayerWin(game, player.score); } } } for(const faction of Object.keys(factionDataToDisplay)) { if(factionDataToDisplay[faction][countChosen] > 0) { factionDataToDisplay[faction][averageScore] /= factionDataToDisplay[faction][countChosen]; factionDataToDisplay[faction][averageScore] = factionDataToDisplay[faction][averageScore].toFixed(1); factionDataToDisplay[faction][winPercent] = (factionDataToDisplay[faction][wins] / factionDataToDisplay[faction][countChosen] * 100).toFixed(1) + '%'; } else { factionDataToDisplay[faction][averageScore] = null; } } if(showGlobalStats) factionDataToDisplay[total][winPercent] = null; const dataToDisplay = {title: title, data: factionDataToDisplay}; displayData(dataToDisplay); } function filterGameData(gameData) { const gaiaGames = gameData.filter(game => game.game.name.toUpperCase().trim() === 'gaia-project'.toUpperCase()); const gaiaGamesNonCancelled = gaiaGames.filter(game => !game.cancelled); const gaiaGamesNonDroppedOrCancelled = gaiaGamesNonCancelled.filter(game => !someoneDropped(game)); return gaiaGamesNonDroppedOrCancelled; } function someoneDropped(game) { for(const player of game.players) { if(player.dropped) return true; } return false; } function didPlayerWin(game, currentPlayerScore) { for(const player of game.players) { if(player.score > currentPlayerScore) return false; } return true; } function displayData(data) { const table = document.createElement('TABLE'); const rowFactions = document.createElement('TR'); rowFactions.appendChild(document.createElement('TH')); for(const faction of Object.keys(data.data)) { const headCell = document.createElement('TH'); headCell.innerHTML = faction; rowFactions.appendChild(headCell); } table.appendChild(rowFactions); const rowProperties = Object.keys(data.data[factions[0]]); for(const rowProperty of rowProperties) { const rowElement = document.createElement('TR'); const rowNameElement = document.createElement('TD'); rowNameElement.innerHTML = rowProperty; rowElement.appendChild(rowNameElement); for(const faction of Object.keys(data.data)) { const cellValueElement = document.createElement('TD'); cellValueElement.innerHTML = data.data[faction][rowProperty]; rowElement.appendChild(cellValueElement); } table.appendChild(rowElement); } // styling: table.classList.add('statistics-table'); if(document.getElementById('stat-loading-indicator')) document.getElementById('stat-loading-indicator').remove(); document.getElementById('my-statistics').insertAdjacentHTML('beforeend', '<h3>' + data.title + '</h3>'); document.getElementById('my-statistics').insertAdjacentElement('beforeend', table); } function getStatisticsForUser() { showGlobalStats = false; username = document.getElementById('stat-input-username').value; getRequest('https://www.boardgamers.space/api/user/infoByName/' + username, loadGameData); } statistics.getStatisticsForUser = getStatisticsForUser; function removeAll() { document.getElementById('my-statistics').remove(); document.getElementById('customTableStyle').remove(); } statistics.removeAll = removeAll; function seeAllStats() { showGlobalStats = true; document.getElementById('my-statistics').insertAdjacentHTML('afterbegin', '<h3 id="stat-loading-indicator">Loading...</h3>') getRequest('https://www.boardgamers.space/api/game/stats', (data) => paginateThroughGames(data.finished)); } statistics.seeAllStats = seeAllStats; // styling element: const customTableStyle = document.createElement('STYLE'); customTableStyle.setAttribute('id', 'customTableStyle'); customTableStyle.innerHTML = ` .statistics-table { text-align: center; margin-bottom: 10px; transform: scale(0.8); transform-origin: left; } .statistics-table tr { line-height: 2; } .statistics-table tr:nth-child(odd) { background-color: #DCDCDC; } .statistics-table th { padding-left: 12px; padding-right: 12px; } ` document.getElementsByTagName('head')[0].appendChild(customTableStyle); document.getElementById("navbar").insertAdjacentHTML('beforebegin', '<div id="my-statistics"> <form onsubmit="statistics.getStatisticsForUser(); return false"><input type="text" placeholder="username" id="stat-input-username"><input type="submit" value="See statistics!"></form> <button onclick="statistics.seeAllStats()">Golbal statistics</button> <button onclick="statistics.removeAll()">Remove all</button> </div?'); } statistics();
I'd like to hear suggestions on how to improve this, but note that I do this in my free time so it might take a while before I pick it up (if I want to in the first place). Of course, feel free to edit it yourself to make it do whatever you want.
@coyotte508: is there a reference for the API available? For example, I currently fetch all games of a player with
'https://www.boardgamers.space/api/user/' + userId + '/games/closed?count=999999&skip=0'
and I don't know if the value of999999
is respected. When leavingcount
out, it only returns 20 games. Is there a way to always get all the games of a player?Edit 1 (2020-07-20): the script should now also work for users who have played more than 100 games.
Edit 2 (2020-07-21): the tables now also show win percentages.
Edit 3 (2020-07-21): a table for each player count, total stats on each table
Edit 4 (2020-07-21): global statistics! It takes a while to load, so don't spam the Global statistics button, so we don't overload the API.
Edit 5 (2020-09-23): fix broken script by filtering out games that were scheduled but not started, due to a lack of players, by using thecancelled
field
Edit 6 (2020-09-23): change requested endpoints to avoid the same bug as inEdit 5
when looking up individual players -
@Ewan It's limited to 100 games per call.
You can do
?skip=100&count=100
to get next page. -
@coyotte508, when requesting games through i.e. https://www.boardgamers.space/api/game/closed?count=100&skip=0, could you add the field
cancelled
in the response? Because currently my script is broken by games with a scheduled start date that lies in the past but without enough players to start (e.g. https://www.boardgamers.space/game/Wizardly-secretary-3749). -
@Ewan Done
-
Hi guys.
I was also collecting some stats from the site, refreshed them today. They are not available online, but I'm sharing some data below. What is important about the data:
- all data comes from finished games. If there was drop, game was cancelled or simply finished before round 6, its not considered at all
- I have no information if game was with auction system or not, so bid values are also not considered. It basically means that average/max scores are lowered by games with auction system as I can't undo the bidding
- there is no additional games filtering (like not taking under account games with bad players, low results, etc)
So lets start with the most basic data, ordered by number of players and then by average scores
Faction Players Max Score Avg Score Games ivits 4 220 149,97 582 itars 4 226 149,69 613 ambas 4 255 148,4 532 taklons 4 222 146,62 541 firaks 4 202 145,09 360 terrans 4 239 144,28 727 baltaks 4 206 143,61 453 gleens 4 231 140,13 351 nevlas 4 222 138,97 418 xenos 4 204 138,41 478 geodens 4 224 137,12 441 lantids 4 202 135,05 299 hadsch-hallas 4 216 134,79 495 bescods 4 205 133,72 358 ivits 3 254 168,73 304 ambas 3 237 157,4 267 firaks 3 232 155,78 186 itars 3 250 154,66 284 taklons 3 246 152,56 229 nevlas 3 231 151,84 210 terrans 3 223 151,43 371 baltaks 3 204 148,91 196 bescods 3 205 147,79 178 hadsch-hallas 3 220 147,28 258 geodens 3 226 145,59 244 gleens 3 220 144,32 186 xenos 3 208 139,69 200 lantids 3 195 134,64 124 ivits 2 251 176,17 716 itars 2 237 159,99 501 firaks 2 228 158,54 336 ambas 2 235 155,06 626 baltaks 2 215 151,6 349 nevlas 2 254 151,41 352 terrans 2 255 150,73 724 geodens 2 233 150,35 458 taklons 2 240 149,43 263 hadsch-hallas 2 224 147,21 442 xenos 2 227 145,23 316 bescods 2 211 143,86 252 gleens 2 210 143,15 326 lantids 2 213 129,77 115
So in all categories (number of players) Ivits has the higher average score, while in 2 and 3 player their domination is very noticeable. In 4 player game they are very close to Itars and Ambas. On other side we have Lantids performing really badly in 2-3 players games. What surprised me the most are Firaks average scores.
Average scores not necessary goes toe to toe with wins ratios as some of the games were with auctions system. And it also depends on other players results:
Players Faction Games 1st 2nd 3rd 4th 1st % 2nd % 3rd % 4th % 4 taklons 541 191 121 105 124 35.30% 22.37% 19.41% 22.92% 4 ivits 582 191 150 146 95 32.82% 25.77% 25.09% 16.32% 4 itars 613 194 171 125 123 31.65% 27.90% 20.39% 20.07% 4 ambas 532 168 136 119 109 31.58% 25.56% 22.37% 20.49% 4 terrans 727 202 179 183 163 27.79% 24.62% 25.17% 22.42% 4 firaks 360 93 88 92 87 25.83% 24.44% 25.56% 24.17% 4 nevlas 418 93 108 100 117 22.25% 25.84% 23.92% 27.99% 4 gleens 351 77 99 89 86 21.94% 28.21% 25.36% 24.50% 4 baltaks 453 96 135 111 111 21.19% 29.80% 24.50% 24.50% 4 xenos 478 101 118 143 116 21.13% 24.69% 29.92% 24.27% 4 lantids 299 60 66 81 92 20.07% 22.07% 27.09% 30.77% 4 geodens 441 88 105 126 122 19.95% 23.81% 28.57% 27.66% 4 bescods 358 66 74 101 117 18.44% 20.67% 28.21% 32.68% 4 hadsch-hallas 495 81 104 139 171 16.36% 21.01% 28.08% 34.55% 3 ivits 304 173 76 55 0 56.91% 25.00% 18.09% 3 taklons 229 95 67 67 0 41.48% 29.26% 29.26% 3 ambas 267 97 93 77 0 36.33% 34.83% 28.84% 3 nevlas 210 76 63 71 0 36.19% 30.00% 33.81% 3 firaks 186 67 65 54 0 36.02% 34.95% 29.03% 3 itars 284 102 91 91 0 35.92% 32.04% 32.04% 3 bescods 178 62 44 72 0 34.83% 24.72% 40.45% 3 terrans 371 120 134 117 0 32.35% 36.12% 31.54% 3 geodens 244 73 73 98 0 29.92% 29.92% 40.16% 3 hadsch-hallas 258 77 98 83 0 29.84% 37.98% 32.17% 3 baltaks 196 53 73 70 0 27.04% 37.24% 35.71% 3 gleens 186 49 67 70 0 26.34% 36.02% 37.63% 3 xenos 200 42 76 82 0 21.00% 38.00% 41.00% 3 lantids 124 23 40 61 0 18.55% 32.26% 49.19% 2 ivits 716 545 171 0 0 76.12% 23.88% 2 itars 501 267 234 0 0 53.29% 46.71% 2 taklons 263 140 123 0 0 53.23% 46.77% 2 firaks 336 170 166 0 0 50.60% 49.40% 2 ambas 626 314 312 0 0 50.16% 49.84% 2 nevlas 352 170 182 0 0 48.30% 51.70% 2 baltaks 349 166 183 0 0 47.56% 52.44% 2 geodens 458 217 241 0 0 47.38% 52.62% 2 terrans 724 330 394 0 0 45.58% 54.42% 2 hadsch-hallas 442 197 245 0 0 44.57% 55.43% 2 xenos 316 130 186 0 0 41.14% 58.86% 2 gleens 326 130 196 0 0 39.88% 60.12% 2 bescods 252 95 157 0 0 37.70% 62.30% 2 lantids 115 36 79 0 0 31.30% 68.70%
Domination of Ivits in 2-3 players games are also visible in their wins-ratio. In 4 players games Taklons are the best. However if you consider 1st and 2nd place together, then Taklons are third (57.67 %) while Ivits second (58.59 %) after Itars first (59.54 %). So its easier to win with Taklons, but by average better result you will get with Ivits and Itars. Lantids are the worst in 2-3 players games, and they do below average in 4 players games. Bescods are last but one in all player setups.
What conclusions we may get from above data:
- Game is more balanced in 4 players setups as the differences between scores and wins ratios are smaller
- Ivits are overpowered, especially in 2-3 players games
- Lantinds and Bescods are underpowered, especially in 2-3 players games. They are also the least picked factions
- Terrans are the most popular faction in all modes, however faction performs "only" above average
And cherry on the cake at the end - top 20 players with the best average scores (games with drops are not counted) with at least 10 finished games:
Players Name Games Avg 4 turkishvancat 11 185,55 4 Rudeiro 11 182,27 4 IRIS 38 177,61 4 ZeroCool 23 177,48 4 Nolegsmoo 17 173,24 4 kyte 26 171,15 4 satchisuta 24 170,71 4 sorpy 17 170,24 4 Molfo 66 169,26 4 ssbon 25 169,16 4 lucasrrr 31 168,87 4 Spooky 26 168,85 4 DDi_yong 12 168,33 4 Wy 10 167,3 4 Babbuc49 99 167,18 4 Ewan 34 167,12 4 jgunter 14 167 4 Kesterer 25 166,88 4 Tennessee Walker 19 166,84 4 Stylish 10 166,8 3 EoMoR 23 188,91 3 Spin 14 187,93 3 Milkleo 16 181,5 3 Lemondas n.lin 18 180,11 3 mbouzada 10 179,9 3 Dinogau 15 177,07 3 gunsung 10 177 3 sorpy 10 176,6 3 seki 10 176,6 3 Akiyama1103 13 176,38 3 Babbuc49 22 174,23 3 Roadrunner19 12 172,42 3 Saltara 10 171,9 3 doldole 14 171,43 3 tanios 12 170,58 3 hikary27 30 170,43 3 TearsInTheRain 79 170,39 3 NineTailCat 10 170 3 redarox 27 169,96 3 Adriano 15 167,2 2 Aillas 14 196,64 2 testrun 49 191,53 2 bigpaullai 14 189,86 2 Amek 11 188,45 2 Milkleo 10 186,8 2 hikary27 18 185 2 MasN 12 184,58 2 b_ 12 184,5 2 Akiyama1103 136 183,9 2 theyellow 71 183,45 2 Poziom 14 181,71 2 Ewan 11 181,45 2 Ansun 17 180,82 2 Gondalf 19 178,79 2 Kesterer 13 178,77 2 Adriano2 47 177,85 2 Babbuc49 30 177,07 2 naname 10 176,7 2 Molfo 12 176,58 2 seki 14 176,57
@coyotte508 - Is it possible to add bidding value to each player result in /api/game/closed endpoint? That would help to recalculate some stats related to average, max score values :)
Enjoy the data.
-
BIG THANKS for the data compilation @Spooky !!!
that are some very interesting insights!