Space_defender / app.py
Ispano's picture
Update app.py
a2127cf verified
import gradio as gr
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import random
# Game setup
WIDTH, HEIGHT = 500, 400
PLAYER_SIZE = 30
ASTEROID_SIZE = 25
BULLET_SIZE = 8
player_x = WIDTH // 2
player_y = HEIGHT - 50
asteroids = []
bullets = []
score = 0
game_over = False
explosions = []
# Starry background
stars = [(random.randint(0, WIDTH), random.randint(0, HEIGHT)) for _ in range(100)]
def draw_frame():
"""Draws the game frame with cool visuals."""
img = Image.new("RGB", (WIDTH, HEIGHT), (10, 5, 30)) # Dark blue space
draw = ImageDraw.Draw(img)
# Draw stars (twinkling background)
for x, y in stars:
brightness = random.randint(100, 255)
draw.ellipse([x, y, x+2, y+2], fill=(brightness, brightness, brightness))
# Draw player (spaceship emoji)
try:
font = ImageFont.truetype("arial.ttf", PLAYER_SIZE)
draw.text((player_x - PLAYER_SIZE//2, player_y - PLAYER_SIZE//2), "πŸ›Έ", font=font)
except:
draw.rectangle( # Fallback if emoji fails
[player_x - PLAYER_SIZE//2, player_y - PLAYER_SIZE//2,
player_x + PLAYER_SIZE//2, player_y + PLAYER_SIZE//2],
fill="blue"
)
# Draw asteroids (rock emoji)
for x, y in asteroids:
try:
draw.text((x - ASTEROID_SIZE//2, y - ASTEROID_SIZE//2), "πŸͺ¨", font=font)
except:
draw.rectangle(
[x - ASTEROID_SIZE//2, y - ASTEROID_SIZE//2,
x + ASTEROID_SIZE//2, y + ASTEROID_SIZE//2],
fill="red"
)
# Draw bullets (light beam)
for x, y in bullets:
draw.rectangle(
[x - BULLET_SIZE//2, y, x + BULLET_SIZE//2, y + BULLET_SIZE*2],
fill=(255, 255, 0) # Yellow
)
# Draw explosions
for x, y in explosions:
draw.text((x - ASTEROID_SIZE, y - ASTEROID_SIZE), "πŸ’₯", font=font)
# Draw score (modern font)
draw.text((10, 10), f"Score: {score}", fill="white", font=font)
if game_over:
draw.text(
(WIDTH//2 - 100, HEIGHT//2 - 20),
"GAME OVER! Press [R]",
fill="red",
font=font
)
return np.array(img)
def update_game(action):
global player_x, asteroids, bullets, score, game_over, explosions
if game_over and action == "r":
reset_game()
return draw_frame()
if game_over:
return draw_frame()
# Handle controls
if action == "a":
player_x = max(PLAYER_SIZE//2, player_x - 10)
elif action == "d":
player_x = min(WIDTH - PLAYER_SIZE//2, player_x + 10)
elif action == " ":
bullets.append([player_x, player_y - PLAYER_SIZE])
# Update bullets
for bullet in bullets[:]:
bullet[1] -= 12
if bullet[1] < 0:
bullets.remove(bullet)
# Spawn asteroids
if random.random() < 0.03:
asteroids.append([random.randint(20, WIDTH-20), 0])
# Update asteroids
for asteroid in asteroids[:]:
asteroid[1] += 5
if asteroid[1] > HEIGHT:
asteroids.remove(asteroid)
# Collision with player
if (abs(asteroid[0] - player_x) < (ASTEROID_SIZE + PLAYER_SIZE)//2 and \
abs(asteroid[1] - player_y) < (ASTEROID_SIZE + PLAYER_SIZE)//2:
game_over = True
explosions.append([asteroid[0], asteroid[1]])
# Bullet-asteroid collisions
for bullet in bullets[:]:
for asteroid in asteroids[:]:
if (abs(bullet[0] - asteroid[0]) < (BULLET_SIZE + ASTEROID_SIZE)//2 and \
abs(bullet[1] - asteroid[1]) < (BULLET_SIZE + ASTEROID_SIZE)//2:
bullets.remove(bullet)
asteroids.remove(asteroid)
explosions.append([asteroid[0], asteroid[1]])
score += 10
break
# Clear explosions after 3 frames
if len(explosions) > 0:
explosions.clear()
return draw_frame()
def reset_game():
global player_x, asteroids, bullets, score, game_over
player_x = WIDTH // 2
asteroids.clear()
bullets.clear()
score = 0
game_over = False
# Gradio UI
with gr.Blocks(title="πŸš€ Space Defender") as demo:
gr.Markdown("# πŸͺ SPACE DEFENDER")
with gr.Row():
game_screen = gr.Image(label="Cosmic Battle", width=500, height=400)
with gr.Row():
gr.Button("⬅️ Left (A)").click(lambda: update_game("a"), outputs=game_screen)
gr.Button("πŸ”« Shoot (Space)").click(lambda: update_game(" "), outputs=game_screen)
gr.Button("➑️ Right (D)").click(lambda: update_game("d"), outputs=game_screen)
gr.Button("πŸ”„ Restart (R)").click(lambda: update_game("r"), outputs=game_screen)
demo.load(lambda: draw_frame(), outputs=game_screen)
demo.launch()