Covered in class.
The following is an implementation of the Game of Life using the Mesa Agent Based Modeling library. This library has a few versions of the Game Of Life, the implementation here is slower but simpler to help you understand how to use the mesa library.
Agent
/ Cell
and Model
To implement an agent based model in mesa we need to create at least two classes:
The Agent
represents the individuals or entities in the model — a tree in the Fire model, a cell in the Game of Life, a person in a SIR model, etc.
state
that represents its current condition, and a set of rules that determine how the agent's state changes over time.pos
attribute that represents the agent's location in the model space and unlike cellular automata, the agent can move.The Model
class represents the overall model and is responsible for creating the agents, storing the model parameters, and updating the agents over time.
Agent
/ Cell
classThe Agent
/ Cell
class should
state
which is DEAD
or ALIVE
.is_alive
to return True
iff state
is ALIVE
.get_neighbors
to return a list of all neighbours.compute_step
to compute the update, to state
, needed in the next step.step
to apply the update (that was computed in compute_step
).So to start, the code for Cell
will look like
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Then as we add the required functionality, we will update this class. First we add the state
and ALIVE
and DEAD
constants.
state
A Cell
has a state
that is either DEAD
or ALIVE
. And since we want to perform a simultaneous update of all agents, we need a second variable _next_state
to store the state that the agent will change to in the next step.
1 2 3 4 5 6 7 8 9 |
|
is_alive
functionThe is_alive
function just returns True
iff the state
is ALIVE
.
1 2 |
|
get_neighbors
functionThe get_neighbors
function returns a list of all neighbours of the agent. If the Model
has a spatial grid, then each agent has a pos
attribute that represents the agent's location in the model space.
Each agent can
assess the model using self.model
,
access the model grid using self.model.grid
,
and from there use grid method get_neighbors
function to get the neighbours of the agent.
For the Game of Life, we use the Moore neighbourhood (so moore=True
), and we do not treat the agent as being a neighbour of itself (so include_center=False
). Hence we have 8 neighbours and use the following code:
1 2 |
|
compute_step
function and step
functionThe compute_step
function computes the value of _next_state
, typically based on the agent's state
and the state
of its neighbours.
1 2 3 4 5 6 7 |
|
The step
function applies the update computed in compute_step
to the state
.
1 2 |
|
Model
classThe Model
class stores the model parameters,
the model geometry (space/grid),
and the agents in the model.
The basic structure of this class is
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Model.__init__
create a grid to store the cells/agentsTo create the grid, we want a rectangular grid with wrap around geometry, and at each position of the grid we want a single cell (so use mesa.space.SingleGrid
):
width
and height
to the model constructor (Model.__init__
).1 |
|
Model.__init__
create the required grid1 2 |
|
Model.__init__
create the agents/cells at each position in the gridIn the constructor, Model.__init__
we:
Cell
and set state
to ALIVE
with probability density
.First add extra parameter to Mode.__init__
1 |
|
Then have loop to iterate over grid locations and for each location, create a cell, set its state, and place in grid.
1 2 3 4 5 6 7 8 |
|
We need a function, draw_model
with structure
1 2 3 4 5 6 7 8 9 10 11 |
|
To setup the axis we want to set the plot limits to match the model grid size, and hide the axis tick marks. So we have
1 2 3 4 5 6 7 |
|
We could use the matplotlib
grid function to draw the grid, but I decided instead in drawing the (horizontal and vertical) lines using a for loop.
1 2 3 4 5 |
|
We next draw the agents, I used a small circle and the colour is set based on the cell state.
1 2 3 4 5 6 7 |
|
Note: in the above I draw the DEAD
cells as white and the ALIVE
cells as green, so the DEAD
cells are not visible. While checking your code, you may want to draw the DEAD
cells as black so that you can see them.
Finally we add a title to the plot. Here we could display the number of steps taken (i.e. the stage) but we could display other information such as the population size, the number of changes etc.
1 2 |
|
Output of the draw_model
function is shown below.
We will animate our models using the same process that we used for the numpy
implementation.
frame
that generates each frame. Typically it updates the model (calling model.step()
) and displays the model (by calling show(model, ax)
).matplotlib
window using (or similar if multiple plots), by calling plt.subplots
we pass the resulting ax
to draw_model
, which will draw the model.animation.FuncAnimation
and HTML(anim.to_jshtml())
.1 2 3 4 5 6 7 8 9 10 11 12 |
|
TODO