webspacex / app.py
ewebspace's picture
Update app.py
b30d1e3 verified
import gradio as gr
import numpy as np
import json
import random
# Three.js template with escaped CSS
THREE_JS_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<title>Webspace Network</title>
<style>
body {{
margin: 0;
overflow: hidden;
font-family: 'Arial', sans-serif;
}}
canvas {{ display: block; }}
#ui {{
position: absolute;
top: 10px;
left: 10px;
color: white;
background: rgba(0, 0, 0, 0.7);
padding: 15px;
border-radius: 10px;
width: 300px;
}}
.bar-container {{
width: 100{percent};
background: #333;
border-radius: 5px;
margin: 5px 0;
}}
.bar {{
height: 20px;
border-radius: 5px;
text-align: center;
line-height: 20px;
color: white;
font-size: 12px;
}}
.health-bar {{ background: linear-gradient(to right, #ff0000, #00ff00); }}
.fuel-bar {{ background: linear-gradient(to right, #0000ff, #00ffff); }}
#resources {{
margin-top: 10px;
}}
.resource-item {{
display: flex;
justify-content: space-between;
margin: 5px 0;
}}
#interaction-prompt {{
position: absolute;
bottom: 20px;
left: 50{percent};
transform: translateX(-50{percent});
background: rgba(0, 0, 0, 0.7);
padding: 10px 20px;
border-radius: 5px;
color: white;
display: none;
}}
#planet-info {{
position: absolute;
top: 10px;
right: 10px;
background: rgba(0, 0, 0, 0.7);
padding: 15px;
border-radius: 10px;
width: 300px;
color: white;
display: none;
}}
#game-menu {{
position: absolute;
top: 50{percent};
left: 50{percent};
transform: translate(-50{percent}, -50{percent});
background: rgba(0, 0, 0, 0.9);
padding: 20px;
border-radius: 10px;
color: white;
text-align: center;
display: none;
}}
.menu-btn {{
margin: 10px;
padding: 10px 20px;
background: #333;
border: none;
border-radius: 5px;
color: white;
cursor: pointer;
}}
.menu-btn:hover {{
background: #555;
}}
</style>
</head>
<body>
<div id="ui">
<h2>Webspace Network</h2>
<div>
<div>Health: <span id="health-value">100</span>/100</div>
<div class="bar-container">
<div id="health-bar" class="bar health-bar" style="width: 100{percent}">100{percent}</div>
</div>
</div>
<div>
<div>Fuel: <span id="fuel-value">100</span>/100</div>
<div class="bar-container">
<div id="fuel-bar" class="bar fuel-bar" style="width: 100{percent}">100{percent}</div>
</div>
</div>
<div id="resources">
<h3>Resources:</h3>
<div id="resource-list"></div>
</div>
<button id="menu-btn" class="menu-btn">Menu</button>
</div>
<div id="interaction-prompt">Press [E] to interact</div>
<div id="planet-info">
<h3 id="planet-name">Planet Name</h3>
<div>Type: <span id="planet-type">Desert</span></div>
<div>Resources: <span id="planet-resources">Iron, Water</span></div>
<div>Habitability: <span id="planet-habitability">0.75</span></div>
<button id="mine-btn" class="menu-btn">Mine Resources</button>
</div>
<div id="game-menu">
<h2>Game Menu</h2>
<button id="resume-btn" class="menu-btn">Resume Game</button>
<button id="save-btn" class="menu-btn">Save Game</button>
<button id="load-btn" class="menu-btn">Load Game</button>
<button id="new-btn" class="menu-btn">New Game</button>
<button id="quit-btn" class="menu-btn">Quit to Desktop</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.js"></script>
<script>
// Game state
const gameState = {{
health: 100,
maxHealth: 100,
fuel: 100,
maxFuel: 100,
resources: {{
'Iron': 10,
'Water': 5,
'Fuel': 20,
'Gold': 2
}},
currentPlanet: null,
inMenu: false
}};
// Universe data
const universeData = {universe_json};
// Scene setup
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000010);
// Camera
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 15);
// Renderer
const renderer = new THREE.WebGLRenderer({{ antialias: true }});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Lighting
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
scene.add(directionalLight);
// Stars background
const starGeometry = new THREE.BufferGeometry();
const starVertices = [];
for (let i = 0; i < 10000; i++) {{
const x = (Math.random() - 0.5) * 2000;
const y = (Math.random() - 0.5) * 2000;
const z = (Math.random() - 0.5) * 2000;
starVertices.push(x, y, z);
}}
starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starVertices, 3));
const starMaterial = new THREE.PointsMaterial({{ color: 0xffffff, size: 1 }});
const stars = new THREE.Points(starGeometry, starMaterial);
scene.add(stars);
// Player ship
const shipGeometry = new THREE.ConeGeometry(1, 3, 8);
const shipMaterial = new THREE.MeshPhongMaterial({{ color: 0x00aaff }});
const ship = new THREE.Mesh(shipGeometry, shipMaterial);
ship.rotation.x = Math.PI / 2;
scene.add(ship);
// Planets
const planets = [];
// Create planets
function createPlanets() {{
universeData.systems[0].planets.forEach((planet, i) => {{
const planetGeometry = new THREE.SphereGeometry(planet.size, 32, 32);
// Planet colors
const colors = {{
'Lava': [0.8, 0.3, 0.1],
'Ocean': [0.1, 0.3, 0.8],
'Desert': [0.9, 0.8, 0.5],
'Ice': [0.7, 0.8, 0.9],
'Jungle': [0.1, 0.7, 0.2],
'Toxic': [0.5, 0.1, 0.7],
'Radioactive': [0.3, 0.8, 0.1]
}};
const planetMaterial = new THREE.MeshStandardMaterial({{
color: new THREE.Color(...colors[planet.type] || [0.5, 0.5, 0.5]),
roughness: 0.8,
metalness: 0.2
}});
const planetMesh = new THREE.Mesh(planetGeometry, planetMaterial);
// Position in orbit
const angle = (i / universeData.systems[0].planets.length) * Math.PI * 2;
const distance = 15 + i * 5;
planetMesh.position.set(
Math.cos(angle) * distance,
0,
Math.sin(angle) * distance
);
planetMesh.userData = planet;
scene.add(planetMesh);
planets.push(planetMesh);
// Add click handler
planetMesh.addEventListener('click', (event) => {{
gameState.currentPlanet = planet;
document.getElementById('planet-name').textContent = planet.name;
document.getElementById('planet-type').textContent = planet.type;
document.getElementById('planet-resources').textContent = planet.resources.join(', ');
document.getElementById('planet-habitability').textContent = planet.habitability.toFixed(2);
document.getElementById('planet-info').style.display = 'block';
event.stopPropagation();
}});
}});
}}
createPlanets();
// Camera controls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 5;
controls.maxDistance = 50;
controls.maxPolarAngle = Math.PI / 2 - 0.05;
// Ship movement
const shipSpeed = 0.1;
const keys = {{}};
window.addEventListener('keydown', (e) => {{
keys[e.key.toLowerCase()] = true;
if (e.key === 'e' && gameState.currentPlanet) {{
mineResources();
}}
if (e.key === 'm') {{
toggleMenu();
}}
}});
window.addEventListener('keyup', (e) => {{
keys[e.key.toLowerCase()] = false;
}});
// Update UI
function updateUI() {{
document.getElementById('health-value').textContent = gameState.health;
document.getElementById('health-bar').style.width = `${{(gameState.health / gameState.maxHealth) * 100}}{percent}`;
document.getElementById('health-bar').textContent = `${{Math.round((gameState.health / gameState.maxHealth) * 100)}}{percent}`;
document.getElementById('fuel-value').textContent = gameState.fuel;
document.getElementById('fuel-bar').style.width = `${{(gameState.fuel / gameState.maxFuel) * 100}}{percent}`;
document.getElementById('fuel-bar').textContent = `${{Math.round((gameState.fuel / gameState.maxFuel) * 100)}}{percent}`;
// Update resources
const resourceList = document.getElementById('resource-list');
resourceList.innerHTML = '';
for (const [resource, amount] of Object.entries(gameState.resources)) {{
const item = document.createElement('div');
item.className = 'resource-item';
item.innerHTML = `<span>${{resource}}:</span><span>${{amount}}</span>`;
resourceList.appendChild(item);
}}
}}
// Mine resources
function mineResources() {{
if (!gameState.currentPlanet) return;
const planet = gameState.currentPlanet;
planet.resources.forEach(resource => {{
gameState.resources[resource] = (gameState.resources[resource] || 0) + 1;
}});
updateUI();
alert(`Mined resources from ${{planet.name}}!`);
}}
// Toggle menu
function toggleMenu() {{
gameState.inMenu = !gameState.inMenu;
document.getElementById('game-menu').style.display = gameState.inMenu ? 'block' : 'none';
controls.enabled = !gameState.inMenu;
}}
// Menu buttons
document.getElementById('resume-btn').addEventListener('click', toggleMenu);
document.getElementById('menu-btn').addEventListener('click', toggleMenu);
document.getElementById('mine-btn').addEventListener('click', mineResources);
// Save game
document.getElementById('save-btn').addEventListener('click', () => {{
localStorage.setItem('webspace_save', JSON.stringify(gameState));
alert('Game saved successfully!');
}});
// Load game
document.getElementById('load-btn').addEventListener('click', () => {{
const save = localStorage.getItem('webspace_save');
if (save) {{
Object.assign(gameState, JSON.parse(save));
updateUI();
alert('Game loaded successfully!');
toggleMenu();
}} else {{
alert('No saved game found!');
}}
}});
// New game
document.getElementById('new-btn').addEventListener('click', () => {{
if (confirm('Start a new game? All progress will be lost.')) {{
Object.assign(gameState, {{
health: 100,
maxHealth: 100,
fuel: 100,
maxFuel: 100,
resources: {{
'Iron': 10,
'Water': 5,
'Fuel': 20,
'Gold': 2
}},
currentPlanet: null
}});
updateUI();
toggleMenu();
}}
}});
// Quit game
document.getElementById('quit-btn').addEventListener('click', () => {{
if (confirm('Quit to desktop?')) {{
// In a real game, this would close the window
alert('Thanks for playing!');
}}
}});
// Initial UI update
updateUI();
// Animation loop
function animate() {{
requestAnimationFrame(animate);
// Ship movement
if (!gameState.inMenu) {{
if (keys['w'] || keys['arrowup']) {{
ship.position.z -= shipSpeed;
}}
if (keys['s'] || keys['arrowdown']) {{
ship.position.z += shipSpeed;
}}
if (keys['a'] || keys['arrowleft']) {{
ship.position.x -= shipSpeed;
}}
if (keys['d'] || keys['arrowright']) {{
ship.position.x += shipSpeed;
}}
if (keys['q']) {{
ship.rotation.z += 0.05;
}}
if (keys['e']) {{
ship.rotation.z -= 0.05;
}}
// Fuel consumption
if (keys['w'] || keys['s'] || keys['a'] || keys['d']) {{
gameState.fuel = Math.max(0, gameState.fuel - 0.05);
updateUI();
}}
}}
// Update camera to follow ship
camera.position.x = ship.position.x;
camera.position.y = ship.position.y + 5;
camera.position.z = ship.position.z + 15;
camera.lookAt(ship.position.x, ship.position.y, ship.position.z);
controls.update();
renderer.render(scene, camera);
}}
animate();
// Handle window resize
window.addEventListener('resize', () => {{
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}});
</script>
</body>
</html>
""".replace("{percent}", "%%") # Replace temporary placeholder with actual % sign
class PlanetGenerator:
def __init__(self, seed=42):
self.rng = np.random.RandomState(seed)
self.planet_types = [
'Lava', 'Ocean', 'Desert', 'Ice', 'Jungle', 'Toxic', 'Radioactive'
]
self.resources = [
'Iron', 'Copper', 'Gold', 'Water', 'Oxygen', 'Hydrogen',
'Silicon', 'Titanium', 'Uranium', 'Platinum'
]
def generate_planet(self, index):
planet_type = self.rng.choice(self.planet_types)
# Select 2-4 random resources
num_resources = self.rng.randint(2, 5)
planet_resources = random.sample(self.resources, num_resources)
return {
'name': f"Planet-{chr(65 + index)}",
'type': planet_type,
'resources': planet_resources,
'size': self.rng.uniform(0.8, 1.5),
'habitability': self.rng.uniform(0.1, 0.9)
}
def generate_system(self, index):
return {
'name': f"System-{index}",
'planets': [self.generate_planet(i) for i in range(self.rng.randint(3, 7))]
}
def generate_universe(self, num_systems=1):
return {
'systems': [self.generate_system(i) for i in range(num_systems)]
}
# Initialize universe
generator = PlanetGenerator()
universe = generator.generate_universe()
def get_threejs_app():
"""Generate the Three.js HTML with current universe data"""
# First replace all {percent} placeholders with %%
template = THREE_JS_TEMPLATE
# Then format the universe_json part
return template.replace("{universe_json}", json.dumps(universe))
with gr.Blocks(title="Webspace Network", css=".gradio-container {background: linear-gradient(to bottom, #000033, #000066);}") as demo:
gr.Markdown("# πŸš€ Webspace Network - Space Exploration Simulator")
gr.Markdown("### Procedurally generated universe inspired by No Man's Sky")
with gr.Row():
with gr.Column(scale=2):
# Three.js renderer
html = gr.HTML(get_threejs_app())
# Game instructions
with gr.Accordion("Game Controls", open=False):
gr.Markdown("""
**Movement:**
- W/S: Move forward/backward
- A/D: Move left/right
- Q/E: Rotate ship
- Mouse: Look around
**Interaction:**
- E: Interact with objects
- M: Open game menu
**Planets:**
- Click on planets to see details
- Press 'Mine Resources' to collect resources
""")
with gr.Column(scale=1):
# Game state display
with gr.Group():
gr.Markdown("### Game State")
health = gr.Slider(0, 100, value=100, label="Health", interactive=False)
fuel = gr.Slider(0, 100, value=100, label="Fuel", interactive=False)
# Resources display
with gr.Group():
gr.Markdown("### Resources")
resources = gr.JSON(value={
'Iron': 10,
'Water': 5,
'Fuel': 20,
'Gold': 2
}, label="Inventory")
# Game actions
with gr.Group():
gr.Markdown("### Actions")
with gr.Row():
save_btn = gr.Button("πŸ’Ύ Save Game")
load_btn = gr.Button("πŸ“‚ Load Game")
new_btn = gr.Button("πŸ†• New Game")
# Current planet info
with gr.Group():
gr.Markdown("### Current Planet")
planet_info = gr.JSON(label="Planet Data", value={})
# Debug console
with gr.Group():
gr.Markdown("### Debug Console")
console = gr.Textbox(label="Game Events", interactive=False)
# Game actions
def save_game():
return {"message": "Game saved successfully!"}
def load_game():
return {"message": "Game loaded successfully!"}
def new_game():
return {
"health": 100,
"fuel": 100,
"resources": {
'Iron': 10,
'Water': 5,
'Fuel': 20,
'Gold': 2
},
"planet_info": {},
"console": "New game started"
}
save_btn.click(
save_game,
outputs=console
)
load_btn.click(
load_game,
outputs=console
)
new_btn.click(
new_game,
outputs=[health, fuel, resources, planet_info, console]
)
if __name__ == "__main__":
demo.launch()