Skip to content

Commit

Permalink
Merge pull request #169 from projectmesa/pr/161
Browse files Browse the repository at this point in the history
Wolf-Sheep Cleanup
  • Loading branch information
jackiekazil committed Feb 10, 2016
2 parents 61745a2 + 46f47fd commit 553bf04
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 39 deletions.
7 changes: 7 additions & 0 deletions examples/WolfSheep/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,11 @@ Another ABM standard. Wolf and sheep wander around the grid randomly and reprodu
- Dynamically adding and removing agents from the schedule
- (Eventually) visualizing a layered grid, with a grass layer, a wolf layer and a sheep layer.

### To run this example

* Launch the model
```python
python WolfSheepServer.py
```
* Visit your browser: http://127.0.0.1:8888/
* In your browser hit *reset*, then *run*
233 changes: 207 additions & 26 deletions examples/WolfSheep/WolfSheep.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
http://ccl.northwestern.edu/netlogo/models/WolfSheepPredation.
Center for Connected Learning and Computer-Based Modeling,
Northwestern University, Evanston, IL.
TODO: Implement grass
'''

import random
from collections import defaultdict

from mesa import Model, Agent
from mesa.space import MultiGrid
from mesa.time import RandomActivation
from mesa.datacollection import DataCollector

from RandomWalk import RandomWalker

Expand All @@ -25,23 +25,28 @@ class WolfSheepPredation(Model):
Wolf-Sheep Predation Model
'''

height = 20
width = 20

initial_sheep = 100
initial_wolves = 50
sheep_gain_from_food = 4

grass = False

wolf_gain_from_food = 20
sheep_reproduce = 0.04
wolf_reproduce = 0.05

height = 20
width = 20
wolf_gain_from_food = 20

grass = False
grass_regrowth_time = 30
sheep_gain_from_food = 4

verbose = False # Print-monitoring

def __init__(self, height=20, width=20,
initial_sheep=100, initial_wolves=50, sheep_reproduce=0.04,
wolf_reproduce=0.05, wolf_gain_from_food=20,
grass=False, sheep_gain_from_food=4):
initial_sheep=100, initial_wolves=50,
sheep_reproduce=0.04, wolf_reproduce=0.05,
wolf_gain_from_food=20,
grass=False, grass_regrowth_time=30, sheep_gain_from_food=4):
'''
Create a new Wolf-Sheep model with the given parameters.
Expand All @@ -52,6 +57,8 @@ def __init__(self, height=20, width=20,
wolf_reproduce: Probability of each wolf reproducing each step
wolf_gain_from_food: Energy a wolf gains from eating a sheep
grass: Whether to have the sheep eat grass for energy
grass_regrowth_time: How long it takes for a grass patch to regrow
once it is eaten
sheep_gain_from_food: Energy sheep gain from grass, if enabled.
'''

Expand All @@ -64,16 +71,21 @@ def __init__(self, height=20, width=20,
self.wolf_reproduce = wolf_reproduce
self.wolf_gain_from_food = wolf_gain_from_food
self.grass = grass
self.grass_regrowth_time = grass_regrowth_time
self.sheep_gain_from_food = sheep_gain_from_food

self.schedule = RandomActivation(self)
self.schedule = RandomActivationByBreed(self)
self.grid = MultiGrid(self.height, self.width, torus=True)
self.datacollector = DataCollector(
{"Wolves": lambda m: m.schedule.get_breed_count(Wolf),
"Sheep": lambda m: m.schedule.get_breed_count(Sheep)})

# Create sheep:
for i in range(self.initial_sheep):
x = random.randrange(self.width)
y = random.randrange(self.height)
sheep = Sheep(self.grid, (x, y), True)
energy = random.randrange(2 * self.sheep_gain_from_food)
sheep = Sheep(self.grid, (x, y), True, energy)
self.grid.place_agent(sheep, (x, y))
self.schedule.add(sheep)

Expand All @@ -86,11 +98,48 @@ def __init__(self, height=20, width=20,
self.grid.place_agent(wolf, (x, y))
self.schedule.add(wolf)

# Create grass patches
if self.grass:
for agent, x, y in self.grid.coord_iter():

fully_grown = random.choice([True, False])

if fully_grown:
countdown = self.grass_regrowth_time
else:
countdown = random.randrange(self.grass_regrowth_time)

patch = GrassPatch(fully_grown, countdown)
self.grid.place_agent(patch, (x, y))
self.schedule.add(patch)

self.running = True

def step(self):
self.schedule.step()
self.datacollector.collect(self)
if self.verbose:
print([self.schedule.time,
self.schedule.get_breed_count(Wolf),
self.schedule.get_breed_count(Sheep)])

def run_model(self, step_count=200):

if self.verbose:
print('Initial number wolves: ',
self.schedule.get_breed_count(Wolf))
print('Initial number sheep: ',
self.schedule.get_breed_count(Sheep))

for i in range(step_count):
self.step()

if self.verbose:
print('')
print('Final number wolves: ',
self.schedule.get_breed_count(Wolf))
print('Final number sheep: ',
self.schedule.get_breed_count(Sheep))

class Sheep(RandomWalker, Agent):
'''
Expand All @@ -99,16 +148,43 @@ class Sheep(RandomWalker, Agent):
The init is the same as the RandomWalker.
'''

energy = None

def __init__(self, grid, pos, moore, energy=None):
super().__init__(grid, pos, moore)
self.energy = energy

def step(self, model):
'''
A model step. Move, then eat grass and reproduce.
'''
self.random_move()
if random.random() < model.sheep_reproduce:
living = True

if model.grass:
# Reduce energy
self.energy -= 1

# If there is grass available, eat it
this_cell = model.grid.get_cell_list_contents([self.pos])
grass_patch = [obj for obj in this_cell
if isinstance(obj, GrassPatch)][0]
if grass_patch.fully_grown:
self.energy += model.sheep_gain_from_food
grass_patch.fully_grown = False

# Death
if self.energy < 0:
model.grid._remove_agent(self.pos, self)
model.schedule.remove(self)
living = False

if living and random.random() < model.sheep_reproduce:
# Create a new sheep:
x, y = self.pos
lamb = Sheep(self.grid, self.pos, self.moore)
model.grid[y][x].add(lamb)
if model.grass:
self.energy /= 2
lamb = Sheep(self.grid, self.pos, self.moore, self.energy)
model.grid.place_agent(lamb, self.pos)
model.schedule.add(lamb)


Expand All @@ -129,20 +205,125 @@ def step(self, model):

# If there are sheep present, eat one
x, y = self.pos
this_cell = model.grid[y][x]
this_cell = model.grid.get_cell_list_contents([self.pos])
sheep = [obj for obj in this_cell if isinstance(obj, Sheep)]
if len(sheep) > 0:
sheep_to_eat = random.choice(sheep)
self.energy += model.wolf_gain_from_food

# Kill the sheep
model.grid[y][x].remove(sheep_to_eat)
model.grid._remove_agent(self.pos, sheep_to_eat)
model.schedule.remove(sheep_to_eat)

# Reproduction:
if random.random() < model.wolf_reproduce:
# Create a new wolf cub
cub = Wolf(self.grid, self.pos, self.moore, self.energy / 2)
self.energy = self.energy / 2
model.grid[y][x].add(cub)
model.schedule.add(cub)
# Death or reproduction
if self.energy < 0:
model.grid._remove_agent(self.pos, self)
model.schedule.remove(self)
else:
if random.random() < model.wolf_reproduce:
# Create a new wolf cub
self.energy /= 2
cub = Wolf(self.grid, self.pos, self.moore, self.energy)
model.grid.place_agent(cub, cub.pos)
model.schedule.add(cub)


class GrassPatch(Agent):
'''
A patch of grass that grows at a fixed rate and it is eaten by sheep
'''

def __init__(self, fully_grown, countdown):
'''
Creates a new patch of grass
Args:
grown: (boolean) Whether the patch of grass is fully grown or not
countdown: Time for the patch of grass to be fully grown again
'''
self.fully_grown = fully_grown
self.countdown = countdown

def step(self, model):
if not self.fully_grown:
if self.countdown <= 0:
# Set as fully grown
self.fully_grown = True
self.countdown = model.grass_regrowth_time
else:
self.countdown -= 1


class RandomActivationByBreed(RandomActivation):
'''
A scheduler which activates each type of agent once per step, in random
order, with the order reshuffled every step.
This is equivalent to the NetLogo 'ask breed...' and is generally the
default behavior for an ABM.
Assumes that all agents have a step(model) method.
'''
agents_by_breed = defaultdict(list)

def __init__(self, model):
super().__init__(model)
self.agents_by_breed = defaultdict(list)

def add(self, agent):
'''
Add an Agent object to the schedule
Args:
agent: An Agent to be added to the schedule.
'''

self.agents.append(agent)
agent_class = type(agent)
self.agents_by_breed[agent_class].append(agent)

def remove(self, agent):
'''
Remove all instances of a given agent from the schedule.
'''

while agent in self.agents:
self.agents.remove(agent)

agent_class = type(agent)
while agent in self.agents_by_breed[agent_class]:
self.agents_by_breed[agent_class].remove(agent)

def step(self, by_breed=True):
'''
Executes the step of each agent breed, one at a time, in random order.
Args:
by_breed: If True, run all agents of a single breed before running
the next one.
'''
if by_breed:
for agent_class in self.agents_by_breed:
self.step_breed(agent_class)
self.steps += 1
self.time += 1
else:
super().step()

def step_breed(self, breed):
'''
Shuffle order and run all agents of a given breed.
Args:
breed: Class object of the breed to run.
'''
agents = self.agents_by_breed[breed]
random.shuffle(agents)
for agent in agents:
agent.step(self.model)

def get_breed_count(self, breed_class):
'''
Returns the current number of agents of certain breed in the queue.
'''
return len(self.agents_by_breed[breed_class])
26 changes: 20 additions & 6 deletions examples/WolfSheep/WolfSheepServer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from WolfSheep import Wolf, Sheep, WolfSheepPredation
from WolfSheep import Wolf, Sheep, GrassPatch, WolfSheepPredation
from mesa.visualization.ModularVisualization import ModularServer
from mesa.visualization.modules import CanvasGrid
from mesa.visualization.modules import CanvasGrid, ChartModule


def wolf_sheep_portrayal(agent):
Expand All @@ -13,16 +13,30 @@ def wolf_sheep_portrayal(agent):
if type(agent) is Sheep:
portrayal["Color"] = "#666666"
portrayal["r"] = 0.8
portrayal["Layer"] = 0
portrayal["Layer"] = 1

elif type(agent) is Wolf:
portrayal["Color"] = "#AA0000"
portrayal["r"] = 0.5
portrayal["Layer"] = 1
portrayal["Layer"] = 2

elif type(agent) is GrassPatch:
if agent.fully_grown:
portrayal["Color"] = "#00AA00"
else:
portrayal["Color"] = "#D6F5D6"
portrayal["Shape"] = "rect"
portrayal["Layer"] = 0
portrayal["w"] = 1
portrayal["h"] = 1


return portrayal

canvas_element = CanvasGrid(wolf_sheep_portrayal, 20, 20, 500, 500)
chart_element = ChartModule([{"Label": "Wolves", "Color": "#AA0000"},
{"Label": "Sheep", "Color": "#666666"}])

server = ModularServer(WolfSheepPredation, [canvas_element],
"WolfSheep")
server = ModularServer(WolfSheepPredation, [canvas_element, chart_element],
"WolfSheep", grass=True)
server.launch()
5 changes: 3 additions & 2 deletions examples/WolfSheep/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
from WolfSheepVisualization import WolfSheepVisualization

if __name__ == "__main__":
model = WolfSheepPredation()
viz = WolfSheepVisualization(model)
model = WolfSheepPredation(grass=True)
model.run_model()
# viz = WolfSheepVisualization(model)
Loading

0 comments on commit 553bf04

Please sign in to comment.