diff --git a/predator_prey.jl b/predator_prey.jl index 3fb1b8b..d66eecd 100644 --- a/predator_prey.jl +++ b/predator_prey.jl @@ -46,17 +46,63 @@ # example, we could have only one type and one additional filed to separate them. # Nevertheless, for the sake of example, we will use two different types.) using Agents, Random - +using CairoMakie @agent struct Sheep(GridAgent{2}) energy::Float64 reproduction_prob::Float64 Δenergy::Float64 + #perception::Int32 + #speed::Float64 + #endurance::Float64 +end +function move!(sheep::Sheep,model) + randomwalk!(sheep, model) + sheep.energy -= 1 +end +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 reproduce!(sheep::Sheep, model) + if rand(abmrng(model)) ≤ sheep.reproduction_prob + sheep.energy /= 2 + replicate!(sheep, model) + end end @agent struct Wolf(GridAgent{2}) energy::Float64 reproduction_prob::Float64 Δenergy::Float64 + #perception::Int32 + #speed::Float64 + #endurance::Float64 +end +function move!(wolf::Wolf,model) + randomwalk!(wolf, model; ifempty=false) + wolf.energy -= 1 +end +function eat!(wolf::Wolf, model) + dinner = first_sheep_in_position(wolf.pos, model) + if !isnothing(dinner) + remove_agent!(dinner, model) + wolf.energy += wolf.Δenergy + end +end +function reproduce!(wolf::Wolf, model) + if rand(abmrng(model)) ≤ wolf.reproduction_prob + wolf.energy /= 2 + replicate!(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 # The function `initialize_model` returns a new model containing sheep, wolves, and grass @@ -119,56 +165,23 @@ end # Notice how the function `sheepwolf_step!`, which is our `agent_step!`, # is dispatched to the appropriate agent type via Julia's Multiple Dispatch system. function sheepwolf_step!(sheep::Sheep, model) - randomwalk!(sheep, model) - sheep.energy -= 1 + move!(sheep, model) if sheep.energy < 0 remove_agent!(sheep, model) return end eat!(sheep, model) - if rand(abmrng(model)) ≤ sheep.reproduction_prob - sheep.energy /= 2 - replicate!(sheep, model) - end + reproduce!(sheep, model) end function sheepwolf_step!(wolf::Wolf, model) - randomwalk!(wolf, model; ifempty=false) - wolf.energy -= 1 + move!(wolf, model) if wolf.energy < 0 remove_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(abmrng(model)) ≤ wolf.reproduction_prob - wolf.energy /= 2 - replicate!(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. If a sheep eats grass, it will acquire -# additional energy and the grass will not be available for consumption until regrowth time -# has elapsed. If a wolf eats a sheep, the sheep dies and the wolf acquires more energy. -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) - remove_agent!(sheep, model) - wolf.energy += wolf.Δenergy - return + eat!(wolf, model) + reproduce!(wolf, model) end # The behavior of grass function differently. If it is fully grown, it is consumable. @@ -187,108 +200,110 @@ function grass_step!(model) end end -sheepwolfgrass = initialize_model() +function run() + sheepwolfgrass = initialize_model() -# ## Running the model -# %% #src -# We will run the model for 500 steps and record the number of sheep, wolves and consumable -# grass patches after each step. First: initialize the model. + # ## Running the model + # %% #src + # We will run the model for 500 steps and record the number of sheep, wolves and consumable + # grass patches after each step. First: initialize the model. -using CairoMakie -CairoMakie.activate!() # hide + CairoMakie.activate!() # hide -# To view our starting population, we can build an overview plot using [`abmplot`](@ref). -# We 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) + # To view our starting population, we can build an overview plot using [`abmplot`](@ref). + # We 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) -# and instruct [`abmplot`](@ref) how to plot grass as a heatmap: -grasscolor(model) = model.countdown ./ model.regrowth_time -# and finally define a colormap for the grass: -heatkwargs = (colormap = [:brown, :green], colorrange = (0, 1)) + # and instruct [`abmplot`](@ref) how to plot grass as a heatmap: + grasscolor(model) = model.countdown ./ model.regrowth_time + # and finally define a colormap for the grass: + heatkwargs = (colormap = [:brown, :green], colorrange = (0, 1)) -# and put everything together and give it to [`abmplot`](@ref) -plotkwargs = (; - agent_color = acolor, - agent_size = 25, - agent_marker = ashape, - offset, - agentsplotkwargs = (strokewidth = 1.0, strokecolor = :black), - heatarray = grasscolor, - heatkwargs = heatkwargs, -) + # and put everything together and give it to [`abmplot`](@ref) + plotkwargs = (; + agent_color = acolor, + agent_size = 25, + agent_marker = ashape, + offset, + agentsplotkwargs = (strokewidth = 1.0, strokecolor = :black), + heatarray = grasscolor, + heatkwargs = heatkwargs, + ) -sheepwolfgrass = initialize_model() + sheepwolfgrass = initialize_model() -fig, ax, abmobs = abmplot(sheepwolfgrass; plotkwargs...) -fig + fig, ax, abmobs = abmplot(sheepwolfgrass; plotkwargs...) + fig -# Now, lets run the simulation and collect some data. Define datacollection: -sheep(a) = a isa Sheep -wolf(a) = a isa Wolf -count_grass(model) = count(model.fully_grown) -# Run simulation: -sheepwolfgrass = initialize_model() -steps = 1000 -adata = [(sheep, count), (wolf, count)] -mdata = [count_grass] -adf, mdf = run!(sheepwolfgrass, steps; adata, mdata) + # Now, lets run the simulation and collect some data. Define datacollection: + sheep(a) = a isa Sheep + wolf(a) = a isa Wolf + count_grass(model) = count(model.fully_grown) + # Run simulation: + sheepwolfgrass = initialize_model() + steps = 1000 + adata = [(sheep, count), (wolf, count)] + mdata = [count_grass] + adf, mdf = run!(sheepwolfgrass, steps; adata, mdata) -# The following plot shows the population dynamics over time. -# Initially, wolves become extinct because they consume the sheep too quickly. -# The few remaining sheep reproduce and gradually reach an -# equilibrium that can be supported by the amount of available grass. -function plot_population_timeseries(adf, mdf) - figure = Figure(size = (600, 400)) - ax = figure[1, 1] = Axis(figure; xlabel = "Step", ylabel = "Population") - sheepl = lines!(ax, adf.time, adf.count_sheep, color = :cornsilk4) - wolfl = lines!(ax, adf.time, adf.count_wolf, color = RGBAf(0.2, 0.2, 0.3)) - grassl = lines!(ax, mdf.time, mdf.count_grass, color = :green) - figure[1, 2] = Legend(figure, [sheepl, wolfl, grassl], ["Sheep", "Wolves", "Grass"]) - figure + # The following plot shows the population dynamics over time. + # Initially, wolves become extinct because they consume the sheep too quickly. + # The few remaining sheep reproduce and gradually reach an + # equilibrium that can be supported by the amount of available grass. + function plot_population_timeseries(adf, mdf) + figure = Figure(size = (600, 400)) + ax = figure[1, 1] = Axis(figure; xlabel = "Step", ylabel = "Population") + sheepl = lines!(ax, adf.time, adf.count_sheep, color = :cornsilk4) + wolfl = lines!(ax, adf.time, adf.count_wolf, color = RGBAf(0.2, 0.2, 0.3)) + grassl = lines!(ax, mdf.time, mdf.count_grass, color = :green) + figure[1, 2] = Legend(figure, [sheepl, wolfl, grassl], ["Sheep", "Wolves", "Grass"]) + figure + end + + plot_population_timeseries(adf, mdf) + + # Altering the input conditions, we now see a landscape where sheep, wolves and grass + # find an equilibrium + # %% #src + 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, 2000; adata, mdata) + plot_population_timeseries(adf, mdf) + + # Finding a parameter combination that leads to long-term coexistence was + # surprisingly difficult. It is for such cases that the + # [Optimizing agent based models](@ref) example is useful! + # %% #src + + # ## Video + # Given that we have defined plotting functions, making a video is as simple as + sheepwolfgrass = initialize_model(;stable_params...) + + abmvideo( + "sheepwolf.mp4", + sheepwolfgrass; + frames = 100, + framerate = 8, + title = "Sheep Wolf Grass", + plotkwargs..., + ) + + # ```@raw html + # + # ``` end - -plot_population_timeseries(adf, mdf) - -# Altering the input conditions, we now see a landscape where sheep, wolves and grass -# find an equilibrium -# %% #src -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, 2000; adata, mdata) -plot_population_timeseries(adf, mdf) - -# Finding a parameter combination that leads to long-term coexistence was -# surprisingly difficult. It is for such cases that the -# [Optimizing agent based models](@ref) example is useful! -# %% #src - -# ## Video -# Given that we have defined plotting functions, making a video is as simple as -sheepwolfgrass = initialize_model(;stable_params...) - -abmvideo( - "sheepwolf.mp4", - sheepwolfgrass; - frames = 100, - framerate = 8, - title = "Sheep Wolf Grass", - plotkwargs..., -) - -# ```@raw html -# -# ``` +run()