awacke1 commited on
Commit
23679ac
·
verified ·
1 Parent(s): 5770ddd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +193 -178
app.py CHANGED
@@ -24,125 +24,102 @@ if len(player_name) != 3 or not player_name.isalpha():
24
 
25
  html_code = f"""
26
  <!DOCTYPE html>
27
- <html>
28
  <head>
29
- <meta charset="utf-8">
 
30
  <title>Galaxian Snake 3D</title>
31
  <style>
32
- body {{ margin: 0; overflow: hidden; }}
33
- #container {{ width: {container_width}px; height: {container_height}px; margin: 0 auto; }}
34
  canvas {{ width: 100%; height: 100%; display: block; }}
35
- .ui-panel {{
36
- position: absolute;
37
- top: 10px;
38
- right: 10px;
39
- background: rgba(0,0,0,0.7);
40
- padding: 15px;
41
- border-radius: 5px;
42
- color: white;
43
- font-family: Arial, sans-serif;
44
- z-index: 1000;
45
  }}
46
- .ui-panel h3 {{ margin: 0 0 10px; }}
47
- #gameOver {{ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: red; font-size: 48px; z-index: 1; display: none; }}
48
  </style>
49
  </head>
50
  <body>
51
- <div id="container"></div>
52
- <div class="ui-panel">
53
- <h3>Leaderboard</h3>
54
- <div id="leaderboard"></div>
55
- <p>Score: <span id="score">0</span></p>
56
- <p>Time: <span id="time">0</span>s</p>
57
- <p>Length: <span id="length">3</span></p>
58
- <p>Lives: <span id="lives">3</span></p>
 
 
 
 
 
59
  </div>
60
- <div id="gameOver">Game Over</div>
61
-
62
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
63
  <script>
64
- let scene, camera, renderer, snake, foodItems = [], lSysCreatures = [], quineAgents = [], buildings = [];
65
  let score = 0, gameTime = 0, lives = 3, gameActive = true;
66
- const playerName = "{player_name}";
67
  const maxGameTime = 300; // 5 minutes in seconds
68
  let initialLength = 3, lastLength = 3, moveCounter = 0, moveInterval = 0.1;
69
  let moveDir = new THREE.Vector3(1, 0, 0);
70
- let highScores = JSON.parse(localStorage.getItem('highScores')) || [];
 
71
 
72
- // Building rules
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  const buildingRules = [
74
  {{name: "Colonial", axiom: "A", rules: {{"A": "B[+F][-F]", "B": "F[-C][+C]F", "C": "D[-E][+E]", "D": "F[+F][-F]F", "E": "F[-F][+F]"}}, iterations: 2, baseHeight: 10, baseWidth: 6, baseDepth: 6, angle: Math.PI/6, probability: 0.2}},
75
- {{name: "Victorian", axiom: "A", rules: {{"A": "B[+C][-C][/D][\\D]", "B": "F[+F][-F][/F][\\F]", "C": "F[++F][--F]", "D": "F[+\\F][-\\F]"}}, iterations: 3, baseHeight: 15, baseWidth: 5, baseDepth: 5, angle: Math.PI/5, probability: 0.15}},
76
- {{name: "Modern", axiom: "A", rules: {{"A": "B[+B][-B]", "B": "F[/C][\\C]", "C": "F"}}, iterations: 2, baseHeight: 20, baseWidth: 8, baseDepth: 8, angle: Math.PI/2, probability: 0.25}},
77
  {{name: "Skyscraper", axiom: "A", rules: {{"A": "FB[+C][-C]", "B": "FB", "C": "F[+F][-F]"}}, iterations: 4, baseHeight: 30, baseWidth: 10, baseDepth: 10, angle: Math.PI/8, probability: 0.15}},
78
  {{name: "Simple", axiom: "F", rules: {{"F": "F[+F][-F]"}}, iterations: 1, baseHeight: 8, baseWidth: 6, baseDepth: 6, angle: Math.PI/4, probability: 0.25}}
79
  ];
80
- const buildingColors = [0x888888, 0x666666, 0x999999, 0xaaaaaa, 0x555555, 0x334455, 0x445566, 0x223344, 0x556677, 0x667788, 0x993333, 0x884422, 0x553333, 0x772222, 0x664433];
81
-
82
- function init() {{
83
- const container = document.getElementById('container');
84
- if (!container) {{
85
- console.error('Container not found');
86
- return;
87
- }}
88
-
89
- // Scene
90
- scene = new THREE.Scene();
91
- scene.background = new THREE.Color(0x87CEEB);
92
-
93
- // Camera with 3:4 aspect ratio
94
- camera = new THREE.PerspectiveCamera(75, 3 / 4, 0.1, 1000);
95
- camera.position.set(0, 20, 30);
96
-
97
- // Renderer
98
- renderer = new THREE.WebGLRenderer({{ antialias: true }});
99
- renderer.setSize({container_width}, {container_height});
100
- renderer.shadowMap.enabled = true;
101
- renderer.shadowMap.type = THREE.PCFSoftShadowMap;
102
- container.appendChild(renderer.domElement);
103
-
104
- // Lights
105
- const ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
106
- scene.add(ambientLight);
107
- const sunLight = new THREE.DirectionalLight(0xffddaa, 0.8);
108
- sunLight.position.set(50, 50, 50);
109
- sunLight.castShadow = true;
110
- sunLight.shadow.mapSize.width = 1024;
111
- sunLight.shadow.mapSize.height = 1024;
112
- sunLight.shadow.camera.near = 0.5;
113
- sunLight.shadow.camera.far = 500;
114
- scene.add(sunLight);
115
-
116
- // Ground
117
- const textureLoader = new THREE.TextureLoader();
118
- const groundGeometry = new THREE.PlaneGeometry(200, 200);
119
- const groundMaterial = new THREE.MeshStandardMaterial({{
120
- color: 0x1a5e1a,
121
- roughness: 0.8,
122
- metalness: 0.2,
123
- bumpMap: textureLoader.load('https://threejs.org/examples/textures/terrain/grasslight-big-nm.jpg'),
124
- bumpScale: 0.1
125
- }});
126
- const ground = new THREE.Mesh(groundGeometry, groundMaterial);
127
- ground.rotation.x = -Math.PI / 2;
128
- ground.receiveShadow = true;
129
- scene.add(ground);
130
-
131
- // Initialize snake
132
- resetSnake();
133
- createCity();
134
- spawnFood();
135
-
136
- // Leaderboard
137
- updateLeaderboard();
138
-
139
- // Start timer
140
- setInterval(updateGame, 1000 / 60); // 60 FPS
141
-
142
- window.addEventListener('resize', onWindowResize);
143
- window.addEventListener('keydown', onKeyDown);
144
- window.addEventListener('keyup', onKeyUp);
145
- }}
146
 
147
  function interpretLSystem(rule, position, rotation) {{
148
  let currentString = rule.axiom;
@@ -154,8 +131,8 @@ html_code = f"""
154
  currentString = newString;
155
  }}
156
 
157
- let group = new THREE.Group();
158
- group.position.copy(position);
159
  const stack = [];
160
  let currentPosition = new THREE.Vector3(0, 0, 0);
161
  let currentRotation = rotation || new THREE.Euler();
@@ -163,20 +140,21 @@ html_code = f"""
163
  const color = buildingColors[Math.floor(Math.random() * buildingColors.length)];
164
  const material = new THREE.MeshStandardMaterial({{color: color, roughness: 0.7, metalness: 0.2}});
165
 
166
- for (let char of currentString) {{
 
167
  switch (char) {{
168
  case 'F':
169
  const width = rule.baseWidth * (0.5 + Math.random() * 0.5) * scale.x;
170
  const height = rule.baseHeight * (0.5 + Math.random() * 0.5) * scale.y;
171
  const depth = rule.baseDepth * (0.5 + Math.random() * 0.5) * scale.z;
172
  const geometry = new THREE.BoxGeometry(width, height, depth);
173
- const part = new THREE.Mesh(geometry, material);
174
- part.position.copy(currentPosition);
175
- part.position.y += height / 2;
176
- part.rotation.copy(currentRotation);
177
- part.castShadow = true;
178
- part.receiveShadow = true;
179
- group.add(part);
180
  const direction = new THREE.Vector3(0, height, 0);
181
  direction.applyEuler(currentRotation);
182
  currentPosition.add(direction);
@@ -193,7 +171,7 @@ html_code = f"""
193
  case '<': scale.multiplyScalar(0.8); break;
194
  }}
195
  }}
196
- return group;
197
  }}
198
 
199
  function createCity() {{
@@ -216,19 +194,17 @@ html_code = f"""
216
  }}
217
  const roadWidth = 8, roadColor = 0x333333;
218
  for (let x = -citySize; x <= citySize; x++) {{
219
- const road = new THREE.Mesh(
220
- new THREE.PlaneGeometry(roadWidth, citySize * 2 * spacing + roadWidth),
221
- new THREE.MeshStandardMaterial({{color: roadColor, roughness: 0.9, metalness: 0.1}})
222
- );
223
  road.rotation.x = -Math.PI / 2;
224
  road.position.set(x * spacing, 0.01, 0);
225
  scene.add(road);
226
  }}
227
  for (let z = -citySize; z <= citySize; z++) {{
228
- const road = new THREE.Mesh(
229
- new THREE.PlaneGeometry(citySize * 2 * spacing + roadWidth, roadWidth),
230
- new THREE.MeshStandardMaterial({{color: roadColor, roughness: 0.9, metalness: 0.1}})
231
- );
232
  road.rotation.x = -Math.PI / 2;
233
  road.position.set(0, 0.01, z * spacing);
234
  scene.add(road);
@@ -334,23 +310,27 @@ html_code = f"""
334
  lastLength = initialLength;
335
  }}
336
 
 
337
  const keys = {{w: false, a: false, s: false, d: false, r: false}};
338
- function onKeyDown(event) {{
339
- const key = event.key.toLowerCase();
340
- if (key === 'w' || key === 'arrowup') keys.w = true;
341
- if (key === 'a' || key === 'arrowleft') keys.a = true;
342
- if (key === 's' || key === 'arrowdown') keys.s = true;
343
- if (key === 'd' || key === 'arrowright') keys.d = true;
344
- if (key === 'r') keys.r = true;
345
- }}
346
- function onKeyUp(event) {{
347
- const key = event.key.toLowerCase();
348
- if (key === 'w' || key === 'arrowup') keys.w = false;
349
- if (key === 'a' || key === 'arrowleft') keys.a = false;
350
- if (key === 's' || key === 'arrowdown') keys.s = false;
351
- if (key === 'd' || key === 'arrowright') keys.d = false;
352
- if (key === 'r' && !gameActive) resetGame();
353
- }}
 
 
 
354
 
355
  function updateSnake(delta) {{
356
  if (!gameActive) return;
@@ -486,74 +466,109 @@ html_code = f"""
486
  resetSnake();
487
  }}
488
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  function resetGame() {{
490
- resetSnake();
491
  foodItems.forEach(f => scene.remove(f));
492
  lSysCreatures.forEach(c => scene.remove(c));
493
  quineAgents.forEach(a => scene.remove(a));
494
  buildings.forEach(b => scene.remove(b));
 
495
  foodItems = [];
496
  lSysCreatures = [];
497
  quineAgents = [];
498
  buildings = [];
499
- score = 0;
500
- lives = 3;
501
- gameActive = true;
502
- gameTime = 0;
503
- moveCounter = 0;
504
  createCity();
505
  spawnFood();
506
- document.getElementById('gameOver').style.display = 'none';
507
- updateUI();
508
- }}
509
-
510
- function endGame() {{
511
- gameActive = false;
512
- document.getElementById('gameOver').style.display = 'block';
513
- saveScore();
514
- updateLeaderboard();
515
- }}
516
-
517
- function updateUI() {{
518
- document.getElementById('score').textContent = Math.floor(score);
519
- document.getElementById('time').textContent = Math.floor(gameTime);
520
- document.getElementById('length').textContent = snake.length;
521
- document.getElementById('lives').textContent = lives;
522
- }}
523
-
524
- function saveScore() {{
525
- highScores.push({{ name: playerName, score: Math.floor(score), time: Math.floor(gameTime) }});
526
- highScores.sort((a, b) => b.score - a.score);
527
- highScores = highScores.slice(0, 5);
528
- localStorage.setItem('highScores', JSON.stringify(highScores));
529
  }}
530
 
531
- function updateLeaderboard() {{
532
- const leaderboard = document.getElementById('leaderboard');
533
- leaderboard.innerHTML = highScores.map(s => `<p>${{s.name}}: ${{s.score}} (${{s.time}}s)</p>`).join('');
534
- }}
 
 
 
535
 
536
- function updateGame() {{
537
- const delta = 1 / 60; // Fixed delta for 60 FPS
538
  if (gameActive) {{
539
- gameTime += delta;
540
- if (gameTime >= maxGameTime) endGame();
541
  updateSnake(delta);
542
  updateScore(delta);
543
  updateQuineAgents(delta);
 
544
  }}
545
  renderer.render(scene, camera);
546
  }}
547
 
548
- function onWindowResize() {{
549
- const width = {container_width};
550
- const height = {container_height};
551
- camera.aspect = 3 / 4;
552
  camera.updateProjectionMatrix();
553
- renderer.setSize(width, height);
554
- }}
555
-
556
- window.onload = init;
 
 
 
 
 
557
  </script>
558
  </body>
559
  </html>
 
24
 
25
  html_code = f"""
26
  <!DOCTYPE html>
27
+ <html lang="en">
28
  <head>
29
+ <meta charset="UTF-8">
30
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
31
  <title>Galaxian Snake 3D</title>
32
  <style>
33
+ body {{ margin: 0; overflow: hidden; font-family: Arial, sans-serif; }}
34
+ #gameContainer {{ width: {container_width}px; height: {container_height}px; position: relative; }}
35
  canvas {{ width: 100%; height: 100%; display: block; }}
36
+ .ui-container {{
37
+ position: absolute; top: 10px; left: 10px; color: white;
38
+ background-color: rgba(0, 0, 0, 0.5); padding: 10px; border-radius: 5px;
39
+ user-select: none;
40
+ }}
41
+ .controls {{
42
+ position: absolute; bottom: 10px; left: 10px; color: white;
43
+ background-color: rgba(0, 0, 0, 0.5); padding: 10px; border-radius: 5px;
 
 
44
  }}
 
 
45
  </style>
46
  </head>
47
  <body>
48
+ <div id="gameContainer">
49
+ <div class="ui-container">
50
+ <h2>Galaxian Snake 3D</h2>
51
+ <div id="score">Score: 0</div>
52
+ <div id="time">Time: 0</div>
53
+ <div id="length">Length: 3</div>
54
+ <div id="lives">Lives: 3</div>
55
+ <div id="leaderboard"></div>
56
+ </div>
57
+ <div class="controls">
58
+ <p>Controls: W/A/S/D or Arrow Keys to move, R to reset</p>
59
+ <p>Eat yellow cubes to grow and score points!</p>
60
+ </div>
61
  </div>
62
+
 
63
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
64
  <script>
65
+ // Game variables
66
  let score = 0, gameTime = 0, lives = 3, gameActive = true;
67
+ let snake = [], foodItems = [], lSysCreatures = [], quineAgents = [], buildings = [];
68
  const maxGameTime = 300; // 5 minutes in seconds
69
  let initialLength = 3, lastLength = 3, moveCounter = 0, moveInterval = 0.1;
70
  let moveDir = new THREE.Vector3(1, 0, 0);
71
+ const highScores = JSON.parse(localStorage.getItem('highScores')) || [];
72
+ const playerName = "{player_name}";
73
 
74
+ // Scene setup
75
+ const scene = new THREE.Scene();
76
+ const camera = new THREE.PerspectiveCamera(75, {container_width} / {container_height}, 0.1, 1000);
77
+ camera.position.set(0, 20, 30);
78
+ const renderer = new THREE.WebGLRenderer({{ antialias: true }});
79
+ renderer.setSize({container_width}, {container_height});
80
+ renderer.shadowMap.enabled = true;
81
+ document.getElementById('gameContainer').appendChild(renderer.domElement);
82
+
83
+ // Lighting
84
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
85
+ scene.add(ambientLight);
86
+
87
+ const sunLight = new THREE.DirectionalLight(0xffddaa, 0.8);
88
+ sunLight.castShadow = true;
89
+ sunLight.shadow.mapSize.width = 2048;
90
+ sunLight.shadow.mapSize.height = 2048;
91
+ sunLight.shadow.camera.near = 1;
92
+ sunLight.shadow.camera.far = 500;
93
+ sunLight.shadow.camera.left = -100;
94
+ sunLight.shadow.camera.right = 100;
95
+ sunLight.shadow.camera.top = 100;
96
+ sunLight.shadow.camera.bottom = -100;
97
+ scene.add(sunLight);
98
+
99
+ // Ground with bump mapping
100
+ const textureLoader = new THREE.TextureLoader();
101
+ const groundGeometry = new THREE.PlaneGeometry(200, 200);
102
+ const groundMaterial = new THREE.MeshStandardMaterial({{
103
+ color: 0x1a5e1a,
104
+ roughness: 0.8,
105
+ metalness: 0.2,
106
+ bumpMap: textureLoader.load('https://threejs.org/examples/textures/terrain/grasslight-big-nm.jpg'),
107
+ bumpScale: 0.1
108
+ }});
109
+ const ground = new THREE.Mesh(groundGeometry, groundMaterial);
110
+ ground.rotation.x = -Math.PI / 2;
111
+ ground.receiveShadow = true;
112
+ scene.add(ground);
113
+
114
+ // Buildings and arrays
115
+ const buildingColors = [0x888888, 0x666666, 0x999999, 0xaaaaaa, 0x555555, 0x334455, 0x445566, 0x223344, 0x556677, 0x667788, 0x993333, 0x884422, 0x553333, 0x772222, 0x664433];
116
  const buildingRules = [
117
  {{name: "Colonial", axiom: "A", rules: {{"A": "B[+F][-F]", "B": "F[-C][+C]F", "C": "D[-E][+E]", "D": "F[+F][-F]F", "E": "F[-F][+F]"}}, iterations: 2, baseHeight: 10, baseWidth: 6, baseDepth: 6, angle: Math.PI/6, probability: 0.2}},
118
+ {{name: "Victorian", axiom: "A", rules: {{"A": "B[+C][-C][/D][\\\\D]", "B": "F[+F][-F][/F][\\\\F]", "C": "F[++F][--F]", "D": "F[+\\\\F][-\\\\F]"}}, iterations: 3, baseHeight: 15, baseWidth: 5, baseDepth: 5, angle: Math.PI/5, probability: 0.15}},
119
+ {{name: "Modern", axiom: "A", rules: {{"A": "B[+B][-B]", "B": "F[/C][\\\\C]", "C": "F"}}, iterations: 2, baseHeight: 20, baseWidth: 8, baseDepth: 8, angle: Math.PI/2, probability: 0.25}},
120
  {{name: "Skyscraper", axiom: "A", rules: {{"A": "FB[+C][-C]", "B": "FB", "C": "F[+F][-F]"}}, iterations: 4, baseHeight: 30, baseWidth: 10, baseDepth: 10, angle: Math.PI/8, probability: 0.15}},
121
  {{name: "Simple", axiom: "F", rules: {{"F": "F[+F][-F]"}}, iterations: 1, baseHeight: 8, baseWidth: 6, baseDepth: 6, angle: Math.PI/4, probability: 0.25}}
122
  ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
  function interpretLSystem(rule, position, rotation) {{
125
  let currentString = rule.axiom;
 
131
  currentString = newString;
132
  }}
133
 
134
+ let buildingGroup = new THREE.Group();
135
+ buildingGroup.position.copy(position);
136
  const stack = [];
137
  let currentPosition = new THREE.Vector3(0, 0, 0);
138
  let currentRotation = rotation || new THREE.Euler();
 
140
  const color = buildingColors[Math.floor(Math.random() * buildingColors.length)];
141
  const material = new THREE.MeshStandardMaterial({{color: color, roughness: 0.7, metalness: 0.2}});
142
 
143
+ for (let i = 0; i < currentString.length; i++) {{
144
+ const char = currentString[i];
145
  switch (char) {{
146
  case 'F':
147
  const width = rule.baseWidth * (0.5 + Math.random() * 0.5) * scale.x;
148
  const height = rule.baseHeight * (0.5 + Math.random() * 0.5) * scale.y;
149
  const depth = rule.baseDepth * (0.5 + Math.random() * 0.5) * scale.z;
150
  const geometry = new THREE.BoxGeometry(width, height, depth);
151
+ const buildingPart = new THREE.Mesh(geometry, material);
152
+ buildingPart.position.copy(currentPosition);
153
+ buildingPart.position.y += height / 2;
154
+ buildingPart.rotation.copy(currentRotation);
155
+ buildingPart.castShadow = true;
156
+ buildingPart.receiveShadow = true;
157
+ buildingGroup.add(buildingPart);
158
  const direction = new THREE.Vector3(0, height, 0);
159
  direction.applyEuler(currentRotation);
160
  currentPosition.add(direction);
 
171
  case '<': scale.multiplyScalar(0.8); break;
172
  }}
173
  }}
174
+ return buildingGroup;
175
  }}
176
 
177
  function createCity() {{
 
194
  }}
195
  const roadWidth = 8, roadColor = 0x333333;
196
  for (let x = -citySize; x <= citySize; x++) {{
197
+ const roadGeometry = new THREE.PlaneGeometry(roadWidth, citySize * 2 * spacing + roadWidth);
198
+ const roadMaterial = new THREE.MeshStandardMaterial({{color: roadColor, roughness: 0.9, metalness: 0.1}});
199
+ const road = new THREE.Mesh(roadGeometry, roadMaterial);
 
200
  road.rotation.x = -Math.PI / 2;
201
  road.position.set(x * spacing, 0.01, 0);
202
  scene.add(road);
203
  }}
204
  for (let z = -citySize; z <= citySize; z++) {{
205
+ const roadGeometry = new THREE.PlaneGeometry(citySize * 2 * spacing + roadWidth, roadWidth);
206
+ const roadMaterial = new THREE.MeshStandardMaterial({{color: roadColor, roughness: 0.9, metalness: 0.1}});
207
+ const road = new THREE.Mesh(roadGeometry, roadMaterial);
 
208
  road.rotation.x = -Math.PI / 2;
209
  road.position.set(0, 0.01, z * spacing);
210
  scene.add(road);
 
310
  lastLength = initialLength;
311
  }}
312
 
313
+ // Snake controls
314
  const keys = {{w: false, a: false, s: false, d: false, r: false}};
315
+ document.addEventListener('keydown', (event) => {{
316
+ switch (event.key.toLowerCase()) {{
317
+ case 'w': case 'arrowup': keys.w = true; break;
318
+ case 'a': case 'arrowleft': keys.a = true; break;
319
+ case 's': case 'arrowdown': keys.s = true; break;
320
+ case 'd': case 'arrowright': keys.d = true; break;
321
+ case 'r': keys.r = true; break;
322
+ }}
323
+ }});
324
+
325
+ document.addEventListener('keyup', (event) => {{
326
+ switch (event.key.toLowerCase()) {{
327
+ case 'w': case 'arrowup': keys.w = false; break;
328
+ case 'a': case 'arrowleft': keys.a = false; break;
329
+ case 's': case 'arrowdown': keys.s = false; break;
330
+ case 'd': case 'arrowright': keys.d = false; break;
331
+ case 'r': if (!gameActive) resetGame(); break;
332
+ }}
333
+ }});
334
 
335
  function updateSnake(delta) {{
336
  if (!gameActive) return;
 
466
  resetSnake();
467
  }}
468
 
469
+ // Sun cycle (simplified, no moon)
470
+ let cycleTime = 0;
471
+ function updateLighting(delta) {{
472
+ cycleTime += delta;
473
+ const cycleDuration = 120;
474
+ const angle = (cycleTime / cycleDuration) * Math.PI * 2;
475
+ sunLight.position.set(
476
+ Math.cos(angle) * 100,
477
+ Math.sin(angle) * 100,
478
+ Math.sin(angle) * 50
479
+ );
480
+ sunLight.intensity = Math.max(0, Math.sin(angle)) * 0.8;
481
+ const dayColor = new THREE.Color(0x87CEEB);
482
+ const nightColor = new THREE.Color(0x001133);
483
+ scene.background = dayColor.clone().lerp(nightColor, Math.max(0, -Math.sin(angle)));
484
+ }}
485
+
486
+ function updateUI() {{
487
+ document.getElementById('score').textContent = `Score: ${{Math.floor(score)}}`;
488
+ document.getElementById('time').textContent = `Time: ${{Math.floor(gameTime)}}`;
489
+ document.getElementById('length').textContent = `Length: ${{snake.length}}`;
490
+ document.getElementById('lives').textContent = `Lives: ${{lives}}`;
491
+ document.getElementById('leaderboard').innerHTML = highScores.map(s => `<p>${{s.name}}: ${{s.score}} (${{s.time}}s)</p>`).join('');
492
+ }}
493
+
494
+ function updateTimer() {{
495
+ if (gameActive && gameTime < maxGameTime) {{
496
+ gameTime++;
497
+ updateUI();
498
+ if (gameTime >= maxGameTime) {{
499
+ gameActive = false;
500
+ endGame();
501
+ }}
502
+ }}
503
+ }}
504
+
505
+ function endGame() {{
506
+ highScores.push({{ name: playerName, score: Math.floor(score), time: Math.floor(gameTime) }});
507
+ highScores.sort((a, b) => b.score - a.score);
508
+ highScores.splice(5); // Keep top 5
509
+ localStorage.setItem('highScores', JSON.stringify(highScores));
510
+ updateUI();
511
+ const endScreen = document.createElement('div');
512
+ endScreen.style.cssText = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(0,0,0,0.8);color:white;padding:20px;border-radius:10px;text-align:center;';
513
+ endScreen.innerHTML = `
514
+ <h2>Game Over!</h2>
515
+ <p>Your final score: ${{Math.floor(score)}}</p>
516
+ <button id="restart-btn" style="padding:10px 20px;background:#4CAF50;color:white;border:none;border-radius:5px;cursor:pointer;margin-top:10px;">Play Again</button>
517
+ `;
518
+ document.getElementById('gameContainer').appendChild(endScreen);
519
+ document.getElementById('restart-btn').addEventListener('click', () => {{
520
+ document.getElementById('gameContainer').removeChild(endScreen);
521
+ score = 0; gameTime = 0; lives = 3; gameActive = true;
522
+ updateUI();
523
+ resetGame();
524
+ }});
525
+ }}
526
+
527
  function resetGame() {{
528
+ snake.forEach(seg => scene.remove(seg));
529
  foodItems.forEach(f => scene.remove(f));
530
  lSysCreatures.forEach(c => scene.remove(c));
531
  quineAgents.forEach(a => scene.remove(a));
532
  buildings.forEach(b => scene.remove(b));
533
+ snake = [];
534
  foodItems = [];
535
  lSysCreatures = [];
536
  quineAgents = [];
537
  buildings = [];
538
+ resetSnake();
 
 
 
 
539
  createCity();
540
  spawnFood();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
  }}
542
 
543
+ // Game loop
544
+ let lastTime = performance.now();
545
+ function animate() {{
546
+ requestAnimationFrame(animate);
547
+ const currentTime = performance.now();
548
+ const delta = (currentTime - lastTime) / 1000;
549
+ lastTime = currentTime;
550
 
 
 
551
  if (gameActive) {{
 
 
552
  updateSnake(delta);
553
  updateScore(delta);
554
  updateQuineAgents(delta);
555
+ updateLighting(delta);
556
  }}
557
  renderer.render(scene, camera);
558
  }}
559
 
560
+ window.addEventListener('resize', () => {{
561
+ camera.aspect = {container_width} / {container_height};
 
 
562
  camera.updateProjectionMatrix();
563
+ renderer.setSize({container_width}, {container_height});
564
+ }});
565
+
566
+ // Initialize
567
+ resetSnake();
568
+ createCity();
569
+ spawnFood();
570
+ setInterval(updateTimer, 1000);
571
+ animate();
572
  </script>
573
  </body>
574
  </html>