Agar.io by ChatGPT

 avatar
unknown
python
a month ago
13 kB
2
Indexable
import pygame
import random
import math
import sys
import array

pygame.init()
pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=512)

# --------------------- USTAWIENIA GRY ---------------------

# Rozmiar okna
WIN_WIDTH, WIN_HEIGHT = 800, 600

# Rozmiar „świata”
WORLD_WIDTH, WORLD_HEIGHT = 1600, 1200

FPS = 60

# Kolory
WHITE  = (255, 255, 255)
BLACK  = (  0,   0,   0)
BLUE   = (  0,   0, 255)

# Przygotowanie okna
screen = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT))
pygame.display.set_caption("Agar.io z mądrzejszymi NPC i rankingiem")
clock = pygame.time.Clock()

# --------------------- GENEROWANIE DŹWIĘKÓW ---------------------
def generate_sine_wave(frequency=440, duration_ms=200, volume=0.5, sample_rate=44100):
    """
    Generuje tablicę bajtów zawierającą próbki sinusoidy (16 bit, stereo)
    o zadanej częstotliwości, czasie trwania i głośności.
    """
    num_samples = int(sample_rate * duration_ms / 1000.0)
    amplitude = int(32767 * volume)  # dla 16-bit max = 32767

    samples = array.array('h')  # 'h' = short (16-bit) - kanał lewy
    for i in range(num_samples):
        sample_value = int(amplitude * math.sin(2.0 * math.pi * frequency * i / sample_rate))
        samples.append(sample_value)

    # Tworzymy stereo: kopiujemy próbkę L jako R
    stereo_samples = array.array('h')
    for s in samples:
        stereo_samples.append(s)   # left
        stereo_samples.append(s)   # right

    sound = pygame.mixer.Sound(buffer=stereo_samples.tobytes())
    return sound

sound_eat_food = generate_sine_wave(frequency=600, duration_ms=100, volume=0.5)
sound_eat_npc  = generate_sine_wave(frequency=300, duration_ms=200, volume=0.6)

# --------------------- KLASY OBIEKTÓW ---------------------

class Blob:
    """Podstawowa klasa dla wszelkich 'kulek' (gracz, NPC, jedzenie)."""
    def __init__(self, x, y, radius, color=BLACK, speed=0):
        self.x = x
        self.y = y
        self.radius = radius
        self.color = color
        self.speed = speed

    def distance_to(self, other) -> float:
        return math.hypot(self.x - other.x, self.y - other.y)

    def check_collision(self, other) -> bool:
        return self.distance_to(other) <= self.radius + other.radius

    def draw(self, surface, camera_offset_x, camera_offset_y):
        screen_x = int(self.x - camera_offset_x)
        screen_y = int(self.y - camera_offset_y)
        pygame.draw.circle(surface, self.color, (screen_x, screen_y), self.radius)

class Player(Blob):
    """Klasa dla gracza (sterowanego przez człowieka)."""
    def __init__(self, x, y, radius, color, speed, name="TY"):
        super().__init__(x, y, radius, color, speed)
        self.name = name

    def move(self):
        keys = pygame.key.get_pressed()
        dx, dy = 0, 0

        if keys[pygame.K_w] or keys[pygame.K_UP]:
            dy = -1
        if keys[pygame.K_s] or keys[pygame.K_DOWN]:
            dy = 1
        if keys[pygame.K_a] or keys[pygame.K_LEFT]:
            dx = -1
        if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
            dx = 1

        # Normalizacja wektora ruchu
        if dx != 0 or dy != 0:
            length = math.hypot(dx, dy)
            dx /= length
            dy /= length

        # Ruch
        self.x += dx * self.speed
        self.y += dy * self.speed

        # Ograniczenie do obszaru świata
        if self.x - self.radius < 0:
            self.x = self.radius
        if self.x + self.radius > WORLD_WIDTH:
            self.x = WORLD_WIDTH - self.radius
        if self.y - self.radius < 0:
            self.y = self.radius
        if self.y + self.radius > WORLD_HEIGHT:
            self.y = WORLD_HEIGHT - self.radius

class NPC(Blob):
    """Klasa dla 'mądrzejszych' NPC."""
    def __init__(self, x, y, radius, color, speed, name):
        super().__init__(x, y, radius, color, speed)
        self.name = name
        # Kierunek startowy - losowy
        self.dir_x = random.choice([-1, 1])
        self.dir_y = random.choice([-1, 1])

    def move(self, player, npcs):
        """
        Prostsza 'inteligencja':
          - Szuka najbliższego obiektu (player lub inny NPC).
          - Jeśli tamten jest większy -> uciekamy.
          - Jeśli tamten jest mniejszy -> gonimy.
          - Jeśli w pobliżu nie ma nikogo -> losowy ruch (z niewielką szansą na zmianę kierunku).
        """
        # Najpierw szukamy najbliższego 'gracza' lub NPC (z pominięciem samego siebie).
        target = self.find_closest(player, npcs)

        if target is not None:
            # Obliczamy odległość i wektor do celu
            dist = self.distance_to(target)
            dx = (target.x - self.x)
            dy = (target.y - self.y)

            if dist != 0:
                dx /= dist
                dy /= dist

            # Decyzja w zależności od wielkości
            if target.radius > self.radius:
                # Uciekamy -> ruch w przeciwną stronę
                dx = -dx
                dy = -dy
            else:
                # Gonimy -> dx, dy jak powyżej (czyli w stronę celu)
                pass

            # Poruszamy się z własną prędkością w wyznaczonym kierunku
            self.x += dx * self.speed
            self.y += dy * self.speed
        else:
            # Brak celu w zasięgu -> losowy ruch, ze zmianą kierunku 1%
            if random.random() < 0.01:
                self.dir_x = random.choice([-1, 1])
                self.dir_y = random.choice([-1, 1])

            self.x += self.dir_x * self.speed
            self.y += self.dir_y * self.speed

        # Odbijanie od krawędzi świata
        if self.x - self.radius < 0:
            self.x = self.radius
            self.dir_x *= -1
        if self.x + self.radius > WORLD_WIDTH:
            self.x = WORLD_WIDTH - self.radius
            self.dir_x *= -1
        if self.y - self.radius < 0:
            self.y = self.radius
            self.dir_y *= -1
        if self.y + self.radius > WORLD_HEIGHT:
            self.y = WORLD_HEIGHT - self.radius
            self.dir_y *= -1

    def find_closest(self, player, npcs, vision_radius=300):
        """
        Znajdź najbliższy obiekt (NPC lub gracza) w promieniu 'vision_radius'.
        Zwraca obiekt (Player/NPC) lub None, jeśli nic nie znaleziono.
        """
        closest_obj = None
        min_dist = float('inf')

        # Najpierw sprawdzamy gracza
        if player != self:
            dist = self.distance_to(player)
            if dist < min_dist and dist <= vision_radius:
                min_dist = dist
                closest_obj = player

        # Następnie sprawdzamy innych NPC
        for npc in npcs:
            if npc is self:
                continue  # omijamy samego siebie
            dist = self.distance_to(npc)
            if dist < min_dist and dist <= vision_radius:
                min_dist = dist
                closest_obj = npc

        return closest_obj

# --------------------- FUNKCJE POMOCNICZE ---------------------

def create_npcs(n):
    """
    Tworzy n NPC-ów w losowych miejscach na planszy.
    Używamy podanej listy nazw i wybieramy je losowo.
    """
    possible_names = [
        "Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot",
        "Gamma", "Hotel", "India", "Juliet", "Kombo",
        "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo",
        "Tango", "Uniform", "Victor",
        "Xray", "Zero"
    ]

    npcs = []
    for _ in range(n):
        radius = random.randint(10, 20)
        x = random.randint(radius, WORLD_WIDTH - radius)
        y = random.randint(radius, WORLD_HEIGHT - radius)
        color = (
            random.randint(50, 255),
            random.randint(50, 255),
            random.randint(50, 255)
        )
        speed = random.randint(1, 3)
        name = random.choice(possible_names)
        npcs.append(NPC(x, y, radius, color, speed, name))
    return npcs

def create_food(n):
    """Tworzy n elementów 'jedzenia' (małe kulki)."""
    food_list = []
    for _ in range(n):
        radius = random.randint(3, 5)
        x = random.randint(radius, WORLD_WIDTH - radius)
        y = random.randint(radius, WORLD_HEIGHT - radius)
        color = (
            random.randint(100, 255),
            random.randint(100, 255),
            random.randint(100, 255)
        )
        food_list.append(Blob(x, y, radius, color, 0))
    return food_list

def show_end_screen(message):
    """Wyświetla ekran końcowy z komunikatem."""
    font = pygame.font.SysFont(None, 80)
    text_surface = font.render(message, True, (255, 0, 0))
    rect = text_surface.get_rect(center=(WIN_WIDTH // 2, WIN_HEIGHT // 2))
    screen.fill(WHITE)
    screen.blit(text_surface, rect)
    pygame.display.flip()
    pygame.time.wait(3000)

def draw_scoreboard(surface, player, npcs):
    """
    Rysuje w lewym górnym rogu listę aktywnych „graczy” (gracz + NPC)
    posortowaną malejąco po promieniu (radius).
    Format: NICK (radius)
    """
    font = pygame.font.SysFont(None, 26)
    all_players = [player] + npcs
    all_players.sort(key=lambda p: p.radius, reverse=True)

    x = 10
    y = 10
    for blob in all_players:
        text_surface = font.render(f"{blob.name} ({blob.radius})", True, BLACK)
        surface.blit(text_surface, (x, y))
        y += 25  # odstęp między kolejnymi liniami

# --------------------- LOGIKA GŁÓWNA ---------------------

def main():
    # Tworzymy obiekty
    player = Player(WORLD_WIDTH//2, WORLD_HEIGHT//2, 15, BLUE, speed=3, name="TY")
    npcs = create_npcs(10)
    food = create_food(50)  # Można zwiększyć liczbę jedzenia

    running = True
    while running:
        clock.tick(FPS)

        # Obsługa zdarzeń
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        # Ruch gracza i NPC
        player.move()
        for npc in npcs:
            npc.move(player, npcs)  # mądrzejszy ruch

        # Kolizje z jedzeniem – dźwięk TYLKO gdy gracz zjada
        for f in food[:]:
            if player.check_collision(f):
                sound_eat_food.play()  # dźwięk tylko dla gracza
                player.radius += f.radius // 2
                food.remove(f)
            else:
                # Sprawdzamy, czy któryś z NPC zjadł jedzenie (bez dźwięku)
                for npc in npcs:
                    if npc.check_collision(f):
                        npc.radius += f.radius // 2
                        food.remove(f)
                        break

        # Kolizje między graczem a NPC
        for npc in npcs[:]:
            if player.check_collision(npc):
                if player.radius > npc.radius:
                    # Gracz zjada NPC -> dźwięk
                    sound_eat_npc.play()
                    player.radius += npc.radius // 2
                    npcs.remove(npc)
                else:
                    # Gracz zostaje zjedzony
                    show_end_screen("Przegrana!")
                    running = False
                    break

        # Kolizje między NPC-ami (bez dźwięku)
        for i in range(len(npcs)):
            if npcs[i] is None:
                continue
            for j in range(i+1, len(npcs)):
                if npcs[j] is None:
                    continue
                if npcs[i].check_collision(npcs[j]):
                    if npcs[i].radius > npcs[j].radius:
                        npcs[i].radius += npcs[j].radius // 2
                        npcs[j] = None
                    else:
                        npcs[j].radius += npcs[i].radius // 2
                        npcs[i] = None
                    break
        npcs = [npc for npc in npcs if npc is not None]

        # Sprawdzenie zwycięstwa: WYGRANA, jeśli nie ma żadnych NPC
        if len(npcs) == 0:
            show_end_screen("Wygrana!")
            running = False

        # Rysowanie tła
        screen.fill(WHITE)

        # Kamera (offset)
        camera_offset_x = player.x - WIN_WIDTH // 2
        camera_offset_y = player.y - WIN_HEIGHT // 2

        if camera_offset_x < 0:
            camera_offset_x = 0
        if camera_offset_x > WORLD_WIDTH - WIN_WIDTH:
            camera_offset_x = WORLD_WIDTH - WIN_WIDTH
        if camera_offset_y < 0:
            camera_offset_y = 0
        if camera_offset_y > WORLD_HEIGHT - WIN_HEIGHT:
            camera_offset_y = WORLD_HEIGHT - WIN_HEIGHT

        # Rysowanie jedzenia, NPC i gracza
        for f in food:
            f.draw(screen, camera_offset_x, camera_offset_y)
        for npc in npcs:
            npc.draw(screen, camera_offset_x, camera_offset_y)
        player.draw(screen, camera_offset_x, camera_offset_y)

        # Rysowanie rankingu
        draw_scoreboard(screen, player, npcs)

        pygame.display.flip()

    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()
Leave a Comment