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 = `${sender}: ${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 = `

Your guess was ${result.distance_km.toFixed(2)} km away.

You scored ${result.score.toFixed(0)} points.

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