File size: 8,989 Bytes
3515cf9
596ac16
1135c55
596ac16
3515cf9
596ac16
 
 
e5492ea
596ac16
 
 
 
 
 
 
 
 
 
 
 
cc2fb6d
 
596ac16
 
 
cc2fb6d
596ac16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import gradio as gr
import random
import time
import base64  # For audio encoding

# --- Game Logic (Adapted from your HTML/JS) ---

class Bird:
    def __init__(self):
        self.x = -50
        self.y = 100 + random.random() * 300
        self.speed = 2 + random.random() * 3
        self.size = 30 + random.random() * 20
        self.color = self.random_color()
        self.wing_angle = 0  # Initial wing angle
        self.last_wing_update = time.time()


    def random_color(self):
        colors = ["#FF5733", "#33FF57", "#3357FF", "#F3FF33", "#FF33F3", "#33FFF3"]
        return random.choice(colors)

    def update(self):
        self.x += self.speed

        # Animate wings (simplified for Gradio, update every 0.1 seconds)
        current_time = time.time()
        if current_time - self.last_wing_update > 0.1:
            self.wing_angle = 15 * (1 if (int(current_time * 10) % 2 == 0) else -1)  # simpler flapping
            self.last_wing_update = current_time


        if self.x > 850:
            return False  # Bird left the screen
        return True

    def check_hit(self, click_x, click_y):
        distance = ((self.x - click_x) ** 2 + (self.y - click_y) ** 2) ** 0.5
        return distance < self.size

    def to_svg(self):
        # Bird body (ellipse)
        body = f'<ellipse cx="{self.x}" cy="{self.y}" rx="{self.size / 2}" ry="{self.size / 3}" fill="{self.color}" />'

        # Bird head (circle)
        head = f'<circle cx="{self.x + self.size / 2}" cy="{self.y - self.size / 6}" r="{self.size / 4}" fill="{self.color}" />'

        # Bird eye (circle)
        eye = f'<circle cx="{self.x + self.size / 2 + self.size / 8}" cy="{self.y - self.size / 6 - self.size / 8}" r="{self.size / 10}" fill="black" />'

        # Bird beak (polygon)
        beak = f'<polygon points="{self.x + self.size / 2 + self.size / 4},{self.y - self.size / 6} {self.x + self.size},{self.y} {self.x + self.size / 2 + self.size / 4},{self.y + self.size / 10}" fill="orange" />'

        # Bird wing (ellipse with rotation)
        wing = f'<ellipse cx="{self.x}" cy="{self.y + self.size/4}" rx="{self.size / 3}" ry="{self.size / 6}" fill="{self.color}" transform="rotate({self.wing_angle} {self.x} {self.y + self.size/4})" />'


        return f'<g>{body}{head}{eye}{beak}{wing}</g>'


class Explosion:
    def __init__(self, x, y, size):
        self.x = x
        self.y = y
        self.size = size
        self.frame = 0
        self.max_frames = 20
        self.sound_played = False # Prevent sound repetition


    def update(self):
        self.frame += 1
        if self.frame >= self.max_frames:
            return False
        return True

    def to_svg(self):
        group = f'<g transform="translate({self.x}, {self.y})">'
        colors = ["#FF0000", "#FF7700", "#FFFF00"]
        particle_count = 12

        for i in range(particle_count):
            angle = (i / particle_count) * 2 * 3.14159  # Use a close approximation of PI
            distance = (self.size / 3) + (self.frame * 2)
            x = (self.size/3 + self.frame *2)  * (1 if i%2 == 0 else -1)
            y = (self.size/3 + self.frame*2) * (1 if i%3 ==0 else -1)
            radius = self.size / 8 * (1 - self.frame / self.max_frames)
            opacity = 1 - (self.frame / self.max_frames)
            particle = f'<circle cx="{x}" cy="{y}" r="{radius}" fill="{random.choice(colors)}" opacity="{opacity}" />'
            group += particle

        center_radius = (self.size / 2) * (1 + self.frame / 5)
        center_opacity = 1 - (self.frame / self.max_frames)
        center = f'<circle cx="0" cy="0" r="{center_radius}" fill="#FF0000" opacity="{center_opacity}" />'
        group += center + '</g>'
        return group


def create_clouds():
    clouds_svg = ""
    cloud_count = 5
    for _ in range(cloud_count):
        cx = random.random() * 800
        cy = 50 + random.random() * 100
        rx = 50 + random.random() * 50
        ry = 20 + random.random() * 20
        clouds_svg += f'<ellipse cx="{cx}" cy="{cy}" rx="{rx}" ry="{ry}" fill="white" opacity="0.8" />'
        # Smaller ellipse
        cloud_part_cx = cx + 30
        cloud_part_cy = cy - 5
        clouds_svg += f'<ellipse cx="{cloud_part_cx}" cy="{cloud_part_cy}" rx="30" ry="15" fill="white" opacity="0.8" />'
    return clouds_svg

# --- Gradio App ---

def play_game(click_data, game_state):
    if game_state is None:  # Initialize game state
        game_state = {"score": 0, "birds": [], "explosions": [], "game_running": True}

    score = game_state["score"]
    birds = game_state["birds"]
    explosions = game_state["explosions"]
    game_running = game_state["game_running"]

    # Sounds (Base64 encoded for embedding)
    explosion_sound = "data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU="
    miss_sound = "data:audio/wav;base64,UklGRiQDAABXQVZFZm10IBAAAAABAAEAESsAABErAAABAAgAZGF0YQADAABkAGQAZABkAGQAfACEAJwAsAC8AMgA1ADsAOQA7ADkANQA0AC8AKgAnACMAHQAX