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.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

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