Skip to content

Commit

Permalink
More documentation and testing
Browse files Browse the repository at this point in the history
  • Loading branch information
dmasad committed Jun 26, 2015
1 parent 6bf1b27 commit 9efb97b
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 4 deletions.
46 changes: 43 additions & 3 deletions examples/Flockers/flockers.py
@@ -1,6 +1,6 @@
'''
Flockers
===============
=============================================================
A Mesa implementation of Craig Reynolds's Boids flocker model.
Uses numpy arrays to represent vectors.
'''
Expand All @@ -19,13 +19,27 @@ class Boid(Agent):
A Boid-style flocker agent.
The agent follows three behaviors to flock:
- Cohesion: steering towards neighboring agents
- Separation:
- Cohesion: steering towards neighboring agents.
- Separation: avoiding getting too close to any other agent.
- Alignment: try to fly in the same direction as the neighbors.
Boids have a vision that defines the radius in which they look for their
neighbors to flock with. Their speed (a scalar) and heading (a unit vector)
define their movement. Separation is their desired minimum distance from
any other Boid.
'''
def __init__(self, unique_id, pos, speed=5, heading=None,
vision=5, separation=1):
'''
Create a new Boid flocker agent.
Args:
unique_id: Unique agent identifyer.
pos: Starting position
speed: Distance to move per step.
heading: numpy vector for the Boid's direction of movement.
vision: Radius to look around for nearby Boids.
separation: Minimum distance to maintain from other Boids.
'''
self.unique_id = unique_id
self.pos = pos
Expand All @@ -48,6 +62,9 @@ def cohere(self, neighbors):
return center / len(neighbors)

def separate(self, neighbors):
'''
Return a vector away from any neighbors closer than separation dist.
'''
my_pos = np.array(self.pos)
sep_vector = np.array([0, 0])
for neighbor in neighbors:
Expand All @@ -58,12 +75,18 @@ def separate(self, neighbors):
return sep_vector

def match_heading(self, neighbors):
'''
Return a vector of the neighbors' average heading.
'''
mean_heading = np.array([0, 0])
for neighbor in neighbors:
mean_heading += neighbor.heading
return mean_heading / len(neighbors)

def step(self, model):
'''
Get the Boid's neighbors, compute the new vector, and move accordingly.
'''
x, y = self.pos
neighbors = model.space.get_neighbors(x, y, self.vision, False)
if len(neighbors) > 0:
Expand All @@ -79,11 +102,25 @@ def step(self, model):


class BoidModel(Model):
'''
Flocker model class. Handles agent creation, placement and scheduling.
'''
N = 100
width = 100
height = 100

def __init__(self, N, width, height, speed, vision, separation):
'''
Create a new Flockers model.
Args:
N: Number of Boids
width, height: Size of the space.
speed: How fast should the Boids move.
vision: How far around should each Boid look for its neighbors
separtion: What's the minimum distance each Boid will attempt to
keep from any other
'''
self.N = N
self.vision = vision
self.speed = speed
Expand All @@ -95,6 +132,9 @@ def __init__(self, N, width, height, speed, vision, separation):
self.running = True

def make_agents(self):
'''
Create N agents, with random positions and starting headings.
'''
for i in range(self.N):
x = random.random() * self.space.x_max
y = random.random() * self.space.y_max
Expand Down
49 changes: 48 additions & 1 deletion tests/test_space.py
Expand Up @@ -6,7 +6,7 @@
TEST_AGENTS = [(-20, -20), (-20, -20.05), (65, 18)]


class TestContinuousSpace(unittest.TestCase):
class TestSpaceToroidal(unittest.TestCase):
'''
Testing a toroidal continuous space.
'''
Expand Down Expand Up @@ -53,3 +53,50 @@ def test_neighborhood_retrieval(self):

neighbors_3 = self.space.get_neighbors(-30, -30, 10)
assert len(neighbors_3) == 1


class TestSpaceNonToroidal(unittest.TestCase):
'''
Testing a toroidal continuous space.
'''

def setUp(self):
'''
Create a test space and populate with Mock Agents.
'''
self.space = ContinuousSpace(70, 20, False, -30, -30, 100, 100)
self.agents = []
for i, pos in enumerate(TEST_AGENTS):
a = MockAgent(i, None)
self.agents.append(a)
self.space.place_agent(a, pos)

def test_agent_positions(self):
'''
Ensure that the agents are all placed properly.
'''
for i, pos in enumerate(TEST_AGENTS):
a = self.agents[i]
assert a.pos == pos

def test_distance_calculations(self):
'''
Test toroidal distance calculations.
'''

pos_2 = (70, 20)
pos_3 = (-30, -20)
assert self.space.get_distance(pos_2, pos_3) == 107.70329614269008

def test_neighborhood_retrieval(self):
'''
Test neighborhood retrieval
'''
neighbors_1 = self.space.get_neighbors(-20, -20, 1)
assert len(neighbors_1) == 2

neighbors_2 = self.space.get_neighbors(40, -10, 10)
assert len(neighbors_2) == 0

neighbors_3 = self.space.get_neighbors(-30, -30, 10)
assert len(neighbors_3) == 0

0 comments on commit 9efb97b

Please sign in to comment.