Conway's game of life
It is also available from the Models module as Models.game_of_life.
using Agents, Random1. Define the rules
Conway's game of life is a cellular automaton, where each cell of the discrete space contains one agent only.
The rules of Conway's game of life are defined based on four numbers: Death, Survival, Reproduction, Overpopulation, grouped as (D, S, R, O) Cells die if the number of their living neighbors is <D or >O, survive if the number of their living neighbors is ≤S, come to life if their living neighbors are ≥R and ≤O.
rules = (2, 3, 3, 3) # (D, S, R, O)2. Build the model
First, define an agent type. It needs to have the compulsary id and pos fields, as well as a status field that is true for cells that are alive and false otherwise.
mutable struct Cell <: AbstractAgent
    id::Int
    pos::Dims{2}
    status::Bool
endThe following function builds a 2D cellular automaton given some rules. dims is a tuple of integers determining the width and height of the grid environment. metric specifies how to measure distances in the space, and in our example it actually decides whether cells connect to their diagonal neighbors.
This function creates a model where all cells are dead.
function build_model(; rules::Tuple, dims = (100, 100), metric = :chebyshev, seed = 120)
    space = GridSpace(dims; metric)
    properties = Dict(:rules => rules)
    model = ABM(Cell, space; properties, rng = MersenneTwister(seed))
    idx = 1
    for x in 1:dims[1]
        for y in 1:dims[2]
            add_agent_pos!(Cell(idx, (x, y), false), model)
            idx += 1
        end
    end
    return model
endNow we define a stepping function for the model to apply the rules to agents. We will also perform a synchronous agent update (meaning that the value of all agents changes after we have decided the new value for each agent individually).
function ca_step!(model)
    new_status = fill(false, nagents(model))
    for agent in allagents(model)
        n = alive_neighbors(agent, model)
        if agent.status == true && (n ≤ model.rules[4] && n ≥ model.rules[1])
            new_status[agent.id] = true
        elseif agent.status == false && (n ≥ model.rules[3] && n ≤ model.rules[4])
            new_status[agent.id] = true
        end
    end
    for id in allids(model)
        model[id].status = new_status[id]
    end
end
function alive_neighbors(agent, model) # count alive neighboring cells
    c = 0
    for n in nearby_agents(agent, model)
        if n.status == true
            c += 1
        end
    end
    return c
endnow we can instantiate the model:
model = build_model(rules = rules, dims = (50, 50))AgentBasedModel with 2500 agents of type Cell space: GridSpace with size (50, 50), metric=chebyshev, periodic=true scheduler: fastest properties: Dict(:rules => (2, 3, 3, 3))
Let's make some random cells on
for i in 1:nagents(model)
    if rand(model.rng) < 0.2
        model.agents[i].status = true
    end
end3. Animate the model
We use the InteractiveDynamics.abm_video for creating an animation and saving it to an mp4
using InteractiveDynamics
import CairoMakie
ac(x) = x.status == true ? :black : :white
am(x) = x.status == true ? '■' : '□'
abm_video(
    "game of life.mp4",
    model,
    dummystep,
    ca_step!;
    title = "Game of Life",
    ac = :black,
    as = 12,
    am,
    framerate = 5,
    scatterkwargs = (strokewidth = 0,),
)