Predator, Prey interactions
aiautomatajavascripthtmlarchivedPredator and Prey Automata Javascript
In this example, we'll look at a more sophisticated (though still very simple) Cellular Automata example dealing with predator / prey interactions. Cells will follow these simple rules:
Prey
- Each turn, produce new prey cells in all empty neighbouring cells
- After each turn, the cell has a chance to die depending on the prey death rate
Predators
- Each turn, consume any neighbouring prey and replace the cells with new predators
- If this cell has not consumed prey, die
- After each turn, the cell has a random chance to die depending on predator death rate
cell.js
We begin by creating two lists; one for all currently active cells, allCells, and one for all cells newly created during this update, newCells. We also use a flag, updatePrey, to tell the simulation whether we're currently updating prey or predators. The different cell types take it in turns during update steps of the simulation. Additionally, we have variables to determine the random death rates of predator and prey cells.
var allCells = [];
var newCells = [];
var updatePrey = true;
var drPred = 0.3;
var drPrey = 0.2;
The cell class begins with a constructor, similar to previous examples, that keeps track of the cells position, whether or not it is alive, and whether or not it is prey:
class Cell
{
constructor(cx, cy, cp)
{
this.x = cx;
this.y = cy;
this.tile = (cy * Map.width) + cx;
this.prey = cp;
this.alive = true;
newCells.push(this);
Map.tiles[this.tile].cell = this;
}
The cell class also has a method to fetch a list of all valid neighbouring tiles in both cardinal and diagonal directions, and a method to kill the cell when it is consumed or has starved:
getNeighbours()
{
var n = [];
for(var y = (this.y - 1); y<= (this.y + 1); y++)
{
for(var x = (this.x - 1); x <= (this.x + 1); x++)
{
if(y < 0 || x < 0 || x >= Map.width || y >= Map.height) { continue; }
n.push((y * Map.width) + x);
}
}
return n;
}
kill()
{
this.alive = false;
Map.tiles[this.tile].cell = null;
}
The update method begins by getting the list of neighbouring tiles. If the cell is prey, it will then create new cells in all empty neighbours:
update()
{
var n = this.getNeighbours();
if(this.prey)
{
for(x in n)
{
if(Map.tiles[n[x]].cell==null)
{
new Cell(Map.tiles[n[x]].x, Map.tiles[n[x]].y, true);
}
}
}
If the cell is a predator, however, it will destroy all neighbouring prey and create new predator cells in their place. If the cell was unable to consume prey, we kill it.
else
{
var prey = [];
for(var x in n)
{
if(Map.tiles[n[x]].cell!=null && Map.tiles[n[x]].cell.prey)
{
prey.push(n[x]);
Map.tiles[n[x]].cell.kill();
new Cell(Map.tiles[n[x]].x, Map.tiles[n[x]].y, false);
}
}
if(!prey.length) { this.kill(); }
}
This is the end of the update method for our cells, and we also close the cell class:
return false;
}
};
In the updateCells method, to update all cells, we begin by calling the update method for every cell that is either predator or prey, depending on which we're currently updating:
function updateCells()
{
for(var x in allCells)
{
if(allCells[x].prey==updatePrey) { allCells[x].update(); }
}
We then add in new cells created during this update:
for(var x in newCells) { if(newCells[x].alive) { allCells.push(newCells[x]); } }
newCells.splice(0, newCells.length);
We then loop through cells again, and if they are of the type currently being updated, we give them a random chance to die.
for(var x in allCells)
{
if((updatePrey && allCells[x].prey && Math.random()<drPrey) ||
(!updatePrey && !allCells[x].prey && Math.random()<drPred)) { allCells[x].kill(); }
}
Finally, we'll remove any cells that are no longer alive, and flip the updatePrey boolean value so the other type of cell will get updated next.
var toRemove = [];
for(var x in allCells)
{
if(!allCells[x].alive) { toRemove.push(allCells[x]); }
}
while(toRemove.length > 0)
{
allCells.splice( allCells.indexOf(toRemove[0]), 1 );
toRemove.shift();
}
updatePrey = !updatePrey;
}
Interaction controls and notes
In this example, the random death rates of predators and prey can be changed with the 1 and 2 keys respectively to values between 0 and 0.9 (90%). You can also change the update delay with d, pause and unpause (p), and step the animation when paused (s).

To reset the animation press the r key, and to randomly insert cells press f (more prey will be created than predators).
Modifying the death rates results in some interesting conclusions to the animation:
- An over-successful predator is more likely to cause its own destruction through erradication of its food source (prey)
- A prey with a higher death rate is more likely to result in the premature extinction of the predators, due to them being unable to find food
I advise playing around with the settings of this example to see some interesting outcomes.