-
Notifications
You must be signed in to change notification settings - Fork 0
/
python-boids.py
144 lines (122 loc) · 4.56 KB
/
python-boids.py
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import pygame
import random
import math
# Constants
WIDTH, HEIGHT = 512, 512
NUM_FISH = 50
MAX_SPEED = 2
NEIGHBOR_RADIUS = 50
SEPARATION_RADIUS = 30
SEPARATION_FORCE = 0.8
ALIGNMENT_FORCE = 0.1
COHESION_FORCE = 0.05
SCREEN_CENTER = (WIDTH // 2, HEIGHT // 2)
BG_COLOR = (15, 10, 15)
FISH_COLOR = (255, 100, 200)
class Fish:
def __init__(self, x, y, vx, vy):
self.x = x
self.y = y
self.vx = vx
self.vy = vy
def update(self, flock):
# Rule 1: Separation
separation = self.separate(flock)
# Rule 2: Alignment
alignment = self.align(flock)
# Rule 3: Cohesion
cohesion = self.cohere(flock)
# Update velocity
self.vx += separation[0] * SEPARATION_FORCE + alignment[0] * ALIGNMENT_FORCE + cohesion[0] * COHESION_FORCE
self.vy += separation[1] * SEPARATION_FORCE + alignment[1] * ALIGNMENT_FORCE + cohesion[1] * COHESION_FORCE
# Limit speed
speed = math.sqrt(self.vx ** 2 + self.vy ** 2)
if speed > MAX_SPEED:
scale_factor = MAX_SPEED / speed
self.vx *= scale_factor
self.vy *= scale_factor
# Update position
self.x += self.vx
self.y += self.vy
# Wrap around screen
if self.x < 0:
self.x = WIDTH
elif self.x > WIDTH:
self.x = 0
if self.y < 0:
self.y = HEIGHT
elif self.y > HEIGHT:
self.y = 0
def separate(self, flock):
separation_vector = [0, 0]
for other_fish in flock:
if other_fish != self:
distance = math.sqrt((self.x - other_fish.x) ** 2 + (self.y - other_fish.y) ** 2)
if distance == 0: # To avoid division by zero
distance = 0.001
if distance < SEPARATION_RADIUS:
separation_vector[0] += (self.x - other_fish.x) / distance
separation_vector[1] += (self.y - other_fish.y) / distance
return separation_vector
def align(self, flock):
avg_velocity = [0, 0]
num_neighbors = 0
for other_fish in flock:
if other_fish != self:
distance = math.sqrt((self.x - other_fish.x) ** 2 + (self.y - other_fish.y) ** 2)
if distance < NEIGHBOR_RADIUS:
avg_velocity[0] += other_fish.vx
avg_velocity[1] += other_fish.vy
num_neighbors += 1
if num_neighbors > 0:
avg_velocity[0] /= num_neighbors
avg_velocity[1] /= num_neighbors
return avg_velocity
def cohere(self, flock):
center_of_mass = [0, 0]
num_neighbors = 0
for other_fish in flock:
if other_fish != self:
distance = math.sqrt((self.x - other_fish.x) ** 2 + (self.y - other_fish.y) ** 2)
if distance < NEIGHBOR_RADIUS:
center_of_mass[0] += other_fish.x
center_of_mass[1] += other_fish.y
num_neighbors += 1
if num_neighbors > 0:
center_of_mass[0] /= num_neighbors
center_of_mass[1] /= num_neighbors
return [center_of_mass[0] - self.x, center_of_mass[1] - self.y]
else:
return [0, 0]
def draw(self, screen):
# Draw fish as an arrow shape
angle = math.atan2(self.vy, self.vx)
pygame.draw.polygon(screen, FISH_COLOR, [(self.x + math.cos(angle) * 10, self.y + math.sin(angle) * 10),
(self.x + math.cos(angle + 5 * math.pi / 6) * 10, self.y + math.sin(angle + 5 * math.pi / 6) * 10),
(self.x + math.cos(angle - 5 * math.pi / 6) * 10, self.y + math.sin(angle - 5 * math.pi / 6) * 10)])
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Boids Simulation")
clock = pygame.time.Clock()
# Create fish
fish = [Fish(random.randint(0, WIDTH), random.randint(0, HEIGHT), random.uniform(-1, 1), random.uniform(-1, 1))
for _ in range(NUM_FISH)]
# Main loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Update fish
for fishy in fish:
fishy.update(fish)
# Draw
screen.fill(BG_COLOR)
for fishy in fish:
fishy.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
if __name__ == "__main__":
main()