# Ch. 11 – Exploring Powerful Ideas

Topics:  Fractals, recursion, Fibonacci numbers, the Golden Ratio, Zipf’s Law, top-down design, Python dictionaries, defining Python classes, Python exceptions, animation, color gradients, Python complex numbers, cymatics and dynamical systems (boids).

In this chapter, we present several advanced computational concepts and algorithmic techniques. For some of them, we will show directly how to use them to make music (as we have done throughout the chapters so far). For others, we will leave it up to you to add a musical dimension to the sample code. More information is provided in the reference textbook.

Here is code from this chapter.

# Fractals and Recursion

Fractals are self-similar objects (i.e., objects consisting of multiple parts, with smaller parts being reduced-size replicas of the larger parts).

In this section we explore a programming technique called, recursion.  Recursion mimics the subdivision process found in nature, e.g., the branching (starting with a single sprout) that results in a pine tree, etc.  Recursion is especially suited for creating fractals.

## Fibonacci numbers and the Golden Ratio

Fibonacci numbers appear in many natural objects and processes, including seashells (see figure below), branching plants, and pine cones, among others. They also appear in the shape of hurricanes, and galaxies.

The code sample below (Ch. 11, p. 342) demonstrates one way to calculate the Fibonacci numbers through the process of recursion:

```# fibonacci.py
#
# Find the nth Fibonacci number using recursion.
#

def fib(n):

if n == 0:    # simple case 1
result = 0
elif n == 1:  # simple case 2
result = 1
else:         # recursive case
result = fib(n-2) + fib(n-1)

return result

# now, let's test it
for i in range(10):
print "fib(" + str(i) + ") =", fib(i)
```

## Fractals and generating a golden tree

The golden ratio is found in natural and human-made artifacts. It is also found in the human body (e.g., the bones of our hands, the cochlea in our ears). It is also found in various musical works by Bach, Beethoven, Mozart, and others.

The following program (Ch. 11, p. 344) demonstrates how to generate a fractal via recursion.  This particular fractal, known as the Golden Tree, incorporates the golden ratio.

Here is the code:

```# goldenTree.py
#
# Demonstrates how to draw a golden tree using recursion.
#

from gui import *
from math import *

# create display
d = Display("Golden Tree", 250, 250)
d.setColor(Color.WHITE)

# calculate phi to the highest accuracy Python allows
phi = (sqrt(5) - 1) / 2     # approx. 0.618033988749895

# recursive drawing parameters
depth = 10                  # amount of detail (or branching)
scale = phi                 # scaling factor of branches

# initial parameters
angle = radians(90)         # starting orientation is North
length = d.getHeight() / 3  # length of initial branch (trunk)
startX = d.getWidth() / 2   # start at bottom center
startY = d.getHeight() - 33

# recursive function for drawing tree
def drawTree(x, y, length, angle, depth):
"""
Recursively draws a tree of depth 'depth' starting at 'x', 'y'.
"""

global d, scale, rotation

#print "depth =", depth, "x =", x, " y =", y, " length =", length,
#print "angle =", degrees(angle)

# draw this line
newX = x + length * cos( angle )    # calculate run
newY = y - length * sin( angle )    # calculate rise
d.drawLine(int(x), int(y), int(newX), int(newY))

# check if we need more detail
if depth &amp;gt; 1:

# draw left branch - use line with length scaled by phi,
# rotated counter-clockwise
drawTree(newX, newY, length*phi, angle - rotation, depth-1)

# draw right branch - use line with length scaled by phi,
# rotated clockwise
drawTree(newX, newY, length*phi, angle + rotation, depth-1)

# draw complete tree (recursively)
drawTree(startX, startY, length, angle, depth)
```

It generates this shape::

## The Sierpinski triangle

Another fractal shape is the Sierpinski triangle. This fractal triangle consists of three smaller triangles (top, bottom left, bottom right) that have the same shape as the main one (see figure below).

Here is the code:

```# sierpinskiTriangle.py
#
# Demonstrates how to draw the Sierpinski triangle using recursion.
#

from gui import *

# create display
d = Display("Sierpinski Triangle", 250, 250)
#d.setColor(Color.WHITE)

# recursive drawing parameters
depth = 6      # amount of detail (number of subdivisions)

# initial points
x      = d.getWidth() / 2    # top corner
y      = 20
width  = d.getWidth() - 40
height = d.getHeight() - y - 30

# recursive function for drawing triangle
def drawTriangle(x, y, width, height, depth):
"""
Recursively draws a Sierpinski triangle of depth 'depth' with top at 'x', 'y', and
provided 'width' and 'height'.
"""
global d  # display to draw tree

# get corners
x1, y1 = x, y                     # top
x2, y2 = x - width/2, y + height  # left
x3, y3 = x + width/2, y + height  # right

# reached the pixel level?
if depth == 1:

d.drawPolygon([x1, x2, x3], [y1, y2, y3])   # yes, so draw a triangle

else:   # no, so continue subdividing

# get top corners of subtriangles
topX, topY     = x, y
leftX, leftY   = x - width/4, y + height/2
rightX, rightY = x + width/4, y + height/2

# and draw them recursively
drawTriangle(topX, topY, width/2, height/2, depth-1)      # draw top subtriangle
drawTriangle(leftX, leftY, width/2, height/2, depth-1)    # draw left subtriangle
drawTriangle(rightX, rightY, width/2, height/2, depth-1)  # draw right subtriangle

# draw complete Sierpinski triangle (recursively)
drawTriangle(x, y, width, height, depth)
```

It generates this shape::

## Measuring Zipf proportions in a MIDI file

The following program (Ch. 11, p. 353) demonstrates how to read in a sequence of MIDI files and measure how well the note pitches in each song follow Zipf’s law. It is relatively easy to expand it to measure additional proportions, such as note durations.

It uses the following MIDI files:

Make sure they are saved in the same folder as the program below, prior to running it.

Here is the code:

```# zipfMetrics.py
#
# Demonstrates how to calculate Zipf metrics from MIDI files for
# comparative analysis. It calculates Zipf slopes and R^2 values
# for pitch.
#
# It also demonstrates how to use Python dictionaries to store
# collections of related items.
#
# Finally, it demonstrates how to implement an algorithm in a top-down
# fashion. First function encountered in the program performs the
# functions.
#

from music import *
from zipf import *

# list of MIDI files to analyze
pieces = ["sonifyBiosignals.mid", "ArvoPart.CantusInMemoriam.mid",
"DeepPurple.SmokeOnTheWater.mid",
"soundscapeLoutrakiSunset.mid",
"Pierre Cage.Structures pour deux chances.mid"]

# define main function
def main( pieces ):
"""Calculates and outputs Zipf statistics of all 'pieces'."""

# read MIDI files and count pitches
for piece in pieces:

# read this MIDI file into a score
score = Score() # create an empty score

# count the score's pitches
histogram = countPitches( score )

# calculate Zipf slope and R^2 value
counts = histogram.values()
slope, r2, yint = byRank(counts)

# output results
print "Zipf slope is", round(slope, 4), ", R^2 is", round(r2, 2),
print "for", piece
print

# now, all the MIDI files have been read into dictionary
print # output one more newline

def countPitches( score ):
"""Returns count of how many times each individual pitch appears
in 'score'.
"""

histogram = {} # holds each of the pitches found and its count

# iterate through every part, and for every part through every
# phrase, and for every phrase through every note (via nested
# loops)
for part in score.getPartList(): # for every part
for phrase in part.getPhraseList(): # for every phrase in part
for note in phrase.getNoteList(): # for every note in phrase

pitch = note.getPitch() # get this note's pitch

# count this pitch, if not a rest
if (pitch != REST):

# increment this pitch's count (or initialize to 1)
histogram[ pitch ] = histogram.get(pitch, 0) + 1

# now, all the notes in this phrase have been counted
# now, all the phrases in this part have been counted
# now, all the parts have been counted

# done, so return counts
return histogram

# start the program
main( pieces )
```

## Implementing the Note class

The following program (Ch. 11, p. 361demonstrates how to define a simplified version of the Note class. This class is used to store related information about musical notes (i.e., pitch, duration, dynamic, and panning). It also provides functions to retrieve (get) and update (set) the information encapsulated in a Note object.

Here is the code:

```# note.py
#
# It demonstrates how to create a class to encapsulate a musical
# note.  (This is a simplified version of Note class found in the
# music library.)
#

from music import *

class Note:

def __init__(self, pitch=C4, duration=QN, dynamic=85, panning=0.5):
"""Initializes a Note object."""

self.pitch = pitch        # holds note pitch (0-127)
self.duration = duration  # holds note duration (QN = 1.0)
self.dynamic = dynamic    # holds note dynamic (0-127)
self.panning = panning    # holds note panning (0.0-1.0)

def getPitch(self):
"""Returns the note's pitch (0-127)."""

return self.pitch

def setPitch(self, pitch):
"""Sets the note's pitch (0-127)."""

# first ensure data integrity, then update
if 0 &amp;lt;= pitch &amp;lt;= 127:  # is pitch in the right range?
self.pitch = pitch     # yes, so update value
else:                  # otherwise let them know
print "TypeError: Note.setPitch(): pitch ranges from",
print "0 to 127 (got " + str(pitch) + ")"
#raise TypeError("Note.setPitch(): pitch ranges from 0 to 127 (got " + str(pitch) + ")")

def getDuration(self):
"""Returns the note's duration."""

return self.duration

def setDuration(self, duration):
"""Sets the note's duration (a float)."""

if type(duration) == type(1.0):    # is duration a float?
self.duration = float(duration)    # yes, so update value
else:                  # otherwise let them know
print "TypeError: Note.setDuration(): duration must be",
print "a float (got " + str(duration) + ")"
# raise TypeError("Note.setDuration(): duration must be a float (got " + str(duration) + ")")

if __name__ == '__main__':

# create a note
print "n = Note(C4, QN)"
n = Note(C4, QN)
print "n.getPitch() =", n.getPitch()
print "n.getDuration() =", n.getDuration()
```

## Implementing a Slider control

The following program (Ch. 11, p. 369) demonstrates how to define a SliderControl class, which can be used to create individual sliders.

A SliderControl object has a display with a single slider. This can be used to control the value of any program variable interactively (as demonstrated below).

Here is the code:

```# sliderControl.py
#
# It creates a simple slider control surface.  It allows arbitrary
# numeric values for max, min, and start, as long as
# min &amp;lt; start &amp;lt; max.  However, the accuracy (or resolution) of the
# slider control (its ability to discriminate between two
# "consecutive" values) depends on screen resolution and mouse
# accuracy. (If the min to max range has too many values in it
# (e.g., 1 to 1000), some will not be "reachable".)
#
from gui import *

class SliderControl:

def __init__(self, title="Slider", updateFunction=None,
minValue=10, maxValue=1000, startValue=None,
x=0, y=0):
"""Initializes a SliderControl object."""

# holds the title of the control display
self.title = title

# external function to call when slider is updated
self.updateFunction = updateFunction

# determine slider start value
if startValue == None:  # if startValue is undefined

# start at middle point between min and max value
startValue = (minValue + maxValue) / 2

# create slider
self.slider = Slider(HORIZONTAL, minValue, maxValue, startValue, self.setValue)

# create control surface display
self.display = Display(self.title, 250, 50, x, y)

# finally, initialize value and title (using 'startValue')
self.setValue( startValue )

def setValue(self, value):
"""Updates the display title, and calls the external
update function with given 'value'.
"""

self.display.setTitle(self.title + " = " + str(value))
self.updateFunction(value)

if __name__ == '__main__':

# one example
def printValue(value):
print value

control = SliderControl("Print", printValue)  # just the title and update function, all else using default values

# another example (demonstrating how to change a global variable - more useful)
testValue = -1

def changeAndPrintValue(value):
global testValue

testValue = value
print testValue

control1 = SliderControl("Change and Print", changeAndPrintValue, 0, 100, 50, 100, 100)  # modify program variable

# and another one
from music import *    # for Play.note()

def playNote(pitch):
"""Plays an 1-sec note with the provided pitch."""
Play.note(pitch, 0, 1000)

control2 = SliderControl("Play Note", playNote, 0, 100, 50, 200, 200)  # modify program variable
```

When run by itself, it generates the following sliders:

## Animation – a revolving music sphere

This code sample (Ch. 11, p. 375) demonstrates how to create an animation involving a revolving sphere.

We model the sphere by drawing points on a GUI display. As the sphere rotates, it plays musical notes. Rotation speed is adjusted using a slider on a separate display.

Here is the code:

```# musicalSphere.py
#
# Demonstrates how to create an animation of a 3D sphere using regular points
# on a Display.  The sphere is modeled using points on a spherical coordinate
# system (see http://en.wikipedia.org/wiki/Spherical_coordinate_system).
# It converts from spherical 3D coordinates to cartesian 2D coordinates (mapping
# the z axis to color).  When a point passes the primary meridian (the imaginary
# vertical line closest to the viewer), a note is played based on its latitude
# (low to high pitch).  Also the point turns red momentarily.
#
# Based on code by Uri Wilensky (1998), distributed with NetLogo
#
from gui import *
from music import *
from random import *
from math import *
from sliderControl import *

class MusicalSphere:
"""Creates a revolving sphere that plays music."""

def __init__(self, radius, density, velocity=0.01, frameRate=30):
"""
Construct a revolving sphere with given 'radius', 'density'
number of points (all on the surface), moving with 'velocity' angular
(theta / azimuthal) velocity, at 'frameRate' frames (or movements) per
second.  Each point plays a note when crossing the zero meridian (the
sphere's meridian (vertical line) closest to the viewer).
"""

# musical parameters
self.instrument = PIANO
self.scale = MAJOR_SCALE
self.lowPitch = C1
self.highPitch = C6
self.noteDuration = 2000    # milliseconds (2 seconds)

Play.setInstrument(self.instrument, 0)   # set the instrument

# visual parameters
self.display.setColor( Color.BLACK )  # set background color to black

self.numPoints = density   # how many points to draw on sphere surface
self.velocity = velocity   # how far it rotates per animation frame
self.frameRate = frameRate # how many animation frames to do per second

self.xCenter = self.display.getWidth() / 2   # to place circle at display's center
self.yCenter = self.display.getHeight() / 2

# sphere data structure (parallel lists)
self.points      = []    # holds the points
self.thetaValues = []    # holds the points' rotation (azimuthal angle)
self.phiValues   = []    # holds the points' latitude (polar angle)

# timer to drive animation
delay = 1000 / frameRate   # convert from frame rate to timer delay (in milliseconds)
self.timer = Timer(delay, self.movePoints)   # timer to schedule movement

# control surface for animation frame rate
xPosition = self.display.getWidth() / 3    # set initial position of display
yPosition = self.display.getHeight() + 45
self.control = SliderControl(title="Frame Rate", updateFunction=self.setFrameRate,
minValue=1, maxValue=120, startValue=self.frameRate,
x=xPosition, y=yPosition)

# orange color gradient (used to display depth, the further away, the darker)
black = [0, 0, 0]         # RGB values for black
orange = [251, 147, 14]   # RGB values for orange
white = [255, 255, 255]   # RGB values for white

# create list of gradient colors from black to orange, and from orange to white
# (a total of 25 colors)
self.gradientColors = colorGradient(black, orange, 12) + colorGradient(orange, white, 12) + [white]  # remember to include the final color

self.initSphere()      # create the circle

self.start()           # and start rotating!

def start(self):
"""Starts sphere animation."""
self.timer.start()

def stop(self):
"""Stops sphere animation."""
self.timer.stop()

def setFrameRate(self, frameRate=30):
"""Controls speed of sphere animation (by setting how many times per second to move points)."""

delay = 1000 / frameRate   # convert from frame rate to delay between each update (in milliseconds)
self.timer.setDelay(delay)

def initSphere(self):
"""Generate a sphere of 'radius' out of points (placed on the surface of the sphere)."""

for i in range(self.numPoints):     # create all the points

# get random spherical coordinates for this point
r = self.radius                                  # all points are placed *on* the surface
theta = mapValue( random(), 0.0, 1.0, 0.0, 2*pi) # random rotation (azimuthal angle)
phi = mapValue( random(), 0.0, 1.0, 0.0, pi)     # random latitude (polar angle)

# project from spherical to cartesian 3D coordinates (z is depth)
x, y, z = self.sphericalToCartesian(r, phi, theta)

# convert depth (z) to color

# create a point (with thickness 1) at these x, y coordinates
point = Point(x, y, color, 1)

# remember this point and its spherical coordinates (r equals self.radius for all points)
# (append data for this point to the three parallel lists)
self.points.append( point )
self.phiValues.append( phi )
self.thetaValues.append( theta )

# add this point to the display

def sphericalToCartesian(self, r, phi, theta):
"""Convert spherical to cartesian coordinates."""

# adjust rotation so that theta is 0 at max z (i.e., closest to viewer)
x = int( r * sin(phi) * cos(theta + pi/2) )   # horizontal axis (pixels are int)
y = int( r * cos(phi) )                       # vertical axis
z = int( r * sin(phi) * sin(theta + pi/2) )   # depth axis

# move sphere's center to display's center
x = x + self.xCenter
y = y + self.yCenter

return x, y, z

"""Map 'depth' to color using the 'gradientColors' RGB colors."""

# create color based on position (points further away have less luminosity)
colorRGB = self.gradientColors[colorIndex]                    # get corresponding RBG value
color = Color(colorRGB, colorRGB, colorRGB)          # and create the color

return color

def movePoints(self):
"""Rotate points on y axis as specified by angular velocity."""

for i in range(self.numPoints):
point = self.points[i]                                   # get this point
theta = (self.thetaValues[i] + self.velocity) % (2*pi)   # increment angle to simulate rotation
phi = self.phiValues[i]                                  # get latitude (altitude)

# convert from spherical to cartesian 3D coordinates
x, y, z = self.sphericalToCartesian(self.radius, phi, theta)

if self.thetaValues[i] &amp;gt; theta:   # did we just cross the primary meridian?
color = Color.RED                  # yes, so sparkle for a split second
pitch = mapScale(phi, 0, pi, self.lowPitch, self.highPitch, self.scale)  # phi is latitude
dynamic = randint(0, 127)          # random dynamic
Play.note(pitch, 0, self.noteDuration, dynamic)  # and play a note (based on latitude)

else:   # we are not on the primary meridian, so
# convert depth to color (points further away have less luminosity)

# adjust this point's position and color
self.display.move(point, x, y)
point.setColor(color)

# now, remember this point's new theta coordinate
self.thetaValues[i] = theta

#################################################
# create a sphere
sphere = MusicalSphere(radius=200, density=200, velocity=0.01, frameRate=30)
```

This code generates the following animation. Since the points are placed randomly, the music generated will be different every time:

## Boids – implementing flocking behavior

The following program (Ch. 11, p. 389) demonstrates the types of behaviors that emerge from self-adapting agents (boids), which sense their surroundings and modify their behavior (movement) accordingly.  These boids (points) move around the display, staying in close proximity to other boids, yet avoiding them, while collectively moving towards the mouse (when the mouse is dragged).

This code may be adapted to resemble the movement of birds, fish, and other animal collectives.  There are numerous ways to add sound to it (proximity, cohesion, movement, speed of movement, rean collisions, and so on).  The possibilities are endless (so, none have been provided here). Explore.

Here is the code:

```# boids.py
#
# This program simulates 2D boid behavior.
#
# See http://www.red3d.com/cwr/boids/ and
#

from gui import *
from math import *
from random import *

# universe parameters
universeWidth  = 1000   # how wide the display
universeHeight = 800    # how high the display

# boid generation parameters
numBoids   = 200        # from 2 to as much as your CPU can handle
boidColor  = Color.BLUE # color of boids

# boid distance parameters
minSeparation  = 30     # min comfortable distance between two boids
flockThershold = 100    # boids closer than this are in a local flock

# boid behavior parameters (higher means quicker/stronger)
separationFactor = 0.01 # how quickly to separate
alignmentFactor = 0.16  # how quickly to align with local flockmates
cohesionFactor = 0.01   # how quickly to converge to attraction point
frictionFactor = 1.1    # how hard it is to move (dampening factor)

### define boid universe, a place where boids exist and interact ####
class BoidUniverse:
"""This is the boid universe, where boids exist and interact.
It is basically a GUI Display, with boids (moving Circles)
added to it.  While boids are represented as circles, they
have a little more logic to them - they can sense where other
boids are, and act accordingly.  While individual boids have
simple rules for sensing their environment and reacting,
very intricate, complex, naturally-looking patterns of behavior
emerges, similar to those of birds flying high in the sky
(among others).  The rules of behavior are the same for all
boids, and are defined in the Boid class (a sister class to
this one).
"""

def __init__(self, title = "", width = 600, height = 400,
frameRate=30):

self.display = Display(title, width, height) # universe display

self.boids = []                              # list of boids

# holds attraction point for boids (initially, universe center)
self.attractPoint = complex(width/2, height/2)

# create timer
delay = 1000 / frameRate     # convert frame rate to delay (ms)
self.timer = Timer(int(delay), self.animate)       # animation timer

# when mouse is dragged, call this function to set the
# attraction point for boids
self.display.onMouseDrag( self.moveAttractionPoint )

def start(self):
"""Starts animation."""
self.timer.start()   # start movement!

def stop(self):
"""Stops animation."""
self.timer.stop()    # stop movement

"""Adds another boid to the system."""

self.boids.append( boid )          # remember this boid

def animate(self):
"""Makes boids come alive."""

### sensing and acting loop for all boids in the universe !!!
for boid in self.boids:   # for every boid

# first observe other boids and decide how to adjust movement
boid.sense(self.boids, self.attractPoint)

# and then, make it so (move)!
boid.act(self.display)

def moveAttractionPoint(self, x, y):
"""Update the attraction point for all boids."""
self.attractPoint = complex(x, y)

### define the boids, individual agents who can sense and act #######
class Boid:
"""This a boid.  A boid is a simplified bird (or other species)
that lives in a flock.  A boid is represented as a circle,
however, it has a little more logic to it - it can sense where
other boids are, and act, simply by adjusting its direction of
movement.  The new direction is a combination of its reactions
from individual rules of thumb (e.g., move towards the center
of the universe, avoid collisions with other boids, fly in the
same general direction as boids around you (follow the local
flock, as you perceive it), and so on.  Out of these simple
rules intricate, complex behavior emerges, similar to that of
real birds (or other species) in nature.
"""

def __init__(self, x, y, radius, color,
initVelocityX=1, initVelocityY=1 ):
"""Initialize boid's position, size, and initial
velocity (x, y).
"""

# a boid is a filled circle
self.circle = Circle(x, y, radius, color, True)

# set boid size, position
self.coordinates = complex(x, y)    # boid coordinates (x, y)

# NOTE: We treat velocity in a simple way, i.e., as the
# x, y displacement to add to the current boid coordinates,
# to find where to move its circle next.  This moving is done
# once per animation frame.

# initialize boid velocity (x, y)
self.velocity = complex(initVelocityX, initVelocityY)

def sense(self, boids, center):
"""
Sense other boids' positions, etc., and adjust velocity
(i.e., the displacement of where to move next).
"""

# use individual rules of thumb, to decide where to move next

# 1. Rule of Separation - move away from other flockmates
#                         to avoid crowding them
self.separation = self.rule1_Separation(boids)

# 2. Rule of Alignment - move towards the average heading
#                        of other flockmates
self.alignment = self.rule2_Alignment(boids)

# 3. Rule of Cohesion - move toward the center of the universe
self.cohesion = self.rule3_Cohesion(boids, center)

# 4. Rule of Avoidance: move to avoid any obstacles
#self.avoidance = self.rule4_Avoidance(boids)

# create composite behavior
self.velocity = (self.velocity / frictionFactor) + \
self.separation + self.alignment + \
self.cohesion

def act(self, display):
"""Move boid to a new position using current velocity."""

# Again, we treat velocity in a simple way, i.e., as the
# x, y displacement to add to the current boid coordinates,
# to find where to move its circle next.

# update coordinates
self.coordinates = self.coordinates + self.velocity

# get boid (x, y) coordinates
x = self.coordinates.real  # get the x part
y = self.coordinates.imag  # get the y part

# act (i.e., move boid to new position)
display.move( self.circle, int(x), int(y) )

##### steering behaviors ####################
def rule1_Separation(self, boids):
"""Return proper velocity to keep separate from other boids,
i.e., avoid collisions.
"""

newVelocity = complex(0, 0)  # holds new velocity

# get distance from every other boid in the flock, and as long
# as we are too close for comfort, calculate direction to
# move away (remember, velocity is just an x, y distance
# to travel in the next animation/movement frame)
for boid in boids:           # for each boid

separation = self.distance(boid)   # how far are we?

# too close for comfort (excluding ourself)?
if separation &amp;lt; minSeparation and boid != self:
# yes, so let's move away from this boid
newVelocity = newVelocity - \
(boid.coordinates - self.coordinates)

return newVelocity * separationFactor  # return new velocity

def rule2_Alignment(self, boids):
"""Return proper velocity to move in the same general direction
as local flockmates.
"""

totalVelocity = complex(0, 0) # holds sum of boid velocities
numLocalFlockmates = 0        # holds count of local flockmates

# iterate through all the boids looking for local flockmates,
# and accumuate all their velocities
for boid in boids:

separation = self.distance(boid)    # get boid distance

# if this a local flockmate, record its velocity
if separation < flockThershold and boid != self:
totalVelocity = totalVelocity + boid.velocity
numLocalFlockmates = numLocalFlockmates + 1

# average flock velocity (excluding ourselves)
if numLocalFlockmates > 0:
avgVelocity = totalVelocity / numLocalFlockmates
else:
avgVelocity = totalVelocity

# adjust velocity by how quickly we want to align
newVelocity = avgVelocity - self.velocity

return newVelocity * alignmentFactor  # return new velocity

def rule3_Cohesion(self, boids, center):
"""Return proper velocity to bring us closer to center of the
universe.
"""

newVelocity = center - self.coordinates

return newVelocity * cohesionFactor  # return new velocity

##### helper function ####################
def distance(self, other):
"""Calculate the Euclidean distance between this and
another boid.
"""

xDistance = (self.coordinates.real - other.coordinates.real)
yDistance = (self.coordinates.imag - other.coordinates.imag)

return sqrt( xDistance*xDistance + yDistance*yDistance )

# start boid simulation
universe = BoidUniverse(title="Boid Flocking Behavior",
width=universeWidth, height=universeHeight,
frameRate=30)

# create and place boids
for i in range(0, numBoids):

# get random position for this boid
x = randint(0, universeWidth)
y = randint(0, universeHeight)

# create a boid with random position and velocity
boid = Boid(x, y, boidRadius, boidColor, 1, 1)

# animate boids
universe.start()

```

It generates an interactive animation similar to this:

## In closing…

This website provides source code and related materials (e.g., API, etc.).

For more details and thorough explanations see the book. Also, the book offers observations, facts, and exercises for further musical explorations, and ways to create music via programming. Enjoy.

Ars longa, vita brevis…