From 8a958fd968be09936bbb29d2cacdae61ce6d7337 Mon Sep 17 00:00:00 2001 From: Daniel Vedder <daniel.vedder@idiv.de> Date: Thu, 23 May 2024 12:54:31 +0200 Subject: [PATCH] Added @here and nagents(), fixed farmplot() and directionto() --- src/core/simulation.jl | 9 +++++++++ src/nature/macros.jl | 16 +++++++++++++--- src/world/landscape.jl | 37 ++++++++++++++++++++++++------------- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/core/simulation.jl b/src/core/simulation.jl index c9ed15a..b4a0228 100644 --- a/src/core/simulation.jl +++ b/src/core/simulation.jl @@ -29,6 +29,15 @@ mutable struct AgricultureModel <: SimulationModel events::Vector{FarmEvent} end +""" + nagents(model) + +Return the total number of agents in a model object. +""" +function nagents(model::AgricultureModel) + length(model.animals)+length(model.farmers)+length(model.farmplots) +end + """ stepagent!(agent, model) diff --git a/src/nature/macros.jl b/src/nature/macros.jl index ccb82d3..fb2f107 100644 --- a/src/nature/macros.jl +++ b/src/nature/macros.jl @@ -124,7 +124,7 @@ is explicitly defined by the user in the species definition block. """ macro phase(species, phase, body) quote - Core.@__doc__ function $(esc(phase))($(esc(:self))::$(species), + Core.@__doc__ function $(esc(phase))($(esc(:self))::$(esc(species)), $(esc(:model))::SimulationModel) $(esc(:pos)) = $(esc(:self)).pos # needed for landscape macros $(esc(body)) @@ -145,7 +145,6 @@ can thus use all macros available in [`@phase`](@ref). """ macro create(species, body) quote - #XXX species are created/referenced as Persefone.<speciesname>, is this relevant? Core.@__doc__ function $(esc(:create!))($(esc(:self))::$(esc(species)), $(esc(:model))::SimulationModel) $(esc(:pos)) = $(esc(:self)).pos # needed for landscape macros @@ -176,10 +175,21 @@ macro isalive(id) :(isalive($(esc(id)), $(esc(:model)))) end +""" + @here() + +Return the landscape pixel of this animal's current location. +This can only be used nested within [`@phase`](@ref). +""" +macro here() + :($(esc(:model)).landscape[$(esc(:self)).pos...]) +end + """ @setphase(newphase) -Switch this animal over to a different phase. This can only be used nested within [`@phase`](@ref). +Switch this animal over to a different phase. +This can only be used nested within [`@phase`](@ref). """ macro setphase(newphase) :($(esc(:self)).phase = $(newphase)) diff --git a/src/world/landscape.jl b/src/world/landscape.jl index ac6cc80..f310cc6 100644 --- a/src/world/landscape.jl +++ b/src/world/landscape.jl @@ -116,11 +116,8 @@ end Return the farm plot at this position, or nothing if there is none (utility wrapper). """ function farmplot(pos::Tuple{Int64,Int64}, model::SimulationModel) - if ismissing(model.landscape[pos...].fieldid) - nothing - else - model[model.landscape[pos...].fieldid] - end + id = model.landscape[pos...].fieldid + ismissing(id) ? nothing : model.farmplots[id] end """ @@ -131,19 +128,22 @@ habitat descriptor function. Returns a coordinate tuple (target - position), or if no matching habitat is found. Caution: can be computationally expensive! """ function directionto(pos::Tuple{Int64,Int64}, model::SimulationModel, habitatdescriptor::Function) - #TODO test + #XXX split this up into a findnearest() and a directionto() function? + # search in expanding rectangles around the origin (habitatdescriptor(pos, model)) && (return (0,0)) dist = 1 width, height = size(model.landscape) - while dist <= width || dist <= height + while dist <= width || dist <= height #XXX is there a quicker method? # check the upper and lower bounds of the enclosing rectangle y1 = pos[2] - dist y2 = pos[2] + dist minx = maximum((pos[1]-dist, 1)) maxx = minimum((pos[1]+dist, width)) + #XXX alternately, we could check individually whether y1 and y2 are in bounds, and loop + # over x for each separately (not sure if that is quicker, but it may be) for x in minx:maxx - (y1 > 0 && habitatdescriptor((x,y1), model)) && (return (x, y1)) - (y2 <= height && habitatdescriptor((x,y2), model)) && (return (x, y2)) + (y1 > 0 && habitatdescriptor((x,y1), model)) && (return (x, y1).-pos) + (y2 <= height && habitatdescriptor((x,y2), model)) && (return (x, y2).-pos) end # check the left and right bounds of the enclosing rectangle x1 = pos[1] - dist @@ -151,14 +151,26 @@ function directionto(pos::Tuple{Int64,Int64}, model::SimulationModel, habitatdes miny = maximum((pos[2]-dist+1, 1)) maxy = minimum((pos[2]+dist-1, height)) for y in miny:maxy - (x1 > 0 && habitatdescriptor((x1,y), model)) && (return (x1,y)) - (x2 <= width && habitatdescriptor((x2,y), model)) && (return (x2,y)) + (x1 > 0 && habitatdescriptor((x1,y), model)) && (return (x1,y).-pos) + (x2 <= width && habitatdescriptor((x2,y), model)) && (return (x2,y).-pos) end dist += 1 end return nothing end +""" + directionto(pos, model, habitattype) + +Calculate the direction from the given location to the closest habitat of the specified type. +Returns a coordinate tuple (target - position), or nothing if no matching habitat is found. +Caution: can be computationally expensive! +""" +function directionto(pos::Tuple{Int64,Int64}, model::SimulationModel, habitattype::LandCover) + # can't use @habitat here because macros.jl is loaded later than this file + directionto(pos, model, function(p,m) landcover(p,m) == habitattype end) +end + """ distanceto(pos, model, habitatdescriptor) @@ -166,10 +178,9 @@ Calculate the distance from the given location to the closest location matching habitat descriptor function. Caution: can be computationally expensive! """ function distanceto(pos::Tuple{Int64,Int64}, model::SimulationModel, habitatdescriptor::Function) - #TODO test target = directionto(pos, model, habitatdescriptor) isnothing(target) && return Inf - return maximum(abs.(target.-pos)) + return maximum(abs.(target)) end """ -- GitLab