# Updated to support 2-player gameplay with progressively slower movement as objects grow import pygame import random import math import sys pygame.init() # Constants WIDTH, HEIGHT = 1900, 950 BALL_START_RADIUS = 10 NUM_BALLS = 20 NUM_FOOD = 0 BALL_SPAWN_INTERVAL = 450 # milliseconds BASE_SPEED = 4 SLOW_EXPONENT = 0.3 # How much size affects speed IMMUNITY_TIME = 3000 # 3 seconds screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("2-Player Triangle Game") clock = pygame.time.Clock() # Generate a random color def random_color(): return (random.randint(50, 255), random.randint(50, 255), random.randint(50, 255)) def drop_food_trail(x, y, angle): offset_distance = 15 # distance behind object trail_x = int(x - math.cos(angle) * offset_distance) trail_y = int(y - math.sin(angle) * offset_distance) if 0 <= trail_x <= WIDTH and 0 <= trail_y <= HEIGHT: food = Food() food.x = trail_x food.y = trail_y foods.append(food) # Ball class class Ball: def __init__(self, x, y): self.x = x self.y = y self.radius = BALL_START_RADIUS self.angle = random.uniform(0, 2 * math.pi) self.spawn_time = pygame.time.get_ticks() self.color = random_color() self.sprinting = False self.last_sprint = pygame.time.get_ticks() def move(self): is_sprinting = random.random() < 0.01 # 5% chance to sprint speed = (BASE_SPEED * 1.8 if is_sprinting else BASE_SPEED) / (self.radius ** SLOW_EXPONENT) dx = math.cos(self.angle) * speed dy = math.sin(self.angle) * speed self.x += dx self.y += dy if is_sprinting and self.radius > 1: self.radius -= 0.03 drop_food_trail(self.x, self.y, self.angle) # Bounce off walls if self.x - self.radius < 0 or self.x + self.radius > WIDTH: self.angle = math.pi - self.angle if self.y - self.radius < 0 or self.y + self.radius > HEIGHT: self.angle = -self.angle if random.random() < 0.01: self.angle += random.uniform(-0.5, 0.5) def draw(self, surface): pygame.draw.circle(surface, self.color, (int(self.x), int(self.y)), int(self.radius)) def is_colliding(self, other): distance = math.hypot(self.x - other.x, self.y - other.y) return distance < self.radius + other.radius def eat_food(self, food): distance = math.hypot(self.x - food.x, self.y - food.y) return distance < self.radius + food.size # Food class class Food: def __init__(self, x=None, y=None): self.x = x if x is not None else random.randint(0, WIDTH) self.y = y if y is not None else random.randint(0, HEIGHT) self.size = 3.5 self.spawn_time = pygame.time.get_ticks() #Store spawn time def draw(self, surface): pygame.draw.rect(surface, (255, 255, 255), (self.x, self.y, self.size, self.size)) # Triangle player class class PlayerTriangle: def __init__(self, controls, color): self.controls = controls self.color = color self.last_food_drop = 0 self.score = 0 self.respawn() def respawn(self): self.x = random.randint(50, WIDTH - 50) self.y = random.randint(50, HEIGHT - 50) self.radius = BALL_START_RADIUS self.alive = True self.spawn_time = pygame.time.get_ticks() self.last_food_drop = 0 def move(self, keys): left, right, up, down, sprint_key = self.controls sprint = keys[sprint_key] base_speed = BASE_SPEED * (1.8 if sprint else 1.0) speed = base_speed / (self.radius ** SLOW_EXPONENT) old_x, old_y = self.x, self.y if keys[left] and self.x - self.radius > 0: self.x -= speed if keys[right] and self.x + self.radius < WIDTH: self.x += speed if keys[up] and self.y - self.radius > 0: self.y -= speed if keys[down] and self.y + self.radius < HEIGHT: self.y += speed if sprint and self.radius > 1: self.radius -= 0.03 now = pygame.time.get_ticks() if now - self.last_food_drop > 200: # 100ms cooldown dx = self.x - old_x dy = self.y - old_y if dx != 0 or dy != 0: angle = math.atan2(dy, dx) drop_food_trail(self.x, self.y, angle) self.last_food_drop = now def draw(self, surface): outline_radius = self.radius + 2 # slightly larger for outline outline_pts = [ (self.x, self.y - outline_radius), (self.x - outline_radius, self.y + outline_radius), (self.x + outline_radius, self.y + outline_radius), ] triangle_pts = [ (self.x, self.y - self.radius), (self.x - self.radius, self.y + self.radius), (self.x + self.radius, self.y + self.radius), ] pygame.draw.polygon(surface, (255, 0, 0), outline_pts) # white outline pygame.draw.polygon(surface, self.color, triangle_pts) # main triangle def is_colliding(self, other): distance = math.hypot(self.x - other.x, self.y - other.y) return distance < self.radius + other.radius # Game setup balls = [Ball(random.randint(50, WIDTH - 50), random.randint(50, HEIGHT - 50)) for _ in range(NUM_BALLS)] foods = [Food() for _ in range(NUM_FOOD)] players = [ PlayerTriangle([pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN, pygame.K_RSHIFT], (255, 255, 0)), PlayerTriangle([pygame.K_a, pygame.K_d, pygame.K_w, pygame.K_s, pygame.K_LSHIFT], (0, 255, 255)) ] last_ball_spawn = pygame.time.get_ticks() running = True # Game loop while running: clock.tick(60) screen.fill((20, 20, 20)) keys = pygame.key.get_pressed() now = pygame.time.get_ticks() for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() for player in players: if player.alive: player.move(keys) player.draw(screen) else: player.respawn() for ball in balls: ball.move() ball.draw(screen) for food in foods[:]: # iterate over a copy if pygame.time.get_ticks() - food.spawn_time > 10000: # 10 seconds foods.remove(food) continue food.draw(screen) eaten = False for ball in balls: if ball.eat_food(food): ball.radius += 0.1 eaten = True break if not eaten: for player in players: if player.alive and abs(player.x - food.x) < player.radius and abs(player.y - food.y) < player.radius: player.radius += 0.1 eaten = True break if eaten: try: foods.remove(food) except ValueError: pass # Already removed foods.append(Food()) for i in range(len(balls) - 1, -1, -1): for j in range(i - 1, -1, -1): b1 = balls[i] b2 = balls[j] if now - b1.spawn_time < IMMUNITY_TIME or now - b2.spawn_time < IMMUNITY_TIME: continue if b1.is_colliding(b2): if b1.radius > b2.radius: b1.radius += 0.1 * b2.radius del balls[j] break elif b2.radius > b1.radius: b2.radius += 0.1 * b1.radius del balls[i] break for player in players: if not player.alive: continue for i in range(len(balls) - 1, -1, -1): ball = balls[i] if now - ball.spawn_time < IMMUNITY_TIME or now - player.spawn_time < IMMUNITY_TIME: continue if player.is_colliding(ball): if player.radius > ball.radius: player.radius += 0.1 * ball.radius del balls[i] else: player.alive = False if players[0].alive and players[1].alive: if now - players[0].spawn_time >= IMMUNITY_TIME and now - players[1].spawn_time >= IMMUNITY_TIME: if players[0].is_colliding(players[1]): if players[0].radius > players[1].radius: players[0].radius += 0.1 * players[1].radius players[1].alive = False elif players[1].radius > players[0].radius: players[1].radius += 0.1 * players[0].radius players[0].alive = False if now - last_ball_spawn >= BALL_SPAWN_INTERVAL: balls.append(Ball(random.randint(50, WIDTH - 50), random.randint(50, HEIGHT - 50))) last_ball_spawn = now pygame.display.flip() pygame.quit()