From 72f6c9006b3fa9d434dedac2dbcaca0afddb6fdd Mon Sep 17 00:00:00 2001
From: Daniel Vedder <daniel.vedder@idiv.de>
Date: Wed, 21 Dec 2022 16:31:59 +0100
Subject: [PATCH] Wrote a first version of the events system

---
 src/core/landscape.jl  | 45 ++++++++++++++++++++++++++++++++++++------
 src/core/simulation.jl |  5 +++--
 src/crop/crops.jl      | 13 ++++++++++++
 src/nature/nature.jl   |  3 ++-
 4 files changed, 57 insertions(+), 9 deletions(-)

diff --git a/src/core/landscape.jl b/src/core/landscape.jl
index aff4e57..959aef5 100644
--- a/src/core/landscape.jl
+++ b/src/core/landscape.jl
@@ -8,6 +8,8 @@
 ## Do not change the order of this enum, or initlandscape() will break!
 @enum LandCover nodata forest grass water builtup soil agriculture
 
+@enum LandscapeEvent test tillage fertiliser pesticide harvest
+
 """
     Pixel
 
@@ -18,8 +20,7 @@ in a single object. The model landscape consists of a matrix of pixels.
 mutable struct Pixel
     landcover::LandCover
     fieldid::Union{Missing,Int64}
-    events::Vector{Symbol} #TODO implement the rest of the events system
-    #FIXME actually this is stupid - I don't want a field ID, I want a field object
+    events::Vector{Tuple{LandscapeEvent,Int64}}
 end
 
 """
@@ -49,6 +50,27 @@ function initlandscape()
     return landscape
 end
 
+"""
+    updatelandscape!(model)
+
+Update the model landscape. (Currently only removes old events.)
+"""
+function updatelandscape!(model::AgentBasedModel)
+    #FIXME This is really slow?!
+    # Instead of cycling through every pixel every time, I should keep a list of active
+    # events, and cycle through those
+    width, height = size(model.landscape)
+    for x in 1:width
+        for y in 1:height
+            newevents::Vector{Tuple{LandscapeEvent,Int64}} = []
+            for e in model.landscape[x,y].events
+                # only keep events that still have a duration > 1 day
+                (e[2] > 1) && push!(newevents, (e[1], e[2]-1))
+            end
+            model.landscape[x,y].events = newevents
+        end
+    end
+end
 
 """
     landcover(model, position)
@@ -60,10 +82,21 @@ function landcover(model::AgentBasedModel, pos::Tuple{Int64,Int64})
 end
 
 """
-    fieldid(model, position)
+    farmplot(model, position)
 
-Return the UID of the field at this position (utility wrapper).
+Return the farm plot at this position (utility wrapper).
 """
-function fieldid(model::AgentBasedModel, pos::Tuple{Int64,Int64})
-    model.landscape[pos...].fieldid
+function farmplot(model::AgentBasedModel, pos::Tuple{Int64,Int64})
+    model[model.landscape[pos...].fieldid]
 end
+
+"""
+    createevent(model, pos, name, duration=1)
+
+Add a landscape event to the pixel at the specified site.
+"""
+function createevent(model::AgentBasedModel, pos::Tuple{Int64,Int64},
+                     name::LandscapeEvent, duration::Int64=1)
+    push!(model.landscape[pos...].events, (name, duration))
+end
+
diff --git a/src/core/simulation.jl b/src/core/simulation.jl
index 3ee8fe7..525e76e 100644
--- a/src/core/simulation.jl
+++ b/src/core/simulation.jl
@@ -37,10 +37,11 @@ Execute one update of the model.
 """
 function stepsimulation!(model::AgentBasedModel)
     @info "Simulating day $(model.date)."
-    for a in Schedulers.ByType((Farmer,Animal,FarmPlot), true)(model)
+    for a in Schedulers.ByType((Farmer,FarmPlot,Animal), true)(model)
         #The animal may have been killed, so we need a try/catch
-        try stepagent!(getindex(model, a), model) catch keyerror end
+        try stepagent!(model[a], model) catch keyerror end
     end
+    #updatelandscape!(model)
     outputdata(model)
     model.date += Day(1)
 end
diff --git a/src/crop/crops.jl b/src/crop/crops.jl
index ccae89b..709a203 100644
--- a/src/crop/crops.jl
+++ b/src/crop/crops.jl
@@ -86,3 +86,16 @@ function averagefieldsize(model::AgentBasedModel)
     round(sum(sizes)/size(sizes)[1], digits=2)
     #sizes
 end
+
+
+"""
+    applyevent(model, farmplotid, name, duration=1)
+
+Apply an event to all pixels in a farm plot.
+"""
+function applyevent(model::AgentBasedModel, farmplotid::Int64,
+                    name::LandscapeEvent, duration::Int64=1)
+    for p in model[farmplotid].pixels
+        createevent(model, p, name, duration)
+    end
+end
diff --git a/src/nature/nature.jl b/src/nature/nature.jl
index 39e388e..9fe3f27 100644
--- a/src/nature/nature.jl
+++ b/src/nature/nature.jl
@@ -122,7 +122,8 @@ This can only be used nested within `@phase`.
 """
 macro respond(eventname, body)
     quote
-        if $(esc(eventname)) in @here(events)
+        #TODO test this
+        if any(e -> e[1] == $(esc(eventname)), @here(events))
             $body
         end
     end
-- 
GitLab