📚 The CoCalc Library - books, templates and other resources
License: OTHER
""" Code example from Complexity and Computation, a book about1exploring complexity science with Python. Available free from23http://greenteapress.com/complexity45Original code by Matt Aasted, modified by Allen Downey.67Based on Reynolds, "Flocks, Herds and Schools" and8Flake, "The Computational Beauty of Nature."910Copyright 2011 Allen B. Downey.11Distributed under the GNU General Public License at gnu.org/licenses/gpl.html.12"""1314import visual15import numpy as np1617# size of the boids18b_radius = 0.0319b_length = 0.12021# radii for sensing different rules22r_avoid = 0.323r_center = 1.024r_copy = 0.52526# viewing angle for different rules, in radians27a_avoid = 2*np.pi28a_center = 229a_copy = 23031# weights for various rules32w_avoid = 433w_center = 334w_copy = 235w_love = 103637# time step38dt = 0.1394041def random_vector(a, b):42"""Create a vector with each element uniformly distributed in [a, b)."""43t = [np.random.uniform(a,b) for i in range(3)]44return visual.vector(t)454647def limit_vector(vect):48"""if the magnitude is greater than 1, set it to 1"""49if vect.mag > 1:50vect.mag = 151return vect525354null_vector = visual.vector(0,0,0)555657class Boid(visual.cone):58"""A Boid is a Visual cone with a velocity"""5960def __init__(self, radius=b_radius, length=b_length):61pos = random_vector(0, 1)62self.vel = random_vector(0, 1).norm()63visual.cone.__init__(self, pos=pos, radius=radius)64self.axis = length * self.vel.norm()6566def get_neighbors(self, others, radius, angle):67"""Return the list of neighbors within the given radius and angle."""68boids = []69for other in others:70if other is self: continue71offset = other.pos - self.pos7273# if not in range, skip it74if offset.mag > radius:75continue7677# if not within viewing angle, skip it78if self.vel.diff_angle(offset) > angle:79continue8081# otherwise add it to the list82boids.append(other)8384return boids8586def avoid(self, others, carrot):87"""Find the center of mass of all objects in range and88returns a vector in the opposite direction, with magnitude89proportional to the inverse of the distance (up to a limit)."""90others = others + [carrot]91close = self.get_neighbors(others, r_avoid, a_avoid)92t = [other.pos for other in close]93if t:94center = np.sum(t)/len(t)95away = visual.vector(self.pos - center)96away.mag = r_avoid / away.mag97return limit_vector(away)98else:99return null_vector100101def center(self, others):102"""Find the center of mass of other boids in range and103returns a vector pointing toward it."""104close = self.get_neighbors(others, r_center, a_center)105t = [other.pos for other in close]106if t:107center = np.sum(t)/len(t)108toward = visual.vector(center - self.pos)109return limit_vector(toward)110else:111return null_vector112113def copy(self, others):114"""Return the average heading of other boids in range.115116others: list of Boids117"""118close = self.get_neighbors(others, r_copy, a_copy)119t = [other.vel for other in close]120if t:121# TODO: replace this with mean122center = np.sum(t)/len(t)123away = visual.vector(self.pos - center)124return limit_vector(away)125else:126return null_vector127128def love(self, carrot):129"""Returns a vector pointing toward the carrot."""130toward = carrot.pos - self.pos131return limit_vector(toward)132133def set_goal(self, boids, carrot):134"""Sets the goal to be the weighted sum of the goal vectors."""135self.goal = (w_avoid * self.avoid(boids, carrot) +136w_center * self.center(boids) +137w_copy * self.copy(boids) +138w_love * self.love(carrot))139self.goal.mag = 1140141def move(self, mu=0.1):142"""Update the velocity, position and axis vectors.143mu controls how fast the boids can turn (maneuverability)."""144145self.vel = (1-mu) * self.vel + mu * self.goal146self.vel.mag = 1147148self.pos += dt * self.vel149self.axis = b_length * self.vel.norm()150151152class World(object):153def __init__(self, n=10):154"""Create n Boids and one carrot.155156tracking: indicates whether the carrot follows the mouse157"""158self.boids = [Boid() for i in range(n)]159self.carrot = visual.sphere(pos=(1,0,0), radius=0.1, color=(1,0,0))160self.tracking = False161162def step(self):163"""Compute one time step."""164# move the boids165for boid in self.boids:166boid.set_goal(self.boids, self.carrot)167boid.move()168169# mouse click toggles tracking170if scene.mouse.clicked:171scene.mouse.getclick()172self.tracking = not self.tracking173174# if we're tracking, move the carrot175if self.tracking:176self.carrot.pos = scene.mouse.pos177178179def main(script, n=20):180global scene181182n = int(n)183size = 5184scene = visual.display(title='Boids', width=800, height=600,185range=(size, size, size))186world = World(n)187scene.center = world.carrot.pos188scene.autoscale = False189190while 1:191# update the screen once per time step192visual.rate(1/dt)193world.step()194195196if __name__ == '__main__':197import sys198main(*sys.argv)199200201