Spaces:
Running
Running
| <html> | |
| <head> | |
| <title>FreeGuessr - DIY Version</title> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <!-- Load Leaflet (Free Map Library) --> | |
| <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" /> | |
| <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script> | |
| <style> | |
| body { margin: 0; padding: 0; font-family: sans-serif; display: flex; flex-direction: column; height: 100vh; } | |
| #game-container { display: flex; height: 100%; } | |
| /* The Photo Area */ | |
| #photo-view { | |
| width: 60%; | |
| background-color: #222; | |
| background-size: cover; | |
| background-position: center; | |
| position: relative; | |
| } | |
| /* The Map Area */ | |
| #map { width: 40%; height: 100%; cursor: crosshair; } | |
| /* Overlay UI */ | |
| #ui-overlay { | |
| position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); | |
| background: rgba(0,0,0,0.8); color: white; padding: 15px; border-radius: 10px; | |
| text-align: center; z-index: 1000; display: none; | |
| } | |
| button { | |
| padding: 10px 20px; font-size: 16px; background: #6a0dad; color: white; border: none; border-radius: 5px; cursor: pointer; | |
| } | |
| button:hover { background: #8a2be2; } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="game-container"> | |
| <div id="photo-view"> | |
| <!-- UI Button to submit guess --> | |
| <div style="position: absolute; bottom: 20px; right: 20px; z-index: 999;"> | |
| <button id="guess-btn" onclick="makeGuess()">GUESS</button> | |
| <button id="next-btn" onclick="nextRound()" style="display:none; background: #28a745;">NEXT ROUND</button> | |
| </div> | |
| <div id="ui-overlay"></div> | |
| </div> | |
| <div id="map"></div> | |
| </div> | |
| <script> | |
| // --- GAME DATA (You can add more locations here) --- | |
| const locations = [ | |
| { | |
| name: "Eiffel Tower, Paris", | |
| lat: 48.8584, lng: 2.2945, | |
| image: "https://images.unsplash.com/photo-1511739001486-6bfe10ce7859?ixlib=rb-1.2.1&auto=format&fit=crop&w=1920&q=80" | |
| }, | |
| { | |
| name: "Times Square, NYC", | |
| lat: 40.7580, lng: -73.9855, | |
| image: "https://images.unsplash.com/photo-1534430480872-6e4308daee20?ixlib=rb-1.2.1&auto=format&fit=crop&w=1920&q=80" | |
| }, | |
| { | |
| name: "Sydney Opera House", | |
| lat: -33.8568, lng: 151.2153, | |
| image: "https://images.unsplash.com/photo-1624138784181-dc7f5b75e52e?ixlib=rb-1.2.1&auto=format&fit=crop&w=1920&q=80" | |
| }, | |
| { | |
| name: "Santorini, Greece", | |
| lat: 36.3932, lng: 25.4615, | |
| image: "https://images.unsplash.com/photo-1570077188670-e3a8d69ac5ff?ixlib=rb-1.2.1&auto=format&fit=crop&w=1920&q=80" | |
| }, | |
| { | |
| name: "Mount Fuji, Japan", | |
| lat: 35.3606, lng: 138.7274, | |
| image: "https://images.unsplash.com/photo-1490806843957-31f4c9a91c65?ixlib=rb-1.2.1&auto=format&fit=crop&w=1920&q=80" | |
| } | |
| ]; | |
| // --- SETUP --- | |
| let currentRound = 0; | |
| let currentMarker = null; | |
| let actualMarker = null; | |
| let polyline = null; | |
| // Initialize Map | |
| const map = L.map('map').setView([20, 0], 2); | |
| L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { | |
| attribution: '© OpenStreetMap contributors' | |
| }).addTo(map); | |
| // Handle Click on Map | |
| map.on('click', function(e) { | |
| if (document.getElementById('next-btn').style.display === 'block') return; // Stop clicks after guess | |
| if (currentMarker) { | |
| map.removeLayer(currentMarker); | |
| } | |
| currentMarker = L.marker(e.latlng).addTo(map); | |
| }); | |
| function loadRound() { | |
| // Reset UI | |
| document.getElementById('next-btn').style.display = 'none'; | |
| document.getElementById('guess-btn').style.display = 'inline-block'; | |
| document.getElementById('ui-overlay').style.display = 'none'; | |
| // Reset Map Markers | |
| if (currentMarker) map.removeLayer(currentMarker); | |
| if (actualMarker) map.removeLayer(actualMarker); | |
| if (polyline) map.removeLayer(polyline); | |
| map.setView([20, 0], 2); | |
| currentMarker = null; | |
| // Set Background Image | |
| document.getElementById('photo-view').style.backgroundImage = `url('${locations[currentRound].image}')`; | |
| } | |
| function makeGuess() { | |
| if (!currentMarker) { | |
| alert("Place a pin on the map first!"); | |
| return; | |
| } | |
| const target = locations[currentRound]; | |
| const guessPos = currentMarker.getLatLng(); | |
| // Calculate Distance (Haversine Formula) | |
| const distance = getDistanceFromLatLonInKm(target.lat, target.lng, guessPos.lat, guessPos.lng); | |
| const score = Math.max(0, 5000 - Math.floor(distance * 2)); // Simple scoring logic | |
| // Show Actual Location | |
| actualMarker = L.marker([target.lat, target.lng]).addTo(map) | |
| .bindPopup(`<b>${target.name}</b>`).openPopup(); | |
| // Draw Line | |
| polyline = L.polyline([ | |
| [target.lat, target.lng], | |
| [guessPos.lat, guessPos.lng] | |
| ], {color: 'red'}).addTo(map); | |
| // Fit bounds to show both pins | |
| map.fitBounds(polyline.getBounds(), {padding: [50, 50]}); | |
| // Update UI | |
| document.getElementById('guess-btn').style.display = 'none'; | |
| document.getElementById('next-btn').style.display = 'inline-block'; | |
| const ui = document.getElementById('ui-overlay'); | |
| ui.innerHTML = `<h2>You were ${Math.floor(distance)} km away!</h2><p>Score: ${score} points</p>`; | |
| ui.style.display = 'block'; | |
| } | |
| function nextRound() { | |
| currentRound++; | |
| if (currentRound >= locations.length) { | |
| alert("Game Over! Refresh to restart."); | |
| currentRound = 0; | |
| } | |
| loadRound(); | |
| } | |
| // Distance Calculator | |
| function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) { | |
| var R = 6371; // Radius of the earth in km | |
| var dLat = deg2rad(lat2-lat1); | |
| var dLon = deg2rad(lon2-lon1); | |
| var a = Math.sin(dLat/2) * Math.sin(dLat/2) + | |
| Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * | |
| Math.sin(dLon/2) * Math.sin(dLon/2); | |
| var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); | |
| var d = R * c; | |
| return d; | |
| } | |
| function deg2rad(deg) { | |
| return deg * (Math.PI/180) | |
| } | |
| // Start Game | |
| loadRound(); | |
| </script> | |
| </body> | |
| </html> |