Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Geoguessr Advanced Stats V6
- // @namespace http://tampermonkey.net/
- // @version 6.0
- // @description Fetches Geoguessr API data to get advanced stats for duel games and displays them on the profile page.
- // @author You
- // @match https://www.geoguessr.com/*/me/profile
- // @match https://www.geoguessr.com/me/profile
- // @icon https://www.geoguessr.com/_next/static/media/favicon.bffdd9d3.png
- // @grant GM_xmlhttpRequest
- // @grant GM_addStyle
- // @require https://code.jquery.com/jquery-3.6.0.min.js
- // @require https://unpkg.com/axios@1.6.7/dist/axios.min.js
- // @require https://cdn.jsdelivr.net/npm/progressbar.js@1.1.0/dist/progressbar.min.js
- // @require https://cdn.jsdelivr.net/npm/chart.js@3.8.0/dist/chart.min.js
- // @connect https://game-server.geoguessr.com/
- // ==/UserScript==
- (function() {
- 'use strict';
- // Constants
- const MAX_SCORE = 5000;
- const API_BASE_URL = 'https://game-server.geoguessr.com/api/duels';
- // Variables
- let playerId = '';
- // Function to convert JSON to CSV
- function jsonToCsv(json) {
- console.log(json)
- const keys = Object.keys(json[0]);
- const csv = json.map(row =>
- keys.map(key => JSON.stringify(row[key], (_, value) => value === null ? '' : value)).join(',')
- );
- return [keys.join(','), ...csv].join('\r\n');
- }
- // Function to download a CSV file
- function downloadCsv(data, filename) {
- const blob = new Blob([data], { type: 'text/csv;charset=utf-8;' });
- const url = URL.createObjectURL(blob);
- const link = document.createElement("a");
- link.setAttribute("href", url);
- link.setAttribute("download", filename);
- link.style.visibility = 'hidden';
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- }
- // Function to extract player round data from duel JSON
- function extractPlayerRoundData(duels, playerId) {
- return duels.flatMap(duel => {
- const player = duel.teams.flatMap(team => team.players).find(p => p.playerId === playerId);
- if (!player) return [];
- return player.guesses.map(guess => {
- const round = duel.rounds.find(r => r.roundNumber === guess.roundNumber);
- return {
- gameId: duel.gameId,
- roundNumber: guess.roundNumber,
- lat: guess.lat,
- lng: guess.lng,
- distance: guess.distance,
- score: guess.score,
- panoramaCountryCode: round.panorama.countryCode,
- roundStartTime: round.startTime,
- roundEndTime: round.endTime,
- roundMultiplier: round.multiplier
- };
- });
- });
- }
- // Updated function to handle CSV button click
- async function handleDownloadCsv(session, gameIds) {
- const results = await fetchDuels(session, gameIds.map(game => game.id));
- const playerRoundData = extractPlayerRoundData(results, playerId);
- const csvData = jsonToCsv(playerRoundData);
- downloadCsv(csvData, 'duels_stats.csv');
- }
- // Initialize CSV download button
- function initCsvDownloadButton(session, gameIds) {
- const button = document.createElement('button');
- button.textContent = 'Download Duel Stats CSV';
- button.style.padding = '10px 20px';
- button.style.fontSize = '16px';
- button.style.color = '#ffffff';
- button.style.backgroundColor = '#1a73e8';
- button.style.border = 'none';
- button.style.borderRadius = '5px';
- button.style.cursor = 'pointer';
- button.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)';
- button.onmouseover = () => {
- button.style.backgroundColor = '#0c5abc';
- };
- button.onmouseleave = () => {
- button.style.backgroundColor = '#1a73e8';
- };
- button.onclick = () => handleDownloadCsv(session, gameIds);
- const container = document.querySelectorAll("[class^='container_content']")[document.querySelectorAll("[class^='container_content']").length - 1];
- container.appendChild(button);
- }
- // Inject custom styles
- function addCustomStyles() {
- GM_addStyle(`
- table { width:96%; border-collapse: collapse; color:black;}
- th { border: 1px solid #ddd; padding: 8px; text-align: left; background-color: #f2f2f2; cursor: pointer; }
- td { border: 1px solid #ddd; padding: 8px; text-align: left; background-color: #ffffff; }
- .chart-container { margin: auto; width: 80%; height: 400px; }
- `);
- }
- // Utility functions for color interpolation
- function interpolateColor(color1, color2, factor) {
- factor = Math.min(1, Math.max(0, factor));
- const color1Parts = parseColor(color1);
- const color2Parts = parseColor(color2);
- return color1Parts.map((part, index) =>
- Math.round(part + (factor * (color2Parts[index] - part)))
- ).reduce((acc, val) => acc + ` ${val}`, 'rgb(').concat(')');
- }
- function parseColor(hex) {
- return [
- parseInt(hex.slice(1, 3), 16),
- parseInt(hex.slice(3, 5), 16),
- parseInt(hex.slice(5, 7), 16)
- ];
- }
- function getColor(value, min, max) {
- const mid = (max + min) / 2;
- return value < mid ?
- interpolateColor("#cd2941", "#ffffff", (value - min) / (mid - min))
- : interpolateColor("#ffffff", "#29cdb5", (value - mid) / (max - mid));
- }
- // Calculate global stats from countryData
- function calculateGlobalStats(countryData) {
- let totalScore = 0;
- let totalAccuracy = 0;
- let totalRounds = 0;
- let totalWins = 0;
- Object.values(countryData).forEach(data => {
- totalScore += data.totalScore;
- totalAccuracy += data.totalAccuracy;
- totalRounds += data.totalRounds;
- totalWins += data.wins;
- });
- const totalAverageScore = Math.floor(totalScore / totalRounds);
- const overallAccuracy = (totalAccuracy / totalRounds).toFixed(2);
- const totalWinRate = ((totalWins / totalRounds) * 100).toFixed(2);
- return { totalAverageScore, overallAccuracy, totalWinRate };
- }
- // Render circular gauges for global stats
- function renderGauges(container, globalStats) {
- if (!container) {
- console.error("Container element not found.");
- return;
- }
- const overallStatsWrapper = document.createElement('div');
- overallStatsWrapper.style.marginBottom = '40px';
- const titleElement = document.createElement('h2');
- titleElement.textContent = "Overall Stats";
- titleElement.style.color = "#ffa43d";
- titleElement.style.textAlign = 'center';
- titleElement.style.marginBottom = '15px';
- overallStatsWrapper.appendChild(titleElement);
- const statsWrapper = document.createElement('div');
- statsWrapper.style.display = 'flex';
- statsWrapper.style.justifyContent = 'space-around';
- statsWrapper.style.marginBottom = '20px';
- const gaugesData = [
- { label: 'Average Score', value: globalStats.totalAverageScore / MAX_SCORE, actualValue: globalStats.totalAverageScore, maxValue: MAX_SCORE },
- { label: 'Accuracy', value: globalStats.overallAccuracy / 100, actualValue: globalStats.overallAccuracy , maxValue : 100},
- { label: 'Win-Rate', value: globalStats.totalWinRate / 100, actualValue: globalStats.totalWinRate, maxValue : 100 }
- ];
- gaugesData.forEach(data => {
- const gaugeWrapper = document.createElement('div');
- gaugeWrapper.style.textAlign = 'center';
- if (typeof ProgressBar === 'undefined' || typeof ProgressBar.Circle === 'undefined') {
- console.error("ProgressBar.js library not found.");
- return;
- }
- const bar = new ProgressBar.Circle(gaugeWrapper, {
- trailColor: 'hsla(0,0%,100%,.6)',
- trailWidth: 15,
- strokeWidth: 15,
- text: {
- value: '',
- style: {
- color: '#FFF',
- position: 'absolute',
- top: '45%',
- left: '50%',
- padding: 0,
- margin: 0,
- transform: 'translate(-50%, -50%)',
- }
- },
- });
- bar.text.style.fontSize = '2rem';
- bar.path.setAttribute('stroke', getColor(data.actualValue, 0, data.maxValue));
- bar.animate(data.value);
- bar.setText(`${data.actualValue}${data.maxValue == 100 ? '%' : ''}`);
- const label = document.createElement('div');
- label.style.marginTop = '10px';
- label.style.color = '#ffa43d';
- label.textContent = data.label;
- gaugeWrapper.appendChild(label);
- statsWrapper.appendChild(gaugeWrapper);
- });
- overallStatsWrapper.appendChild(statsWrapper);
- if (overallStatsWrapper.children.length > 0) {
- container.append(overallStatsWrapper);
- }
- }
- // Geoguessr API interactions
- async function fetchDuels(session, gameIds) {
- console.log(`Try to fetch ${gameIds.length} games`);
- const results = await Promise.all(gameIds.map(async (gameId) => {
- try {
- const response = await session.get(`${API_BASE_URL}/${gameId}`);
- return response.data;
- } catch (error) {
- console.error(`Failed to fetch data for game ID ${gameId}:`, error);
- return null;
- }
- }));
- console.log(`Fetched ${results.filter(Boolean).length} games`);
- return results.filter(Boolean);
- }
- async function getGameIds(session) {
- const gameIds = [];
- let paginationToken = null;
- try {
- while (true) {
- const response = await session.get('https://www.geoguessr.com/api/v4/feed/private', { params: { paginationToken } });
- const data = response.data;
- paginationToken = data.paginationToken;
- if (!playerId && data.entries.length > 0) {
- playerId = data.entries[0].user.id;
- }
- data.entries.forEach(entry => {
- try {
- extractGameIds(entry, gameIds);
- } catch (error) {
- console.error(error);
- }
- });
- if (!paginationToken) break;
- }
- } catch (error) {
- console.error("An error occurred while fetching game IDs:", error);
- }
- return gameIds;
- }
- function extractGameIds(entry, gameIds) {
- const payloadJson = JSON.parse(entry.payload);
- const time = entry.time
- const payloadArray = Array.isArray(payloadJson) ? payloadJson : [payloadJson];
- payloadArray.forEach(payload => {
- // Test if partyId is undefined
- if (payload.gameMode === 'Duels' && payload.partyId === undefined) {
- gameIds.push({ id: payload.gameId, time: time });
- } else if (payload.payload && payload.payload.gameMode === 'Duels' && payload.payload.partyId === undefined) {
- gameIds.push({ id: payload.payload.gameId, time: time });
- }
- });
- }
- // Table rendering logic
- async function renderTable(session, gameIds) {
- try {
- const dataList = await fetchDuels(session, gameIds.map(game => game.id));
- const weeklyStats = calculateWeeklyStats(dataList, gameIds);
- const countryData = processGameData(dataList);
- const globalStats = calculateGlobalStats(countryData);
- const tableData = transformCountryData(countryData);
- const container = document.querySelectorAll("[class^='container_content']")[document.querySelectorAll("[class^='container_content']").length - 1];
- console.log("Global Stats:", globalStats);
- renderGauges(container, globalStats);
- renderHistogram(container, weeklyStats);
- createCountryTable(container, tableData);
- } catch (error) {
- console.error("Error fetching or processing data:", error);
- } finally {
- console.timeEnd('loading_games');
- }
- }
- function calculateWeeklyStats(dataList, gameTimeData) {
- const weeklyStats = {};
- dataList.forEach((data, index) => {
- const gameTime = new Date(gameTimeData[index].time);
- const weekYear = `${gameTime.getFullYear()}-W${Math.ceil((((gameTime - new Date(gameTime.getFullYear(),0,1)) / 86400000) + gameTime.getDay() + 1)/7)}`;
- if (!weeklyStats[weekYear]) {
- weeklyStats[weekYear] = { totalAccuracy: 0, totalRounds: 0 };
- }
- const player = data.teams.flatMap(team => team.players).find(player => player.playerId === playerId);
- if (!player) return;
- data.rounds.forEach(round => {
- const playerGuess = player.guesses.find(g => g.roundNumber === round.roundNumber);
- if (playerGuess) {
- weeklyStats[weekYear].totalAccuracy += (playerGuess.score * 100) / MAX_SCORE;
- weeklyStats[weekYear].totalRounds++;
- }
- });
- });
- return Object.keys(weeklyStats).map(week => ({
- week,
- accuracy: (weeklyStats[week].totalAccuracy / weeklyStats[week].totalRounds).toFixed(2)
- }));
- }
- function processGameData(dataList) {
- const countryData = {};
- dataList.forEach(data => {
- const player = data.teams.flatMap(team => team.players).find(player => player.playerId === playerId);
- if (!player) return;
- data.rounds.forEach(round => aggregateRoundData(countryData, round, player, data.teams));
- });
- return countryData;
- }
- function aggregateRoundData(countryData, round, player, teams) {
- const countryCode = round.panorama.countryCode;
- const playerGuess = player.guesses.find(g => g.roundNumber === round.roundNumber);
- if (playerGuess) {
- if (!countryData[countryCode]) {
- countryData[countryCode] = { totalRounds: 0, totalScore: 0, totalPointDiff: 0, wins: 0, totalAccuracy: 0 };
- }
- let sumPointDiff = 0;
- countryData[countryCode].totalRounds++;
- countryData[countryCode].totalScore += playerGuess.score;
- countryData[countryCode].totalAccuracy += (playerGuess.score * 100) / MAX_SCORE;
- teams.forEach(team => {
- team.players.forEach(opponent => {
- if (opponent.playerId !== playerId) {
- const opponentGuess = opponent.guesses.find(g => g.roundNumber === round.roundNumber);
- if (opponentGuess) {
- let pointDiff = playerGuess.score - opponentGuess.score;
- sumPointDiff += pointDiff;
- if (playerGuess.score > opponentGuess.score) {
- countryData[countryCode].wins++;
- }
- }
- }
- });
- });
- countryData[countryCode].totalPointDiff += sumPointDiff;
- }
- }
- function transformCountryData(countryData) {
- return Object.keys(countryData).map(countryCode => {
- const totalRounds = countryData[countryCode].totalRounds;
- return {
- country: countryCode,
- totalRounds: totalRounds,
- averageScore: (countryData[countryCode].totalScore / totalRounds).toFixed(2),
- winRate: ((countryData[countryCode].wins / totalRounds) * 100).toFixed(2),
- totalPointDiff: countryData[countryCode].totalPointDiff,
- averagePointDiff: (countryData[countryCode].totalPointDiff / totalRounds).toFixed(2),
- countryAccuracy: (countryData[countryCode].totalAccuracy / totalRounds).toFixed(2)
- };
- });
- }
- function createCountryTable(container, tableData) {
- const countryNames = getCountryNames();
- const overallCountryStatsWrapper = document.createElement('div');
- const titleElement = document.createElement('h2');
- titleElement.textContent = "Country Stats";
- titleElement.style.color = "#ffa43d";
- titleElement.style.textAlign = 'center';
- titleElement.style.marginBottom = '15px';
- overallCountryStatsWrapper.appendChild(titleElement);
- const table = document.createElement('table');
- const thead = document.createElement('thead');
- const headers = ['Country', 'Total Rounds', 'Average Score', 'Win Rate (%)', 'Total Point Diff', 'Average Point Diff', 'Country Accuracy (%)'];
- const headerRow = document.createElement('tr');
- headers.forEach((header, index) => {
- const th = document.createElement('th');
- th.textContent = header;
- th.addEventListener('click', () => sortTable(table, index));
- headerRow.appendChild(th);
- });
- thead.appendChild(headerRow);
- table.appendChild(thead);
- const tbody = document.createElement('tbody');
- tableData.forEach(row => {
- const countryNameWithFlag = countryNames[row.country] || row.country;
- const urlFriendlyName = countryNameWithFlag
- .replace(/[\u{1F1E6}-\u{1F1FF}]/gu, '')
- .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
- .trim().toLowerCase().replace(/ /g, '-');
- const countryLink = `<a href="https://www.plonkit.net/${urlFriendlyName}" target="_blank">${countryNameWithFlag}</a>`;
- const tr = document.createElement('tr');
- tr.innerHTML = `
- <td>${countryLink}</td>
- <td>${row.totalRounds}</td>
- <td style="background-color:${getColor(row.averageScore, 0, MAX_SCORE)};">${row.averageScore}</td>
- <td style="background-color:${getColor(row.winRate, 0, 100)};">${row.winRate}</td>
- <td style="background-color:${getColor(row.totalPointDiff, -MAX_SCORE, MAX_SCORE)};">${row.totalPointDiff}</td>
- <td style="background-color:${getColor(row.averagePointDiff, -1000, 1000)};">${row.averagePointDiff}</td>
- <td style="background-color:${getColor(row.countryAccuracy, 0, 100)};">${row.countryAccuracy}</td>
- `;
- tbody.appendChild(tr);
- });
- table.appendChild(tbody);
- overallCountryStatsWrapper.appendChild(table);
- container.appendChild(overallCountryStatsWrapper);
- }
- function sortTable(table, columnIndex) {
- const tbody = table.querySelector('tbody');
- const rows = Array.from(tbody.querySelectorAll('tr'));
- const isAsc = table.getAttribute('data-sort-direction') === 'asc';
- table.setAttribute('data-sort-direction', isAsc ? 'desc' : 'asc');
- rows.sort((a, b) => {
- const aText = a.querySelectorAll('td')[columnIndex].textContent.trim();
- const bText = b.querySelectorAll('td')[columnIndex].textContent.trim();
- return !isNaN(aText) && !isNaN(bText) ?
- (isAsc ? aText - bText : bText - aText)
- : (isAsc ? aText.localeCompare(bText) : bText.localeCompare(aText));
- });
- rows.forEach(row => tbody.appendChild(row));
- }
- function getCountryNames() {
- // Return a mapping object for country codes to country names
- return {
- 'ad': 'Andorra ๐ฆ๐ฉ',
- 'ae': 'United Arab Emirates ๐ฆ๐ช',
- 'af': 'Afghanistan ๐ฆ๐ซ',
- 'ag': 'Antigua and Barbuda ๐ฆ๐ฌ',
- 'ai': 'Anguilla ๐ฆ๐ฎ',
- 'al': 'Albania ๐ฆ๐ฑ',
- 'am': 'Armenia ๐ฆ๐ฒ',
- 'ao': 'Angola ๐ฆ๐ด',
- 'aq': 'Antarctica ๐ฆ๐ถ',
- 'ar': 'Argentina ๐ฆ๐ท',
- 'as': 'American Samoa ๐ฆ๐ธ',
- 'at': 'Austria ๐ฆ๐น',
- 'au': 'Australia ๐ฆ๐บ',
- 'aw': 'Aruba ๐ฆ๐ผ',
- 'ax': 'ร land Islands ๐ฆ๐ฝ',
- 'az': 'Azerbaijan ๐ฆ๐ฟ',
- 'ba': 'Bosnia and Herzegovina ๐ง๐ฆ',
- 'bb': 'Barbados ๐ง๐ง',
- 'bd': 'Bangladesh ๐ง๐ฉ',
- 'be': 'Belgium ๐ง๐ช',
- 'bf': 'Burkina Faso ๐ง๐ซ',
- 'bg': 'Bulgaria ๐ง๐ฌ',
- 'bh': 'Bahrain ๐ง๐ญ',
- 'bi': 'Burundi ๐ง๐ฎ',
- 'bj': 'Benin ๐ง๐ฏ',
- 'bl': 'Saint Barthรฉlemy ๐ง๐ฑ',
- 'bm': 'Bermuda ๐ง๐ฒ',
- 'bn': 'Brunei Darussalam ๐ง๐ณ',
- 'bo': 'Bolivia ๐ง๐ด',
- 'bq': 'Bonaire, Sint Eustatius and Saba ๐ง๐ถ',
- 'br': 'Brazil ๐ง๐ท',
- 'bs': 'Bahamas ๐ง๐ธ',
- 'bt': 'Bhutan ๐ง๐น',
- 'bv': 'Bouvet Island ๐ง๐ป',
- 'bw': 'Botswana ๐ง๐ผ',
- 'by': 'Belarus ๐ง๐พ',
- 'bz': 'Belize ๐ง๐ฟ',
- 'ca': 'Canada ๐จ๐ฆ',
- 'cc': 'Cocos (Keeling) Islands ๐จ๐จ',
- 'cd': 'Congo (Democratic Republic of the) ๐จ๐ฉ',
- 'cf': 'Central African Republic ๐จ๐ซ',
- 'cg': 'Congo ๐จ๐ฌ',
- 'ch': 'Switzerland ๐จ๐ญ',
- 'ci': 'Cรดte d\'Ivoire ๐จ๐ฎ',
- 'ck': 'Cook Islands ๐จ๐ฐ',
- 'cl': 'Chile ๐จ๐ฑ',
- 'cm': 'Cameroon ๐จ๐ฒ',
- 'cn': 'China ๐จ๐ณ',
- 'co': 'Colombia ๐จ๐ด',
- 'cr': 'Costa Rica ๐จ๐ท',
- 'cu': 'Cuba ๐จ๐บ',
- 'cv': 'Cabo Verde ๐จ๐ป',
- 'cw': 'Curaรงao ๐จ๐ผ',
- 'cx': 'Christmas Island ๐จ๐ฝ',
- 'cy': 'Cyprus ๐จ๐พ',
- 'cz': 'Czechia ๐จ๐ฟ',
- 'de': 'Germany ๐ฉ๐ช',
- 'dj': 'Djibouti ๐ฉ๐ฏ',
- 'dk': 'Denmark ๐ฉ๐ฐ',
- 'dm': 'Dominica ๐ฉ๐ฒ',
- 'do': 'Dominican Republic ๐ฉ๐ด',
- 'dz': 'Algeria ๐ฉ๐ฟ',
- 'ec': 'Ecuador ๐ช๐จ',
- 'ee': 'Estonia ๐ช๐ช',
- 'eg': 'Egypt ๐ช๐ฌ',
- 'eh': 'Western Sahara ๐ช๐ญ',
- 'er': 'Eritrea ๐ช๐ท',
- 'es': 'Spain ๐ช๐ธ',
- 'et': 'Ethiopia ๐ช๐น',
- 'fi': 'Finland ๐ซ๐ฎ',
- 'fj': 'Fiji ๐ซ๐ฏ',
- 'fk': 'Falkland Islands (Malvinas) ๐ซ๐ฐ',
- 'fm': 'Micronesia (Federated States of) ๐ซ๐ฒ',
- 'fo': 'Faroe Islands ๐ซ๐ด',
- 'fr': 'France ๐ซ๐ท',
- 'ga': 'Gabon ๐ฌ๐ฆ',
- 'gb': 'United Kingdom ๐ฌ๐ง',
- 'gd': 'Grenada ๐ฌ๐ฉ',
- 'ge': 'Georgia ๐ฌ๐ช',
- 'gf': 'French Guiana ๐ฌ๐ซ',
- 'gg': 'Guernsey ๐ฌ๐ฌ',
- 'gh': 'Ghana ๐ฌ๐ญ',
- 'gi': 'Gibraltar ๐ฌ๐ฎ',
- 'gl': 'Greenland ๐ฌ๐ฑ',
- 'gm': 'Gambia ๐ฌ๐ฒ',
- 'gn': 'Guinea ๐ฌ๐ณ',
- 'gp': 'Guadeloupe ๐ฌ๐ต',
- 'gq': 'Equatorial Guinea ๐ฌ๐ถ',
- 'gr': 'Greece ๐ฌ๐ท',
- 'gs': 'South Georgia and the South Sandwich Islands ๐ฌ๐ธ',
- 'gt': 'Guatemala ๐ฌ๐น',
- 'gu': 'Guam ๐ฌ๐บ',
- 'gw': 'Guinea-Bissau ๐ฌ๐ผ',
- 'gy': 'Guyana ๐ฌ๐พ',
- 'hk': 'Hong Kong ๐ญ๐ฐ',
- 'hm': 'Heard Island and McDonald Islands ๐ญ๐ฒ',
- 'hn': 'Honduras ๐ญ๐ณ',
- 'hr': 'Croatia ๐ญ๐ท',
- 'ht': 'Haiti ๐ญ๐น',
- 'hu': 'Hungary ๐ญ๐บ',
- 'id': 'Indonesia ๐ฎ๐ฉ',
- 'ie': 'Ireland ๐ฎ๐ช',
- 'il': 'Israel ๐ฎ๐ฑ',
- 'im': 'Isle of Man ๐ฎ๐ฒ',
- 'in': 'India ๐ฎ๐ณ',
- 'io': 'British Indian Ocean Territory ๐ฎ๐ด',
- 'iq': 'Iraq ๐ฎ๐ถ',
- 'ir': 'Iran ๐ฎ๐ท',
- 'is': 'Iceland ๐ฎ๐ธ',
- 'it': 'Italy ๐ฎ๐น',
- 'je': 'Jersey ๐ฏ๐ช',
- 'jm': 'Jamaica ๐ฏ๐ฒ',
- 'jo': 'Jordan ๐ฏ๐ด',
- 'jp': 'Japan ๐ฏ๐ต',
- 'ke': 'Kenya ๐ฐ๐ช',
- 'kg': 'Kyrgyzstan ๐ฐ๐ฌ',
- 'kh': 'Cambodia ๐ฐ๐ญ',
- 'ki': 'Kiribati ๐ฐ๐ฎ',
- 'km': 'Comoros ๐ฐ๐ฒ',
- 'kn': 'Saint Kitts and Nevis ๐ฐ๐ณ',
- 'kp': 'North Korea ๐ฐ๐ต',
- 'kr': 'South Korea ๐ฐ๐ท',
- 'kw': 'Kuwait ๐ฐ๐ผ',
- 'ky': 'Cayman Islands ๐ฐ๐พ',
- 'kz': 'Kazakhstan ๐ฐ๐ฟ',
- 'la': 'Laos ๐ฑ๐ฆ',
- 'lb': 'Lebanon ๐ฑ๐ง',
- 'lc': 'Saint Lucia ๐ฑ๐จ',
- 'li': 'Liechtenstein ๐ฑ๐ฎ',
- 'lk': 'Sri Lanka ๐ฑ๐ฐ',
- 'lr': 'Liberia ๐ฑ๐ท',
- 'ls': 'Lesotho ๐ฑ๐ธ',
- 'lt': 'Lithuania ๐ฑ๐น',
- 'lu': 'Luxembourg ๐ฑ๐บ',
- 'lv': 'Latvia ๐ฑ๐ป',
- 'ly': 'Libya ๐ฑ๐พ',
- 'ma': 'Morocco ๐ฒ๐ฆ',
- 'mc': 'Monaco ๐ฒ๐จ',
- 'md': 'Moldova ๐ฒ๐ฉ',
- 'me': 'Montenegro ๐ฒ๐ช',
- 'mf': 'Saint Martin ๐ฒ๐ซ',
- 'mg': 'Madagascar ๐ฒ๐ฌ',
- 'mh': 'Marshall Islands ๐ฒ๐ญ',
- 'mk': 'North Macedonia ๐ฒ๐ฐ',
- 'ml': 'Mali ๐ฒ๐ฑ',
- 'mm': 'Myanmar ๐ฒ๐ฒ',
- 'mn': 'Mongolia ๐ฒ๐ณ',
- 'mo': 'Macao ๐ฒ๐ด',
- 'mp': 'Northern Mariana Islands ๐ฒ๐ต',
- 'mq': 'Martinique ๐ฒ๐ถ',
- 'mr': 'Mauritania ๐ฒ๐ท',
- 'ms': 'Montserrat ๐ฒ๐ธ',
- 'mt': 'Malta ๐ฒ๐น',
- 'mu': 'Mauritius ๐ฒ๐บ',
- 'mv': 'Maldives ๐ฒ๐ป',
- 'mw': 'Malawi ๐ฒ๐ผ',
- 'mx': 'Mexico ๐ฒ๐ฝ',
- 'my': 'Malaysia ๐ฒ๐พ',
- 'mz': 'Mozambique ๐ฒ๐ฟ',
- 'na': 'Namibia ๐ณ๐ฆ',
- 'nc': 'New Caledonia ๐ณ๐จ',
- 'ne': 'Niger ๐ณ๐ช',
- 'nf': 'Norfolk Island ๐ณ๐ซ',
- 'ng': 'Nigeria ๐ณ๐ฌ',
- 'ni': 'Nicaragua ๐ณ๐ฎ',
- 'nl': 'Netherlands ๐ณ๐ฑ',
- 'no': 'Norway ๐ณ๐ด',
- 'np': 'Nepal ๐ณ๐ต',
- 'nr': 'Nauru ๐ณ๐ท',
- 'nu': 'Niue ๐ณ๐บ',
- 'nz': 'New Zealand ๐ณ๐ฟ',
- 'om': 'Oman ๐ด๐ฒ',
- 'pa': 'Panama ๐ต๐ฆ',
- 'pe': 'Peru ๐ต๐ช',
- 'pf': 'French Polynesia ๐ต๐ซ',
- 'pg': 'Papua New Guinea ๐ต๐ฌ',
- 'ph': 'Philippines ๐ต๐ญ',
- 'pk': 'Pakistan ๐ต๐ฐ',
- 'pl': 'Poland ๐ต๐ฑ',
- 'pm': 'Saint Pierre and Miquelon ๐ต๐ฒ',
- 'pn': 'Pitcairn ๐ต๐ณ',
- 'pr': 'Puerto Rico ๐ต๐ท',
- 'ps': 'Palestine ๐ต๐ธ',
- 'pt': 'Portugal ๐ต๐น',
- 'pw': 'Palau ๐ต๐ผ',
- 'py': 'Paraguay ๐ต๐พ',
- 'qa': 'Qatar ๐ถ๐ฆ',
- 're': 'Rรฉunion ๐ท๐ช',
- 'ro': 'Romania ๐ท๐ด',
- 'rs': 'Serbia ๐ท๐ธ',
- 'ru': 'Russia ๐ท๐บ',
- 'rw': 'Rwanda ๐ท๐ผ',
- 'sa': 'Saudi Arabia ๐ธ๐ฆ',
- 'sb': 'Solomon Islands ๐ธ๐ง',
- 'sc': 'Seychelles ๐ธ๐จ',
- 'sd': 'Sudan ๐ธ๐ฉ',
- 'se': 'Sweden ๐ธ๐ช',
- 'sg': 'Singapore ๐ธ๐ฌ',
- 'sh': 'Saint Helena ๐ธ๐ญ',
- 'si': 'Slovenia ๐ธ๐ฎ',
- 'sj': 'Svalbard and Jan Mayen ๐ธ๐ฏ',
- 'sk': 'Slovakia ๐ธ๐ฐ',
- 'sl': 'Sierra Leone ๐ธ๐ฑ',
- 'sm': 'San Marino ๐ธ๐ฒ',
- 'sn': 'Senegal ๐ธ๐ณ',
- 'so': 'Somalia ๐ธ๐ด',
- 'sr': 'Suriname ๐ธ๐ท',
- 'ss': 'South Sudan ๐ธ๐ธ',
- 'st': 'Sao Tome and Principe ๐ธ๐น',
- 'sv': 'El Salvador ๐ธ๐ป',
- 'sx': 'Sint Maarten ๐ธ๐ฝ',
- 'sy': 'Syria ๐ธ๐พ',
- 'sz': 'Eswatini ๐ธ๐ฟ',
- 'tc': 'Turks and Caicos Islands ๐น๐จ',
- 'td': 'Chad ๐น๐ฉ',
- 'tf': 'French Southern Territories ๐น๐ซ',
- 'tg': 'Togo ๐น๐ฌ',
- 'th': 'Thailand ๐น๐ญ',
- 'tj': 'Tajikistan ๐น๐ฏ',
- 'tk': 'Tokelau ๐น๐ฐ',
- 'tl': 'Timor-Leste ๐น๐ฑ',
- 'tm': 'Turkmenistan ๐น๐ฒ',
- 'tn': 'Tunisia ๐น๐ณ',
- 'to': 'Tonga ๐น๐ด',
- 'tr': 'Turkey ๐น๐ท',
- 'tt': 'Trinidad and Tobago ๐น๐น',
- 'tv': 'Tuvalu ๐น๐ป',
- 'tw': 'Taiwan ๐น๐ผ',
- 'tz': 'Tanzania ๐น๐ฟ',
- 'ua': 'Ukraine ๐บ๐ฆ',
- 'ug': 'Uganda ๐บ๐ฌ',
- 'um': 'United States Minor Outlying Islands ๐บ๐ฒ',
- 'us': 'United States ๐บ๐ธ',
- 'uy': 'Uruguay ๐บ๐พ',
- 'uz': 'Uzbekistan ๐บ๐ฟ',
- 'va': 'Vatican City ๐ป๐ฆ',
- 'vc': 'Saint Vincent and the Grenadines ๐ป๐จ',
- 've': 'Venezuela ๐ป๐ช',
- 'vg': 'British Virgin Islands ๐ป๐ฌ',
- 'vi': 'U.S. Virgin Islands ๐ป๐ฎ',
- 'vn': 'Vietnam ๐ป๐ณ',
- 'vu': 'Vanuatu ๐ป๐บ',
- 'wf': 'Wallis and Futuna ๐ผ๐ซ',
- 'ws': 'Samoa ๐ผ๐ธ',
- 'xk': 'Kosovo ๐ฝ๐ฐ',
- 'ye': 'Yemen ๐พ๐ช',
- 'yt': 'Mayotte ๐พ๐น',
- 'za': 'South Africa ๐ฟ๐ฆ',
- 'zm': 'Zambia ๐ฟ๐ฒ',
- 'zw': 'Zimbabwe ๐ฟ๐ผ',
- };
- }
- // Function to render a histogram showing weekly accuracy
- function renderHistogram(container, weeklyStats) {
- // reverse the array to show the most recent week last
- weeklyStats.reverse();
- const histogramWrapper = document.createElement('div');
- const titleElement = document.createElement('h2');
- titleElement.textContent = "Weekly Accuracy";
- titleElement.style.color = "#ffa43d";
- titleElement.style.textAlign = 'center';
- titleElement.style.marginBottom = '15px';
- histogramWrapper.appendChild(titleElement);
- const chartCanvas = document.createElement('canvas');
- histogramWrapper.appendChild(chartCanvas);
- histogramWrapper.classList.add('chart-container');
- histogramWrapper.style.marginBottom = '60px';
- container.appendChild(histogramWrapper);
- const ctx = chartCanvas.getContext('2d');
- const chartData = {
- labels: weeklyStats.map(stat => stat.week),
- datasets: [{
- label: 'Accuracy (%)',
- data: weeklyStats.map(stat => stat.accuracy),
- backgroundColor: 'rgba(41, 205, 181, 0.5)',
- borderColor: '#29cdb5',
- borderWidth: 1,
- fill: true,
- tension: 0.1
- }]
- };
- new Chart(ctx, {
- type: 'line',
- data: chartData,
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- position: 'top',
- labels: {
- color: '#ffffff',
- }
- },
- title: {
- display: true,
- text: 'Weekly Accuracy',
- color: '#ffa43d'
- }
- },
- scales: {
- x: {
- title: {
- display: true,
- text: 'Week',
- color: '#ffffff'
- },
- ticks: {
- color: '#ffffff'
- }
- },
- y: {
- beginAtZero: true,
- max: 100,
- title: {
- display: true,
- text: 'Accuracy (%)',
- color: '#ffffff'
- },
- ticks: {
- color: '#ffffff'
- }
- }
- }
- }
- });
- }
- // Initialization
- function init() {
- console.time('loading_games');
- addCustomStyles();
- const session = axios.create({
- withCredentials: true
- });
- // gameIds.splice(0,100) to get only your last 100 games. Change the number to get more or less games
- getGameIds(session).then(gameIds => {
- const recentGameIds = gameIds.splice(0, 100);
- renderTable(session, recentGameIds);
- initCsvDownloadButton(session, recentGameIds);
- });
- }
- $(document).ready(init);
- })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement