| |

ChatGPT GPT-4o

I found great utility from ChatGPT for coding learning and assistance. This seems to be quite a bit better in practice with GPT-4o.

I asked ChatGPT to create a swarm simulation using python, like the old ‘swarm’ screensaver. Then added a ‘hawk’ that catches them. When they are all caught, it restarts.

When it was done, I asked how to make it work in a browser. ChatGPT told me to use p5.js and rewrote the code for me. It took maybe 10 minutes to create the code… then far longer to get it sized and not ruining the formatting within WordPress.

Note that it should work okay on a newer cellphone, but will likely hide the sides and total number of birds left unless you run it in landscape mode. It is really meant for a larger screen.

Swarm Simulation with Hawk

It is amusing to see the speed increase as the birds are consumed when running the python version.

Here is the python code for anyone interested. 'H' to turn on the hawk and 'C' to change from 'relocate' the bird when caught to 'consume'.

import pygame
import random
import numpy as np

# Constants
WIDTH, HEIGHT = 1200, 600  # Wider window
BIRD_SIZE = 3
HAWK_SIZE = 7
NUM_BIRDS = 150  # Increase number of birds initially
MAX_SPEED = 5
HISTORY_LENGTH = 20  # Length of the trail history

# More appealing colors - we will use a predefined palette
BIRD_COLORS = [
    (255, 109, 194),  # Pink
    (109, 218, 255),  # Light Blue
    (109, 255, 132)   # Light Green
]

# Different behaviors for different colors
BEHAVIORS = {
    (255, 109, 194): {'cohesion': 0.005, 'separation': 0.1, 'alignment': 0.05},
    (109, 218, 255): {'cohesion': 0.01, 'separation': 0.15, 'alignment': 0.1},
    (109, 255, 132): {'cohesion': 0.002, 'separation': 0.05, 'alignment': 0.03},
}
HAWK_SPEED = 7  # Faster hawk

# Initialize Pygame
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.RESIZABLE)  # Resizable screen
pygame.display.set_caption("Flock Simulation with Hawk")
font = pygame.font.SysFont(None, 36)  # Font for bird count

# Select the current behavior when the hawk catches a bird
CATCH_BEHAVIOR = 'relocate'  # Initial behavior

# Bird Class
class Bird:
    def __init__(self, x, y, color):
        self.position = np.array([x, y], dtype=float)
        self.velocity = np.random.rand(2) * 2 - 1  # Random direction
        self.velocity /= np.linalg.norm(self.velocity)
        self.velocity *= MAX_SPEED / 2  # Adjusting initial speed
        self.color = color
        self.behavior = BEHAVIORS[color]
        self.history = []  # To store position history for the trail

    def update(self, birds, hawk):
        self.flock(birds, hawk)
        self.position += self.velocity
        # Keep a copy of the previous position for boundary check
        prev_position = self.position.copy()
        # Screen looping
        self.position[0] %= WIDTH
        self.position[1] %= HEIGHT
        # Add current position to history and maintain length
        if len(self.history) >= HISTORY_LENGTH:
            self.history.pop(0)

        # Break the trail if the bird wraps around the screen
        if np.linalg.norm(self.position - prev_position) > MAX_SPEED:
            self.history.clear()

        self.history.append(self.position.copy())

    def flock(self, birds, hawk):
        sep = np.zeros(2)
        align = np.zeros(2)
        coh = np.zeros(2)
        count = 0

        for bird in birds:
            if bird != self:
                diff = bird.position - self.position
                distance = np.linalg.norm(diff)
                if distance < 50:
                    coh += bird.position
                    align += bird.velocity
                    count += 1
                    if distance < 20:
                        sep -= diff / distance

        if count > 0:
            coh = (coh / count - self.position) * self.behavior['cohesion']
            align = (align / count - self.velocity) * self.behavior['alignment']

        if hawk:
            diff = hawk.position - self.position
            distance = np.linalg.norm(diff)
            if distance < 10:  # Catching distance
                if CATCH_BEHAVIOR == 'relocate':
                    self.position = np.random.rand(2) * np.array([WIDTH, HEIGHT], dtype=float)
                    self.history.clear()  # Clear the history when relocating
                elif CATCH_BEHAVIOR == 'consume':
                    birds.remove(self)
                    return
            elif distance < 100:
                sep -= diff / distance * self.behavior['separation'] * 10

        self.velocity += coh + align + sep
        speed = np.linalg.norm(self.velocity)
        if speed > MAX_SPEED:
            self.velocity = self.velocity / speed * MAX_SPEED

# Hawk Class
class Hawk:
    def __init__(self):
        self.position = np.random.rand(2) * np.array([WIDTH, HEIGHT], dtype=float)
        self.velocity = np.random.rand(2) * 2 - 1
        self.velocity /= np.linalg.norm(self.velocity)
        self.velocity *= HAWK_SPEED

    def update(self, birds):
        if birds:
            closest_bird = min(birds, key=lambda bird: np.linalg.norm(bird.position - self.position))
            direction = closest_bird.position - self.position
            self.velocity = direction / np.linalg.norm(direction) * HAWK_SPEED
        
        self.position += self.velocity
        self.position[0] %= WIDTH
        self.position[1] %= HEIGHT

def draw_trail(screen, bird):
    for i in range(1, len(bird.history)):
        start_pos = bird.history[i - 1]
        end_pos = bird.history[i]
        alpha = int((i / HISTORY_LENGTH) * 255)
        color = (bird.color[0] * alpha // 255, bird.color[1] * alpha // 255, bird.color[2] * alpha // 255)
        pygame.draw.line(screen, color, start_pos, end_pos, BIRD_SIZE)

def main():
    global WIDTH, HEIGHT, screen, CATCH_BEHAVIOR  # Declare global variables here
    birds = [Bird(random.uniform(0, WIDTH), random.uniform(0, HEIGHT), random.choice(BIRD_COLORS)) for _ in range(NUM_BIRDS)]
    hawk = None
    running = True
    clock = pygame.time.Clock()

    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_h:
                if hawk is None:
                    hawk = Hawk()
                else:
                    hawk = None
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_c:
                CATCH_BEHAVIOR = 'consume' if CATCH_BEHAVIOR == 'relocate' else 'relocate'
            elif event.type == pygame.VIDEORESIZE:
                WIDTH, HEIGHT = event.size
                screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.RESIZABLE)  # Update the screen with new size

        screen.fill((0, 0, 0))

        if hawk:
            hawk.update(birds)
            pygame.draw.circle(screen, (255, 0, 0), hawk.position.astype(int), HAWK_SIZE)

        for bird in birds:
            bird.update(birds, hawk)
            draw_trail(screen, bird)  # Draw the trail
            pygame.draw.circle(screen, bird.color, bird.position.astype(int), BIRD_SIZE)

        # Display bird count
        bird_count_text = font.render(f'Birds: {len(birds)}', True, (255, 255, 255))
        screen.blit(bird_count_text, (10, 10))

        pygame.display.flip()
        clock.tick(60)

    pygame.quit()

if __name__ == "__main__":
    main()

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *