Navigation

    BGS

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups

    Statistics - just wondering on the progress?

    Comments & Feedback
    7
    12
    538
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • R
      Robert Shepherd last edited by

      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
      1 Reply Last reply Reply Quote 2
      • J
        jobe last edited by

        I second statistics!! Just that I want about a zillion more options then Robert ;)

        1 Reply Last reply Reply Quote 2
        • C
          coyotte508 last edited by

          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.

          1 Reply Last reply Reply Quote 2
          • B
            Babbuc49 last edited by

            Are you planning to organize tournament within the site or on an external website like TM tour?

            1 Reply Last reply Reply Quote 0
            • B
              Babbuc49 last edited by

              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?

              C 1 Reply Last reply Reply Quote 0
              • C
                coyotte508 @Babbuc49 last edited by

                @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.

                1 Reply Last reply Reply Quote 0
                • E
                  Ewan last edited by Ewan

                  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

                  C 1 Reply Last reply Reply Quote 1
                  • C
                    coyotte508 @Ewan last edited by

                    @Ewan It's limited to 100 games per call.

                    You can do ?skip=100&count=100 to get next page.

                    1 Reply Last reply Reply Quote 0
                    • E
                      Ewan last edited by

                      @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).

                      C 1 Reply Last reply Reply Quote 0
                      • C
                        coyotte508 @Ewan last edited by

                        @Ewan Done

                        1 Reply Last reply Reply Quote 0
                        • S
                          Spooky last edited by

                          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.

                          1 Reply Last reply Reply Quote 5
                          • J
                            JenniL last edited by

                            BIG THANKS for the data compilation @Spooky !!!
                            that are some very interesting insights!

                            1 Reply Last reply Reply Quote 0
                            • First post
                              Last post