webspacex / app.py
ewebspace's picture
Update app.py
bb25ccb verified
raw
history blame
20.5 kB
import gradio as gr
import numpy as np
import json
import random
import math
# Three.js template for the space simulation
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%;
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%;
transform: translateX(-50%);
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%;
left: 50%;
transform: translate(-50%, -50%);
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%">100%</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%">100%</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
};
// 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 = [];
const planetData = %s;
// Create planets
function createPlanets() {
planetData.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 / planetData.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}%`;
document.getElementById('health-bar').textContent = `${Math.round((gameState.health / gameState.maxHealth) * 100)}%`;
document.getElementById('fuel-value').textContent = gameState.fuel;
document.getElementById('fuel-bar').style.width = `${(gameState.fuel / gameState.maxFuel) * 100}%`;
document.getElementById('fuel-bar').textContent = `${Math.round((gameState.fuel / gameState.maxFuel) * 100)}%`;
// 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>
"""
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"""
return THREE_JS_TEMPLATE % 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()