awacke1 commited on
Commit
11ab863
·
verified ·
1 Parent(s): 3ecf58b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +371 -0
app.py ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from streamlit.components.v1 import html
3
+ import random
4
+ import string
5
+
6
+ # Define the enhanced HTML content with Three.js game
7
+ game_html = """
8
+ <!DOCTYPE html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="UTF-8">
12
+ <title>Galaxxon - Enhanced Arcade Game</title>
13
+ <style>
14
+ body { margin: 0; overflow: hidden; background: #000; font-family: Arial; }
15
+ canvas { display: block; width: 100%; height: 100%; }
16
+ #ui { position: absolute; top: 10px; left: 10px; color: white; }
17
+ #sidebar { position: absolute; top: 10px; right: 10px; color: white; width: 200px; background: rgba(0,0,0,0.7); padding: 10px; }
18
+ #lives { position: absolute; top: 40px; left: 10px; color: white; }
19
+ .bonus { position: absolute; color: yellow; font-size: 20px; }
20
+ </style>
21
+ </head>
22
+ <body>
23
+ <div id="ui">Score: <span id="score">0</span> | Multiplier: <span id="multiplier">1</span>x | Time: <span id="timer">0</span>s</div>
24
+ <div id="lives">Lives: <span id="livesCount">5</span></div>
25
+ <div id="sidebar">
26
+ <h3>High Scores</h3>
27
+ <div id="highScores"></div>
28
+ <button onclick="saveScore()">Save Score</button>
29
+ </div>
30
+ <script type="module">
31
+ import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
32
+
33
+ let scene, camera, renderer, player, enemies = [], bullets = [], buildings = [];
34
+ let clock = new THREE.Clock();
35
+ let moveLeft = false, moveRight = false, moveUp = false, moveDown = false, shoot = false;
36
+ let score = 0, multiplier = 1, gameTime = 0, lastHitTime = 0, lives = 5, buildingStreak = 0;
37
+ let highScores = JSON.parse(localStorage.getItem('highScores')) || [];
38
+ let exploding = false;
39
+
40
+ function init() {
41
+ scene = new THREE.Scene();
42
+ camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
43
+ renderer = new THREE.WebGLRenderer({ antialias: true });
44
+ renderer.setSize(window.innerWidth, window.innerHeight);
45
+ document.body.appendChild(renderer.domElement);
46
+
47
+ camera.position.set(0, 5, 15);
48
+ camera.lookAt(0, 0, -50);
49
+
50
+ const playerGeometry = new THREE.BoxGeometry(1, 1, 1);
51
+ const playerMaterial = new THREE.MeshPhongMaterial({ color: 0x00ff00, shininess: 100 });
52
+ player = new THREE.Mesh(playerGeometry, playerMaterial);
53
+ player.position.set(0, 0, 0);
54
+ scene.add(player);
55
+
56
+ spawnBuildings();
57
+
58
+ const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
59
+ scene.add(ambientLight);
60
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
61
+ directionalLight.position.set(5, 10, 5);
62
+ scene.add(directionalLight);
63
+
64
+ window.addEventListener('keydown', onKeyDown);
65
+ window.addEventListener('keyup', onKeyUp);
66
+ window.addEventListener('resize', onWindowResize);
67
+
68
+ updateHighScoresUI();
69
+ animate();
70
+ }
71
+
72
+ function spawnBuildings() {
73
+ const primitives = [
74
+ new THREE.BoxGeometry(2, 2, 2),
75
+ new THREE.CylinderGeometry(1, 1, 3, 8),
76
+ new THREE.ConeGeometry(1.5, 2, 8)
77
+ ];
78
+ const material = new THREE.MeshPhongMaterial({ color: 0x808080, shininess: 50 });
79
+ for (let i = 0; i < 10; i++) {
80
+ const building = new THREE.Group();
81
+ const height = Math.random() * 5 + 2;
82
+ for (let j = 0; j < height; j++) {
83
+ const primitive = primitives[Math.floor(Math.random() * primitives.length)].clone();
84
+ const segment = new THREE.Mesh(primitive, material);
85
+ segment.position.y = j * 2 - height;
86
+ building.add(segment);
87
+ }
88
+ building.position.set(Math.random() * 40 - 20, height / 2, -50 - Math.random() * 50);
89
+ building.spawnTimer = 0;
90
+ buildings.push(building);
91
+ scene.add(building);
92
+ }
93
+ }
94
+
95
+ function spawnEnemyFromPosition(position) {
96
+ const enemyGeometry = new THREE.BoxGeometry(0.8, 0.8, 0.8);
97
+ const enemyMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000, shininess: 50 });
98
+ const enemy = new THREE.Mesh(enemyGeometry, enemyMaterial);
99
+ enemy.position.copy(position);
100
+ enemy.velocity = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, 1).normalize();
101
+ enemies.push(enemy);
102
+ scene.add(enemy);
103
+ }
104
+
105
+ function onKeyDown(event) {
106
+ switch (event.code) {
107
+ case 'ArrowLeft': case 'KeyA': moveLeft = true; break;
108
+ case 'ArrowRight': case 'KeyD': moveRight = true; break;
109
+ case 'ArrowUp': case 'KeyW': moveUp = true; break;
110
+ case 'ArrowDown': case 'KeyS': moveDown = true; break;
111
+ case 'Space': shoot = true; break;
112
+ }
113
+ }
114
+
115
+ function onKeyUp(event) {
116
+ switch (event.code) {
117
+ case 'ArrowLeft': case 'KeyA': moveLeft = false; break;
118
+ case 'ArrowRight': case 'KeyD': moveRight = false; break;
119
+ case 'ArrowUp': case 'KeyW': moveUp = false; break;
120
+ case 'ArrowDown': case 'KeyS': moveDown = false; break;
121
+ case 'Space': shoot = false; break;
122
+ }
123
+ }
124
+
125
+ function updatePlayer(delta) {
126
+ if (exploding) return;
127
+ const speed = 10;
128
+ if (moveLeft && player.position.x > -20) player.position.x -= speed * delta;
129
+ if (moveRight && player.position.x < 20) player.position.x += speed * delta;
130
+ if (moveUp && player.position.y < 10) player.position.y += speed * delta;
131
+ if (moveDown && player.position.y > -5) player.position.y -= speed * delta;
132
+
133
+ if (shoot && clock.getElapsedTime() > 0.2) {
134
+ shootBullet();
135
+ clock = new THREE.Clock();
136
+ }
137
+ }
138
+
139
+ function shootBullet() {
140
+ const bulletGeometry = new THREE.SphereGeometry(0.2, 8, 8);
141
+ const bulletMaterial = new THREE.MeshPhongMaterial({ color: 0xffff00, shininess: 100 });
142
+ const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial);
143
+ bullet.position.copy(player.position);
144
+ bullet.position.z -= 1;
145
+ bullet.velocity = new THREE.Vector3(0, 0, -1);
146
+ bullet.bounces = 0;
147
+ bullet.timer = 5;
148
+ bullets.push(bullet);
149
+ scene.add(bullet);
150
+ }
151
+
152
+ function updateBullets(delta) {
153
+ const bulletSpeed = 20;
154
+ for (let i = bullets.length - 1; i >= 0; i--) {
155
+ bullets[i].position.add(bullets[i].velocity.clone().multiplyScalar(bulletSpeed * delta));
156
+ bullets[i].timer -= delta;
157
+
158
+ if (bullets[i].position.z < -100 || bullets[i].timer <= 0) {
159
+ scene.remove(bullets[i]);
160
+ bullets.splice(i, 1);
161
+ continue;
162
+ }
163
+
164
+ if (bullets[i].position.x < -20 || bullets[i].position.x > 20) {
165
+ bullets[i].velocity.x *= -1;
166
+ bullets[i].bounces++;
167
+ }
168
+ if (bullets[i].position.y < -5 || bullets[i].position.y > 10) {
169
+ bullets[i].velocity.y *= -1;
170
+ bullets[i].bounces++;
171
+ }
172
+ if (bullets[i].bounces > 3) {
173
+ scene.remove(bullets[i]);
174
+ bullets.splice(i, 1);
175
+ continue;
176
+ }
177
+
178
+ checkCollisions(bullets[i], i);
179
+ }
180
+ }
181
+
182
+ function checkCollisions(bullet, bulletIndex) {
183
+ for (let i = enemies.length - 1; i >= 0; i--) {
184
+ if (bullet.position.distanceTo(enemies[i].position) < 1) {
185
+ scene.remove(enemies[i]);
186
+ enemies.splice(i, 1);
187
+ scene.remove(bullet);
188
+ bullets.splice(bulletIndex, 1);
189
+ score += 10 * multiplier;
190
+ if (clock.getElapsedTime() - lastHitTime < 2) multiplier += 0.5;
191
+ lastHitTime = clock.getElapsedTime();
192
+ updateUI();
193
+ break;
194
+ }
195
+ }
196
+ }
197
+
198
+ function updateFlockingEnemies(delta) {
199
+ const speed = 3;
200
+ enemies.forEach(enemy => {
201
+ enemy.position.add(enemy.velocity.clone().multiplyScalar(delta * speed));
202
+ if (enemy.position.z > 10) {
203
+ scene.remove(enemy);
204
+ enemies.splice(enemies.indexOf(enemy), 1);
205
+ }
206
+ });
207
+ }
208
+
209
+ function updateBuildings(delta) {
210
+ const buildingSpeed = 5;
211
+ for (let i = buildings.length - 1; i >= 0; i--) {
212
+ const building = buildings[i];
213
+ building.position.z += buildingSpeed * delta;
214
+ if (building.position.z > 20) {
215
+ building.position.z = -50 - Math.random() * 50;
216
+ building.position.x = Math.random() * 40 - 20;
217
+ building.children.forEach(child => child.visible = true);
218
+ }
219
+
220
+ const distToPlayer = building.position.distanceTo(player.position);
221
+ if (distToPlayer < 10 && building.spawnTimer <= 0) {
222
+ spawnEnemyFromBuilding(building);
223
+ building.spawnTimer = 2;
224
+ }
225
+ building.spawnTimer -= delta;
226
+
227
+ if (!exploding && distToPlayer < 3 && building.children.some(child => child.visible)) {
228
+ explodePlayer();
229
+ destroyBuilding(building, i);
230
+ }
231
+ }
232
+ }
233
+
234
+ function spawnEnemyFromBuilding(building) {
235
+ const topPos = building.position.clone();
236
+ topPos.y += building.children.length * 2;
237
+ spawnEnemyFromPosition(topPos);
238
+ }
239
+
240
+ function explodePlayer() {
241
+ exploding = true;
242
+ lives--;
243
+ updateUI();
244
+ const particleGeometry = new THREE.SphereGeometry(0.1, 8, 8);
245
+ const particleMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
246
+ for (let i = 0; i < 30; i++) {
247
+ const particle = new THREE.Mesh(particleGeometry, particleMaterial);
248
+ particle.position.copy(player.position);
249
+ particle.velocity = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).multiplyScalar(5);
250
+ scene.add(particle);
251
+ setTimeout(() => scene.remove(particle), 1000);
252
+ }
253
+ player.visible = false;
254
+ setTimeout(() => {
255
+ player.visible = true;
256
+ exploding = false;
257
+ player.position.set(0, 0, 0);
258
+ if (lives === 1) alert("Last life remaining!");
259
+ if (lives <= 0) {
260
+ alert("Game Over! Final Score: " + score);
261
+ saveScore();
262
+ lives = 5;
263
+ score = 0;
264
+ multiplier = 1;
265
+ buildingStreak = 0;
266
+ updateUI();
267
+ }
268
+ }, 1000);
269
+ }
270
+
271
+ function destroyBuilding(building, index) {
272
+ const explosionPos = building.position.clone();
273
+ const particleGeometry = new THREE.SphereGeometry(0.2, 8, 8);
274
+ const particleMaterial = new THREE.MeshBasicMaterial({ color: 0xff8800 });
275
+ for (let i = 0; i < 20; i++) {
276
+ const particle = new THREE.Mesh(particleGeometry, particleMaterial);
277
+ particle.position.copy(explosionPos);
278
+ particle.velocity = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).multiplyScalar(3);
279
+ scene.add(particle);
280
+ setTimeout(() => scene.remove(particle), 1000);
281
+ }
282
+
283
+ building.children.forEach((segment, idx) => {
284
+ setTimeout(() => segment.visible = false, idx * 100);
285
+ });
286
+
287
+ buildingStreak++;
288
+ const bonus = 50 * buildingStreak;
289
+ score += bonus;
290
+ showBonus(explosionPos, bonus);
291
+
292
+ if (Math.random() < 0.5) {
293
+ for (let i = 0; i < 2; i++) {
294
+ setTimeout(() => spawnEnemyFromPosition(explosionPos), Math.random() * 500);
295
+ }
296
+ }
297
+ }
298
+
299
+ function showBonus(position, points) {
300
+ const bonusDiv = document.createElement('div');
301
+ bonusDiv.className = 'bonus';
302
+ bonusDiv.innerText = `+${points}`;
303
+ bonusDiv.style.left = `${(position.x + 20) * window.innerWidth / 40}px`;
304
+ bonusDiv.style.top = `${(10 - position.y) * window.innerHeight / 15}px`;
305
+ document.body.appendChild(bonusDiv);
306
+ setTimeout(() => document.body.removeChild(bonusDiv), 1000);
307
+ }
308
+
309
+ function updateUI() {
310
+ document.getElementById('score').innerText = score;
311
+ document.getElementById('multiplier').innerText = multiplier.toFixed(1);
312
+ document.getElementById('timer').innerText = Math.floor(gameTime);
313
+ document.getElementById('livesCount').innerText = lives;
314
+ if (clock.getElapsedTime() - lastHitTime > 5) multiplier = 1;
315
+ }
316
+
317
+ function updateHighScoresUI() {
318
+ const scoresDiv = document.getElementById('highScores');
319
+ scoresDiv.innerHTML = highScores.map(s => `${s.name}: ${s.score} (${s.time}s)`).join('<br>');
320
+ }
321
+
322
+ window.saveScore = function() {
323
+ const name = prompt("Enter 3-letter name:", generateRandomName());
324
+ if (name && name.length === 3) {
325
+ highScores.push({ name, score, time: Math.floor(gameTime) });
326
+ highScores.sort((a, b) => b.score - a.score);
327
+ highScores = highScores.slice(0, 5);
328
+ localStorage.setItem('highScores', JSON.stringify(highScores));
329
+ updateHighScoresUI();
330
+ }
331
+ }
332
+
333
+ function generateRandomName() {
334
+ const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
335
+ return Array(3).fill().map(() => letters[Math.floor(Math.random() * letters.length)]).join('');
336
+ }
337
+
338
+ function onWindowResize() {
339
+ camera.aspect = window.innerWidth / window.innerHeight;
340
+ camera.updateProjectionMatrix();
341
+ renderer.setSize(window.innerWidth, window.innerHeight);
342
+ }
343
+
344
+ function animate() {
345
+ requestAnimationFrame(animate);
346
+ const delta = clock.getDelta();
347
+ gameTime += delta;
348
+
349
+ updatePlayer(delta);
350
+ updateBullets(delta);
351
+ updateFlockingEnemies(delta);
352
+ updateBuildings(delta);
353
+ updateUI();
354
+
355
+ renderer.render(scene, camera);
356
+ }
357
+
358
+ init();
359
+ </script>
360
+ </body>
361
+ </html>
362
+ """
363
+
364
+ # Streamlit app
365
+ st.title("Galaxxon - Enhanced Arcade Game")
366
+ st.write("Use WASD or Arrow Keys to move, Spacebar to shoot. Crash into buildings for bonus points, avoid enemies!")
367
+
368
+ # Render the HTML game
369
+ html(game_html, height=600, scrolling=False)
370
+
371
+ st.write("Note: The game runs in your browser. Ensure you have an internet connection for Three.js to load.")