Update app.py
Browse files
app.py
CHANGED
@@ -31,10 +31,10 @@ game_html = """
|
|
31 |
<script type="module">
|
32 |
import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
|
33 |
|
34 |
-
let scene, camera, renderer, snake, foodItems = [], lSysCreatures = [], messages = [];
|
35 |
let clock = new THREE.Clock();
|
36 |
-
let moveDir = new THREE.Vector3(1, 0, 0), moveSpeed =
|
37 |
-
let score = 0, gameTime = 0, lives = 3;
|
38 |
let highScores = JSON.parse(localStorage.getItem('highScores')) || [];
|
39 |
let gameOver = false;
|
40 |
|
@@ -45,21 +45,14 @@ game_html = """
|
|
45 |
renderer.setSize(window.innerWidth, window.innerHeight);
|
46 |
document.body.appendChild(renderer.domElement);
|
47 |
|
48 |
-
camera.position.set(0,
|
49 |
camera.lookAt(0, 0, 0);
|
50 |
|
51 |
// Initialize 3D snake
|
52 |
-
|
53 |
-
const snakeMaterial = new THREE.MeshPhongMaterial({ color: 0x00ff00, shininess: 100 });
|
54 |
-
for (let i = 0; i < 3; i++) {
|
55 |
-
const segment = new THREE.Mesh(new THREE.SphereGeometry(0.5, 16, 16), snakeMaterial);
|
56 |
-
segment.position.set(-i * 1.2, 0, 0);
|
57 |
-
snake.push(segment);
|
58 |
-
scene.add(segment);
|
59 |
-
}
|
60 |
|
61 |
spawnFood();
|
62 |
-
|
63 |
|
64 |
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
|
65 |
scene.add(ambientLight);
|
@@ -97,30 +90,34 @@ game_html = """
|
|
97 |
}
|
98 |
}
|
99 |
|
100 |
-
function
|
101 |
const lSys = {
|
102 |
axiom: "F",
|
103 |
-
rules: { "F": "F[+F]F[-F]F" },
|
104 |
-
angle:
|
105 |
-
length:
|
106 |
-
iterations:
|
107 |
};
|
108 |
-
const material = new THREE.MeshPhongMaterial({ color:
|
109 |
-
for (let i = 0; i <
|
110 |
let turtleString = lSys.axiom;
|
111 |
for (let j = 0; j < lSys.iterations; j++) {
|
112 |
turtleString = turtleString.split('').map(c => lSys.rules[c] || c).join('');
|
113 |
}
|
114 |
-
const
|
115 |
let stack = [], pos = new THREE.Vector3((Math.random() - 0.5) * 40, 0, (Math.random() - 0.5) * 40);
|
116 |
let dir = new THREE.Vector3(0, lSys.length, 0);
|
117 |
|
118 |
for (let char of turtleString) {
|
119 |
if (char === 'F') {
|
120 |
-
const
|
|
|
|
|
|
|
|
|
121 |
segment.position.copy(pos).add(dir.clone().multiplyScalar(0.5));
|
122 |
segment.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir.clone().normalize());
|
123 |
-
|
124 |
pos.add(dir);
|
125 |
} else if (char === '+') {
|
126 |
dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), lSys.angle * Math.PI / 180);
|
@@ -134,15 +131,56 @@ game_html = """
|
|
134 |
dir = state.dir;
|
135 |
}
|
136 |
}
|
137 |
-
|
138 |
-
|
139 |
-
scene.add(
|
140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
}
|
|
|
|
|
|
|
|
|
142 |
}
|
143 |
|
144 |
function sendQuineMessage(sender) {
|
145 |
-
const quine = "function q(){
|
146 |
const messageDiv = document.createElement('div');
|
147 |
messageDiv.className = 'message';
|
148 |
messageDiv.innerText = 'Quine Msg';
|
@@ -170,18 +208,22 @@ game_html = """
|
|
170 |
}
|
171 |
if (gameOver) return;
|
172 |
switch (event.code) {
|
173 |
-
case 'ArrowLeft': case 'KeyA': moveDir.set(-1, 0, 0); break;
|
174 |
-
case 'ArrowRight': case 'KeyD': moveDir.set(1, 0, 0); break;
|
175 |
-
case 'ArrowUp': case 'KeyW': moveDir.set(0, 0, -1); break;
|
176 |
-
case 'ArrowDown': case 'KeyS': moveDir.set(0, 0, 1); break;
|
177 |
}
|
178 |
}
|
179 |
|
180 |
function updateSnake(delta) {
|
181 |
if (gameOver) return;
|
|
|
|
|
|
|
|
|
182 |
const head = snake[0];
|
183 |
const newHead = new THREE.Mesh(head.geometry, head.material);
|
184 |
-
newHead.position.copy(head.position).add(moveDir.clone().multiplyScalar(
|
185 |
|
186 |
// Boundary check
|
187 |
if (Math.abs(newHead.position.x) > 20 || Math.abs(newHead.position.z) > 20) {
|
@@ -197,6 +239,14 @@ game_html = """
|
|
197 |
}
|
198 |
}
|
199 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
200 |
snake.unshift(newHead);
|
201 |
scene.add(newHead);
|
202 |
|
@@ -207,11 +257,11 @@ game_html = """
|
|
207 |
foodItems.splice(i, 1);
|
208 |
score += 10;
|
209 |
spawnFood(); // Add new food
|
210 |
-
|
211 |
break;
|
212 |
} else {
|
213 |
-
snake.pop();
|
214 |
-
scene.remove(
|
215 |
}
|
216 |
}
|
217 |
|
@@ -266,13 +316,16 @@ game_html = """
|
|
266 |
resetSnake();
|
267 |
foodItems.forEach(f => scene.remove(f));
|
268 |
lSysCreatures.forEach(c => scene.remove(c));
|
|
|
269 |
foodItems = [];
|
270 |
lSysCreatures = [];
|
|
|
271 |
spawnFood();
|
272 |
-
|
273 |
score = 0;
|
274 |
lives = 3;
|
275 |
gameOver = false;
|
|
|
276 |
updateUI();
|
277 |
}
|
278 |
|
@@ -338,7 +391,7 @@ with st.sidebar:
|
|
338 |
st.write("- R to reset after game over")
|
339 |
st.write("**Objective:**")
|
340 |
st.write("- Eat alien food (pink dodecahedrons) to grow")
|
341 |
-
st.write("- Avoid L-system creatures
|
342 |
st.write("- Watch creatures exchange quine messages")
|
343 |
|
344 |
# Render the HTML game
|
|
|
31 |
<script type="module">
|
32 |
import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
|
33 |
|
34 |
+
let scene, camera, renderer, snake, foodItems = [], lSysCreatures = [], messages = [], cityscape = [];
|
35 |
let clock = new THREE.Clock();
|
36 |
+
let moveDir = new THREE.Vector3(1, 0, 0), moveSpeed = 2; // Adjusted speed for visibility
|
37 |
+
let score = 0, gameTime = 0, lives = 3, moveCounter = 0, moveInterval = 0.1; // Ticker interval in seconds
|
38 |
let highScores = JSON.parse(localStorage.getItem('highScores')) || [];
|
39 |
let gameOver = false;
|
40 |
|
|
|
45 |
renderer.setSize(window.innerWidth, window.innerHeight);
|
46 |
document.body.appendChild(renderer.domElement);
|
47 |
|
48 |
+
camera.position.set(0, 30, 40);
|
49 |
camera.lookAt(0, 0, 0);
|
50 |
|
51 |
// Initialize 3D snake
|
52 |
+
resetSnake();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
|
54 |
spawnFood();
|
55 |
+
spawnCityscape();
|
56 |
|
57 |
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
|
58 |
scene.add(ambientLight);
|
|
|
90 |
}
|
91 |
}
|
92 |
|
93 |
+
function spawnCityscape() {
|
94 |
const lSys = {
|
95 |
axiom: "F",
|
96 |
+
rules: { "F": "F[+F]F[-F][F]" },
|
97 |
+
angle: 30,
|
98 |
+
length: 3,
|
99 |
+
iterations: 2
|
100 |
};
|
101 |
+
const material = new THREE.MeshPhongMaterial({ color: 0x808080 });
|
102 |
+
for (let i = 0; i < 10; i++) {
|
103 |
let turtleString = lSys.axiom;
|
104 |
for (let j = 0; j < lSys.iterations; j++) {
|
105 |
turtleString = turtleString.split('').map(c => lSys.rules[c] || c).join('');
|
106 |
}
|
107 |
+
const building = new THREE.Group();
|
108 |
let stack = [], pos = new THREE.Vector3((Math.random() - 0.5) * 40, 0, (Math.random() - 0.5) * 40);
|
109 |
let dir = new THREE.Vector3(0, lSys.length, 0);
|
110 |
|
111 |
for (let char of turtleString) {
|
112 |
if (char === 'F') {
|
113 |
+
const height = Math.random() * 2 + 1;
|
114 |
+
const segment = new THREE.Mesh(
|
115 |
+
Math.random() > 0.5 ? new THREE.BoxGeometry(1, height, 1) : new THREE.CylinderGeometry(0.5, 0.5, height, 8),
|
116 |
+
material
|
117 |
+
);
|
118 |
segment.position.copy(pos).add(dir.clone().multiplyScalar(0.5));
|
119 |
segment.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir.clone().normalize());
|
120 |
+
building.add(segment);
|
121 |
pos.add(dir);
|
122 |
} else if (char === '+') {
|
123 |
dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), lSys.angle * Math.PI / 180);
|
|
|
131 |
dir = state.dir;
|
132 |
}
|
133 |
}
|
134 |
+
building.position.set(pos.x, 0, pos.z);
|
135 |
+
cityscape.push(building);
|
136 |
+
scene.add(building);
|
137 |
+
if (Math.random() > 0.7) spawnLSysCreature(building.position);
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
function spawnLSysCreature(position) {
|
142 |
+
const lSys = {
|
143 |
+
axiom: "F",
|
144 |
+
rules: { "F": "F[+F]F[-F]F" },
|
145 |
+
angle: 25,
|
146 |
+
length: 2,
|
147 |
+
iterations: 2
|
148 |
+
};
|
149 |
+
const material = new THREE.MeshPhongMaterial({ color: 0x0000ff });
|
150 |
+
let turtleString = lSys.axiom;
|
151 |
+
for (let j = 0; j < lSys.iterations; j++) {
|
152 |
+
turtleString = turtleString.split('').map(c => lSys.rules[c] || c).join('');
|
153 |
+
}
|
154 |
+
const creature = new THREE.Group();
|
155 |
+
let stack = [], pos = position.clone(), dir = new THREE.Vector3(0, lSys.length, 0);
|
156 |
+
|
157 |
+
for (let char of turtleString) {
|
158 |
+
if (char === 'F') {
|
159 |
+
const segment = new THREE.Mesh(new THREE.CylinderGeometry(0.1, 0.1, lSys.length, 8), material);
|
160 |
+
segment.position.copy(pos).add(dir.clone().multiplyScalar(0.5));
|
161 |
+
segment.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir.clone().normalize());
|
162 |
+
creature.add(segment);
|
163 |
+
pos.add(dir);
|
164 |
+
} else if (char === '+') {
|
165 |
+
dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), lSys.angle * Math.PI / 180);
|
166 |
+
} else if (char === '-') {
|
167 |
+
dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), -lSys.angle * Math.PI / 180);
|
168 |
+
} else if (char === '[') {
|
169 |
+
stack.push({ pos: pos.clone(), dir: dir.clone() });
|
170 |
+
} else if (char === ']') {
|
171 |
+
const state = stack.pop();
|
172 |
+
pos = state.pos;
|
173 |
+
dir = state.dir;
|
174 |
+
}
|
175 |
}
|
176 |
+
creature.position.copy(position);
|
177 |
+
lSysCreatures.push(creature);
|
178 |
+
scene.add(creature);
|
179 |
+
sendQuineMessage(creature);
|
180 |
}
|
181 |
|
182 |
function sendQuineMessage(sender) {
|
183 |
+
const quine = "function q(){console.log('Alive! '+q.toString())}q()";
|
184 |
const messageDiv = document.createElement('div');
|
185 |
messageDiv.className = 'message';
|
186 |
messageDiv.innerText = 'Quine Msg';
|
|
|
208 |
}
|
209 |
if (gameOver) return;
|
210 |
switch (event.code) {
|
211 |
+
case 'ArrowLeft': case 'KeyA': if (moveDir.x !== 1) moveDir.set(-1, 0, 0); break;
|
212 |
+
case 'ArrowRight': case 'KeyD': if (moveDir.x !== -1) moveDir.set(1, 0, 0); break;
|
213 |
+
case 'ArrowUp': case 'KeyW': if (moveDir.z !== 1) moveDir.set(0, 0, -1); break;
|
214 |
+
case 'ArrowDown': case 'KeyS': if (moveDir.z !== -1) moveDir.set(0, 0, 1); break;
|
215 |
}
|
216 |
}
|
217 |
|
218 |
function updateSnake(delta) {
|
219 |
if (gameOver) return;
|
220 |
+
moveCounter += delta;
|
221 |
+
if (moveCounter < moveInterval) return;
|
222 |
+
moveCounter = 0;
|
223 |
+
|
224 |
const head = snake[0];
|
225 |
const newHead = new THREE.Mesh(head.geometry, head.material);
|
226 |
+
newHead.position.copy(head.position).add(moveDir.clone().multiplyScalar(1)); // Move by 1 unit
|
227 |
|
228 |
// Boundary check
|
229 |
if (Math.abs(newHead.position.x) > 20 || Math.abs(newHead.position.z) > 20) {
|
|
|
239 |
}
|
240 |
}
|
241 |
|
242 |
+
// Cityscape collision
|
243 |
+
for (let building of cityscape) {
|
244 |
+
if (newHead.position.distanceTo(building.position) < 2) {
|
245 |
+
loseLife();
|
246 |
+
return;
|
247 |
+
}
|
248 |
+
}
|
249 |
+
|
250 |
snake.unshift(newHead);
|
251 |
scene.add(newHead);
|
252 |
|
|
|
257 |
foodItems.splice(i, 1);
|
258 |
score += 10;
|
259 |
spawnFood(); // Add new food
|
260 |
+
if (Math.random() > 0.8) spawnLSysCreature(newHead.position.clone());
|
261 |
break;
|
262 |
} else {
|
263 |
+
const tail = snake.pop();
|
264 |
+
scene.remove(tail);
|
265 |
}
|
266 |
}
|
267 |
|
|
|
316 |
resetSnake();
|
317 |
foodItems.forEach(f => scene.remove(f));
|
318 |
lSysCreatures.forEach(c => scene.remove(c));
|
319 |
+
cityscape.forEach(b => scene.remove(b));
|
320 |
foodItems = [];
|
321 |
lSysCreatures = [];
|
322 |
+
cityscape = [];
|
323 |
spawnFood();
|
324 |
+
spawnCityscape();
|
325 |
score = 0;
|
326 |
lives = 3;
|
327 |
gameOver = false;
|
328 |
+
moveCounter = 0;
|
329 |
updateUI();
|
330 |
}
|
331 |
|
|
|
391 |
st.write("- R to reset after game over")
|
392 |
st.write("**Objective:**")
|
393 |
st.write("- Eat alien food (pink dodecahedrons) to grow")
|
394 |
+
st.write("- Avoid cityscape buildings and L-system creatures")
|
395 |
st.write("- Watch creatures exchange quine messages")
|
396 |
|
397 |
# Render the HTML game
|