Hi all,
I am trying the Predator-prey dynamic and it returns:
LoadError: UndefVarError: randomwalk! not defined
I can run Zombie Outbreak fine. Why there is no randomwalk function defined in the tutorial?
I use Julia REPL and this is the code:
# https://juliadynamics.github.io/Agents.jl/stable/examples/predator_prey/
# Making the model
using Agents, Random
@agent Sheep GridAgent{2} begin
energy::Float64
reproduction_prob::Float64
Δenergy::Float64
end
@agent Wolf GridAgent{2} begin
energy::Float64
reproduction_prob::Float64
Δenergy::Float64
end
function initialize_model(;
n_sheep = 100,
n_wolves = 50,
dims = (20, 20),
regrowth_time = 30,
Δenergy_sheep = 4,
Δenergy_wolf = 20,
sheep_reproduce = 0.04,
wolf_reproduce = 0.05,
seed = 23182,
)
rng = MersenneTwister(seed)
space = GridSpace(dims, periodic = true)
# Model properties contain the grass as two arrays: whether it is fully grown
# and the time to regrow. Also have static parameter `regrowth_time`.
# Notice how the properties are a `NamedTuple` to ensure type stability.
properties = (
fully_grown = falses(dims),
countdown = zeros(Int, dims),
regrowth_time = regrowth_time,
)
model = ABM(Union{Sheep, Wolf}, space;
properties, rng, scheduler = Schedulers.randomly, warn = false
)
# Add agents
for _ in 1:n_sheep
energy = rand(model.rng, 1:(Δenergy_sheep*2)) - 1
add_agent!(Sheep, model, energy, sheep_reproduce, Δenergy_sheep)
end
for _ in 1:n_wolves
energy = rand(model.rng, 1:(Δenergy_wolf*2)) - 1
add_agent!(Wolf, model, energy, wolf_reproduce, Δenergy_wolf)
end
# Add grass with random initial growth
for p in positions(model)
fully_grown = rand(model.rng, Bool)
countdown = fully_grown ? regrowth_time : rand(model.rng, 1:regrowth_time) - 1
model.countdown[p...] = countdown
model.fully_grown[p...] = fully_grown
end
return model
end
sheepwolfgrass = initialize_model()
# Defining the stepping functions
function sheepwolf_step!(sheep::Sheep, model)
randomwalk!(sheep, model)
sheep.energy -= 1
if sheep.energy < 0
kill_agent!(sheep, model)
return
end
eat!(sheep, model)
if rand(model.rng) ≤ sheep.reproduction_prob
reproduce!(sheep, model)
end
end
function sheepwolf_step!(wolf::Wolf, model)
randomwalk!(wolf, model; ifempty=false)
wolf.energy -= 1
if wolf.energy < 0
kill_agent!(wolf, model)
return
end
# If there is any sheep on this grid cell, it's dinner time!
dinner = first_sheep_in_position(wolf.pos, model)
!isnothing(dinner) && eat!(wolf, dinner, model)
if rand(model.rng) ≤ wolf.reproduction_prob
reproduce!(wolf, model)
end
end
function first_sheep_in_position(pos, model)
ids = ids_in_position(pos, model)
j = findfirst(id -> model[id] isa Sheep, ids)
isnothing(j) ? nothing : model[ids[j]]::Sheep
end
# Sheep and wolves have separate eat! functions.
function eat!(sheep::Sheep, model)
if model.fully_grown[sheep.pos...]
sheep.energy += sheep.Δenergy
model.fully_grown[sheep.pos...] = false
end
return
end
function eat!(wolf::Wolf, sheep::Sheep, model)
kill_agent!(sheep, model)
wolf.energy += wolf.Δenergy
return
end
# Sheep and wolves share a common reproduction method.
function reproduce!(agent::A, model) where {A}
agent.energy /= 2
id = nextid(model)
offspring = A(id, agent.pos, agent.energy, agent.reproduction_prob, agent.Δenergy)
add_agent_pos!(offspring, model)
return
end
# The behavior of grass function differently. If it is fully grown, it is consumable.
function grass_step!(model)
@inbounds for p in positions(model) # we don't have to enable bound checking
if !(model.fully_grown[p...])
if model.countdown[p...] ≤ 0
model.fully_grown[p...] = true
model.countdown[p...] = model.regrowth_time
else
model.countdown[p...] -= 1
end
end
end
end
# Running the model
using InteractiveDynamics
using CairoMakie
# define the plotting details for the wolves and sheep
offset(a) = a isa Sheep ? (-0.1, -0.1*rand()) : (+0.1, +0.1*rand())
ashape(a) = a isa Sheep ? :circle : :utriangle
acolor(a) = a isa Sheep ? RGBAf(1.0, 1.0, 1.0, 0.8) : RGBAf(0.2, 0.2, 0.3, 0.8)
# how to plot grass as a heatmap:
grasscolor(model) = model.countdown ./ model.regrowth_time
heatkwargs = (colormap = [:brown, :green], colorrange = (0, 1))
plotkwargs = (;
ac = acolor,
as = 25,
am = ashape,
offset,
scatterkwargs = (strokewidth = 1.0, strokecolor = :black),
heatarray = grasscolor,
heatkwargs = heatkwargs,
)
sheepwolfgrass = initialize_model()
fig, ax, abmobs = abmplot(sheepwolfgrass;
agent_step! = sheepwolf_step!,
model_step! = grass_step!,
plotkwargs...)
fig
# Run the simulation
sheep(a) = a isa Sheep
wolf(a) = a isa Wolf
count_grass(model) = count(model.fully_grown)
sheepwolfgrass = initialize_model()
steps = 1000
adata = [(sheep, count), (wolf, count)]
mdata = [count_grass]
adf, mdf = run!(sheepwolfgrass, sheepwolf_step!, grass_step!, steps; adata, mdata)
# The following plot shows the population dynamics over time.
function plot_population_timeseries(adf, mdf)
figure = Figure(resolution = (600, 400))
ax = figure[1, 1] = Axis(figure; xlabel = "Step", ylabel = "Population")
sheepl = lines!(ax, adf.step, adf.count_sheep, color = :cornsilk4)
wolfl = lines!(ax, adf.step, adf.count_wolf, color = RGBAf(0.2, 0.2, 0.3))
grassl = lines!(ax, mdf.step, mdf.count_grass, color = :green)
figure[1, 2] = Legend(figure, [sheepl, wolfl, grassl], ["Sheep", "Wolves", "Grass"])
figure
end
# Altering the input conditions to reach an equilibrium
stable_params = (;
n_sheep = 140,
n_wolves = 20,
dims = (30, 30),
Δenergy_sheep = 5,
sheep_reproduce = 0.31,
wolf_reproduce = 0.06,
Δenergy_wolf = 30,
seed = 71758,
)
sheepwolfgrass = initialize_model(;stable_params...)
adf, mdf = run!(sheepwolfgrass, sheepwolf_step!, grass_step!, 2000; adata, mdata)
plot_population_timeseries(adf, mdf)
# Making a video
sheepwolfgrass = initialize_model(;stable_params...)
abmvideo(
"sheepwolf.mp4",
sheepwolfgrass,
sheepwolf_step!,
grass_step!;
frames = 100,
framerate = 8,
title = "Sheep Wolf Grass",
plotkwargs...,
)