ZaxxoGalaxian / app.py
awacke1's picture
Update app.py
06c1427 verified
raw
history blame
11.4 kB
import streamlit as st
from streamlit.components.v1 import html
import random
import string
# Define the enhanced HTML content with Three.js game
game_html = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Galaxxon - Enhanced Arcade Game</title>
<style>
body { margin: 0; overflow: hidden; background: #000; font-family: Arial; }
canvas { display: block; width: 100%; height: 100%; }
#ui { position: absolute; top: 10px; left: 10px; color: white; }
#sidebar { position: absolute; top: 10px; right: 10px; color: white; width: 200px; background: rgba(0,0,0,0.7); padding: 10px; }
</style>
</head>
<body>
<div id="ui">Score: <span id="score">0</span> | Multiplier: <span id="multiplier">1</span>x | Time: <span id="timer">0</span>s</div>
<div id="sidebar">
<h3>High Scores</h3>
<div id="highScores"></div>
<button onclick="saveScore()">Save Score</button>
</div>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
let scene, camera, renderer, player, enemies = [], bullets = [], buildings = [];
let clock = new THREE.Clock();
let moveLeft = false, moveRight = false, moveUp = false, moveDown = false, shoot = false;
let score = 0, multiplier = 1, gameTime = 0, lastHitTime = 0;
let highScores = JSON.parse(localStorage.getItem('highScores')) || [];
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.set(0, 20, 30);
camera.lookAt(0, 0, 0);
const playerGeometry = new THREE.BoxGeometry(1, 1, 1);
const playerMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
player = new THREE.Mesh(playerGeometry, playerMaterial);
player.position.y = -10;
scene.add(player);
spawnFlockingEnemies();
spawnBuildings();
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
window.addEventListener('resize', onWindowResize);
updateHighScoresUI();
animate();
}
function spawnFlockingEnemies() {
const enemyGeometry = new THREE.BoxGeometry(0.8, 0.8, 0.8);
const enemyMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
for (let i = 0; i < 10; i++) {
const enemy = new THREE.Mesh(enemyGeometry, enemyMaterial);
enemy.position.set(Math.random() * 20 - 10, Math.random() * 10 + 5, 0);
enemy.velocity = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, 0);
enemies.push(enemy);
scene.add(enemy);
}
}
function spawnBuildings() {
const primitives = [
new THREE.BoxGeometry(2, 2, 2),
new THREE.CylinderGeometry(1, 1, 3, 8),
new THREE.ConeGeometry(1.5, 2, 8)
];
const material = new THREE.MeshBasicMaterial({ color: 0x808080 });
for (let i = 0; i < 5; i++) {
const building = new THREE.Group();
const height = Math.random() * 3 + 1;
for (let j = 0; j < height; j++) {
const primitive = primitives[Math.floor(Math.random() * primitives.length)].clone();
const segment = new THREE.Mesh(primitive, material);
segment.position.y = j * 2;
building.add(segment);
}
building.position.set(Math.random() * 30 - 15, -15 + height, 0);
buildings.push(building);
scene.add(building);
}
}
function onKeyDown(event) {
switch (event.code) {
case 'ArrowLeft': case 'KeyA': moveLeft = true; break;
case 'ArrowRight': case 'KeyD': moveRight = true; break;
case 'ArrowUp': case 'KeyW': moveUp = true; break;
case 'ArrowDown': case 'KeyS': moveDown = true; break;
case 'Space': shoot = true; break;
}
}
function onKeyUp(event) {
switch (event.code) {
case 'ArrowLeft': case 'KeyA': moveLeft = false; break;
case 'ArrowRight': case 'KeyD': moveRight = false; break;
case 'ArrowUp': case 'KeyW': moveUp = false; break;
case 'ArrowDown': case 'KeyS': moveDown = false; break;
case 'Space': shoot = false; break;
}
}
function updatePlayer(delta) {
const speed = 10;
if (moveLeft && player.position.x > -15) player.position.x -= speed * delta;
if (moveRight && player.position.x < 15) player.position.x += speed * delta;
if (moveUp && player.position.y < 0) player.position.y += speed * delta;
if (moveDown && player.position.y > -15) player.position.y -= speed * delta;
if (shoot && clock.getElapsedTime() > 0.2) {
shootBullet();
clock = new THREE.Clock();
}
}
function shootBullet() {
const bulletGeometry = new THREE.SphereGeometry(0.2, 8, 8);
const bulletMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
bullet.position.copy(player.position);
bullet.position.y += 1;
bullets.push(bullet);
scene.add(bullet);
}
function updateBullets(delta) {
const bulletSpeed = 20;
for (let i = bullets.length - 1; i >= 0; i--) {
bullets[i].position.y += bulletSpeed * delta;
if (bullets[i].position.y > 20) {
scene.remove(bullets[i]);
bullets.splice(i, 1);
} else {
checkCollisions(bullets[i], i);
}
}
}
function checkCollisions(bullet, bulletIndex) {
for (let i = enemies.length - 1; i >= 0; i--) {
if (bullet.position.distanceTo(enemies[i].position) < 1) {
scene.remove(enemies[i]);
enemies.splice(i, 1);
scene.remove(bullet);
bullets.splice(bulletIndex, 1);
score += 10 * multiplier;
if (clock.getElapsedTime() - lastHitTime < 2) multiplier += 0.5;
lastHitTime = clock.getElapsedTime();
updateUI();
if (enemies.length < 5) spawnFlockingEnemies();
break;
}
}
}
function updateFlockingEnemies(delta) {
const cohesion = 0.05, separation = 0.1, alignment = 0.05, speed = 2;
enemies.forEach(enemy => {
let avgPos = new THREE.Vector3(), avgVel = new THREE.Vector3(), separationForce = new THREE.Vector3();
let neighbors = 0;
enemies.forEach(other => {
if (enemy !== other) {
const dist = enemy.position.distanceTo(other.position);
if (dist < 5) {
avgPos.add(other.position);
avgVel.add(other.velocity);
if (dist < 2) separationForce.sub(other.position.clone().sub(enemy.position).normalize().divideScalar(dist));
neighbors++;
}
}
});
if (neighbors > 0) {
avgPos.divideScalar(neighbors).sub(enemy.position).multiplyScalar(cohesion);
avgVel.divideScalar(neighbors).sub(enemy.velocity).multiplyScalar(alignment);
separationForce.multiplyScalar(separation);
enemy.velocity.add(avgPos).add(avgVel).add(separationForce).clampLength(0, speed);
}
enemy.position.add(enemy.velocity.clone().multiplyScalar(delta));
if (enemy.position.y < -15) enemy.position.y = 15;
if (enemy.position.x < -15 || enemy.position.x > 15) enemy.velocity.x *= -1;
});
}
function updateBuildings(delta) {
const buildingSpeed = 1;
buildings.forEach(building => {
building.position.y += buildingSpeed * delta;
if (building.position.y > 15) building.position.y = -15;
});
}
function updateUI() {
document.getElementById('score').innerText = score;
document.getElementById('multiplier').innerText = multiplier.toFixed(1);
document.getElementById('timer').innerText = Math.floor(gameTime);
if (clock.getElapsedTime() - lastHitTime > 5) multiplier = 1;
}
function updateHighScoresUI() {
const scoresDiv = document.getElementById('highScores');
scoresDiv.innerHTML = highScores.map(s => `${s.name}: ${s.score} (${s.time}s)`).join('<br>');
}
window.saveScore = function() {
const name = prompt("Enter 3-letter name:", generateRandomName());
if (name && name.length === 3) {
highScores.push({ name, score, time: Math.floor(gameTime) });
highScores.sort((a, b) => b.score - a.score);
highScores = highScores.slice(0, 5);
localStorage.setItem('highScores', JSON.stringify(highScores));
updateHighScoresUI();
}
}
function generateRandomName() {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return Array(3).fill().map(() => letters[Math.floor(Math.random() * letters.length)]).join('');
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
gameTime += delta;
updatePlayer(delta);
updateBullets(delta);
updateFlockingEnemies(delta);
updateBuildings(delta);
updateUI();
renderer.render(scene, camera);
}
init();
</script>
</body>
</html>
"""
# Streamlit app
st.title("Galaxxon - Enhanced Arcade Game")
st.write("Use WASD or Arrow Keys to move, Spacebar to shoot. Destroy red enemies for points! Chain hits for multiplier bonuses.")
# Render the HTML game
html(game_html, height=600, scrolling=False)
st.write("Note: The game runs in your browser. Ensure you have an internet connection for Three.js to load.")