sagar007 commited on
Commit
cc2fb6d
·
verified ·
1 Parent(s): 1135c55

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +128 -518
app.py CHANGED
@@ -2,554 +2,164 @@ import gradio as gr
2
  import numpy as np
3
  import time
4
  from PIL import Image, ImageDraw
5
- import random
6
- import json
7
  import base64
8
 
9
- class SpaceShooterGame:
 
10
  def __init__(self):
11
- # Canvas dimensions
12
  self.width = 480
13
  self.height = 640
14
-
15
- # Game state
16
  self.score = 0
17
  self.lives = 3
18
- self.game_over = False
19
- self.game_started = False
20
- self.last_update = time.time()
21
 
22
  # Player
23
  self.player = {
24
- 'x': self.width / 2,
25
- 'y': self.height - 60,
26
- 'width': 40,
27
- 'height': 40,
28
- 'speed': 5,
29
- 'color': '#3498db',
30
- 'is_moving_left': False,
31
- 'is_moving_right': False,
32
- 'is_moving_up': False,
33
- 'is_moving_down': False,
34
- 'is_shooting': False,
35
- 'last_shot': 0,
36
- 'shoot_cooldown': 300 # milliseconds
37
  }
38
 
39
- # Game elements
40
- self.enemies = []
41
  self.bullets = []
42
- self.stars = []
43
- self.particles = []
44
-
45
- # Timers
46
- self.last_enemy_spawn = 0
47
- self.enemy_spawn_rate = 1500 # milliseconds
48
- self.last_star_spawn = 0
49
- self.star_spawn_rate = 200 # milliseconds
50
-
51
- # Initialize stars
52
- self.init_stars()
53
-
54
- def init_stars(self):
55
- for _ in range(50):
56
- self.stars.append({
57
- 'x': random.random() * self.width,
58
- 'y': random.random() * self.height,
59
- 'size': random.random() * 2 + 1,
60
- 'speed': random.random() * 2 + 1
61
- })
62
-
63
- def update_stars(self, timestamp):
64
- # Move existing stars
65
- for i in range(len(self.stars) - 1, -1, -1):
66
- self.stars[i]['y'] += self.stars[i]['speed']
67
-
68
- # Remove stars that go off screen
69
- if self.stars[i]['y'] > self.height:
70
- self.stars.pop(i)
71
-
72
- # Add new stars occasionally
73
- if timestamp - self.last_star_spawn > self.star_spawn_rate:
74
- self.stars.append({
75
- 'x': random.random() * self.width,
76
- 'y': 0,
77
- 'size': random.random() * 2 + 1,
78
- 'speed': random.random() * 2 + 1
79
- })
80
- self.last_star_spawn = timestamp
81
-
82
- def shoot(self, timestamp):
83
- if self.player['is_shooting'] and timestamp - self.player['last_shot'] > self.player['shoot_cooldown']:
84
- self.bullets.append({
85
- 'x': self.player['x'],
86
- 'y': self.player['y'] - self.player['height'] / 2,
87
- 'width': 4,
88
- 'height': 15,
89
- 'speed': 10,
90
- 'color': '#ffff00'
91
- })
92
- self.player['last_shot'] = timestamp
93
-
94
- def update_bullets(self):
95
- for i in range(len(self.bullets) - 1, -1, -1):
96
- self.bullets[i]['y'] -= self.bullets[i]['speed']
97
-
98
- # Remove bullets that go off screen
99
- if self.bullets[i]['y'] < 0:
100
- self.bullets.pop(i)
101
-
102
- def spawn_enemies(self, timestamp):
103
- if timestamp - self.last_enemy_spawn > self.enemy_spawn_rate:
104
- size = random.random() * 20 + 20
105
  self.enemies.append({
106
- 'x': random.random() * (self.width - size) + size / 2,
107
- 'y': 0,
108
- 'width': size,
109
- 'height': size,
110
- 'speed': random.random() * 2 + 1,
111
- 'color': f'hsl({random.random() * 360}, 100%, 50%)'
112
  })
113
- self.last_enemy_spawn = timestamp
114
-
115
- # Increase difficulty over time
116
- if self.enemy_spawn_rate > 500:
117
- self.enemy_spawn_rate -= 10
118
-
119
- def update_enemies(self):
120
- for i in range(len(self.enemies) - 1, -1, -1):
121
- self.enemies[i]['y'] += self.enemies[i]['speed']
122
-
123
- # Game over if enemy reaches bottom
124
- if self.enemies[i]['y'] > self.height:
125
- self.enemies.pop(i)
126
- self.lives -= 1
127
-
128
- if self.lives <= 0:
129
- self.game_over = True
130
-
131
- def create_explosion(self, x, y, color):
132
- particle_count = 15
133
- for _ in range(particle_count):
134
- angle = random.random() * 3.14159 * 2
135
- speed = random.random() * 3 + 1
136
- self.particles.append({
137
- 'x': x,
138
- 'y': y,
139
- 'vx': np.cos(angle) * speed,
140
- 'vy': np.sin(angle) * speed,
141
- 'radius': random.random() * 3 + 1,
142
- 'color': color,
143
- 'life': 30 # frames
144
  })
145
-
146
- def update_particles(self):
147
- for i in range(len(self.particles) - 1, -1, -1):
148
- self.particles[i]['x'] += self.particles[i]['vx']
149
- self.particles[i]['y'] += self.particles[i]['vy']
150
- self.particles[i]['life'] -= 1
151
-
152
- if self.particles[i]['life'] <= 0:
153
- self.particles.pop(i)
154
-
155
- def check_collisions(self):
156
- # Check bullet-enemy collisions
157
- for i in range(len(self.bullets) - 1, -1, -1):
158
- bullet_removed = False
159
- for j in range(len(self.enemies) - 1, -1, -1):
160
- if (
161
- self.bullets[i]['x'] < self.enemies[j]['x'] + self.enemies[j]['width'] / 2 and
162
- self.bullets[i]['x'] + self.bullets[i]['width'] > self.enemies[j]['x'] - self.enemies[j]['width'] / 2 and
163
- self.bullets[i]['y'] < self.enemies[j]['y'] + self.enemies[j]['height'] / 2 and
164
- self.bullets[i]['y'] + self.bullets[i]['height'] > self.enemies[j]['y'] - self.enemies[j]['height'] / 2
165
- ):
166
- # Collision detected
167
- self.create_explosion(self.enemies[j]['x'], self.enemies[j]['y'], self.enemies[j]['color'])
168
- self.score += int(self.enemies[j]['width'])
169
-
170
- # Remove the bullet and enemy
171
- if not bullet_removed:
172
- self.bullets.pop(i)
173
- bullet_removed = True
174
- self.enemies.pop(j)
175
- break
176
-
177
- if bullet_removed:
178
- break
179
 
180
- # Check player-enemy collisions
181
- for i in range(len(self.enemies) - 1, -1, -1):
182
- dx = self.player['x'] - self.enemies[i]['x']
183
- dy = self.player['y'] - self.enemies[i]['y']
184
- distance = np.sqrt(dx * dx + dy * dy)
185
-
186
- if distance < (self.player['width'] / 2 + self.enemies[i]['width'] / 2):
187
- # Collision detected
188
- self.create_explosion(self.player['x'], self.player['y'], self.player['color'])
189
- self.create_explosion(self.enemies[i]['x'], self.enemies[i]['y'], self.enemies[i]['color'])
190
-
191
- # Remove the enemy
192
- self.enemies.pop(i)
193
-
194
- # Decrease player lives
195
  self.lives -= 1
196
-
197
- if self.lives <= 0:
198
- self.game_over = True
199
- break
200
-
201
- def update_player(self):
202
- # Update player position based on current movement flags
203
- if self.player['is_moving_left'] and self.player['x'] > self.player['width'] / 2:
204
- self.player['x'] -= self.player['speed']
205
- if self.player['is_moving_right'] and self.player['x'] < self.width - self.player['width'] / 2:
206
- self.player['x'] += self.player['speed']
207
- if self.player['is_moving_up'] and self.player['y'] > self.player['height']:
208
- self.player['y'] -= self.player['speed']
209
- if self.player['is_moving_down'] and self.player['y'] < self.height - self.player['height'] / 2:
210
- self.player['y'] += self.player['speed']
211
-
212
- def render_frame(self):
213
  # Create image
214
- img = Image.new('RGB', (self.width, self.height), (17, 17, 17))
215
  draw = ImageDraw.Draw(img)
216
-
217
- # Draw stars
218
- for star in self.stars:
219
- draw.ellipse(
220
- [star['x'] - star['size'], star['y'] - star['size'],
221
- star['x'] + star['size'], star['y'] + star['size']],
222
- fill='white'
223
- )
224
-
225
- # Draw enemies
226
- for enemy in self.enemies:
227
- # Draw enemy as a circle
228
- draw.ellipse(
229
- [enemy['x'] - enemy['width'] / 2, enemy['y'] - enemy['height'] / 2,
230
- enemy['x'] + enemy['width'] / 2, enemy['y'] + enemy['height'] / 2],
231
- fill=enemy['color']
232
- )
233
-
234
- # Add details to the enemy
235
- draw.ellipse(
236
- [enemy['x'] - enemy['width'] / 3, enemy['y'] - enemy['height'] / 3,
237
- enemy['x'] + enemy['width'] / 3, enemy['y'] + enemy['height'] / 3],
238
- outline='white'
239
- )
240
-
241
  # Draw bullets
242
  for bullet in self.bullets:
243
- draw.rectangle(
244
- [bullet['x'] - bullet['width'] / 2, bullet['y'],
245
- bullet['x'] + bullet['width'] / 2, bullet['y'] + bullet['height']],
246
- fill=bullet['color']
247
- )
248
-
249
- # Draw player ship
250
- if not self.game_over:
251
- # Ship body (triangle)
252
- draw.polygon(
253
- [
254
- (self.player['x'], self.player['y'] - self.player['height'] / 2),
255
- (self.player['x'] - self.player['width'] / 2, self.player['y'] + self.player['height'] / 2),
256
- (self.player['x'] + self.player['width'] / 2, self.player['y'] + self.player['height'] / 2)
257
- ],
258
- fill=self.player['color']
259
- )
260
-
261
- # Engine glow
262
- draw.polygon(
263
- [
264
- (self.player['x'] - self.player['width'] / 4, self.player['y'] + self.player['height'] / 2),
265
- (self.player['x'], self.player['y'] + self.player['height'] / 2 + 10),
266
- (self.player['x'] + self.player['width'] / 4, self.player['y'] + self.player['height'] / 2)
267
- ],
268
- fill='#ff9900'
269
- )
270
-
271
- # Draw particles
272
- for particle in self.particles:
273
- # Calculate alpha based on life
274
- alpha = int(255 * (particle['life'] / 30))
275
- color = self.hex_to_rgb(particle['color'])
276
- particle_color = (color[0], color[1], color[2], alpha)
277
-
278
- draw.ellipse(
279
- [particle['x'] - particle['radius'], particle['y'] - particle['radius'],
280
- particle['x'] + particle['radius'], particle['y'] + particle['radius']],
281
- fill=particle['color']
282
- )
283
-
284
- # Draw UI
285
- draw.text((10, 10), f"Score: {self.score}", fill='white')
286
- draw.text((self.width - 70, 10), f"Lives: {self.lives}", fill='white')
287
-
288
- # Draw game over screen
289
- if self.game_over:
290
- # Semi-transparent background
291
- overlay = Image.new('RGBA', (self.width, self.height), (0, 0, 0, 180))
292
- img = Image.alpha_composite(img.convert('RGBA'), overlay)
293
- draw = ImageDraw.Draw(img)
294
-
295
- # Game over text
296
- draw.text((self.width // 2 - 40, self.height // 2 - 30), "GAME OVER", fill='red')
297
- draw.text((self.width // 2 - 50, self.height // 2), f"Final Score: {self.score}", fill='white')
298
- draw.text((self.width // 2 - 65, self.height // 2 + 30), "Click to play again", fill='white')
299
-
300
- # Draw start screen
301
- if not self.game_started and not self.game_over:
302
- # Semi-transparent background
303
- overlay = Image.new('RGBA', (self.width, self.height), (0, 0, 0, 180))
304
- img = Image.alpha_composite(img.convert('RGBA'), overlay)
305
- draw = ImageDraw.Draw(img)
306
-
307
- # Start game text
308
- draw.text((self.width // 2 - 60, self.height // 2 - 50), "SPACE SHOOTER", fill='#ff5555')
309
- draw.text((self.width // 2 - 90, self.height // 2 - 20), "Use arrow keys to move", fill='white')
310
- draw.text((self.width // 2 - 85, self.height // 2), "Click mouse to shoot", fill='white')
311
- draw.text((self.width // 2 - 55, self.height // 2 + 30), "Click to start", fill='white')
312
-
313
- return img
314
-
315
- def update(self):
316
- if not self.game_started or self.game_over:
317
- return self.render_frame()
318
-
319
- current_time = time.time() * 1000 # Convert to milliseconds
320
-
321
- # Calculate delta time
322
- delta_time = current_time - self.last_update
323
- self.last_update = current_time
324
-
325
- # Update game state
326
- self.update_stars(current_time)
327
- self.spawn_enemies(current_time)
328
- self.update_enemies()
329
- self.update_player()
330
- self.shoot(current_time)
331
- self.update_bullets()
332
- self.update_particles()
333
- self.check_collisions()
334
-
335
- return self.render_frame()
336
-
337
- def handle_click(self, evt: gr.SelectData):
338
- # Start game if not started
339
- if not self.game_started:
340
- self.game_started = True
341
- return self.update()
342
-
343
- # Restart game if game over
344
- if self.game_over:
345
- self.reset_game()
346
- return self.update()
347
-
348
- # Handle shooting on left click
349
- if evt.index == 1: # Left click
350
- self.player['is_shooting'] = True
351
- self.shoot(time.time() * 1000)
352
- self.player['is_shooting'] = False
353
-
354
- return self.update()
355
-
356
- def handle_keypress(self, key):
357
- if not self.game_started or self.game_over:
358
- self.game_started = True
359
- if self.game_over:
360
- self.reset_game()
361
- return self.update()
362
-
363
- # Handle arrow key movement
364
- if key == "ArrowLeft":
365
- self.player['is_moving_left'] = True
366
- elif key == "ArrowRight":
367
- self.player['is_moving_right'] = True
368
- elif key == "ArrowUp":
369
- self.player['is_moving_up'] = True
370
- elif key == "ArrowDown":
371
- self.player['is_moving_down'] = True
372
- elif key == "a":
373
- self.player['is_moving_left'] = True
374
- elif key == "d":
375
- self.player['is_moving_right'] = True
376
- elif key == "w":
377
- self.player['is_moving_up'] = True
378
- elif key == "s":
379
- self.player['is_moving_down'] = True
380
- elif key == " ":
381
- self.player['is_shooting'] = True
382
-
383
- return self.update()
384
-
385
- def handle_keyrelease(self, key):
386
- if key == "ArrowLeft":
387
- self.player['is_moving_left'] = False
388
- elif key == "ArrowRight":
389
- self.player['is_moving_right'] = False
390
- elif key == "ArrowUp":
391
- self.player['is_moving_up'] = False
392
- elif key == "ArrowDown":
393
- self.player['is_moving_down'] = False
394
- elif key == "a":
395
- self.player['is_moving_left'] = False
396
- elif key == "d":
397
- self.player['is_moving_right'] = False
398
- elif key == "w":
399
- self.player['is_moving_up'] = False
400
- elif key == "s":
401
- self.player['is_moving_down'] = False
402
- elif key == " ":
403
- self.player['is_shooting'] = False
404
-
405
- return self.update()
406
-
407
- def handle_mouse_move(self, evt: gr.SelectData):
408
- # Update player position based on mouse position if game is running
409
- if self.game_started and not self.game_over:
410
- x_coordinates = evt.index[0] if isinstance(evt.index, tuple) else evt.index
411
- self.player['x'] = max(self.player['width'] / 2, min(x_coordinates, self.width - self.player['width'] / 2))
412
-
413
- return self.update()
414
-
415
- def reset_game(self):
416
- # Reset game state
417
- self.score = 0
418
- self.lives = 3
419
- self.game_over = False
420
- self.game_started = True
421
- self.enemies = []
422
- self.bullets = []
423
- self.particles = []
424
- self.last_enemy_spawn = 0
425
- self.enemy_spawn_rate = 1500
426
-
427
- # Reset player position
428
- self.player['x'] = self.width / 2
429
- self.player['y'] = self.height - 60
430
-
431
- self.last_update = time.time() * 1000
432
-
433
- def hex_to_rgb(self, hex_color):
434
- hex_color = hex_color.lstrip('#')
435
- if len(hex_color) == 6:
436
- return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
437
- else:
438
- # Handle HSL colors by converting to a default color
439
- return (255, 0, 0) # Return red by default
440
-
441
- # Create game instance
442
- game = SpaceShooterGame()
443
 
444
- def update_frame():
445
- return game.update()
 
 
 
 
446
 
447
- def handle_click(img, evt: gr.SelectData):
448
- return game.handle_click(evt)
449
 
450
- def handle_keypress(key):
451
- return game.handle_keypress(key)
452
 
453
- def handle_keyrelease(key):
454
- return game.handle_keyrelease(key)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
 
456
- def handle_mouse_move(img, evt: gr.SelectData):
457
- return game.handle_mouse_move(evt)
 
 
458
 
459
- # Set up Gradio interface
460
- with gr.Blocks() as demo:
461
- gr.Markdown("# Space Shooter Game")
462
- gr.Markdown("Use arrow keys (or WASD) to move. Left click to shoot.")
463
 
464
  with gr.Row():
465
- game_display = gr.Image(game.render_frame(), elem_id="game-canvas")
 
466
 
467
  with gr.Row():
468
- gr.Markdown("### Controls:")
469
- gr.Markdown("- **Arrow Keys or WASD**: Move ship")
470
- gr.Markdown("- **Left Click**: Shoot")
471
- gr.Markdown("- **Click on Game**: Start/Restart")
472
-
473
- # Handle events
474
- game_display.select(handle_click, [game_display], [game_display])
475
-
476
- # Game loop using Gradio's JavaScript event system
477
- demo.load(update_frame, [], [game_display], every=0.1)
478
-
479
- # Handle keyboard events
480
- demo.queue()
481
-
482
- # Handle keyboard inputs via JavaScript events
483
- js_keyboard = """
484
- function setupKeyboardHandlers() {
485
- const gameCanvas = document.getElementById('game-canvas');
486
- if (!gameCanvas) {
487
- setTimeout(setupKeyboardHandlers, 100);
488
- return;
489
- }
490
-
491
- document.addEventListener('keydown', function(e) {
492
- if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'a', 'd', 'w', 's', ' '].includes(e.key)) {
493
- e.preventDefault();
494
- keyPressEvent(e.key);
495
- }
496
- });
497
-
498
- document.addEventListener('keyup', function(e) {
499
- if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'a', 'd', 'w', 's', ' '].includes(e.key)) {
500
- e.preventDefault();
501
- keyReleaseEvent(e.key);
502
- }
503
- });
504
-
505
- // Add mousemove handler with debouncing for better performance
506
- let lastMove = 0;
507
- gameCanvas.addEventListener('mousemove', function(e) {
508
- const now = Date.now();
509
- if (now - lastMove < 50) return; // Only process every 50ms
510
- lastMove = now;
511
-
512
- const rect = gameCanvas.getBoundingClientRect();
513
- const x = e.clientX - rect.left;
514
- const y = e.clientY - rect.top;
515
-
516
- // Only update if mouse is over the canvas
517
- if (x >= 0 && x <= rect.width && y >= 0 && y <= rect.height) {
518
- // Scale coordinates to match game dimensions
519
- const scaleX = 480 / rect.width;
520
- const scaledX = x * scaleX;
521
-
522
- mouseMove([scaledX, 0]);
523
- }
524
- });
525
- }
526
-
527
- // Set up the event handlers once the page loads
528
- setTimeout(setupKeyboardHandlers, 100);
529
- """
530
-
531
- demo.load(None, [], [], _js=js_keyboard)
532
-
533
- # Add events for keyboard handling
534
- keypress_event = demo.input(fn=handle_keypress, inputs=[], outputs=[game_display])
535
- keyrelease_event = demo.input(fn=handle_keyrelease, inputs=[], outputs=[game_display])
536
- mousemove_event = demo.input(fn=handle_mouse_move, inputs=[game_display], outputs=[game_display])
537
-
538
- # Connect JavaScript events to Python functions
539
- demo.after_setup(_js=f"""
540
- function keyPressEvent(key) {{
541
- {keypress_event.name}(key);
542
- }}
543
-
544
- function keyReleaseEvent(key) {{
545
- {keyrelease_event.name}(key);
546
- }}
547
-
548
- function mouseMove(coords) {{
549
- {mousemove_event.name}(null, coords);
550
- }}
551
- """)
552
 
553
- # Launch the app
554
- if __name__ == "__main__":
555
- demo.launch()
 
2
  import numpy as np
3
  import time
4
  from PIL import Image, ImageDraw
5
+ import io
 
6
  import base64
7
 
8
+ # Game state class
9
+ class SpaceShooter:
10
  def __init__(self):
 
11
  self.width = 480
12
  self.height = 640
 
 
13
  self.score = 0
14
  self.lives = 3
 
 
 
15
 
16
  # Player
17
  self.player = {
18
+ "x": self.width / 2,
19
+ "y": self.height - 60,
20
+ "width": 40,
21
+ "height": 40,
22
+ "speed": 5
 
 
 
 
 
 
 
 
23
  }
24
 
 
 
25
  self.bullets = []
26
+ self.enemies = []
27
+ self.last_enemy_spawn = time.time()
28
+ self.enemy_spawn_rate = 1.5 # seconds
29
+ self.last_shot = 0
30
+ self.shoot_cooldown = 0.3 # seconds
31
+
32
+ def spawn_enemy(self, current_time):
33
+ if current_time - self.last_enemy_spawn > self.enemy_spawn_rate:
34
+ size = np.random.uniform(20, 40)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  self.enemies.append({
36
+ "x": np.random.uniform(size/2, self.width - size/2),
37
+ "y": 0,
38
+ "width": size,
39
+ "height": size,
40
+ "speed": np.random.uniform(1, 3)
 
41
  })
42
+ self.last_enemy_spawn = current_time
43
+
44
+ def shoot(self, current_time):
45
+ if current_time - self.last_shot > self.shoot_cooldown:
46
+ self.bullets.append({
47
+ "x": self.player["x"],
48
+ "y": self.player["y"] - self.player["height"]/2,
49
+ "width": 4,
50
+ "height": 15,
51
+ "speed": 10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  })
53
+ self.last_shot = current_time
54
+
55
+ def update(self):
56
+ current_time = time.time()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
+ # Update bullets
59
+ for bullet in self.bullets[:]:
60
+ bullet["y"] -= bullet["speed"]
61
+ if bullet["y"] < 0:
62
+ self.bullets.remove(bullet)
63
+
64
+ # Update enemies
65
+ for enemy in self.enemies[:]:
66
+ enemy["y"] += enemy["speed"]
67
+ if enemy["y"] > self.height:
68
+ self.enemies.remove(enemy)
 
 
 
 
69
  self.lives -= 1
70
+
71
+ # Check collisions
72
+ for bullet in self.bullets[:]:
73
+ for enemy in self.enemies[:]:
74
+ if (bullet["x"] < enemy["x"] + enemy["width"]/2 and
75
+ bullet["x"] + bullet["width"] > enemy["x"] - enemy["width"]/2 and
76
+ bullet["y"] < enemy["y"] + enemy["height"]/2 and
77
+ bullet["y"] + bullet["height"] > enemy["y"] - enemy["height"]/2):
78
+ self.bullets.remove(bullet)
79
+ self.enemies.remove(enemy)
80
+ self.score += int(enemy["width"])
81
+ break
82
+
83
+ # Spawn new enemies
84
+ self.spawn_enemy(current_time)
85
+
86
+ def draw(self):
87
  # Create image
88
+ img = Image.new('RGB', (self.width, self.height), color='black')
89
  draw = ImageDraw.Draw(img)
90
+
91
+ # Draw player
92
+ draw.polygon([
93
+ (self.player["x"], self.player["y"] - self.player["height"]/2),
94
+ (self.player["x"] - self.player["width"]/2, self.player["y"] + self.player["height"]/2),
95
+ (self.player["x"] + self.player["width"]/2, self.player["y"] + self.player["height"]/2)
96
+ ], fill='#3498db')
97
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  # Draw bullets
99
  for bullet in self.bullets:
100
+ draw.rectangle([
101
+ (bullet["x"] - bullet["width"]/2, bullet["y"]),
102
+ (bullet["x"] + bullet["width"]/2, bullet["y"] + bullet["height"])
103
+ ], fill='#ff0')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
+ # Draw enemies
106
+ for enemy in self.enemies:
107
+ draw.ellipse([
108
+ (enemy["x"] - enemy["width"]/2, enemy["y"] - enemy["height"]/2),
109
+ (enemy["x"] + enemy["width"]/2, enemy["y"] + enemy["height"]/2)
110
+ ], fill='#ff5555')
111
 
112
+ return img
 
113
 
114
+ # Game instance
115
+ game = SpaceShooter()
116
 
117
+ def move_player(direction):
118
+ if game.lives <= 0:
119
+ return game.draw(), f"Game Over! Score: {game.score}"
120
+
121
+ if direction == "left" and game.player["x"] > game.player["width"]/2:
122
+ game.player["x"] -= game.player["speed"]
123
+ elif direction == "right" and game.player["x"] < game.width - game.player["width"]/2:
124
+ game.player["x"] += game.player["speed"]
125
+ elif direction == "shoot":
126
+ game.shoot(time.time())
127
+
128
+ game.update()
129
+ status = f"Score: {game.score} | Lives: {game.lives}"
130
+ if game.lives <= 0:
131
+ status = f"Game Over! Score: {game.score}"
132
+
133
+ return game.draw(), status
134
 
135
+ def reset_game():
136
+ global game
137
+ game = SpaceShooter()
138
+ return game.draw(), f"Score: {game.score} | Lives: {game.lives}"
139
 
140
+ # Gradio interface
141
+ with gr.Blocks(title="Space Shooter") as demo:
142
+ gr.Markdown("# Space Shooter")
143
+ gr.Markdown("Use the buttons to move left, right, and shoot. Game updates after each action.")
144
 
145
  with gr.Row():
146
+ game_output = gr.Image(label="Game", width=480, height=640)
147
+ status_output = gr.Textbox(label="Status")
148
 
149
  with gr.Row():
150
+ left_btn = gr.Button("Left")
151
+ right_btn = gr.Button("Right")
152
+ shoot_btn = gr.Button("Shoot")
153
+ reset_btn = gr.Button("Reset")
154
+
155
+ # Initial state
156
+ game_output.value = game.draw()
157
+ status_output.value = f"Score: {game.score} | Lives: {game.lives}"
158
+
159
+ # Event handlers
160
+ left_btn.click(move_player, inputs=gr.State(value="left"), outputs=[game_output, status_output])
161
+ right_btn.click(move_player, inputs=gr.State(value="right"), outputs=[game_output, status_output])
162
+ shoot_btn.click(move_player, inputs=gr.State(value="shoot"), outputs=[game_output, status_output])
163
+ reset_btn.click(reset_game, inputs=None, outputs=[game_output, status_output])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
+ demo.launch()