akhaliq HF staff commited on
Commit
4d71127
·
verified ·
1 Parent(s): b9232bd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +156 -121
app.py CHANGED
@@ -8,34 +8,24 @@ with demo:
8
  <head>
9
  <meta charset="UTF-8">
10
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
11
- <title>3D Maze Game Demo</title>
12
  <style>
13
- body {
14
- margin: 0;
15
- overflow: hidden;
16
- }
17
- #container {
18
- position: relative;
19
- }
20
- #debug {
21
  position: absolute;
22
  top: 10px;
23
  left: 10px;
24
  color: white;
25
- background: rgba(0, 0, 0, 0.5);
26
- padding: 10px;
27
  font-family: Arial, sans-serif;
28
  }
29
  </style>
30
  </head>
31
  <body>
32
  <div id="container"></div>
33
- <div id="debug">
34
- <p>Position: <span id="position"></span></p>
35
- <p>Rotation: <span id="rotation"></span></p>
36
- </div>
37
 
38
- <!-- Libraries from CDNs -->
39
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
40
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/GLTFLoader.js"></script>
41
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
@@ -62,7 +52,7 @@ with demo:
62
  canvas.width = 256;
63
  canvas.height = 256;
64
  const context = canvas.getContext('2d');
65
- context.fillStyle = '#8B4513'; // Brown tone
66
  context.fillRect(0, 0, 256, 256);
67
  context.strokeStyle = '#FFFFFF';
68
  context.lineWidth = 2;
@@ -89,7 +79,7 @@ with demo:
89
  canvas.width = 64;
90
  canvas.height = 64;
91
  const context = canvas.getContext('2d');
92
- context.fillStyle = '#808080'; // Gray base
93
  context.fillRect(0, 0, 64, 64);
94
  context.fillStyle = '#A0A0A0';
95
  for (let i = 0; i < 100; i++) {
@@ -125,145 +115,190 @@ with demo:
125
  }
126
  }
127
 
128
- // Walls and Collision Bounds
129
  const wallTexture = createWallTexture();
130
  const wallGeometry = new THREE.BoxGeometry(cellSize, cellSize, cellSize);
131
  const wallMaterial = new THREE.MeshBasicMaterial({ map: wallTexture });
132
- const walls = [];
133
- const wallBounds = [];
134
  for (let i = 0; i < gridSize; i++) {
135
  for (let j = 0; j < gridSize; j++) {
136
  if (maze[i][j] === 1) {
137
  const wall = new THREE.Mesh(wallGeometry, wallMaterial);
138
- const x = (j - 7.5) * cellSize;
139
- const z = (i - 7.5) * cellSize;
140
- wall.position.set(x, cellSize / 2, z);
141
  scene.add(wall);
142
- walls.push(wall);
143
- wallBounds.push({
144
- minX: x - cellSize / 2,
145
- maxX: x + cellSize / 2,
146
- minZ: z - cellSize / 2,
147
- maxZ: z + cellSize / 2
148
- });
149
  }
150
  }
151
  }
152
 
153
- // Soldier Setup
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  const loader = new THREE.GLTFLoader();
155
- let soldier, mixer, idleAction, runAction, activeAction;
 
 
 
156
  loader.load(
157
  'https://threejs.org/examples/models/gltf/Soldier.glb',
158
  (gltf) => {
159
- soldier = gltf.scene;
160
- soldier.scale.set(2, 2, 2);
161
- soldier.position.set(0, 0, 0);
162
- scene.add(soldier);
163
-
164
- mixer = new THREE.AnimationMixer(soldier);
165
  const animations = gltf.animations;
166
- idleAction = mixer.clipAction(THREE.AnimationClip.findByName(animations, 'Idle'));
167
- runAction = mixer.clipAction(THREE.AnimationClip.findByName(animations, 'Run'));
168
- activeAction = idleAction;
169
- activeAction.play();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  },
171
  undefined,
172
  (error) => console.error('Error loading soldier:', error)
173
  );
174
 
175
  // Camera and Controls
176
- camera.position.set(0, 5, 10);
177
  const controls = new THREE.OrbitControls(camera, renderer.domElement);
178
  controls.target.set(0, 0, 0);
179
  controls.update();
180
 
181
- // Movement Controls
182
- const keys = new Set();
183
- window.addEventListener('keydown', (e) => keys.add(e.key.toLowerCase()));
184
- window.addEventListener('keyup', (e) => keys.delete(e.key.toLowerCase()));
185
-
186
- const moveSpeed = 10; // Units per second
187
- const collisionRadius = 0.5;
188
-
189
- function updateSoldier(delta) {
190
- if (!soldier) return;
191
-
192
- const forward = new THREE.Vector3();
193
- camera.getWorldDirection(forward);
194
- forward.y = 0;
195
- forward.normalize();
196
-
197
- const right = new THREE.Vector3();
198
- right.crossVectors(forward, new THREE.Vector3(0, 1, 0));
199
-
200
- const moveDirection = new THREE.Vector3();
201
- if (keys.has('w')) moveDirection.add(forward);
202
- if (keys.has('s')) moveDirection.add(forward.clone().negate());
203
- if (keys.has('a')) moveDirection.add(right.clone().negate());
204
- if (keys.has('d')) moveDirection.add(right);
205
-
206
- if (moveDirection.lengthSq() > 0) {
207
- moveDirection.normalize();
208
- const newPosition = soldier.position.clone().add(
209
- moveDirection.multiplyScalar(moveSpeed * delta)
210
- );
211
-
212
- // Collision Detection
213
- let colliding = false;
214
- for (const wall of wallBounds) {
215
- if (
216
- newPosition.x > wall.minX - collisionRadius &&
217
- newPosition.x < wall.maxX + collisionRadius &&
218
- newPosition.z > wall.minZ - collisionRadius &&
219
- newPosition.z < wall.maxZ + collisionRadius
220
- ) {
221
- colliding = true;
222
- break;
223
- }
224
- }
225
-
226
- if (!colliding) {
227
- soldier.position.copy(newPosition);
228
- const angle = Math.atan2(moveDirection.x, moveDirection.z);
229
- soldier.rotation.y = angle + Math.PI; // Orientation fix
230
- }
231
-
232
- // Animation Transition to Run
233
- if (activeAction !== runAction) {
234
- activeAction.fadeOut(0.2);
235
- runAction.reset().fadeIn(0.2).play();
236
- activeAction = runAction;
237
- }
238
- } else {
239
- // Animation Transition to Idle
240
- if (activeAction !== idleAction) {
241
- activeAction.fadeOut(0.2);
242
- idleAction.reset().fadeIn(0.2).play();
243
- activeAction = idleAction;
244
- }
245
- }
246
- }
247
-
248
  // Animation Loop
249
  const clock = new THREE.Clock();
250
  function animate() {
251
  requestAnimationFrame(animate);
252
  const delta = clock.getDelta();
253
 
254
- if (mixer) mixer.update(delta);
255
- updateSoldier(delta);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
 
257
- if (soldier) {
258
- controls.target.copy(soldier.position);
259
- const pos = soldier.position;
260
- document.getElementById('position').textContent =
261
- `(${pos.x.toFixed(2)}, ${pos.y.toFixed(2)}, ${pos.z.toFixed(2)})`;
262
- document.getElementById('rotation').textContent =
263
- soldier.rotation.y.toFixed(2);
 
 
 
 
 
 
 
264
  }
265
 
266
- controls.update();
267
  renderer.render(scene, camera);
268
  }
269
  animate();
 
8
  <head>
9
  <meta charset="UTF-8">
10
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
11
+ <title>Autonomous Maze Game Demo</title>
12
  <style>
13
+ body { margin: 0; overflow: hidden; }
14
+ #status {
 
 
 
 
 
 
15
  position: absolute;
16
  top: 10px;
17
  left: 10px;
18
  color: white;
19
+ background: rgba(0, 0, 0, 0.7);
20
+ padding: 5px;
21
  font-family: Arial, sans-serif;
22
  }
23
  </style>
24
  </head>
25
  <body>
26
  <div id="container"></div>
27
+ <div id="status">Status: In progress</div>
 
 
 
28
 
 
29
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
30
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/GLTFLoader.js"></script>
31
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
 
52
  canvas.width = 256;
53
  canvas.height = 256;
54
  const context = canvas.getContext('2d');
55
+ context.fillStyle = '#8B4513'; // Brown
56
  context.fillRect(0, 0, 256, 256);
57
  context.strokeStyle = '#FFFFFF';
58
  context.lineWidth = 2;
 
79
  canvas.width = 64;
80
  canvas.height = 64;
81
  const context = canvas.getContext('2d');
82
+ context.fillStyle = '#808080'; // Gray
83
  context.fillRect(0, 0, 64, 64);
84
  context.fillStyle = '#A0A0A0';
85
  for (let i = 0; i < 100; i++) {
 
115
  }
116
  }
117
 
118
+ // Walls
119
  const wallTexture = createWallTexture();
120
  const wallGeometry = new THREE.BoxGeometry(cellSize, cellSize, cellSize);
121
  const wallMaterial = new THREE.MeshBasicMaterial({ map: wallTexture });
 
 
122
  for (let i = 0; i < gridSize; i++) {
123
  for (let j = 0; j < gridSize; j++) {
124
  if (maze[i][j] === 1) {
125
  const wall = new THREE.Mesh(wallGeometry, wallMaterial);
126
+ wall.position.set((j - 7.5) * cellSize, cellSize / 2, (i - 7.5) * cellSize);
 
 
127
  scene.add(wall);
 
 
 
 
 
 
 
128
  }
129
  }
130
  }
131
 
132
+ // Flag
133
+ const flagGeometry = new THREE.BoxGeometry(1, 2, 1);
134
+ const flagMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 }); // Yellow
135
+ const flag = new THREE.Mesh(flagGeometry, flagMaterial);
136
+ flag.position.set(2, 1, 2); // Center of maze (8,8) translated to world coords
137
+ scene.add(flag);
138
+
139
+ // A* Pathfinding
140
+ function aStar(maze, start, goal) {
141
+ const openSet = [start];
142
+ const cameFrom = {};
143
+ const gScore = { [start]: 0 };
144
+ const fScore = { [start]: heuristic(start, goal) };
145
+
146
+ while (openSet.length > 0) {
147
+ let current = openSet.reduce((a, b) => fScore[a] < fScore[b] ? a : b);
148
+ if (current === goal) return reconstructPath(cameFrom, current);
149
+
150
+ openSet.splice(openSet.indexOf(current), 1);
151
+ for (let neighbor of getNeighbors(maze, current)) {
152
+ let tentative_gScore = gScore[current] + 1;
153
+ if (!(neighbor in gScore) || tentative_gScore < gScore[neighbor]) {
154
+ cameFrom[neighbor] = current;
155
+ gScore[neighbor] = tentative_gScore;
156
+ fScore[neighbor] = gScore[neighbor] + heuristic(neighbor, goal);
157
+ if (!openSet.includes(neighbor)) openSet.push(neighbor);
158
+ }
159
+ }
160
+ }
161
+ return [];
162
+ }
163
+
164
+ function heuristic(a, b) {
165
+ const [aRow, aCol] = a.split(',').map(Number);
166
+ const [bRow, bCol] = b.split(',').map(Number);
167
+ return Math.abs(aRow - bRow) + Math.abs(aCol - bCol);
168
+ }
169
+
170
+ function getNeighbors(maze, node) {
171
+ const [row, col] = node.split(',').map(Number);
172
+ const neighbors = [];
173
+ if (row > 0 && maze[row - 1][col] === 0) neighbors.push(`${row - 1},${col}`);
174
+ if (row < 15 && maze[row + 1][col] === 0) neighbors.push(`${row + 1},${col}`);
175
+ if (col > 0 && maze[row][col - 1] === 0) neighbors.push(`${row},${col - 1}`);
176
+ if (col < 15 && maze[row][col + 1] === 0) neighbors.push(`${row},${col + 1}`);
177
+ return neighbors;
178
+ }
179
+
180
+ function reconstructPath(cameFrom, current) {
181
+ const path = [current];
182
+ while (current in cameFrom) {
183
+ current = cameFrom[current];
184
+ path.unshift(current);
185
+ }
186
+ return path;
187
+ }
188
+
189
+ // Soldiers
190
  const loader = new THREE.GLTFLoader();
191
+ let soldiers = [];
192
+ let winner = null;
193
+ const moveSpeed = 5;
194
+
195
  loader.load(
196
  'https://threejs.org/examples/models/gltf/Soldier.glb',
197
  (gltf) => {
 
 
 
 
 
 
198
  const animations = gltf.animations;
199
+
200
+ for (let team = 0; team < 2; team++) {
201
+ for (let i = 0; i < 3; i++) {
202
+ const soldierClone = gltf.scene.clone();
203
+ const mixer = new THREE.AnimationMixer(soldierClone);
204
+ soldierClone.scale.set(2, 2, 2);
205
+
206
+ const soldierObj = {
207
+ mesh: soldierClone,
208
+ mixer: mixer,
209
+ team: team,
210
+ path: [],
211
+ idleAction: mixer.clipAction(THREE.AnimationClip.findByName(animations, 'Idle')),
212
+ runAction: mixer.clipAction(THREE.AnimationClip.findByName(animations, 'Run')),
213
+ activeAction: null
214
+ };
215
+
216
+ soldierObj.activeAction = soldierObj.idleAction;
217
+ soldierObj.activeAction.play();
218
+
219
+ // Color soldiers
220
+ soldierClone.traverse((child) => {
221
+ if (child.isMesh) {
222
+ child.material = child.material.clone();
223
+ child.material.color.set(team === 0 ? 0x0000ff : 0xff0000);
224
+ }
225
+ });
226
+
227
+ // Random starting position
228
+ let gridX, gridZ;
229
+ do {
230
+ gridZ = Math.floor(Math.random() * 16);
231
+ gridX = team === 0 ? Math.floor(Math.random() * 8) : Math.floor(Math.random() * 8) + 8;
232
+ } while (maze[gridZ][gridX] === 1);
233
+ const worldX = (gridX - 7.5) * cellSize;
234
+ const worldZ = (gridZ - 7.5) * cellSize;
235
+ soldierClone.position.set(worldX, 0, worldZ);
236
+ scene.add(soldierClone);
237
+
238
+ // Path to flag
239
+ const start = `${gridZ},${gridX}`;
240
+ const goal = "8,8";
241
+ const path = aStar(maze, start, goal);
242
+ soldierObj.path = path.map(node => {
243
+ const [row, col] = node.split(',').map(Number);
244
+ return new THREE.Vector3((col - 7.5) * cellSize, 0, (row - 7.5) * cellSize);
245
+ });
246
+
247
+ soldiers.push(soldierObj);
248
+ }
249
+ }
250
  },
251
  undefined,
252
  (error) => console.error('Error loading soldier:', error)
253
  );
254
 
255
  // Camera and Controls
256
+ camera.position.set(0, 20, 30);
257
  const controls = new THREE.OrbitControls(camera, renderer.domElement);
258
  controls.target.set(0, 0, 0);
259
  controls.update();
260
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  // Animation Loop
262
  const clock = new THREE.Clock();
263
  function animate() {
264
  requestAnimationFrame(animate);
265
  const delta = clock.getDelta();
266
 
267
+ if (!winner) {
268
+ for (let soldier of soldiers) {
269
+ if (soldier.mesh.position.distanceTo(flag.position) < 1) {
270
+ winner = soldier.team;
271
+ document.getElementById('status').textContent = `Status: Team ${winner} wins!`;
272
+ break;
273
+ } else if (soldier.path.length > 0) {
274
+ const target = soldier.path[0];
275
+ const direction = target.clone().sub(soldier.mesh.position).normalize();
276
+ const step = direction.multiplyScalar(moveSpeed * delta);
277
+ soldier.mesh.position.add(step);
278
+
279
+ const angle = Math.atan2(direction.x, direction.z);
280
+ soldier.mesh.rotation.y = angle + Math.PI;
281
+
282
+ if (soldier.mesh.position.distanceTo(target) < 0.5) {
283
+ soldier.path.shift();
284
+ }
285
 
286
+ if (soldier.activeAction !== soldier.runAction) {
287
+ soldier.activeAction.fadeOut(0.2);
288
+ soldier.runAction.reset().fadeIn(0.2).play();
289
+ soldier.activeAction = soldier.runAction;
290
+ }
291
+ } else {
292
+ if (soldier.activeAction !== soldier.idleAction) {
293
+ soldier.activeAction.fadeOut(0.2);
294
+ soldier.idleAction.reset().fadeIn(0.2).play();
295
+ soldier.activeAction = soldier.idleAction;
296
+ }
297
+ }
298
+ soldier.mixer.update(delta);
299
+ }
300
  }
301
 
 
302
  renderer.render(scene, camera);
303
  }
304
  animate();