Emergent behavior: flocking
The goal of this exercise, is to implement virtual "creatures" that move like a swarm of fish or a flock of birds.
Step 0: Getting started
To get started, I am providing a program that displays two creatures that move around on the screen and bounce of the edges. Download the following two files, store them in the same folder, and then run not_a_swarm_yet.py.
Study this program so that you get an overview of how it works. Especially, notice how each boid is represented as a list of 5 values: the boid's position on the x axis, its position on the y-axis, its velocity along the x-axis, its velocity along the y-axis, and its color. The boids are then collected in a list of boids, i.e., a list of lists.
Step 1: Creating a group of creatures
Change the program such that run_swarm takes a number n as its parameter and then creates a group of n creatures. That is, instead of assigning a fixed list of two elements to the variable boids
, create a new list of n creatures and assign this list to the variable boids.
All creatures in the list should be placed at different random positions, have a different random velocity and a random color.
swarm_step1.py (solution to step 1)
Step 2: Steering Behaviors
Steering behaviors are simple rules that are applied to each boid and determine its movements. When the right steering behaviors are combined, more complex behaviors can emerge. For example, if each boid in a group of boids follows the following three rules, the group as a whole will display flocking behavior like a flock of birds or a school of fish.
- Cohesion: try to move to the center of your group
- Separation: try to move away from other group members that are too close
- Alignment: try to match your velocity to the average velocity of the other group members
Much more information on flocking.
We are now going to add these three behaviors or rules to our creatures.
Cohesion
Download this file: cohesion.py. It contains a function called cohesion
which determines for one boid how this boid's velocity should be adjusted for the boid to move toward the center of its group. Look at the code and read the comments.
Add the function to your program, and call it in the update function. The cohesion function returns a list of numbers. The first element in that list is the amount by which the current x-velocity of the boid needs to be increased (remember that the boid's x-velocity is the third element in the list representing the boid), and the second element in the list returned by the cohesion function is the amount by which the current y-velocity of the boid needs to be increased. So, add the first element of the return value to the boid's x-velocity and the second to its y-velocity and see what that does to the boids' behavior.
The behavior of your creatures should now look a little different. They should not be all over the place anymore, but they should be staying a bit closer together. Some of the may follow a curved path. The whole group maybe moves a little like a swarm of insects.
swarm_step2.py (swarm after adding cohesion)
Separation
Now define a function called separation. Like the cohesion function, this separation function should take two parameters: a list of boids (the group) and a number (the index of the boid that we are currently dealing with). Also like the cohesion function, it should return a list of two numbers. The first number in that list is the amount that should be added to the horizontal velocity of the boid and the second number is the amount that should be added to the vertical velocity of the boid.
Here is the idea behind what the separation function should do. The goal is to find what amounts we need to add to the velocity of a boid to make it move away from all boids that are getting close. To do that, look at every other boid (i.e., all boids that are different from the one whose velocities we are currently adjusting). If we find another boid that gets closer than a certain threshold (e.g., 50 pixels), then subtract the difference between the other boid's x position and our boid's x position from the amount that we are going to add to our boids velocity.
Here is the code for the separation function:
# Returns the amount by which a boids velocity should be adjusted in # order to move away from other boids that are very close. def separation(index, boids): b = boids[index] v2 = [0, 0] for other_index in range(len(boids)): if other_index != index: other_b = boids[other_index] distance = math.sqrt((other_b[0] - b[0]) ** 2 + (other_b[1] - b[1]) ** 2) if distance < 50: v2[0] -= (other_b[0] - b[0]) v2[1] -= (other_b[1] - b[1]) return v2
Add this code to your swarm prrogram. Call the separation function in your update function and use its return value to adjust the boid's velocity. You should now clearly see how the creatures turn away from each other when they get too close.
Alignment
Now add alignment. That is, define a function called alignment that takes the same two parameters as the cohesion and the separation functions. Also like cohesion and separation, alignment should return a list of two numbers. These numbers are the amount by which a boid's velocity should be adjusted in order to align its velocity with the velocity of the other boids in its group.
Here is the idea for what the alignment function should do. The goal is to adjust a given boids velocity so that it gets more similar to the velocity of the other boids in the group. So, calculate the average velocity of all other boids. Then calculate the difference between the average velocity and our boids velocity. If we simply added that difference to our boids velocity, that would make its velocity the same as the average velocity. A better effect is created if we only add a fraction of that difference to our boids velocity.
Here is the code for the alignment function:
# Returns the amount by which a boids velocity should be adjusted in # order to align its velocity with the velocity of the other boids in # its group. def alignment(index, boids): b = boids[index] # Calculate the average velocity of all other boids. avg_v = [0, 0] for other_index in range(len(boids)): if other_index != index: other_b = boids[other_index] avg_v[0] += other_b[2] avg_v[1] += other_b[3] avg_v[0] /= len(boids) - 1 avg_v[1] /= len(boids) - 1 # calculate by how much to adjust the boid's velocity add_x = (avg_v[0] - b[2]) / 50 add_y = (avg_v[1] - b[3]) / 50 return [add_x, add_y]
Integrate it into your program.
Submit
Give the following name to your file: lastname_firstname_swarm.py
where lastname
and firstname
are your last and first name. The submit it on Blackboard. Please try that first. If it doesn't work, email it to me with the subject line "CSC 106 swarm homework".