Joffrey Thomas
merge geoguessr
860e1a7
let map, panorama, guessMarker, gameId, googleMapsApiKey;
let startLocation;
let onFirstLinksLoaded;
function api(path) {
// Use relative paths so mounting under a subpath works
return path.startsWith('/') ? path.slice(1) : path;
}
function initLobby() {
const replayForm = document.getElementById('replay-form');
if (replayForm) {
replayForm.addEventListener('submit', (e) => {
e.preventDefault();
replayGame();
});
}
const playAgain = document.getElementById('play-again');
if (playAgain) {
playAgain.addEventListener('click', showLobby);
}
}
function showLobby() {
document.getElementById('lobby-container').style.display = 'block';
document.getElementById('game-container').style.display = 'none';
document.getElementById('result-screen').style.display = 'none';
}
function showGame() {
document.getElementById('lobby-container').style.display = 'none';
document.getElementById('game-container').style.display = 'flex';
document.getElementById('result-screen').style.display = 'none';
}
function startGame() {
showGame();
const difficultyEl = document.getElementById('difficulty-select-lobby');
const difficulty = difficultyEl ? difficultyEl.value : 'easy';
fetch(api('/start_game'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ difficulty: difficulty })
})
.then(response => response.json())
.then(data => {
if (data.error) {
alert(data.error);
showLobby();
return;
}
gameId = data.game_id;
startLocation = data.start_location || null;
googleMapsApiKey = data.google_maps_api_key || null;
const chatLog = document.getElementById('chat-log');
chatLog.innerHTML = '';
addChatMessage('Agent', `New game started (ID: ${gameId}). Finding my location...`);
if (startLocation) {
initStreetView(startLocation);
initMap();
}
});
}
function replayGame() {
const replayId = document.getElementById('replay-id-input').value;
if (!replayId) {
alert('Please enter a Game ID to replay.');
return;
}
fetch(api(`/game/${replayId}/state`))
.then(response => {
if (!response.ok) {
throw new Error('Game not found.');
}
return response.json();
})
.then(data => {
if (!data.game_over) {
alert('This game has not finished yet.');
return;
}
showGame();
gameId = replayId;
startLocation = data.start_location;
const chatLog = document.getElementById('chat-log');
chatLog.innerHTML = '';
addChatMessage('System', `Replaying game: ${gameId}`);
initStreetView(startLocation);
initMap(true);
replayActions(data.actions);
})
.catch(error => {
alert(error.message);
});
}
async function replayActions(actions) {
for (const action of actions) {
await sleep(2000);
if (action.type === 'move') {
addChatMessage('Agent (Replay)', `Moved to: ${action.location.lat.toFixed(4)}, ${action.location.lng.toFixed(4)}`);
panorama.setPosition(action.location);
} else if (action.type === 'guess') {
addChatMessage('Agent (Replay)', `Guessed: ${action.location.lat.toFixed(4)}, ${action.location.lng.toFixed(4)}`);
placeGuessMarker(action.location);
await sleep(2000);
const resultData = {
guess_location: action.location,
actual_location: startLocation,
distance_km: action.result.distance_km,
score: action.result.score
};
showResultScreen(resultData);
}
}
}
function initStreetView(location) {
onFirstLinksLoaded = new Promise(resolve => {
panorama = new google.maps.StreetViewPanorama(
document.getElementById('streetview'), {
position: location,
pov: { heading: 34, pitch: 10 },
visible: true,
linksControl: true,
clickToGo: true,
}
);
const linksChangedListener = panorama.addListener('links_changed', () => {
google.maps.event.removeListener(linksChangedListener);
resolve();
});
panorama.addListener('position_changed', function() {
const newLocation = panorama.getPosition();
updateAgentLocation(newLocation.lat(), newLocation.lng());
});
});
}
function initMap(isReplay = false) {
map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 0, lng: 0 },
zoom: 1,
});
if (!isReplay) {
map.addListener('click', function(e) {
placeGuessMarker(e.latLng);
makeGuess(e.latLng.lat(), e.latLng.lng());
});
}
}
function placeGuessMarker(location) {
if (guessMarker) {
guessMarker.setMap(null);
}
guessMarker = new google.maps.Marker({
position: location,
map: map
});
map.setCenter(location);
}
function addChatMessage(sender, message) {
const chatLog = document.getElementById('chat-log');
const messageElement = document.createElement('div');
messageElement.innerHTML = `<strong>${sender}:</strong> ${message}`;
chatLog.appendChild(messageElement);
chatLog.scrollTop = chatLog.scrollHeight;
}
async function runFakeAgent() {}
async function takeActionWithScreenshot(actionMessage) {}
async function updateAgentLocation(lat, lng) {
await fetch(api(`/game/${gameId}/move`), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ lat: lat, lng: lng }),
});
}
async function makeGuess(lat, lng) {
addChatMessage('You', `Guessed: ${lat.toFixed(4)}, ${lng.toFixed(4)}`);
const response = await fetch(api(`/game/${gameId}/guess`), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ lat: lat, lng: lng }),
});
const result = await response.json();
showResultScreen(result);
}
function showResultScreen(result) {
document.getElementById('game-container').style.display = 'none';
document.getElementById('result-screen').style.display = 'block';
const resultSummary = document.getElementById('result-summary');
resultSummary.innerHTML = `
<p>Your guess was ${result.distance_km.toFixed(2)} km away.</p>
<p>You scored ${result.score.toFixed(0)} points.</p>
`;
const resultMap = new google.maps.Map(document.getElementById('result-map'), {
zoom: 3,
center: result.actual_location
});
new google.maps.Marker({
position: result.actual_location,
map: resultMap,
label: 'A'
});
new google.maps.Marker({
position: result.guess_location,
map: resultMap,
label: 'G'
});
new google.maps.Polyline({
path: [result.actual_location, result.guess_location],
geodesic: true,
strokeColor: '#F97316',
strokeOpacity: 1.0,
strokeWeight: 2,
map: resultMap
});
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}