Predator-Prey Ecosystems

Excursions in Programming
Mathematica in Education and Research
Vol. 5 No.1

by
Richard J. Gaylord and Kazume Nishidate

Introduction

Ecology is concerned with the interrelationships between organisms and their environment. Observations of ecosystems containing two species, one of whom (known as the prey) serves as a food source for the other (known as the predator), indicate that the populations of the two species show out-of-phase oscillatory behavior. These cyclic fluctuations have been modeled analytically using coupled differential equations known as the Lotka-Volterra equations; however, it has been pointed out [Dewdney, 1984] that these equations "assume ... a continuous predator continuously in search of a continuous prey". A discrete approach to modeling so-called 'predator-prey' ecosystems employs a lattice system which represents territory that predators and prey co-habitat. Some of the lattice sites are empty and other sites are occupied by a predator or a prey. As the system evolves over a number of time steps, the predators and/or prey execute random walks on the lattice in the course of which, predators eat prey, prey are eaten by predators, predators and preys give birth, and predators starve to death. We will develop a simple cellular automaton version of the predator-prey model which exhibits out-of-phase oscillatory fluctuations in the populations of the predator and prey species.

The Grazing Herd CA

In the 'grazing herd' cellular automaton, the prey (the food source) remain stationary while the predators move about, and new food sources appear randomly in vacant places in each time step. This model might be used to represent a herd of animals (eg., cows or sheep)  grazing on the vegetation (eg., grass) in a pasture.

The system

The predator-prey system employs a two-dimensional square lattice with periodic boundary conditions. Each lattice site is either empty or is occupied by a predator  or a prey. 

The lattice sites that are empty have value 0 and the lattice sites that contain a food source (ie., that are occupied by a prey) have value 1. The value of a lattice site occupied by a predator is a tuplet, {x, y, z}.

The first component of the tuplet is an integer value 1, 2, 3, or 4 indicating the direction faced by the predator. The value 1 indicates a site occupied by a north-facing predator, the value  2 indicates a site occupied by a east-facing predator, the value 3 indicates a site occupied by a south-facing predator, and the value 4 indicates a site occupied by a west-facing predator. The value of the first component is randomly chosen in each time step.
 
The second component of the tuplet is a non-negative integer value indicating the amount of time that remains until a newborn predator is due to be born. At the moment that a predator gives birth, the value of the birth time clock is set equal to 'preg' (this is the normal gestation period for the predator species). At each ensuing time step, the value of the birth clock decreases by 1 unless its value is 0, in which case it remains 0. When the birth clock value is 0, the baby is born when the predator moves (but not while it remains in place).

The third component of the tuplet is a non-negative integer value indicating the amount of time that remains before a predator starves to death. At the moment that a predator eats a prey, its diet time clock is set equal to 'starve'. At each ensuing time step, the value of the diet clock decreases by 1. When the diet clock value is 0, the predator dies and vanishes from the lattice.

Initially, predators and prey are randomly placed on the lattice with probability, predDensity and preyDensity, respectively (in the large system limit, the densities of predators and prey will be given by these quantities), using

pasture =
 Table[Floor[Random[] + (preyDensity + predDensity)] * 
           Floor[1 + Random[] + predDensity/(preyDensity + predDensity)], {n}, {n}] /. 
       2 :> {RND, Random[Integer, {0, preg}], Random[Integer, {1, starve}]}

where

RND := Random[Integer, {1, 4}]

Each predator in pasture faces a randomly chosen direction, its birth clock value is randomly set between 0 and preg, and its diet clock value is randomly set between 0 and starve.

The update rules

The rules governing the behaviors of the predators and prey are implemented as rewrite rules for updating the values of the sites of the CA lattice. These rules take thirteen arguments in the following order:

eco[site, N, E, S, W, NE, SE, SW, NW, Nn, Ee, Ss, Ww]

where the thirteen arguments represent the value of the site, the values of the four nearest neighbors in the N, E, S, W directions, the values of the four nearest neighbors in the NE, SE, SW, NW directions, and the values of four next nearest neighbors in the N, E, S, W directions.

The fifty-three rewrite rules are as follows:

- A predator (with 0 birth and diet clock values) starves to death and vacates the site, leaving behind a newborn predator facing a randomly chosen direction with a birth clock value of preg and a diet clock value of starve.

eco[{_, 0, 0}, _, _, _, _, _, _, _, _, _, _, _, _] := {RND, preg, starve}

- A predator (with a 0 diet clock value and a non-zero birth clock value) starves to death and vanishes, leaving an empty site.

eco[{_, _, 0}, _, _, _, _, _, _, _, _, _, _, _, _] := 0

- A predator (with a positive diet clock value) that faces an adjacent empty or food site that is faced by at least one other predator  (with a positive diet clock value), remains in place, randomly chooses a direction to face, decrements its diet clock by 1, and decrements its birth clock by one unless its value is 0.

eco[{1, a_, b_?Positive}, 0 | 1, _, _, _, {4, _, _?Positive}, _, _, _, _, _, _, _] := {RND, Max[0, a - 1], b - 1}
eco[{1, a_, b_?Positive}, 0 | 1, _, _, _, _, _, _, {2, _, _?Positive}, _, _, _, _] := {RND, Max[0, a - 1], b - 1}
eco[{1, a_, b_?Positive}, 0 | 1, _, _, _, _, _, _, _, {3, _, _?Positive}, _, _, _] := {RND, Max[0, a - 1], b - 1}
eco[{2, a_, b_?Positive}, _, 0 | 1, _, _, {3, _, _?Positive}, _, _, _, _, _, _, _] := {RND, Max[0, a - 1], b - 1}
eco[{2, a_, b_?Positive}, _, 0 | 1, _, _, _, {1, _, _?Positive}, _, _, _, _, _, _] := {RND, Max[0, a - 1], b - 1} 
eco[{2, a_, b_?Positive}, _, 0 | 1, _, _, _, _, _, _, _, {4, _, _?Positive}, _, _] := {RND, Max[0, a - 1], b - 1}
eco[{3, a_, b_?Positive}, _, _, 0 | 1, _, _, {4, _, _?Positive}, _, _, _, _, _, _] := {RND, Max[0, a - 1], b - 1} 
eco[{3, a_, b_?Positive}, _, _, 0 | 1, _, _, _, {2, _, _?Positive}, _, _, _, _, _] := {RND, Max[0, a - 1], b - 1}
eco[{3, a_, b_?Positive}, _, _, 0 | 1, _, _, _, _, _, _, _, {1, _, _?Positive}, _] := {RND, Max[0, a - 1], b - 1}
eco[{4, a_, b_?Positive}, _, _, _, 0 | 1, _, _, {1, _, _?Positive}, _, _, _, _, _] := {RND, Max[0, a - 1], b - 1}
eco[{4, a_, b_?Positive}, _, _, _, 0 | 1, _, _, _, {3, _, _?Positive}, _, _, _, _] := {RND, Max[0, a - 1], b - 1}
eco[{4, a_, b_?Positive}, _, _, _, 0 | 1, _, _, _, _, _, _, _, {2, _, _?Positive}] := {RND, Max[0, a - 1], b - 1}

- A prey site that is faced by two or more predators (with positive diet clock values) on adjacent sites remains unchanged.

eco[1, {3, _, _?Positive}, {4, _, _?Positive}, _, _, _, _, _, _, _, _, _, _] := 1
eco[1, {3, _, _?Positive}, _, {1, _, _?Positive}, _, _, _, _, _, _, _, _, _] := 1
eco[1, {3, _, _?Positive}, _, _, {2, _, _?Positive}, _, _, _, _, _, _, _, _] := 1
eco[1, _, {4, _, _?Positive}, {1, _, _?Positive}, _, _, _, _, _, _, _, _, _] := 1
eco[1, _, {4, _, _?Positive}, _, {2, _, _?Positive}, _, _, _, _, _, _, _, _] := 1
eco[1, _, _, {1, _, _?Positive}, {2, _, _?Positive}, _, _, _, _, _, _, _, _] := 1

- An empty site that is faced by two or more predators (with positive diet clock values) becomes a food source with probability p.

eco[0, {3, _, _?Positive}, {4, _, _?Positive}, _, _, _, _, _, _, _, _, _, _] := Floor[p + Random[]]
eco[0, {3, _, _?Positive}, _, {1, _, _?Positive}, _, _, _, _, _, _, _, _, _] := Floor[p + Random[]]
eco[0, {3, _, _?Positive}, _, _, {2, _, _?Positive}, _, _, _, _, _, _, _, _] := Floor[p + Random[]]
eco[0, _, {4, _, _?Positive}, {1, _, _?Positive}, _, _, _, _, _, _, _, _, _] := Floor[p + Random[]]
eco[0, _, {4, _, _?Positive}, _, {2, _, _?Positive}, _, _, _, _, _, _, _, _] := Floor[p + Random[]]
eco[0, _, _, {1, _, _?Positive}, {2, _, _?Positive}, _, _, _, _, _, _, _, _] := Floor[p + Random[]]

- A predator (with a 0 birth clock value and a positive diet clock value) that faces an adjacent empty or prey site that is faced by no other predator (with a positive diet clock value), vacates the site it is occupying, leaving behind a newborn predator facing a randomly chosen direction with a birth clock value of preg and a diet clock value of starve.

eco[{1, 0, _?Positive}, 0 | 1, _, _, _, _, _, _, _, _, _, _, _] := {RND, preg, starve}
eco[{2, 0, _?Positive}, _, 0 | 1, _, _, _, _, _, _, _, _, _, _] := {RND, preg, starve}
eco[{3, 0, _?Positive}, _, _, 0 | 1, _, _, _, _, _, _, _, _, _] := {RND, preg, starve}
eco[{4, 0, _?Positive}, _, _, _, 0 | 1, _, _, _, _, _, _, _, _] := {RND, preg, starve}

- A predator (with positive birth and diet clock values) that faces an adjacent empty or prey site that is faced by no other predator (with a positive diet clock value), vacates the site it is occupying, leaving it empty.

eco[{1, _, _?Positive}, 0 | 1, _, _, _, _, _, _, _, _, _, _, _] := 0
eco[{2, _, _?Positive}, _, 0 | 1, _, _, _, _, _, _, _, _, _, _] := 0
eco[{3, _, _?Positive}, _, _, 0 | 1, _, _, _, _, _, _, _, _, _] := 0
eco[{4, _, _?Positive}, _, _, _, 0 | 1, _, _, _, _, _, _, _, _] := 0

- A prey site that is faced by exactly one predator (with a 0 birth clock value and a positive diet clock value) on an adjacent site, is occupied by the predator which randomly chooses a direction to face, resets its birth clock to preg and resets its diet clock to starve.

eco[1, {3, 0, _?Positive}, _, _, _, _, _, _, _, _, _, _, _] := {RND, preg, starve}
eco[1, _, {4, 0, _?Positive}, _, _, _, _, _, _, _, _, _, _] := {RND, preg, starve}
eco[1, _, _, {1, 0, _?Positive}, _, _, _, _, _, _, _, _, _] := {RND, preg, starve}
eco[1, _, _, _, {2, 0, _?Positive}, _, _, _, _, _, _, _, _] := {RND, preg, starve}

- An empty site that is faced by exactly one predator (with a 0 birth clock value and a positive diet clock value) on an adjacent site is occupied by the predator which randomly chooses a direction to face, decrements its diet clock by 1 and resets its birth clock to preg.

eco[0, {3, 0, b_?Positive}, _, _, _, _, _, _, _, _, _, _, _] := {RND, preg, b - 1}
eco[0, _, {4, 0, b_?Positive}, _, _, _, _, _, _, _, _, _, _] := {RND, preg, b - 1}
eco[0, _, _, {1, 0, b_?Positive}, _, _, _, _, _, _, _, _, _] := {RND, preg, b - 1}
eco[0, _, _, _, {2, 0, b_?Positive}, _, _, _, _, _, _, _, _] := {RND, preg, b - 1}

- A  prey site that is faced by exactly one predator (with positive birth and diet clock values) on an adjacent site, is occupied by the predator which randomly selects a direction to face, decrements its birth clock by 1, and resets its diet clock to starve.

eco[1, {3, a_, _?Positive}, _, _, _, _, _, _, _, _, _, _, _] := {RND, a -1, starve}
eco[1, _, {4, a_, _?Positive}, _, _, _, _, _, _, _, _, _, _] := {RND, a -1, starve}
eco[1, _, _, {1, a_, _?Positive}, _, _, _, _, _, _, _, _, _] := {RND, a -1, starve}
eco[1, _, _, _, {2, a_, _?Positive}, _, _, _, _, _, _, _, _] := {RND, a -1, starve}

- An empty site that is faced by exactly one predator (with positive diet and birth clock values) on an adjacent site is occupied by the predator which randomly selects a direction to face and decrements its diet and birth clocks by 1.

eco[0, {3, a_, b_?Positive}, _, _, _, _, _, _, _, _, _, _, _] := {RND, a - 1, b - 1}
eco[0, _, {4, a_, b_?Positive}, _, _, _, _, _, _, _, _, _, _] := {RND, a - 1, b - 1}
eco[0, _, _, {1, a_, b_?Positive}, _, _, _, _, _, _, _, _, _] := {RND, a - 1, b - 1}
eco[0, _, _, _, {2, a_, b_?Positive}, _, _, _, _, _, _, _, _] := {RND, a - 1, b - 1}

- Any other predator remains in place, randomly chooses a direction to face, and decrements its diet and birth time clocks by 1.

eco[{_, a_, b_}, _, _, _, _, _, _, _, _, _, _, _, _] := {RND, a - 1, b - 1}

- Any other prey site remains unchanged.

eco[1, _, _, _, _, _, _, _, _, _, _, _, _] := 1

- Any other empty site becomes a food source with probability p.

eco[0, _, _, _, _, _, _, _, _, _, _, _, _] := Floor[p + Random[]]

Applying the rules

The sites in the lattice are updated at each time step by applying the following anonymous function to the CA lattice:

MvonN[eco, #]&

where

MvonN[func__, lat_] :=   
  MapThread[func, 
                     Map[RotateRight[lat, #]&, {{0, 0}, {1, 0}, {0, -1}, {-1, 0}, {0, 1}, 
                                                                  {1,  -1}, {-1, -1}, {-1, 1}, {1, 1}, 
                                                                  {2, 0}, {0, -2}, {-2, 0}, {0, 2}}], 2]

The pasture of predators and prey evolves over t time steps using the following Nest operation.

NestList[MvonN[eco, #]&, initConf, t]

The program

PredatorPrey[n_, preyDensity_, predDensity_, preg_, starve_, p_, t_] /; preyDensity + predDensity < 1 :=
    Module[{eco, RND, pasture}, 

RND:=Random[Integer,{1,4}];

pasture =
  Table[Floor[Random[] + (preyDensity + predDensity)] * 
            Floor[1 + Random[] + predDensity/(preyDensity + predDensity)], {n}, {n}] /. 
        2 :> {Random[Integer, {1, 4}], Random[Integer, {1, preg}], Random[Integer, {1, starve}]};

eco[{_, 0, 0}, _, _, _, _, _, _, _, _, _, _, _, _] := {RND, preg, starve};
eco[{_, _, 0}, _, _, _, _, _, _, _, _, _, _, _, _] := 0;
eco[{1, a_, b_?Positive}, 0 | 1, _, _, _, {4, _, _?Positive}, _, _, _, _, _, _, _] := {RND, Max[0, a - 1], b - 1};
eco[{1, a_, b_?Positive}, 0 | 1, _, _, _, _, _, _, {2, _, _?Positive}, _, _, _, _] := {RND, Max[0, a - 1], b - 1};
eco[{1, a_, b_?Positive}, 0 | 1, _, _, _, _, _, _, _, {3, _, _?Positive}, _, _, _] := {RND, Max[0, a - 1], b - 1};
eco[{2, a_, b_?Positive}, _, 0 | 1, _, _, {3, _, _?Positive}, _, _, _, _, _, _, _] := {RND, Max[0, a - 1], b - 1};
eco[{2, a_, b_?Positive}, _, 0 | 1, _, _, _, {1, _, _?Positive}, _, _, _, _, _, _] := {RND, Max[0, a - 1], b - 1}; 
eco[{2, a_, b_?Positive}, _, 0 | 1, _, _, _, _, _, _, _, {4, _, _?Positive}, _, _] := {RND, Max[0, a - 1], b - 1};
eco[{3, a_, b_?Positive}, _, _, 0 | 1, _, _, {4, _, _?Positive}, _, _, _, _, _, _] := {RND, Max[0, a - 1], b - 1}; 
eco[{3, a_, b_?Positive}, _, _, 0 | 1, _, _, _, {2, _, _?Positive}, _, _, _, _, _] := {RND, Max[0, a - 1], b - 1};
eco[{3, a_, b_?Positive}, _, _, 0 | 1, _, _, _, _, _, _, _, {1, _, _?Positive}, _] := {RND, Max[0, a - 1], b - 1};
eco[{4, a_, b_?Positive}, _, _, _, 0 | 1, _, _, {1, _, _?Positive}, _, _, _, _, _] := {RND, Max[0, a - 1], b - 1};
eco[{4, a_, b_?Positive}, _, _, _, 0 | 1, _, _, _, {3, _, _?Positive}, _, _, _, _] := {RND, Max[0, a - 1], b - 1};
eco[{4, a_, b_?Positive}, _, _, _, 0 | 1, _, _, _, _, _, _, _, {2, _, _?Positive}] := {RND, Max[0, a - 1], b - 1};
eco[1, {3, _, _?Positive}, {4, _, _?Positive}, _, _, _, _, _, _, _, _, _, _] := 1;
eco[1, {3, _, _?Positive}, _, {1, _, _?Positive}, _, _, _, _, _, _, _, _, _] := 1;
eco[1, {3, _, _?Positive}, _, _, {2, _, _?Positive}, _, _, _, _, _, _, _, _] := 1;
eco[1, _, {4, _, _?Positive}, {1, _, _?Positive}, _, _, _, _, _, _, _, _, _] := 1;
eco[1, _, {4, _, _?Positive}, _, {2, _, _?Positive}, _, _, _, _, _, _, _, _] := 1;
eco[1, _, _, {1, _, _?Positive}, {2, _, _?Positive}, _, _, _, _, _, _, _, _] := 1;
eco[0, {3, _, _?Positive}, {4, _, _?Positive}, _, _, _, _, _, _, _, _, _, _] := Floor[p + Random[]];
eco[0, {3, _, _?Positive}, _, {1, _, _?Positive}, _, _, _, _, _, _, _, _, _] := Floor[p + Random[]];
eco[0, {3, _, _?Positive}, _, _, {2, _, _?Positive}, _, _, _, _, _, _, _, _] := Floor[p + Random[]];
eco[0, _, {4, _, _?Positive}, {1, _, _?Positive}, _, _, _, _, _, _, _, _, _] := Floor[p + Random[]];
eco[0, _, {4, _, _?Positive}, _, {2, _, _?Positive}, _, _, _, _, _, _, _, _] := Floor[p + Random[]];
eco[0, _, _, {1, _, _?Positive}, {2, _, _?Positive}, _, _, _, _, _, _, _, _] := Floor[p + Random[]];
eco[{1, 0, _?Positive}, 0 | 1, _, _, _, _, _, _, _, _, _, _, _] := {RND, preg, starve};
eco[{2, 0, _?Positive}, _, 0 | 1, _, _, _, _, _, _, _, _, _, _] := {RND, preg, starve};
eco[{3, 0, _?Positive}, _, _, 0 | 1, _, _, _, _, _, _, _, _, _] := {RND, preg, starve};
eco[{4, 0, _?Positive}, _, _, _, 0 | 1, _, _, _, _, _, _, _, _] := {RND, preg, starve};
eco[{1, _, _?Positive}, 0 | 1, _, _, _, _, _, _, _, _, _, _, _] := 0;
eco[{2, _, _?Positive}, _, 0 | 1, _, _, _, _, _, _, _, _, _, _] := 0;
eco[{3, _, _?Positive}, _, _, 0 | 1, _, _, _, _, _, _, _, _, _] := 0;
eco[{4, _, _?Positive}, _, _, _, 0 | 1, _, _, _, _, _, _, _, _] := 0;
eco[1, {3, 0, _?Positive}, _, _, _, _, _, _, _, _, _, _, _] := {RND, preg, starve};
eco[1, _, {4, 0, _?Positive}, _, _, _, _, _, _, _, _, _, _] := {RND, preg, starve};
eco[1, _, _, {1, 0, _?Positive}, _, _, _, _, _, _, _, _, _] := {RND, preg, starve};
eco[1, _, _, _, {2, 0, _?Positive}, _, _, _, _, _, _, _, _] := {RND, preg, starve};
eco[0, {3, 0, b_?Positive}, _, _, _, _, _, _, _, _, _, _, _] := {RND, preg, b - 1};
eco[0, _, {4, 0, b_?Positive}, _, _, _, _, _, _, _, _, _, _] := {RND, preg, b - 1};
eco[0, _, _, {1, 0, b_?Positive}, _, _, _, _, _, _, _, _, _] := {RND, preg, b - 1};
eco[0, _, _, _, {2, 0, b_?Positive}, _, _, _, _, _, _, _, _] := {RND, preg, b - 1};
eco[1, {3, a_, _?Positive}, _, _, _, _, _, _, _, _, _, _, _] := {RND, a -1, starve};
eco[1, _, {4, a_, _?Positive}, _, _, _, _, _, _, _, _, _, _] := {RND, a -1, starve};
eco[1, _, _, {1, a_, _?Positive}, _, _, _, _, _, _, _, _, _] := {RND, a -1, starve};
eco[1, _, _, _, {2, a_, _?Positive}, _, _, _, _, _, _, _, _] := {RND, a -1, starve};
eco[0, {3, a_, b_?Positive}, _, _, _, _, _, _, _, _, _, _, _] := {RND, a - 1, b - 1};
eco[0, _, {4, a_, b_?Positive}, _, _, _, _, _, _, _, _, _, _] := {RND, a - 1, b - 1};
eco[0, _, _, {1, a_, b_?Positive}, _, _, _, _, _, _, _, _, _] := {RND, a - 1, b - 1};
eco[0, _, _, _, {2, a_, b_?Positive}, _, _, _, _, _, _, _, _] := {RND, a - 1, b - 1};
eco[{_, a_, b_}, _, _, _, _, _, _, _, _, _, _, _, _] := {RND, a - 1, b - 1};
eco[1, _, _, _, _, _, _, _, _, _, _, _, _] := 1;
eco[0, _, _, _, _, _, _, _, _, _, _, _, _] := Floor[p + Random[]];

MvonN[func__, lat_] :=   
  MapThread[func, 
                     Map[RotateRight[lat, #]&, {{0, 0}, {1, 0}, {0, -1}, {-1, 0}, {0, 1}, 
                                                                  {1,  -1}, {-1, -1}, {-1, 1}, {1, 1}, 
                                                                  {2, 0}, {0, -2}, {-2, 0}, {0, 2}}], 2];

evolve = NestList[MvonN[eco, #]&, pasture, t]
]

Running the program

We can run the PredatorPrey program and then calculate the number of predators and prey as a function of time.

SeedRandom[2] 
graze = PredatorPrey[20, 0.2, 0.1, 2, 3, 0.1, 100];

predatorPop = Map[Count[Flatten[#, 1], {__}]&, graze];

preyPop = Map[Count[Flatten[#, 1], 1]&, graze]

We can look at the oscillatory behavior in the numbers of predators and prey over time by dropping the initial twenty time steps in predatorPop and preyPop,

PredPreyPop = Map[Drop[#, 20]&, {predatorPop, preyPop}];

pairing-off each of the remaining population values with its corresponding time step value,

PredPreyPopTimePairs = Map[Transpose[{Range[20, 100], #}]&, PredPreyPop];

and using the MultipleListPlot function in the Graphics package.

Needs["Graphics`MultipleListPlot`"]

MultipleListPlot[ PredPreyPopTimePairs /. {x__} -> x,
     PlotLabel -> FontForm["Grazing Herd Ecosystem", {"Times-Bold", 12}],
     AxesOrigin -> {0,  Min[PredPreyPop]  - 10},
     AxesLabel -> {FontForm["time", {"Times-Italic", 8}],  
                              FontForm["population", {"Times-Italic", 8}]}, 
     PlotRange -> {{0, 120}, {Min[#] - 10, Max[#] + 10}&[PredPreyPop]},  
     PlotJoined -> True]

As the figure shows, the predators and prey display oscillatory population density fluctuations which are out-of-phase with one another. This cyclic behavior isn't  surprising. The predators (eg., cows or sheep) eat the prey (eg., grass) and reproduce, increasing the size of the herd while depleting the food source supply. When the food source is sufficiently depleted, the predators start to die of starvation and therefore breed less. When the size of the herd is sufficently diminished, the growth of new food sources begins to increase, and so on.

Computer simulation projects

1. Our predator-prey CA, which assumes that prey remain stationary while predators move around, is appropriate for an animal-vegetation ecosystem. For an animal-animal ecosystem, on the other hand, both the predator and prey should be taken to be mobile (see for example, the Wa-Tor model [Dewdney, 1984]). Modify the grazing herd CA to allow for mobile prey which gestate.

References

Dewdney, A. K.,"Sharks and fish wage an ecological war on the toroidal planet Wa-Tor". Scientific American, 251(12), 1984. pp.14 - 22

Mitchel Resnick. "Turtles, Termites, and Traffic Jams",  MIT Press, 1994. (see pp. 88-94).