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