Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name GeoGuessr Country Statistics Tracker Enhanced
- // @version 1.1.0
- // @description Tracks statistics on your country guessing accuracy in GeoGuessr with enhanced UI and point tracking
- // @match https://www.geoguessr.com/*
- // @author gruen132U
- // @license MIT
- // @require https://greasyfork.org/scripts/460322-geoguessr-styles-scan/code/Geoguessr%20Styles%20Scan.js?version=1151654
- // @icon https://www.google.com/s2/favicons?sz=64&domain=geoguessr.com
- // @namespace https://greasyfork.org/users/gruen132U
- // @grant none
- // ==/UserScript==
- (function() {
- 'use strict';
- // Main configuration
- const AUTOMATIC = true;
- const API_Key = 'INSERT_BIGDATACLOUD_API_KEY_HERE'; // Optional: add your API key for better results
- const ERROR_RESP = -1000000;
- const MAX_POINTS = 5000; // Maximum points per round in GeoGuessr
- let lastGuess = { lat: 91, lng: 0 };
- // GeoGuessr UI colors for styling
- const GEOGUESSR_COLORS = {
- primary: '#4ca852', // Green
- secondary: '#4d508a', // Purple
- dark: '#1a1a1a',
- darkGray: '#2d2d2d',
- mediumGray: '#444444',
- lightGray: '#777777',
- lightText: '#ffffff',
- lightTextSecondary: '#b3b3b3',
- danger: '#d63030' // Red
- };
- // Country code mapping
- const CountryDict = {
- AF: 'AF', AX: 'FI', AL: 'AL', DZ: 'DZ', AS: 'US', AD: 'AD', AO: 'AO', AI: 'GB', AQ: 'AQ', AG: 'AG',
- AR: 'AR', AM: 'AM', AW: 'NL', AU: 'AU', AT: 'AT', AZ: 'AZ', BS: 'BS', BH: 'BH', BD: 'BD', BB: 'BB',
- BY: 'BY', BE: 'BE', BZ: 'BZ', BJ: 'BJ', BM: 'GB', BT: 'BT', BO: 'BO', BQ: 'NL', BA: 'BA', BW: 'BW',
- BV: 'NO', BR: 'BR', IO: 'GB', BN: 'BN', BG: 'BG', BF: 'BF', BI: 'BI', KH: 'KH', CM: 'CM', CA: 'CA',
- CV: 'CV', KY: 'UK', CF: 'CF', TD: 'TD', CL: 'CL', CN: 'CN', CX: 'AU', CC: 'AU', CO: 'CO', KM: 'KM',
- CG: 'CG', CD: 'CD', CK: 'NZ', CR: 'CR', CI: 'CI', HR: 'HR', CU: 'CU', CW: 'NL', CY: 'CY', CZ: 'CZ',
- DK: 'DK', DJ: 'DJ', DM: 'DM', DO: 'DO', EC: 'EC', EG: 'EG', SV: 'SV', GQ: 'GQ', ER: 'ER', EE: 'EE',
- ET: 'ET', FK: 'GB', FO: 'DK', FJ: 'FJ', FI: 'FI', FR: 'FR', GF: 'FR', PF: 'FR', TF: 'FR', GA: 'GA',
- GM: 'GM', GE: 'GE', DE: 'DE', GH: 'GH', GI: 'UK', GR: 'GR', GL: 'DK', GD: 'GD', GP: 'FR', GU: 'US',
- GT: 'GT', GG: 'GB', GN: 'GN', GW: 'GW', GY: 'GY', HT: 'HT', HM: 'AU', VA: 'VA', HN: 'HN', HK: 'CN',
- HU: 'HU', IS: 'IS', IN: 'IN', ID: 'ID', IR: 'IR', IQ: 'IQ', IE: 'IE', IM: 'GB', IL: 'IL', IT: 'IT',
- JM: 'JM', JP: 'JP', JE: 'GB', JO: 'JO', KZ: 'KZ', KE: 'KE', KI: 'KI', KR: 'KR', KW: 'KW', KG: 'KG',
- LA: 'LA', LV: 'LV', LB: 'LB', LS: 'LS', LR: 'LR', LY: 'LY', LI: 'LI', LT: 'LT', LU: 'LU', MO: 'CN',
- MK: 'MK', MG: 'MG', MW: 'MW', MY: 'MY', MV: 'MV', ML: 'ML', MT: 'MT', MH: 'MH', MQ: 'FR', MR: 'MR',
- MU: 'MU', YT: 'FR', MX: 'MX', FM: 'FM', MD: 'MD', MC: 'MC', MN: 'MN', ME: 'ME', MS: 'GB', MA: 'MA',
- MZ: 'MZ', MM: 'MM', NA: 'NA', NR: 'NR', NP: 'NP', NL: 'NL', AN: 'NL', NC: 'FR', NZ: 'NZ', NI: 'NI',
- NE: 'NE', NG: 'NG', NU: 'NZ', NF: 'AU', MP: 'US', NO: 'NO', OM: 'OM', PK: 'PK', PW: 'PW', PS: 'IL',
- PA: 'PA', PG: 'PG', PY: 'PY', PE: 'PE', PH: 'PH', PN: 'GB', PL: 'PL', PT: 'PT', PR: 'US', QA: 'QA',
- RE: 'FR', RO: 'RO', RU: 'RU', RW: 'RW', BL: 'FR', SH: 'GB', KN: 'KN', LC: 'LC', MF: 'FR', PM: 'FR',
- VC: 'VC', WS: 'WS', SM: 'SM', ST: 'ST', SA: 'SA', SN: 'SN', RS: 'RS', SC: 'SC', SL: 'SL', SG: 'SG',
- SX: 'NL', SK: 'SK', SI: 'SI', SB: 'SB', SO: 'SO', ZA: 'ZA', GS: 'GB', ES: 'ES', LK: 'LK', SD: 'SD',
- SR: 'SR', SJ: 'NO', SZ: 'SZ', SE: 'SE', CH: 'CH', SY: 'SY', TW: 'TW', TJ: 'TJ', TZ: 'TZ', TH: 'TH',
- TL: 'TL', TG: 'TG', TK: 'NZ', TO: 'TO', TT: 'TT', TN: 'TN', TR: 'TR', TM: 'TM', TC: 'GB', TV: 'TV',
- UG: 'UG', UA: 'UA', AE: 'AE', GB: 'GB', US: 'US', UM: 'US', UY: 'UY', UZ: 'UZ', VU: 'VU', VE: 'VE',
- VN: 'VN', VG: 'GB', VI: 'US', WF: 'FR', EH: 'MA', YE: 'YE', ZM: 'ZM', ZW: 'ZW', UK: 'GB'
- };
- // Country code to country name mapping
- const countryCodeToName = {
- 'AF': 'Afghanistan', 'AL': 'Albania', 'DZ': 'Algeria', 'AD': 'Andorra', 'AO': 'Angola',
- 'AG': 'Antigua and Barbuda', 'AR': 'Argentina', 'AM': 'Armenia', 'AU': 'Australia', 'AT': 'Austria',
- 'AZ': 'Azerbaijan', 'BS': 'Bahamas', 'BH': 'Bahrain', 'BD': 'Bangladesh', 'BB': 'Barbados',
- 'BY': 'Belarus', 'BE': 'Belgium', 'BZ': 'Belize', 'BJ': 'Benin', 'BT': 'Bhutan',
- 'BO': 'Bolivia', 'BA': 'Bosnia and Herzegovina', 'BW': 'Botswana', 'BR': 'Brazil', 'BN': 'Brunei',
- 'BG': 'Bulgaria', 'BF': 'Burkina Faso', 'BI': 'Burundi', 'KH': 'Cambodia', 'CM': 'Cameroon',
- 'CA': 'Canada', 'CV': 'Cape Verde', 'CF': 'Central African Republic', 'TD': 'Chad', 'CL': 'Chile',
- 'CN': 'China', 'CO': 'Colombia', 'KM': 'Comoros', 'CG': 'Congo', 'CD': 'DR Congo',
- 'CR': 'Costa Rica', 'CI': 'Ivory Coast', 'HR': 'Croatia', 'CU': 'Cuba', 'CY': 'Cyprus',
- 'CZ': 'Czech Republic', 'DK': 'Denmark', 'DJ': 'Djibouti', 'DM': 'Dominica', 'DO': 'Dominican Republic',
- 'EC': 'Ecuador', 'EG': 'Egypt', 'SV': 'El Salvador', 'GQ': 'Equatorial Guinea', 'ER': 'Eritrea',
- 'EE': 'Estonia', 'ET': 'Ethiopia', 'FJ': 'Fiji', 'FI': 'Finland', 'FR': 'France',
- 'GA': 'Gabon', 'GM': 'Gambia', 'GE': 'Georgia', 'DE': 'Germany', 'GH': 'Ghana',
- 'GR': 'Greece', 'GD': 'Grenada', 'GT': 'Guatemala', 'GN': 'Guinea', 'GW': 'Guinea-Bissau',
- 'GY': 'Guyana', 'HT': 'Haiti', 'VA': 'Vatican City', 'HN': 'Honduras', 'HU': 'Hungary',
- 'IS': 'Iceland', 'IN': 'India', 'ID': 'Indonesia', 'IR': 'Iran', 'IQ': 'Iraq',
- 'IE': 'Ireland', 'IL': 'Israel', 'IT': 'Italy', 'JM': 'Jamaica', 'JP': 'Japan',
- 'JO': 'Jordan', 'KZ': 'Kazakhstan', 'KE': 'Kenya', 'KI': 'Kiribati', 'KR': 'South Korea',
- 'KW': 'Kuwait', 'KG': 'Kyrgyzstan', 'LA': 'Laos', 'LV': 'Latvia', 'LB': 'Lebanon',
- 'LS': 'Lesotho', 'LR': 'Liberia', 'LY': 'Libya', 'LI': 'Liechtenstein', 'LT': 'Lithuania',
- 'LU': 'Luxembourg', 'MK': 'North Macedonia', 'MG': 'Madagascar', 'MW': 'Malawi', 'MY': 'Malaysia',
- 'MV': 'Maldives', 'ML': 'Mali', 'MT': 'Malta', 'MH': 'Marshall Islands', 'MR': 'Mauritania',
- 'MU': 'Mauritius', 'MX': 'Mexico', 'FM': 'Micronesia', 'MD': 'Moldova', 'MC': 'Monaco',
- 'MN': 'Mongolia', 'ME': 'Montenegro', 'MA': 'Morocco', 'MZ': 'Mozambique', 'MM': 'Myanmar',
- 'NA': 'Namibia', 'NR': 'Nauru', 'NP': 'Nepal', 'NL': 'Netherlands', 'NZ': 'New Zealand',
- 'NI': 'Nicaragua', 'NE': 'Niger', 'NG': 'Nigeria', 'NO': 'Norway', 'OM': 'Oman',
- 'PK': 'Pakistan', 'PW': 'Palau', 'PS': 'Palestine', 'PA': 'Panama', 'PG': 'Papua New Guinea',
- 'PY': 'Paraguay', 'PE': 'Peru', 'PH': 'Philippines', 'PL': 'Poland', 'PT': 'Portugal',
- 'QA': 'Qatar', 'RO': 'Romania', 'RU': 'Russia', 'RW': 'Rwanda', 'KN': 'Saint Kitts and Nevis',
- 'LC': 'Saint Lucia', 'VC': 'Saint Vincent and the Grenadines', 'WS': 'Samoa', 'SM': 'San Marino',
- 'ST': 'Sao Tome and Principe', 'SA': 'Saudi Arabia', 'SN': 'Senegal', 'RS': 'Serbia',
- 'SC': 'Seychelles', 'SL': 'Sierra Leone', 'SG': 'Singapore', 'SK': 'Slovakia', 'SI': 'Slovenia',
- 'SB': 'Solomon Islands', 'SO': 'Somalia', 'ZA': 'South Africa', 'ES': 'Spain', 'LK': 'Sri Lanka',
- 'SD': 'Sudan', 'SR': 'Suriname', 'SZ': 'Eswatini', 'SE': 'Sweden', 'CH': 'Switzerland',
- 'SY': 'Syria', 'TW': 'Taiwan', 'TJ': 'Tajikistan', 'TZ': 'Tanzania', 'TH': 'Thailand',
- 'TL': 'Timor-Leste', 'TG': 'Togo', 'TO': 'Tonga', 'TT': 'Trinidad and Tobago', 'TN': 'Tunisia',
- 'TR': 'Turkey', 'TM': 'Turkmenistan', 'TV': 'Tuvalu', 'UG': 'Uganda', 'UA': 'Ukraine',
- 'AE': 'United Arab Emirates', 'GB': 'United Kingdom', 'US': 'United States', 'UY': 'Uruguay',
- 'UZ': 'Uzbekistan', 'VU': 'Vanuatu', 'VE': 'Venezuela', 'VN': 'Vietnam', 'YE': 'Yemen',
- 'ZM': 'Zambia', 'ZW': 'Zimbabwe', 'AQ': 'Antarctica', 'UK': 'United Kingdom'
- };
- // Stats storage
- let countryStats = {
- wrongGuesses: {}, // format: { "actualCountry": count }
- mistakenIdentities: {}, // format: { "actualCountry": { "guessedAs": count } }
- pointsLost: {} // format: { "country": { total: number, count: number } }
- };
- // Load stats from localStorage
- function loadStats() {
- const savedStats = localStorage.getItem('geoguessrCountryStats');
- if (savedStats) {
- try {
- const parsedStats = JSON.parse(savedStats);
- countryStats = {
- wrongGuesses: parsedStats.wrongGuesses || {},
- mistakenIdentities: parsedStats.mistakenIdentities || {},
- pointsLost: parsedStats.pointsLost || {}
- };
- } catch (e) {
- console.error("Error loading stats:", e);
- countryStats = { wrongGuesses: {}, mistakenIdentities: {}, pointsLost: {} };
- }
- }
- }
- // Save stats to localStorage
- function saveStats() {
- localStorage.setItem('geoguessrCountryStats', JSON.stringify(countryStats));
- }
- // Initialize by loading stats
- loadStats();
- // Check if we're in a game
- function checkGameMode() {
- const isGame = location.pathname.includes("/game/") || location.pathname.includes("/challenge/");
- const isMultiplayer = location.pathname.includes("/battle-royale/") ||
- location.pathname.includes("/duels/") ||
- location.pathname.includes("/party/");
- return isGame && !isMultiplayer;
- }
- // Get country code from coordinates
- async function getCountryCode(coords) {
- if (coords[0] <= -85.05) return 'AQ';
- if (API_Key.toLowerCase().match("^(bdc_)?[a-f0-9]{32}$") != null) {
- const api = "https://api.bigdatacloud.net/data/reverse-geocode?latitude="+coords.lat+"&longitude="+coords.lng+"&localityLanguage=en&key="+API_Key;
- return await fetch(api)
- .then(res => (res.status !== 200) ? ERROR_RESP : res.json())
- .then(out => (out === ERROR_RESP) ? ERROR_RESP : CountryDict[out.countryCode]);
- } else {
- const api = `https://nominatim.openstreetmap.org/reverse.php?lat=${coords.lat}&lon=${coords.lng}&zoom=21&format=jsonv2&accept-language=en`;
- return await fetch(api)
- .then(res => (res.status !== 200) ? ERROR_RESP : res.json())
- .then(out => (out === ERROR_RESP) ? ERROR_RESP : CountryDict[out?.address?.country_code?.toUpperCase()]);
- }
- }
- // Record a wrong guess
- function recordWrongGuess(actualCountry, guessedCountry) {
- // Update wrong guesses count
- if (!countryStats.wrongGuesses[actualCountry]) {
- countryStats.wrongGuesses[actualCountry] = 0;
- }
- countryStats.wrongGuesses[actualCountry]++;
- // Update mistaken identity mapping
- if (!countryStats.mistakenIdentities[actualCountry]) {
- countryStats.mistakenIdentities[actualCountry] = {};
- }
- if (!countryStats.mistakenIdentities[actualCountry][guessedCountry]) {
- countryStats.mistakenIdentities[actualCountry][guessedCountry] = 0;
- }
- countryStats.mistakenIdentities[actualCountry][guessedCountry]++;
- // Save updated stats
- saveStats();
- }
- // Record points lost for a country (when country is correct but region is wrong)
- function recordPointsLost(country, points) {
- if (!countryStats.pointsLost[country]) {
- countryStats.pointsLost[country] = { total: 0, count: 0 };
- }
- const pointsLost = MAX_POINTS - points;
- countryStats.pointsLost[country].total += pointsLost;
- countryStats.pointsLost[country].count += 1;
- // Save updated stats
- saveStats();
- }
- // Calculate distance between two coordinates (haversine formula)
- function calculateDistance(lat1, lon1, lat2, lon2) {
- const R = 6371; // Earth's radius in km
- const dLat = (lat2 - lat1) * Math.PI / 180;
- const dLon = (lon2 - lon1) * Math.PI / 180;
- const a =
- Math.sin(dLat/2) * Math.sin(dLat/2) +
- Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
- Math.sin(dLon/2) * Math.sin(dLon/2);
- const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
- return R * c; // Distance in km
- }
- // Check for wrong guesses and record stats
- function check() {
- const gameTag = location.href.substring(location.href.lastIndexOf('/') + 1);
- let apiUrl = "https://www.geoguessr.com/api/v3/games/" + gameTag;
- if (location.pathname.includes("/challenge/")) {
- apiUrl = "https://www.geoguessr.com/api/v3/challenges/" + gameTag + "/game";
- }
- fetch(apiUrl)
- .then(res => res.json())
- .then((out) => {
- const guessCounter = out.player.guesses.length;
- const round = out.rounds[guessCounter-1];
- const guess = out.player.guesses[guessCounter-1];
- // Skip if this is the same guess as before
- if (guess.lat == lastGuess.lat && guess.lng == lastGuess.lng) return;
- lastGuess = guess;
- Promise.all([getCountryCode(guess), getCountryCode(round)]).then(codes => {
- if (codes[0] == ERROR_RESP || codes[1] == ERROR_RESP) {
- console.log("Error determining countries");
- } else if (codes[0] != codes[1]) {
- // Wrong country!
- recordWrongGuess(codes[1], codes[0]);
- } else {
- // Correct country but might have lost points due to distance
- const distance = calculateDistance(guess.lat, guess.lng, round.lat, round.lng);
- const points = guess.roundScoreInPoints || 0;
- // If they got less than max points, record the points lost
- if (points < MAX_POINTS) {
- recordPointsLost(codes[0], points);
- }
- }
- updateStatsDisplay();
- });
- }).catch(err => { console.error("Error fetching game data:", err); });
- }
- // Check function called when results are shown
- function doCheck() {
- if (!document.querySelector('div[class*="result-layout_root__"]')) {
- sessionStorage.setItem("Checked", 0);
- } else if ((sessionStorage.getItem("Checked") || 0) == 0) {
- check();
- sessionStorage.setItem("Checked", 1);
- }
- }
- // Create stats panel UI
- function createStatsPanel() {
- const panel = document.createElement('div');
- panel.id = 'country-stats-panel';
- panel.style = `
- position: fixed;
- bottom: 20px;
- right: -350px;
- width: 320px;
- max-height: 500px;
- background-color: ${GEOGUESSR_COLORS.dark};
- color: ${GEOGUESSR_COLORS.lightText};
- border-radius: 8px;
- padding: 15px;
- transition: right 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
- z-index: 9999;
- overflow-y: auto;
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
- font-family: 'Roboto', 'Open Sans', Arial, sans-serif;
- border: 2px solid ${GEOGUESSR_COLORS.darkGray};
- `;
- panel.innerHTML = `
- <div style="display: flex; justify-content: space-between; margin-bottom: 15px; align-items: center;">
- <div style="display: flex; align-items: center;">
- <img src="https://www.geoguessr.com/favicon-32x32.png" style="width: 20px; height: 20px; margin-right: 8px;">
- <h2 style="margin: 0; color: ${GEOGUESSR_COLORS.lightText}; font-size: 18px; font-weight: 500;">Country Statistics</h2>
- </div>
- <button id="close-stats-btn" style="background: none; border: none; color: ${GEOGUESSR_COLORS.lightText}; cursor: pointer; font-size: 18px; padding: 0; width: 24px; height: 24px; display: flex; justify-content: center; align-items: center;">Γ</button>
- </div>
- <div id="stats-content" style="margin-bottom: 10px; max-height: 400px; overflow-y: auto; padding-right: 5px;">
- <p>Loading stats...</p>
- </div>
- <div style="margin-top: 15px; display: flex; justify-content: space-between; align-items: center; border-top: 1px solid ${GEOGUESSR_COLORS.darkGray}; padding-top: 10px;">
- <button id="clear-stats-btn" style="background: ${GEOGUESSR_COLORS.danger}; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 13px; transition: all 0.2s ease;">Reset</button>
- <div id="stats-mode-switch" style="display: flex; align-items: center;">
- <span style="margin-right: 8px; font-size: 14px; color: ${GEOGUESSR_COLORS.lightTextSecondary};">View: </span>
- <select id="stats-view-mode" style="background: ${GEOGUESSR_COLORS.mediumGray}; color: white; border: none; padding: 6px 10px; border-radius: 4px; font-size: 13px; outline: none; cursor: pointer;">
- <option value="missed">Most Missed</option>
- <option value="confused">Confused With</option>
- <option value="points">Most Missed Points</option>
- </select>
- </div>
- </div>
- `;
- document.body.appendChild(panel);
- // Make stats content scrollable with custom styling
- const statsContent = document.getElementById('stats-content');
- statsContent.style.scrollbarWidth = 'thin';
- statsContent.style.scrollbarColor = `${GEOGUESSR_COLORS.lightGray} ${GEOGUESSR_COLORS.dark}`;
- // Add toggle button
- const toggleButton = document.createElement('div');
- toggleButton.id = 'toggle-stats-button';
- toggleButton.style = `
- position: fixed;
- bottom: 20px;
- right: 20px;
- background-color: ${GEOGUESSR_COLORS.primary};
- color: white;
- padding: 10px 15px;
- border-radius: 50px;
- cursor: pointer;
- z-index: 9998;
- font-weight: 500;
- font-size: 14px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
- transition: all 0.2s ease;
- display: flex;
- align-items: center;
- justify-content: center;
- `;
- toggleButton.innerHTML = 'π Stats';
- toggleButton.addEventListener('mouseover', () => {
- toggleButton.style.backgroundColor = '#3e8a43'; // Darken on hover
- toggleButton.style.transform = 'scale(1.05)';
- });
- toggleButton.addEventListener('mouseout', () => {
- toggleButton.style.backgroundColor = GEOGUESSR_COLORS.primary;
- toggleButton.style.transform = 'scale(1)';
- });
- document.body.appendChild(toggleButton);
- // Add event listeners
- document.getElementById('toggle-stats-button').addEventListener('click', toggleStatsPanel);
- document.getElementById('close-stats-btn').addEventListener('click', closeStatsPanel);
- document.getElementById('clear-stats-btn').addEventListener('click', clearStats);
- document.getElementById('stats-view-mode').addEventListener('change', updateStatsDisplay);
- // Style the clear button with hover effects
- const clearBtn = document.getElementById('clear-stats-btn');
- clearBtn.addEventListener('mouseover', () => {
- clearBtn.style.backgroundColor = '#b52828'; // Darken red on hover
- });
- clearBtn.addEventListener('mouseout', () => {
- clearBtn.style.backgroundColor = GEOGUESSR_COLORS.danger;
- });
- updateStatsDisplay();
- }
- // Toggle stats panel visibility
- function toggleStatsPanel() {
- const panel = document.getElementById('country-stats-panel');
- if (panel.style.right === '20px') {
- panel.style.right = '-350px';
- } else {
- panel.style.right = '20px';
- }
- }
- // Close stats panel
- function closeStatsPanel() {
- document.getElementById('country-stats-panel').style.right = '-350px';
- }
- // Clear all statistics
- function clearStats() {
- if (confirm('Are you sure you want to reset all country statistics?')) {
- countryStats = { wrongGuesses: {}, mistakenIdentities: {}, pointsLost: {} };
- saveStats();
- updateStatsDisplay();
- }
- }
- // Update the stats display
- function updateStatsDisplay() {
- const statsContent = document.getElementById('stats-content');
- if (!statsContent) return;
- const viewMode = document.getElementById('stats-view-mode').value;
- let content = '';
- // Shared table styles
- const tableStyle = `
- width: 100%;
- border-collapse: collapse;
- border-radius: 4px;
- overflow: hidden;
- margin-bottom: 10px;
- font-size: 14px;
- `;
- const headerStyle = `
- background: ${GEOGUESSR_COLORS.darkGray};
- text-align: left;
- padding: 8px 10px;
- font-weight: 500;
- color: ${GEOGUESSR_COLORS.lightText};
- `;
- const cellStyle = `
- padding: 8px 10px;
- border-bottom: 1px solid ${GEOGUESSR_COLORS.darkGray};
- `;
- const rowHoverJS = `
- const rows = document.querySelectorAll('tr.data-row');
- rows.forEach(row => {
- row.addEventListener('mouseover', () => {
- row.style.backgroundColor = '${GEOGUESSR_COLORS.mediumGray}';
- });
- row.addEventListener('mouseout', () => {
- row.style.backgroundColor = 'transparent';
- });
- });
- `;
- if (viewMode === 'missed') {
- // Sort countries by most missed
- const sortedCountries = Object.entries(countryStats.wrongGuesses)
- .sort((a, b) => b[1] - a[1]);
- if (sortedCountries.length === 0) {
- content = `<div style="text-align: center; padding: 20px; color: ${GEOGUESSR_COLORS.lightTextSecondary};">
- <div style="font-size: 24px; margin-bottom: 10px;">π</div>
- <p>No wrong guesses recorded yet.</p>
- <p style="font-size: 13px;">Play some games to collect statistics!</p>
- </div>`;
- } else {
- content = `<h3 style="margin-top: 0; font-weight: 500; color: ${GEOGUESSR_COLORS.primary};">Countries You Miss Most Often</h3>`;
- content += `<table style="${tableStyle}">`;
- content += `<tr>
- <th style="${headerStyle} width: 60%;">Country</th>
- <th style="${headerStyle} width: 40%; text-align: right;">Missed</th>
- </tr>`;
- sortedCountries.forEach(([code, count], index) => {
- const countryName = countryCodeToName[code] || code;
- const opacity = Math.max(1 - (index * 0.05), 0.5); // Fade out for lower entries
- content += `<tr class="data-row">
- <td style="${cellStyle} opacity: ${opacity};">${countryName}</td>
- <td style="${cellStyle} text-align: right; opacity: ${opacity};">${count}</td>
- </tr>`;
- });
- content += '</table>';
- }
- } else if (viewMode === 'confused') {
- // Confused with mode
- content = `<h3 style="margin-top: 0; font-weight: 500; color: ${GEOGUESSR_COLORS.primary};">Country Confusion Analysis</h3>`;
- content += `<p style="font-size: 14px; color: ${GEOGUESSR_COLORS.lightTextSecondary};">Select a country to see what you confuse it with:</p>`;
- content += `<select id="country-select" style="
- width: 100%;
- margin-bottom: 15px;
- padding: 8px 10px;
- background: ${GEOGUESSR_COLORS.mediumGray};
- color: ${GEOGUESSR_COLORS.lightText};
- border: none;
- border-radius: 4px;
- outline: none;
- cursor: pointer;
- font-size: 14px;
- ">`;
- content += '<option value="">-- Select a country --</option>';
- const countries = Object.keys(countryStats.mistakenIdentities).sort((a, b) => {
- const nameA = countryCodeToName[a] || a;
- const nameB = countryCodeToName[b] || b;
- return nameA.localeCompare(nameB);
- });
- if (countries.length === 0) {
- content = `<div style="text-align: center; padding: 20px; color: ${GEOGUESSR_COLORS.lightTextSecondary};">
- <div style="font-size: 24px; margin-bottom: 10px;">πΊοΈ</div>
- <p>No confusion data available yet.</p>
- <p style="font-size: 13px;">Play more games to see which countries you confuse!</p>
- </div>`;
- } else {
- countries.forEach(code => {
- const countryName = countryCodeToName[code] || code;
- content += `<option value="${code}">${countryName}</option>`;
- });
- content += '</select>';
- content += '<div id="confusion-details"></div>';
- }
- } else if (viewMode === 'points') {
- // Most missed points mode (correct country but wrong region)
- const pointsData = [];
- // Calculate average points lost per country
- Object.entries(countryStats.pointsLost).forEach(([country, data]) => {
- const avgPointsLost = Math.round(data.total / data.count);
- pointsData.push({ country, avgPointsLost, occurrences: data.count });
- });
- // Sort by average points lost (descending)
- pointsData.sort((a, b) => b.avgPointsLost - a.avgPointsLost);
- if (pointsData.length === 0) {
- content = `<div style="text-align: center; padding: 20px; color: ${GEOGUESSR_COLORS.lightTextSecondary};">
- <div style="font-size: 24px; margin-bottom: 10px;">π―</div>
- <p>No points data recorded yet.</p>
- <p style="font-size: 13px;">This tracks when you get the country right but miss the exact location.</p>
- </div>`;
- } else {
- content = `<h3 style="margin-top: 0; font-weight: 500; color: ${GEOGUESSR_COLORS.primary};">Most Points Lost by Country</h3>`;
- content += `<p style="font-size: 14px; color: ${GEOGUESSR_COLORS.lightTextSecondary};">These are countries where you get the country right but miss the region:</p>`;
- content += `<table style="${tableStyle}">`;
- content += `<tr>
- <th style="${headerStyle} width: 40%;">Country</th>
- <th style="${headerStyle} width: 30%; text-align: right;">Avg. Points Lost</th>
- <th style="${headerStyle} width: 30%; text-align: right;">Occurrences</th>
- </tr>`;
- pointsData.forEach(({ country, avgPointsLost, occurrences }, index) => {
- const countryName = countryCodeToName[country] || country;
- const opacity = Math.max(1 - (index * 0.05), 0.5); // Fade out for lower entries
- // Calculate color based on points lost (red gradient)
- const pointsPercentage = Math.min(avgPointsLost / 5000, 1);
- const red = Math.floor(255 * pointsPercentage);
- const green = Math.floor(100 * (1 - pointsPercentage));
- const blue = Math.floor(100 * (1 - pointsPercentage));
- const pointsColor = `rgb(${red}, ${green}, ${blue})`;
- content += `<tr class="data-row">
- <td style="${cellStyle} opacity: ${opacity};">${countryName}</td>
- <td style="${cellStyle} text-align: right; color: ${pointsColor}; opacity: ${opacity};">${avgPointsLost}</td>
- <td style="${cellStyle} text-align: right; opacity: ${opacity};">${occurrences}</td>
- </tr>`;
- });
- content += '</table>';
- }
- }
- statsContent.innerHTML = content;
- // Add event listener for country select if in confused mode
- if (viewMode === 'confused' && Object.keys(countryStats.mistakenIdentities).length > 0) {
- const countrySelect = document.getElementById('country-select');
- if (countrySelect) {
- countrySelect.addEventListener('change', showConfusionDetails);
- }
- }
- // Add hover effects for table rows
- const script = document.createElement('script');
- script.textContent = rowHoverJS;
- document.body.appendChild(script);
- setTimeout(() => script.remove(), 100); // Clean up script tag after execution
- }
- // Show details about what a country is confused with
- function showConfusionDetails() {
- const countryCode = document.getElementById('country-select').value;
- const detailsDiv = document.getElementById('confusion-details');
- if (!countryCode || !detailsDiv) {
- return;
- }
- const countryName = countryCodeToName[countryCode] || countryCode;
- const confusions = countryStats.mistakenIdentities[countryCode];
- if (!confusions) {
- detailsDiv.innerHTML = `<p style="color: ${GEOGUESSR_COLORS.lightTextSecondary};">No data for ${countryName}.</p>`;
- return;
- }
- // Sort by most frequent confusions
- const sortedConfusions = Object.entries(confusions)
- .sort((a, b) => b[1] - a[1]);
- const tableStyle = `
- width: 100%;
- border-collapse: collapse;
- border-radius: 4px;
- overflow: hidden;
- margin-top: 10px;
- font-size: 14px;
- `;
- const headerStyle = `
- background: ${GEOGUESSR_COLORS.darkGray};
- text-align: left;
- padding: 8px 10px;
- font-weight: 500;
- color: ${GEOGUESSR_COLORS.lightText};
- `;
- const cellStyle = `
- padding: 8px 10px;
- border-bottom: 1px solid ${GEOGUESSR_COLORS.darkGray};
- `;
- let content = `<div style="margin-top: 15px; padding-top: 10px; border-top: 1px solid ${GEOGUESSR_COLORS.darkGray};">`;
- content += `<h4 style="margin-top: 0; color: ${GEOGUESSR_COLORS.secondary}; font-weight: 500;">When it's ${countryName}, you guess:</h4>`;
- content += `<table style="${tableStyle}">`;
- content += `<tr>
- <th style="${headerStyle} width: 70%;">Country</th>
- <th style="${headerStyle} width: 30%; text-align: right;">Times</th>
- </tr>`;
- sortedConfusions.forEach(([code, count], index) => {
- const guessName = countryCodeToName[code] || code;
- const opacity = Math.max(1 - (index * 0.05), 0.6); // Fade out for lower entries
- content += `<tr class="data-row">
- <td style="${cellStyle} opacity: ${opacity};">${guessName}</td>
- <td style="${cellStyle} text-align: right; opacity: ${opacity};">${count}</td>
- </tr>`;
- });
- content += '</table></div>';
- detailsDiv.innerHTML = content;
- // Add hover effects for table rows
- const script = document.createElement('script');
- script.textContent = `
- const rows = document.querySelectorAll('#confusion-details tr.data-row');
- rows.forEach(row => {
- row.addEventListener('mouseover', () => {
- row.style.backgroundColor = '${GEOGUESSR_COLORS.mediumGray}';
- });
- row.addEventListener('mouseout', () => {
- row.style.backgroundColor = 'transparent';
- });
- });
- `;
- document.body.appendChild(script);
- setTimeout(() => script.remove(), 100); // Clean up script tag after execution
- }
- // Initialize UI if not already done
- function initializeUI() {
- if (!document.getElementById('country-stats-panel')) {
- createStatsPanel();
- }
- }
- // Set up mutation observer to detect when results are shown
- let lastDoCheckCall = 0;
- new MutationObserver(async (mutations) => {
- if (!checkGameMode() || lastDoCheckCall >= (Date.now() - 50)) return;
- lastDoCheckCall = Date.now();
- await scanStyles();
- if (AUTOMATIC) doCheck();
- initializeUI();
- }).observe(document.body, { subtree: true, childList: true });
- // Initialize the UI on first load
- window.addEventListener('load', initializeUI);
- })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement