Discussions

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

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.

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?

Replying to a message· Babbuc49

@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 of 999999 is respected. When leaving count 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 the cancelled field Edit 6 (2020-09-23): change requested endpoints to avoid the same bug as in Edit 5 when looking up individual players

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.