diff --git a/src/nature/nature.jl b/src/nature/nature.jl index 3162a3cbad7dc87256d17980b1b6cadb35c143e3..01cf3df255219157cda25f680328f35fdda141f7 100644 --- a/src/nature/nature.jl +++ b/src/nature/nature.jl @@ -13,7 +13,9 @@ Animal This is the generic agent type for all animals. Species are differentiated -by trait dictionaries passed by them during initialisation. +by trait dictionaries passed by them during initialisation. (Note that each +trait variable can still be accessed as if it were a normal field name, +i.e. the trait `phase` can be accessed and modified with `animal.phase`.) """ @agent Animal GridAgent{2} begin #XXX is it (performance-)wise to use a dict for the traits? @@ -22,19 +24,37 @@ by trait dictionaries passed by them during initialisation. # to deepcopy the speciesdict. traits::Dict{String,Any} sex::Sex - age::Int32 + age::Int end -#TODO If I write a `getproperty` method for `Animal`, I could get around the ugly -# `animal.traits[property]` syntax (like Agents.jl does in `model.jl`). +# Overloading the `getproperty` and `setproperty!` methods for `Animal` allows +# us to write `animal.property` instead of `animal.traits["property"]`. +# (This is inspired by Agents.jl in `model.jl`.) +function Base.getproperty(animal::Animal, s::Symbol) + if s in fieldnames(Animal) + return getfield(animal, s) + else + return animal.traits[String(s)] + end +end + +function Base.setproperty!(animal::Animal, s::Symbol, x) + if s in fieldnames(Animal) + setfield!(animal, s, x) + else + animal.traits[String(s)] = x + end +end + + """ animalid(animal) A small utility function to return a string with the species name and ID of an animal. """ function animalid(a::Animal) - return "$(a.traits["name"]) $(a.id)" + return "$(a.name) $(a.id)" end """ @@ -44,7 +64,7 @@ Update an animal by one day, executing it's currently active phase function. """ function stepagent!(animal::Animal, model::AgentBasedModel) animal.age += 1 - animal.traits[animal.traits["phase"]](animal,model) + animal.traits[animal.phase](animal,model) end """ @@ -150,7 +170,8 @@ such phase, and the conditions under which the animal transitions to another pha animal is in the relevant phase. When it is called, it has access to the following variables: - `animal` a reference to the animal itself. This provides access to `animal.age`, - `animal.sex`, and `animal.traits` (a dict that gives access to all species parameters). + `animal.sex`, and `animal.<trait>` (where <trait> is a variable that was defined + in the top part of the species definition body). - `pos` gives the animal's current position as a coordinate tuple. - `model` a reference to the model world (an object of type `AgentBasedModel`). This allows access to `model.date` (the current simulation date) and @@ -187,12 +208,7 @@ macro trait(traitname) # (i.e. outside of a @phase block). Although this is specified in the documentation, # it is unexpected and liable to be overlooked. Can we add a third clause to # compensate for that? - #TODO replace with `animal.traitname` syntax using `getproperty()`/`setproperty!()` - if traitname in fieldnames(Animal) - :($(esc(:animal)).$(traitname)) - else - :($(esc(:animal)).traits[$(String(traitname))]) - end + :($(esc(:animal)).$(traitname)) end """ @@ -202,7 +218,7 @@ Switch this animal over to a different phase. This can only be used nested withi """ macro setphase(newphase) #XXX make this usable in the top part of a species definition? - :($(esc(:animal)).traits["phase"] = $(String(newphase))) + :($(esc(:animal)).phase = $(String(newphase))) end """