Spaces:
Sleeping
Sleeping
| 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)); | |
| } | |