From fc239eaaa3fb14d60adf3afa9bdb0ee7035680d7 Mon Sep 17 00:00:00 2001
From: Daniel Vedder <daniel.vedder@idiv.de>
Date: Mon, 5 Aug 2024 15:14:06 +0200
Subject: [PATCH] Bug fixes in the skylark model

---
 src/nature/individuals.jl     | 10 +++++++---
 src/nature/populations.jl     |  2 +-
 src/nature/species/skylark.jl | 17 ++++++++---------
 src/world/landscape.jl        |  2 +-
 4 files changed, 17 insertions(+), 14 deletions(-)

diff --git a/src/nature/individuals.jl b/src/nature/individuals.jl
index 8ba9098..934d219 100644
--- a/src/nature/individuals.jl
+++ b/src/nature/individuals.jl
@@ -76,14 +76,18 @@ end
 """
     occupy!(animal, model, position)
 
-Add the given location to the animal's territory.
+Add the given location to the animal's territory. Returns `true` if successful
+(i.e. if the location was not already occupied by a conspecific), `false` if not.
 """
 function occupy!(animal::Animal, model::SimulationModel, position::Tuple{Int64,Int64})
     if isoccupied(model, speciesof(animal), position) #XXX should this be an error?
         @warn "Position $position is already occupied by a $(speciesof(animal))." animal
+        return false
+    else
+        push!(animal.territory, position)
+        push!(model.landscape[position...].territories, animalid(animal))
+        return true
     end
-    push!(animal.territory, position)
-    push!(model.landscape[position...].territories, animal.id)
 end
 
 """
diff --git a/src/nature/populations.jl b/src/nature/populations.jl
index f3c7bed..a31a840 100644
--- a/src/nature/populations.jl
+++ b/src/nature/populations.jl
@@ -146,7 +146,7 @@ Test whether this location is part of the territory of an animal of the given sp
 """
 function isoccupied(model::SimulationModel, species::String, position::Tuple{Int64,Int64})
     for id in model.landscape[position...].territories
-        (speciesof(model.animals[id]) == species) && return true
+        occursin(species, id) && return true
     end
     return false
 end
diff --git a/src/nature/species/skylark.jl b/src/nature/species/skylark.jl
index 87ac542..dfd8f2f 100644
--- a/src/nature/species/skylark.jl
+++ b/src/nature/species/skylark.jl
@@ -77,6 +77,7 @@
     mate::Int64 = -1 # the agent ID of the mate (-1 if none)
     nest::Tuple = () # coordinates of current nest
     clutch::Int64 = 0 # number and life stage of offspring in current clutch
+    #following::Int64 = -1 # ID of the individual being followed in the non-breeding phase
 end
 
 
@@ -140,8 +141,7 @@ adjusting it to new conditions when and as necessary.
     @move(@rand(self.territory))
     if model.date > self.nestingend
         # once the breeding season is over and all the young have left the nest, stop breeding
-        #FIXME under which conditions would @animal(self.mate) == nothing?
-        if self.mate == -1 || !isnothing(@animal(self.mate)) || @animal(self.mate).clutch == 0
+        if self.mate == -1 || isnothing(@animal(self.mate)) || @animal(self.mate).clutch == 0
             @setphase(nonbreeding)
         end
     end
@@ -162,8 +162,8 @@ Females returning from migration move around to look for a suitable partner with
                 @setphase(nesting)
                 return
             else
-                self.mate = -1
                 @animal(self.mate).mate = -1
+                self.mate = -1
             end
         else
             self.mate = -1
@@ -279,7 +279,7 @@ If it is, return the list of coordinates that make up the new territory, else re
 function findterritory(skylark::Skylark, model::SimulationModel)
     effectivesize::Area = 0m² # the usable size of the territory, weighted by habitat quality
     territory::Vector{Tuple{Int64,Int64}} = []
-    msize = size(model.landscape)
+    width, height = size(model.landscape)
     radius = 0
     constrained = false
     # Inspect the landscape in concentric circles around the individual until enough pixels have
@@ -304,7 +304,7 @@ function findterritory(skylark::Skylark, model::SimulationModel)
         end
         #FIXME some duplicates remain?
         for c in coords # ...then inspect them
-            (c[1] <= 0 || c[2] <= 0 || c[1] > msize[1] || c[2] > msize[2]) && continue
+            (c[1] <= 0 || c[2] <= 0 || c[1] > width || c[2] > height) && continue
             (isoccupied(model, "Skylark", c)) && continue
             push!(territory, c)
             quality = foragequality(skylark, model, c)
@@ -347,10 +347,9 @@ function allowsnesting(skylark::Skylark, model::SimulationModel, pos::Tuple{Int6
     (@landcover() == grass ||
      (@landcover() == agriculture &&
       (skylark.nestingheight[1] <= @cropheight() <= skylark.nestingheight[2]) &&
-      (skylark.nestingcover[1] <= @cropcover() <= skylark.nestingcover[2]))) #&&
-    #FIXME if we add the distance requirement, females don't find a nesting spot?
-      #(@distanceto(forest) < skylark.mindistancetoedge) &&
-      #(@distanceto(builtup) < skylark.mindistancetoedge)
+      (skylark.nestingcover[1] <= @cropcover() <= skylark.nestingcover[2])) &&
+     (@distanceto(forest) < skylark.mindistancetoedge) &&
+     (@distanceto(builtup) < skylark.mindistancetoedge))
 end
 
 """
diff --git a/src/world/landscape.jl b/src/world/landscape.jl
index b6fc167..762ea8b 100644
--- a/src/world/landscape.jl
+++ b/src/world/landscape.jl
@@ -22,7 +22,7 @@ mutable struct Pixel
     fieldid::Union{Missing,Int64} # ID of the farmplot (if any) at this position
     events::Vector{Management}    # management events that have been applied to this pixel
     animals::Vector{Int64}        # IDs of animals currently at this position
-    territories::Vector{Int64}    # IDs of animals that claim this pixel as part of their territory
+    territories::Vector{String}    # IDs of animals that claim this pixel as part of their territory
 end
 
 Pixel(landcover::LandCover, fieldid::Union{Missing,Int64}) =
-- 
GitLab