Guess Incorrector

Adds a percentage of incorrectness to your GeoGuessr guesses. For example, if set to 10%, and your guess would have been 10km away, your guess will be moved 11km away.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         Guess Incorrector
// @description  Adds a percentage of incorrectness to your GeoGuessr guesses. For example, if set to 10%, and your guess would have been 10km away, your guess will be moved 11km away.
// @license      MIT
// @namespace    http://tampermonkey.net/
// @version      2026-03-12
// @match        *://*.geoguessr.com/*
// @run-at       document-start
// @grant        unsafeWindow
// @grant        GM_registerMenuCommand
// @require      https://cdn.jsdelivr.net/npm/[email protected]/lib/index.js
// ==/UserScript==

let percentage = parseInt( localStorage.getItem( 'incorrectorpercentage' ), 10 ) || 0;
let gcoords = [ 0, 0 ];

const showOptions = () => {
	let optionsWindow = document.querySelector('#incorrectorOptions');
	if ( ! optionsWindow ) {
		optionsWindow = document.createElement( 'div' );
		optionsWindow.id = 'incorrectorOptions';
		optionsWindow.style.borderRadius = '16px';
		optionsWindow.style.textAlign = 'center';
		optionsWindow.style.padding = '14px';
		optionsWindow.style.position = 'absolute';
		optionsWindow.style.width = '600px';
		optionsWindow.style.zIndex = '999999';
		optionsWindow.style.top = '20%';
  		optionsWindow.style.left = 'calc( 50vw - 300px )';
		optionsWindow.style.backgroundColor = 'rgba(255,255,255,.94)';
		const h1 = document.createElement( 'h1' );
		h1.innerText = 'Guess Incorrector Settings';
		const h2 = document.createElement( 'h2' );
		h2.innerText = `Add ${ percentage }% incorrectness`;
		const h3 = document.createElement( 'h3' );
		h3.innerText = `A guess that is 100km away would change to ${ Math.round( 100 * ( 1 + percentage / 100 ) ) }km away`;
		h3.style.marginBottom = '22px';
		const pInput = document.createElement( 'input' );
		pInput.type='range';
		pInput.min='0';
		pInput.max='180';
		pInput.step='1';
		pInput.value=percentage <= 100 ? percentage : 100 + ( percentage - 100 ) / 10;
		const update = () => {
			const value = parseInt( pInput.value, 10 )
			percentage = value <= 100 ? value : 100 + ( value - 100 ) * 10;
			h2.innerText = `Add ${ percentage }% incorrectness`;
			h3.innerText = `A guess that is 100km away would change to ${ Math.round( 100 * ( 1 + percentage / 100 ) ) }km away`;
			localStorage.setItem( 'incorrectorpercentage', percentage );
		};
		pInput.oninput = update;
		pInput.onchange = update;
		const close = document.createElement( 'button' );
		close.innerText = 'Close Settings';
		close.onclick = () => optionsWindow.style.display = 'none';
		close.style.margin='12px';
		close.style.padding='8px';
		close.style.borderRadius='6px';
		close.style.width='60%';
		close.style.backgroundColor='#23c921';
		const note = document.createElement( 'div' );
		note.innerText = "If you've already placed a marker, setting won't take effect until next marker placement.";
		note.style.fontSize = '0.85em';
		note.style.color = '#777';
		optionsWindow.appendChild( h1 );
		optionsWindow.appendChild( h2 );
		optionsWindow.appendChild( pInput );
		optionsWindow.appendChild( h3 );
		optionsWindow.appendChild( close );
		optionsWindow.appendChild( note );

		document.body.appendChild( optionsWindow );
		optionsWindow.style.display = 'block';
	} else if ( optionsWindow.style.display === 'block' ){
		optionsWindow.style.display = 'none';
	} else {
		optionsWindow.style.display = 'block';
	}
};

GM_registerMenuCommand( "Configure", showOptions );

function calculateIncorrectedLatLong( latitude, longitude ) {
	const distance = window.geolib.getPreciseDistance(
		{ latitude: gcoords[0], longitude: gcoords[1] },
		{ latitude, longitude }
	);

	const bearing = window.geolib.getGreatCircleBearing(
		{ latitude: gcoords[0], longitude: gcoords[1] },
		{ latitude, longitude }
	);

	const incorrected = window.geolib.computeDestinationPoint(
		{ latitude: gcoords[0], longitude: gcoords[1] },
		// 20000000 meters is roughly half the earth's circumference
		// If we move further than that, we actually may be making the guess closer
		Math.min( distance * ( 1 + percentage / 100 ), 20000000 ),
		bearing
	);
	return incorrected;
}

const originalFetch = unsafeWindow.fetch;

const updateButton = () => {
	const button = document.querySelector('button[data-qa=perform-guess]');
	if ( ! button || button.disabled ) return;

	button.style.background = percentage > 0 ? '#cb0561' : '';
	button.innerText = percentage > 0 ? `GUESS (+${percentage}% incorrect)` : 'GUESS';
};
setInterval( updateButton, 250 );

unsafeWindow.fetch = new Proxy( originalFetch, {
	apply: function (target, that, args) {

		let [resource, config] = args;

		if ( percentage > 0 ) {

			if (
				resource.match && (
					resource?.match( /^https:\/\/www\.geoguessr\.com\/api\/v\d\/games\// ) ||
					resource?.match( /^https:\/\/[^\.]+\.geoguessr\.com\/.*?\/(guess|pin)$/ )
				)
			) {
				if ( config?.body ) {
					const orig = JSON.parse( config.body );
					if ( orig.lat && orig.lng ) {
						const incorrected = calculateIncorrectedLatLong( orig.lat, orig.lng );
						if ( incorrected.latitude && incorrected.longitude ) {
							config.body = JSON.stringify( {
								...orig,
								lat: incorrected.latitude,
								lng: incorrected.longitude,
							} );
						}
					}
				}
			}
		}

		const promise = Reflect.apply(target, that, args);

		return promise;
	}
} );


const originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
	if (method.toUpperCase() === 'POST' &&
		(url.startsWith('https://maps.googleapis.com/$rpc/google.internal.maps.mapsjs.v1.MapsJsInternalService/GetMetadata') ||
			url.startsWith('https://maps.googleapis.com/$rpc/google.internal.maps.mapsjs.v1.MapsJsInternalService/SingleImageSearch'))) {

		this.addEventListener('load', function () {
			const pattern = /-?\d+\.\d+,-?\d+\.\d+/g;
			const match = this.responseText.match(pattern);
			if (match && match[0]) {
				const [lat, lng] = match[0].split(",").map(Number);
				gcoords = [ lat, lng ];
			}
		});
	}
	return originalOpen.apply(this, arguments);
};