Update app.py
Browse files
app.py
CHANGED
@@ -17,10 +17,10 @@ from audio_recorder_streamlit import audio_recorder
|
|
17 |
nest_asyncio.apply()
|
18 |
|
19 |
# Page Config
|
20 |
-
st.set_page_config(page_title="Galaxian Snake
|
21 |
|
22 |
-
st.title("Galaxian Snake
|
23 |
-
st.write("Navigate a
|
24 |
|
25 |
# Sliders for container size
|
26 |
max_width = min(1200, st.session_state.get('window_width', 1200))
|
@@ -148,14 +148,14 @@ async def load_chat():
|
|
148 |
content = f.read().strip()
|
149 |
return content.split('\n')
|
150 |
|
151 |
-
# Game HTML
|
152 |
html_code = f"""
|
153 |
<!DOCTYPE html>
|
154 |
<html lang="en">
|
155 |
<head>
|
156 |
<meta charset="UTF-8">
|
157 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
158 |
-
<title>Galaxian Snake
|
159 |
<style>
|
160 |
body {{ margin: 0; overflow: hidden; font-family: Arial, sans-serif; }}
|
161 |
#gameContainer {{ width: {container_width}px; height: {container_height}px; position: relative; background: #000; }}
|
@@ -180,7 +180,7 @@ html_code = f"""
|
|
180 |
<body>
|
181 |
<div id="gameContainer">
|
182 |
<div class="ui-container">
|
183 |
-
<h2>Galaxian Snake
|
184 |
<div id="players">Players: 1</div>
|
185 |
<div id="score">Score: 0</div>
|
186 |
<div id="length">Length: 3</div>
|
@@ -200,7 +200,7 @@ html_code = f"""
|
|
200 |
console.log('Starting game initialization...');
|
201 |
// Game variables
|
202 |
let score = 0, players = {{}}, foodItems = [], lSysCreatures = [], quineAgents = [], buildings = [], lights = [];
|
203 |
-
let snake = [], moveDir = new THREE.Vector3(1, 0, 0), moveCounter = 0, moveInterval = 0.
|
204 |
const initialLength = 3, playerName = "{st.session_state.username}";
|
205 |
const highScores = JSON.parse(localStorage.getItem('highScores')) || [];
|
206 |
let ws = new WebSocket('ws://localhost:8765');
|
@@ -209,26 +209,29 @@ html_code = f"""
|
|
209 |
// Scene setup
|
210 |
const scene = new THREE.Scene();
|
211 |
const camera = new THREE.PerspectiveCamera(75, {container_width} / {container_height}, 0.1, 1000);
|
212 |
-
camera.position.set(0,
|
213 |
-
camera.lookAt(0, 0, 0);
|
214 |
console.log('Camera initialized at:', camera.position);
|
215 |
|
216 |
const renderer = new THREE.WebGLRenderer({{ antialias: true }});
|
217 |
renderer.setSize({container_width}, {container_height});
|
218 |
renderer.shadowMap.enabled = true;
|
219 |
document.getElementById('gameContainer').appendChild(renderer.domElement);
|
220 |
-
console.log('Renderer initialized
|
221 |
|
222 |
// Lighting
|
223 |
-
const ambientLight = new THREE.AmbientLight(0xffffff, 0.
|
224 |
scene.add(ambientLight);
|
225 |
-
|
|
|
|
|
|
|
|
|
226 |
|
227 |
// Ground
|
228 |
const textureLoader = new THREE.TextureLoader();
|
229 |
-
const groundGeometry = new THREE.PlaneGeometry(
|
230 |
const groundMaterial = new THREE.MeshStandardMaterial({{
|
231 |
-
color: 0x1a5e1a,
|
232 |
bumpMap: textureLoader.load('https://threejs.org/examples/textures/terrain/grasslight-big-nm.jpg'),
|
233 |
bumpScale: 0.1
|
234 |
}});
|
@@ -236,12 +239,12 @@ html_code = f"""
|
|
236 |
ground.rotation.x = -Math.PI / 2;
|
237 |
ground.receiveShadow = true;
|
238 |
scene.add(ground);
|
239 |
-
console.log('Ground added
|
240 |
|
241 |
// Building rules
|
242 |
const buildingColors = [0x888888, 0x666666, 0x999999];
|
243 |
const buildingRules = [
|
244 |
-
{{name: "Simple", axiom: "F", rules: {{"F": "F[+F][-F]"}}, iterations: 1, baseHeight:
|
245 |
];
|
246 |
|
247 |
function interpretLSystem(rule, position, rotation) {{
|
@@ -261,7 +264,7 @@ html_code = f"""
|
|
261 |
let currentRotation = rotation || new THREE.Euler();
|
262 |
let scale = new THREE.Vector3(1, 1, 1);
|
263 |
const color = buildingColors[Math.floor(Math.random() * buildingColors.length)];
|
264 |
-
const material = new THREE.MeshStandardMaterial({{color: color
|
265 |
|
266 |
for (let i = 0; i < currentString.length; i++) {{
|
267 |
const char = currentString[i];
|
@@ -284,32 +287,26 @@ html_code = f"""
|
|
284 |
break;
|
285 |
case '+': currentRotation.y += rule.angle; break;
|
286 |
case '-': currentRotation.y -= rule.angle; break;
|
287 |
-
case '/': currentRotation.x += rule.angle; break;
|
288 |
-
case '\\\\': currentRotation.x -= rule.angle; break;
|
289 |
-
case '^': currentRotation.z += rule.angle; break;
|
290 |
-
case '&': currentRotation.z -= rule.angle; break;
|
291 |
case '[': stack.push({{position: currentPosition.clone(), rotation: currentRotation.clone(), scale: scale.clone()}}); break;
|
292 |
case ']': if (stack.length > 0) {{ const state = stack.pop(); currentPosition = state.position; currentRotation = state.rotation; scale = state.scale; }} break;
|
293 |
-
case '>': scale.multiplyScalar(1.2); break;
|
294 |
-
case '<': scale.multiplyScalar(0.8); break;
|
295 |
}}
|
296 |
}}
|
297 |
return buildingGroup;
|
298 |
}}
|
299 |
|
300 |
function createCity() {{
|
301 |
-
const citySize =
|
302 |
for (let x = -citySize; x <= citySize; x++) {{
|
303 |
for (let z = -citySize; z <= citySize; z++) {{
|
304 |
-
if (Math.random() < 0.
|
305 |
const position = new THREE.Vector3(x * spacing, 0, z * spacing);
|
306 |
const building = interpretLSystem(buildingRules[0], position, new THREE.Euler());
|
307 |
scene.add(building);
|
308 |
buildings.push(building);
|
309 |
-
console.log('Building
|
310 |
}}
|
311 |
}}
|
312 |
-
const roadWidth =
|
313 |
for (let x = -citySize; x <= citySize; x++) {{
|
314 |
const road = new THREE.Mesh(
|
315 |
new THREE.PlaneGeometry(roadWidth, citySize * 2 * spacing + roadWidth),
|
@@ -328,34 +325,20 @@ html_code = f"""
|
|
328 |
road.position.set(0, 0.01, z * spacing);
|
329 |
scene.add(road);
|
330 |
}}
|
331 |
-
for (let x = -citySize; x <= citySize; x++) {{
|
332 |
-
for (let z = -citySize; z <= citySize; z++) {{
|
333 |
-
const light = new THREE.PointLight(0xffff99, 1, 20);
|
334 |
-
light.position.set(x * spacing, 5, z * spacing);
|
335 |
-
light.castShadow = true;
|
336 |
-
scene.add(light);
|
337 |
-
lights.push(light);
|
338 |
-
console.log('Light added at:', light.position);
|
339 |
-
}}
|
340 |
-
}}
|
341 |
-
}}
|
342 |
-
|
343 |
-
function evolveCity() {{
|
344 |
-
// Simplified for debugging
|
345 |
}}
|
346 |
|
347 |
function spawnFood() {{
|
348 |
-
if (foodItems.length <
|
349 |
const food = new THREE.Mesh(
|
350 |
new THREE.BoxGeometry(1, 1, 1),
|
351 |
new THREE.MeshStandardMaterial({{color: 0xffff00}})
|
352 |
);
|
353 |
-
const x = (Math.random() *
|
354 |
-
const z = (Math.random() *
|
355 |
food.position.set(x, 0.5, z);
|
356 |
foodItems.push(food);
|
357 |
scene.add(food);
|
358 |
-
console.log('Food
|
359 |
}}
|
360 |
}}
|
361 |
|
@@ -406,10 +389,10 @@ html_code = f"""
|
|
406 |
|
407 |
const head = snake[0];
|
408 |
const newHead = new THREE.Mesh(head.geometry, head.material);
|
409 |
-
newHead.position.copy(head.position).add(moveDir.clone().multiplyScalar(
|
410 |
newHead.castShadow = true;
|
411 |
|
412 |
-
if (Math.abs(newHead.position.x) >
|
413 |
resetSnake();
|
414 |
return;
|
415 |
}}
|
@@ -453,18 +436,17 @@ html_code = f"""
|
|
453 |
scene.remove(tail);
|
454 |
}}
|
455 |
}}
|
456 |
-
}}
|
457 |
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
}}
|
462 |
|
463 |
function updateUI() {{
|
464 |
document.getElementById('players').textContent = `Players: ${{Object.keys(players).length}}`;
|
465 |
document.getElementById('score').textContent = `Score: ${{score}}`;
|
466 |
document.getElementById('length').textContent = `Length: ${{snake.length}}`;
|
467 |
-
debug.innerHTML = `Scene
|
468 |
}}
|
469 |
|
470 |
// WebSocket chat
|
@@ -488,7 +470,6 @@ html_code = f"""
|
|
488 |
lastTime = currentTime;
|
489 |
|
490 |
updateSnake(delta);
|
491 |
-
updateLighting(delta);
|
492 |
spawnFood();
|
493 |
updateUI();
|
494 |
|
@@ -536,7 +517,6 @@ async def main_async():
|
|
536 |
if not st.session_state.get('server_running', False):
|
537 |
await start_websocket_server()
|
538 |
|
539 |
-
# Run the WebSocket server in the main event loop
|
540 |
loop = asyncio.get_event_loop()
|
541 |
if not st.session_state.get('server_running', False):
|
542 |
loop.run_until_complete(main_async())
|
@@ -545,6 +525,6 @@ st.sidebar.write("""
|
|
545 |
### How to Play
|
546 |
- **W/A/S/D or Arrow Keys**: Move your snake
|
547 |
- Eat yellow cubes to grow and score
|
548 |
-
-
|
549 |
- Chat with other players in real-time
|
550 |
""")
|
|
|
17 |
nest_asyncio.apply()
|
18 |
|
19 |
# Page Config
|
20 |
+
st.set_page_config(page_title="Galaxian Snake 3D Multiplayer", layout="wide")
|
21 |
|
22 |
+
st.title("Galaxian Snake 3D Multiplayer")
|
23 |
+
st.write("Navigate a 3D city with others, eat food, and chat in real-time!")
|
24 |
|
25 |
# Sliders for container size
|
26 |
max_width = min(1200, st.session_state.get('window_width', 1200))
|
|
|
148 |
content = f.read().strip()
|
149 |
return content.split('\n')
|
150 |
|
151 |
+
# Game HTML
|
152 |
html_code = f"""
|
153 |
<!DOCTYPE html>
|
154 |
<html lang="en">
|
155 |
<head>
|
156 |
<meta charset="UTF-8">
|
157 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
158 |
+
<title>Galaxian Snake 3D Multiplayer</title>
|
159 |
<style>
|
160 |
body {{ margin: 0; overflow: hidden; font-family: Arial, sans-serif; }}
|
161 |
#gameContainer {{ width: {container_width}px; height: {container_height}px; position: relative; background: #000; }}
|
|
|
180 |
<body>
|
181 |
<div id="gameContainer">
|
182 |
<div class="ui-container">
|
183 |
+
<h2>Galaxian Snake 3D</h2>
|
184 |
<div id="players">Players: 1</div>
|
185 |
<div id="score">Score: 0</div>
|
186 |
<div id="length">Length: 3</div>
|
|
|
200 |
console.log('Starting game initialization...');
|
201 |
// Game variables
|
202 |
let score = 0, players = {{}}, foodItems = [], lSysCreatures = [], quineAgents = [], buildings = [], lights = [];
|
203 |
+
let snake = [], moveDir = new THREE.Vector3(1, 0, 0), moveCounter = 0, moveInterval = 0.05; // Faster movement
|
204 |
const initialLength = 3, playerName = "{st.session_state.username}";
|
205 |
const highScores = JSON.parse(localStorage.getItem('highScores')) || [];
|
206 |
let ws = new WebSocket('ws://localhost:8765');
|
|
|
209 |
// Scene setup
|
210 |
const scene = new THREE.Scene();
|
211 |
const camera = new THREE.PerspectiveCamera(75, {container_width} / {container_height}, 0.1, 1000);
|
212 |
+
camera.position.set(0, 15, 20); // Lower, closer camera
|
|
|
213 |
console.log('Camera initialized at:', camera.position);
|
214 |
|
215 |
const renderer = new THREE.WebGLRenderer({{ antialias: true }});
|
216 |
renderer.setSize({container_width}, {container_height});
|
217 |
renderer.shadowMap.enabled = true;
|
218 |
document.getElementById('gameContainer').appendChild(renderer.domElement);
|
219 |
+
console.log('Renderer initialized');
|
220 |
|
221 |
// Lighting
|
222 |
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
|
223 |
scene.add(ambientLight);
|
224 |
+
const sunLight = new THREE.DirectionalLight(0xffddaa, 1);
|
225 |
+
sunLight.position.set(50, 50, 50);
|
226 |
+
sunLight.castShadow = true;
|
227 |
+
scene.add(sunLight);
|
228 |
+
console.log('Lights added');
|
229 |
|
230 |
// Ground
|
231 |
const textureLoader = new THREE.TextureLoader();
|
232 |
+
const groundGeometry = new THREE.PlaneGeometry(200, 200);
|
233 |
const groundMaterial = new THREE.MeshStandardMaterial({{
|
234 |
+
color: 0x1a5e1a,
|
235 |
bumpMap: textureLoader.load('https://threejs.org/examples/textures/terrain/grasslight-big-nm.jpg'),
|
236 |
bumpScale: 0.1
|
237 |
}});
|
|
|
239 |
ground.rotation.x = -Math.PI / 2;
|
240 |
ground.receiveShadow = true;
|
241 |
scene.add(ground);
|
242 |
+
console.log('Ground added');
|
243 |
|
244 |
// Building rules
|
245 |
const buildingColors = [0x888888, 0x666666, 0x999999];
|
246 |
const buildingRules = [
|
247 |
+
{{name: "Simple", axiom: "F", rules: {{"F": "F[+F][-F]"}}, iterations: 1, baseHeight: 10, baseWidth: 5, baseDepth: 5, angle: Math.PI/4, probability: 1}}
|
248 |
];
|
249 |
|
250 |
function interpretLSystem(rule, position, rotation) {{
|
|
|
264 |
let currentRotation = rotation || new THREE.Euler();
|
265 |
let scale = new THREE.Vector3(1, 1, 1);
|
266 |
const color = buildingColors[Math.floor(Math.random() * buildingColors.length)];
|
267 |
+
const material = new THREE.MeshStandardMaterial({{color: color}});
|
268 |
|
269 |
for (let i = 0; i < currentString.length; i++) {{
|
270 |
const char = currentString[i];
|
|
|
287 |
break;
|
288 |
case '+': currentRotation.y += rule.angle; break;
|
289 |
case '-': currentRotation.y -= rule.angle; break;
|
|
|
|
|
|
|
|
|
290 |
case '[': stack.push({{position: currentPosition.clone(), rotation: currentRotation.clone(), scale: scale.clone()}}); break;
|
291 |
case ']': if (stack.length > 0) {{ const state = stack.pop(); currentPosition = state.position; currentRotation = state.rotation; scale = state.scale; }} break;
|
|
|
|
|
292 |
}}
|
293 |
}}
|
294 |
return buildingGroup;
|
295 |
}}
|
296 |
|
297 |
function createCity() {{
|
298 |
+
const citySize = 3, spacing = 20; // Wider spacing
|
299 |
for (let x = -citySize; x <= citySize; x++) {{
|
300 |
for (let z = -citySize; z <= citySize; z++) {{
|
301 |
+
if (Math.random() < 0.7) continue; // Fewer buildings
|
302 |
const position = new THREE.Vector3(x * spacing, 0, z * spacing);
|
303 |
const building = interpretLSystem(buildingRules[0], position, new THREE.Euler());
|
304 |
scene.add(building);
|
305 |
buildings.push(building);
|
306 |
+
console.log('Building at:', position);
|
307 |
}}
|
308 |
}}
|
309 |
+
const roadWidth = 12; // Wider roads
|
310 |
for (let x = -citySize; x <= citySize; x++) {{
|
311 |
const road = new THREE.Mesh(
|
312 |
new THREE.PlaneGeometry(roadWidth, citySize * 2 * spacing + roadWidth),
|
|
|
325 |
road.position.set(0, 0.01, z * spacing);
|
326 |
scene.add(road);
|
327 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
328 |
}}
|
329 |
|
330 |
function spawnFood() {{
|
331 |
+
if (foodItems.length < 10) {{
|
332 |
const food = new THREE.Mesh(
|
333 |
new THREE.BoxGeometry(1, 1, 1),
|
334 |
new THREE.MeshStandardMaterial({{color: 0xffff00}})
|
335 |
);
|
336 |
+
const x = (Math.random() * 60) - 30;
|
337 |
+
const z = (Math.random() * 60) - 30;
|
338 |
food.position.set(x, 0.5, z);
|
339 |
foodItems.push(food);
|
340 |
scene.add(food);
|
341 |
+
console.log('Food at:', food.position);
|
342 |
}}
|
343 |
}}
|
344 |
|
|
|
389 |
|
390 |
const head = snake[0];
|
391 |
const newHead = new THREE.Mesh(head.geometry, head.material);
|
392 |
+
newHead.position.copy(head.position).add(moveDir.clone().multiplyScalar(2)); // Faster step
|
393 |
newHead.castShadow = true;
|
394 |
|
395 |
+
if (Math.abs(newHead.position.x) > 100 || Math.abs(newHead.position.z) > 100) {{
|
396 |
resetSnake();
|
397 |
return;
|
398 |
}}
|
|
|
436 |
scene.remove(tail);
|
437 |
}}
|
438 |
}}
|
|
|
439 |
|
440 |
+
const headPos = newHead.position;
|
441 |
+
camera.position.set(headPos.x, 15, headPos.z + 20); // Closer chase cam
|
442 |
+
camera.lookAt(headPos);
|
443 |
}}
|
444 |
|
445 |
function updateUI() {{
|
446 |
document.getElementById('players').textContent = `Players: ${{Object.keys(players).length}}`;
|
447 |
document.getElementById('score').textContent = `Score: ${{score}}`;
|
448 |
document.getElementById('length').textContent = `Length: ${{snake.length}}`;
|
449 |
+
debug.innerHTML = `Scene: ${{scene.children.length}}<br>Buildings: ${{buildings.length}}<br>Snake: ${{snake.length}}`;
|
450 |
}}
|
451 |
|
452 |
// WebSocket chat
|
|
|
470 |
lastTime = currentTime;
|
471 |
|
472 |
updateSnake(delta);
|
|
|
473 |
spawnFood();
|
474 |
updateUI();
|
475 |
|
|
|
517 |
if not st.session_state.get('server_running', False):
|
518 |
await start_websocket_server()
|
519 |
|
|
|
520 |
loop = asyncio.get_event_loop()
|
521 |
if not st.session_state.get('server_running', False):
|
522 |
loop.run_until_complete(main_async())
|
|
|
525 |
### How to Play
|
526 |
- **W/A/S/D or Arrow Keys**: Move your snake
|
527 |
- Eat yellow cubes to grow and score
|
528 |
+
- Wider roads and fewer buildings for easier movement
|
529 |
- Chat with other players in real-time
|
530 |
""")
|