Greasy Fork is available in English.

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.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==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);
};