# Wolf, Sheep, and Grass¶

Let's use some of our computing skills to build a simulation, but this time let's have some interation among the objects to see if we can get some interesting behavior to emerge.

First, we'll need some ability to create objects in random places, with random properties. And we'll want to visualize our results.

In [22]:
import random
import Graphics


For these simulations, we want to have sheep who eat grass and wolves who eat sheep.

In [23]:
class Sheep:
def __init__(self, world):
self.world = world
self.x = random.randint(0, world.width - 1)
self.y = random.randint(0, world.height - 1)
self.energy = self.initial_energy()
self.state = "alive"
self.t = 0
self.gestation = random.randint(int(self.energy * 1.5),
int(self.energy * 2.0))

def initial_energy(self):
return 10

def move(self):
self.t += 1
if self.energy <= 0:
elif self.t % self.gestation == 0:
newSheep = Sheep(self.world)
if self.clear(newSheep.x, newSheep.y):
self.world.sheep.append(newSheep)
else:
if self.world.grass[self.x][self.y] > 0:
self.world.grass[self.x][self.y] -= 1
self.energy += 1
else:
self.energy -= 1
self.x += random.choice([-1, 0, 1])
self.y += random.choice([-1, 0, 1])
if self.x < 0:
self.x = self.world.width - 1
elif self.x >= self.world.width:
self.x = 0
if self.y < 0:
self.y = self.world.height - 1
elif self.y >= self.world.height:
self.y = 0

def clear(self, x, y):
for w in self.world.wolf:
if w.x == x and w.y == y:
return False
for s in self.world.sheep:
if s.x == x and s.y == y:
return False
return True



Next, we'll create a Wolf that extends, and overrides some of the Sheep's properties.

In [24]:
class Wolf(Sheep):
def initial_energy(self):
return 20

def move(self):
self.t += 1
if self.energy <= 0:
elif self.t % self.gestation == 0:
newWolf = Wolf(self.world)
if self.clear(newWolf.x, newWolf.y):
self.world.wolf.append(newWolf)
else:
for s in self.world.sheep:
if s.state == "alive" and s.x == self.x and s.y == self.y:
# yum!
self.energy += s.initial_energy()
return
self.energy -= 1
self.x += random.choice([-1, 0, 1])
self.y += random.choice([-1, 0, 1])
if self.x < 0:
self.x = self.world.width - 1
elif self.x >= self.world.width:
self.x = 0
if self.y < 0:
self.y = self.world.height - 1
elif self.y >= self.world.height:
self.y = 0

def clear(self, x, y):
for w in self.world.wolf:
if w.x == x and w.y == y:
return False
return True



A now a World where they can both live.

In [25]:
class World:
def __init__(self, width, height, wolves, sheep):
self.width = width
self.height = height

self.wolf = [Wolf(self) for w in range(wolves)]
self.sheep = [Sheep(self) for s in range(sheep)]

self.grass = [[0 for y in range(height)] for x in range(width)]

for x in range(width):
for y in range(height):
if random.random() < .3:
self.grass[x][y] = 10

self.size = 20
self.window = Graphics.Window(width * self.size, height * self.size)
self.window.mode = "bitmap"

def show(self):
for x in range(self.width):
for y in range(self.height):
grass = self.grass[x][y]
rec = Graphics.Rectangle((x * self.size, y * self.size),
((x + 1) * self.size, (y + 1) * self.size))
rec.fill = Graphics.Color(0, grass/20 * 255, 0)
rec.draw(self.window)
for w in self.wolf:
if w.state == "alive":
wolf = Graphics.Text((w.x * self.size + self.size/2,
w.y * self.size + self.size/2), "W")
wolf.draw(self.window)
for s in self.sheep:
if s.state == "alive":
sheep = Graphics.Text((s.x * self.size + self.size/2,
s.y * self.size + self.size/2), "s")
sheep.fill = Graphics.Color(255,255,255)
sheep.draw(self.window)
self.window.step()
return self.window

def move(self):
for s in self.sheep[:]:
if s.state == "alive":
s.move()
else:
self.sheep.remove(s)
for w in self.wolf[:]:
if w.state == "alive":
w.move()
else:
self.wolf.remove(w)
for x in range(self.width):
for y in range(self.height):
if random.random() < .005:
self.grass[x][y] = max(10, self.grass[x][y])

In [26]:
world = World(25, 25, 0, 50)

In [27]:
world.show()

Out[27]:
In [28]:
count = 0
while True:
world.move()
calico.animate(world.show())
print(count,
len([w for w in world.wolf if w.state == "alive"]),
len([s for s in world.sheep if s.state == "alive"]))
count += 1

40 0 160

Running script aborted!

In [29]:
world.show()

Out[29]:
In [32]:
world = World(25, 25, 10, 50)

In [33]:
world.show()

Out[33]:

In [34]:
count = 0
while True:
world.move()
calico.animate(world.show())
print(count,
len([w for w in world.wolf if w.state == "alive"]),
len([s for s in world.sheep if s.state == "alive"]))
count += 1

65 9 142

Running script aborted!

In [35]:
world.show()

Out[35]:
In [ ]: