ConFooBio / gmse

An R package for simulating Generalised Management Strategy Evaluation
http://confoobio.github.io/gmse
10 stars 5 forks source link

Resource-landscape interaction #56

Closed bradduthie closed 4 years ago

bradduthie commented 4 years ago

At the moment, GMSE only allows for a fixed carrying capacity that is not tied to resource consumption on landscape cells. Now GMSE is being used for projects with @AdrianBach @jejoenje @nbunne and Lovisa, with a major element of these models being resources (e.g., geese) feeding on the landscape. Hence, I am tempted to code a link between resource consumption and resource mortality. In other words, geese could survive as long as they consumed sufficient landscape yield in a time step, else they would die. This would lead to a natural carrying capacity (which could still be adjusted by adjusting the required amount consumed to ensure survival), and would tie population dynamics with resource availability more naturally.

I think that the reason I hesitated to do this in the first place was to allow for some flexibility much later on down the line, creating multiple resource types and having those types interact on the same data frame. I would like to get there eventually, but making use of the landscape properties to have resource dynamics dependent upon consumption might be good in the mean time. It would also potentially make strategies such as kill_crops potentially relevant.

What do you all think? If I were to add a new option allowing resource survival (or reproduction) to depend on landscape consumption, would you be likely to use it, or is this unnecessary for the models that you are working on?

AdrianBach commented 4 years ago

Hi Brad,

Good idea, I would find it really useful! The carrying capacity can be a confusing notion sometimes, and less straightforward a measure as a yield or a consumption rate. Do manager usually use it? Moreover, it would avoid setting a fixed value for the carrying capacity, which is likely to vary over time and with changes in the environment.

Cheers, Adrian


From: Brad Duthie notifications@github.com Sent: 06 January 2020 13:21 To: ConFooBio/gmse gmse@noreply.github.com Cc: Adrian Bach adrian.bach@stir.ac.uk; Mention mention@noreply.github.com Subject: [ConFooBio/gmse] Resource-landscape interaction (#56)

At the moment, GMSE only allows for a fixed carrying capacity that is not tied to resource consumption on landscape cells. Now GMSE is being used for projects with @AdrianBachhttps://github.com/AdrianBach @jejoenjehttps://github.com/jejoenje @nbunnehttps://github.com/nbunne and Lovisa, with a major element of these models being resources (e.g., geese) feeding on the landscape. Hence, I am tempted to code a link between resource consumption and resource mortality. In other words, geese could survive as long as they consumed sufficient landscape yield in a time step, else they would die. This would lead to a natural carrying capacity (which could still be adjusted by adjusting the required amount consumed to ensure survival), and would tie population dynamics with resource availability more naturally.

I think that the reason I hesitated to do this in the first place was to allow for some flexibility much later on down the line, creating multiple resource types and having those types interact on the same data frame. I would like to get there eventually, but making use of the landscape properties to have resource dynamics dependent upon consumption might be good in the mean time. It would also potentially make strategies such as kill_crops potentially relevant.

What do you all think? If I were to add a new option allowing resource survival (or reproduction) to depend on landscape consumption, would you be likely to use it, or is this unnecessary for the models that you are working on?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/ConFooBio/gmse/issues/56?email_source=notifications&email_token=AKMP6UXYH7YPILQKKXT2LUTQ4MV6TA5CNFSM4KDEIVK2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4IEHCRMQ, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AKMP6UVWAQUTD6L4FUD2XD3Q4MV6TANCNFSM4KDEIVKQ.

[ { "@context": "http://schema.org", "@type": "EmailMessage", "potentialAction": { "@type": "ViewAction", "target": "https://github.com/ConFooBio/gmse/issues/56?email_source=notifications\u0026email_token=AKMP6UXYH7YPILQKKXT2LUTQ4MV6TA5CNFSM4KDEIVK2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4IEHCRMQ", "url": "https://github.com/ConFooBio/gmse/issues/56?email_source=notifications\u0026email_token=AKMP6UXYH7YPILQKKXT2LUTQ4MV6TA5CNFSM4KDEIVK2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4IEHCRMQ", "name": "View Issue" }, "description": "View this Issue on GitHub", "publisher": { "@type": "Organization", "name": "GitHub", "url": "https://github.com" } } ]


The University achieved an overall 5 stars in the QS World University Rankings 2018 The University of Stirling is a charity registered in Scotland, number SC 011159.

jejoenje commented 4 years ago

Agreed I think this would be a great additional feature and a natural next step! I'll give this a bit more thought, but the following elements/features come to mind -

  1. Retaining the option to set a fixed CC and have no link between resource consumption and survival (i.e. defaulting to the current set up). Although I agree with @AdrianBach that it may be unlikely the CC concept is actually used in practical terms by managers, I think this would be important in terms of backwards compatibility.

  2. Allowing one or more parameter(s) to set the strength (or perhaps even the shape) of the relationship between consumption and survival.

  3. As a follow on from (2), I think it would be good to implement some sort of error/variation in that relationship - e.g. the probability of survival would be a function of consumption, as opposed having some sort of threshold value. While a bit more complicated than a simple threshold, I think such a probabilistic approach would be more naturally extensible, and would also introduce a little bit of stochasticity, which may e.g. reflect the effect of environmental variability on survival.

  4. As discussed a bit before Christmas, as a next step in the modelling I've been doing (looking at concurrent effects of varying land ownership and budget - BES presentation), I want to look at re-distributing resources as a function of landscape yield - i.e. each resource individual would relocate to a given cell with a probability increasing with the yield in that cell. Given that your idea @bradduthie is kind of related to this, I'm wondering if it may make sense to incorporate this in the same update?

bradduthie commented 4 years ago

Many thanks @AdrianBach and @jejoenje -- this helps a lot! I agree that having population dynamics (and therefore carrying capacity) more mechanistically affected by yield would be more straightforward. Regarding your points @jejeonje:

  1. I definitely agree that any new addition should ensure backwards compatibility. Code run before this hypothetical new version should run exactly as before. The way that I imagine the new addition is to add a new option(s) for the gmse argument res_death_type. At the moment, the natural numbers 1-3 allow for different combinations of density independent and density dependent mortality. The number 4 could just be added such that mortality would be enacted by a landscape-dependent mechanism, with new arguments to gmse clarifying any new parameters that would be needed to do this. We could also allow this for res_birth_type and res_move_type, if desired. Under the hood, these are all just different cases in a switch function within the C file for the resource model (as a side note, I kind of want to refactor this anyway so that each case calls a separate function; the res_remove function is eventually going to get too long to be readable, if it's not already).

  2. I like this -- we can start easy with just the strength of the relationship between consumption and survival probability. The easiest would be a threshold value, but it wouldn't be much more difficult to add another parameter value affecting the slope of the relationship between probability of survival and consumption. In practice, what this will mean is, I think, is one function in resource.c that is called within the res_remove function. The function would just input res_removing, paras, and landscape and set res_removing[resource][rm_row] = -1 when the mortality condition is met. Whatever determines that condition can be specified in the new function, and new parameters, as always, are stored in the big junk bag that is the paras vector.

  3. See above.

  4. I like the idea of having some sort of behavioural rules that could make resources drawn to cells as a function of cell yield. This could be an option for the res_move_type argument in gmse, and work by adding new functions to the res_mover function, which is actually in the utilities.c, since resources are moved in other submodels as well (or, at least, I left open the option for this).

There is a complicating factor in the coding since I wrote a separate function res_landscape_interaction that affects the values on landscapes -- this comes after everything else in the time step at the moment (movement, birth death), and the order of operations might be something to think about. I'm slightly nervous about this line. I don't like the hard coded values, and it looks like it's multiplying by offspring number instead of death rate -- then again, the variables esize_grow and esize_death appear to be parameterised with a hard coded zero during initialisation, so I don't think it can actually affect anything in practice. I suspect that this is a hook I left myself to do exactly what this issue was introduced for, which I have just forgotten about. This seems like a good reason to clean the code up here and make these changes properly.

nbunne commented 4 years ago

That sounds all really good. Re 2) I think a function rather than a threshold is really important for the link between consumption and survival, because ecological theory supports gradual changes rather than hard tipping points. I think that would be a really great step forward.

bradduthie commented 4 years ago

With commit 6d1e5e6ca41bc6bb45c62cb174f34480c3c5add1, I believe that I now have resolved Issue #56 and created a version of GMSE that now allows resource death and reproduction to be tied to resource consumption on the landscape. Initial testing shows that this works in both gmse and gmse_apply. I have not had a chance to code in the suggestion from @jejoenje to have it be the probability of survival or reproduction that changes with increasing consumption. Rather, given that a resource has consumed a sufficient quantity of resources consume_surv, the resource will survive. A separate new gmse argument consume_repr ties reproduction to landscape consumption by having resources produce floor(consumed / consume_repr) offspring given some total consumption consumed.

There is still considerable stochasticity introduced in terms of whether or not a resource will survive or reproduce. First, each resource can potentially feed more than once by using the new times_feeding argument. When times_feeding > 1 (default is 1), resources will consume the landscape, then move, then consume, and so forth until all of their times_feeding have run out. In practice, all resources get the same times_feeding value, but the code for executing it depends on a new column in the resources array. This means that there can be variation in the number of times a resource feeds if using gmse_apply (or gmse, if we later decide to sample feeding rates from a distribution). Feeding events also occur in a random order, which avoids the first resource on the list filling themselves up before moving on to the next resource (as would be a natural way to program it with a for loop). I did it this (slightly less efficient) way to more realistically model how resources would really move around simultaneously on a landscape eating as they move from place to place.

Hence, in the end, there is a lot of uncertainty surrounding whether an individual resource will have enough to survive and reproduce in a time step. But the probabilistic idea of @jejoenje could also be imposed with a function of some sort that would link consumption to a probability of death (will need to think of the best way to actually do this in the code though). Testing shows that resource increases to a carrying capacity imposed by the constraints of the landscape, with a lot more stochasticity than occurs just by settting a value for global res_death_K. To get started, for example, the below demonstrates the new features.

sim <- gmse(consume_surv = 2, consume_repr = 3, times_feeding = 6, 
            res_birth_type = 0, res_death_type = 0, land_ownership = TRUE, 
            stakeholders = 12, scaring = TRUE, time_max = 40, user_budget = 1);

I set the user_budget to unity above just to show how the population rises to a natural carrying capacity when the manager and users are unable to cause any population change. We can recover a (usually) well-regulate population by settting the user_budget back to the default.

sim <- gmse(consume_surv = 2, consume_repr = 3, times_feeding = 6, 
            res_birth_type = 0, res_death_type = 0, land_ownership = TRUE, 
            stakeholders = 12, scaring = TRUE, time_max = 40, 
            user_budget = 1000);

Just to clarify, each landscape cell still puts out a value of 1. The argument consume_surv = 2 means that resources need to consume two cells worth to survive a time step (by default, resources consume half of what's on a landscape cell with each visit). The argument consume_repr = 3 means that resources need to consume three cells worth to produce one offspring. The argument times_feeding = 6 means that resources get to feed on six cells in each time step.

Setting res_birth_type = 0 and res_death_type = 0 effectively removes all other factors in GMSE that affect resource birth and death. Hence, what is observed from running the above code is purely a consequence of the new rules limiting survival and reproduction due to landscape consumption. Note that these values need to be specified, else GMSE will assume that other defaults apply in addition to the effects of landscape consumption. In other words, the global external carrying capacity from res_death_K will still limit population growth because by default res_death_type = 2, and the mean birth number lambda will still apply because res_birth_type = 2. I don't want to override this just in case anyone wants to include multiple types of processes affecting birth and death (I also want the new version to be backwards compatible with existing code), but I think it will be important for me to write some documentation that makes using these new features really clear. I'll start a new vignette.

The new version is GMSE v0.5.0.0 with @AdrianBach now included in the list of contributors. The action_thres and budget_bonus can now be run in gmse and gmse_apply, and usr_budget_rng and age_repr are also available options. The new version passes all CRAN checks with no notes, warnings, or errors. The new version should be completely backwards compatible. If you have existing code to run, it should do the same thing in the new version that it did in previous versions. Everything is now just a new feature that can be used, but is not by default.

The last thing that I think I want to do before merging with master, updating the website, and sending to CRAN, is to make a new default landscape. It would be good to make each landscape into more farm-like blocks rather than the narrow stripes that exist now. I don't have a good algorithm for doing this for an arbitrary number of stakeholders (i.e., for N stakeholders, separate an X by Y landscape into roughly equal blocks of sizes ca (X*Y / N)). If anyone wants to have a go at this and create a function that does it, please let me know! Else I'll try to get around to it before early next month where the plan is to send to CRAN. If anyone has time (@nbunne @jejoenje @AdrianBach), feel free to download from the dev branch and go.

install.packages("devtools");
library(devtools);
install_github("ConFooBio/GMSE", ref = "dev")

It should be up now.

jejoenje commented 4 years ago

This sounds fantastic @bradduthie! Nice work indeed!

I much look forward to playing with this. Indeed, I'm currently working on a set of simulations varying user/manager budgets as well as land distribution among users, as discussed, so it would be fairly straightforward for me to replicate the exact scenarios I'm running but with the new consmuption parameters.

Just to check if I understand the new logic correctly - in the new set up, resource movement is still random, correct? So resource agents simply move in a random fashion until times_feeding is reached?

Finally, re. your point about a new default landscape (i.e. "non-striped"). I have a potential way of doing this, although it would somewhat rely on an external package. I'll see if I can set some examples up to demonstrate what I'm thinking.

jejoenje commented 4 years ago

Actually thinking about it - the "easy" procedure for more "natural" landscape distribution among stakeholders I was thinking of, is essentially a tesselation as produced by NLMR::nlm_mosaictess. While this is very fast and can produce some very satisfying-looking "landscapes", it does result in landscapes with a significant amount of (non-uniform) variation among landowners, which is clearly not what we want for a default landscape.

I've actually been going around in circles about this problem - I don't think it's as trivial a question as I first thought. So we want to subdivide a square in as-close-to-equal parts, but have the subdivisions contiguous, correct?

bradduthie commented 4 years ago

Awesome @jejoenje -- yeah, resource movement has not changed. To keep everything backwards compatible, the res_mover function is still called in the default resource model. The lines immediately below are only called if consumption affects birth or death (defaults are zero, meaning that it will go unused as in old versions of GMSE). The resource_feeding function is called if such conditions are fulfilled. In resource_feeding, resources essentially move times_feeding times on the landscape according to the same rules as in res_mover (i.e., by default, random movement within res_movement cells in any direction). Movements are randomly sequenced though, so there is no predictable pattern for when a focal resource will use its movement(s) relative to the other resources. After moving once, the resource feeds on the cell on which it lands.

So, essentially, resource move first without feeding. Then they move in the same fashion in random orders times_feeding times, but with feeding after every movement from once cell to the next. This is slightly unsatisfying, as resources are essentially moving once unproductively -- just land on a new cell but not actually eating on it. I could potentially reverse the order though? So that a resource feeds first and the eats, so that the first movement wouldn't be so useless (my goal is to avoid changing something in a way that causes old code to run differently -- doing this will be inevitable at some point, but I'm wanting to hold off for now). What do you think? I actually think that this might just be a matter of swapping two lines.

I agree on what we want for the landscape, ideally -- though I don't know if rectangular is necessarily as important as contiguous and less long and narrow (for lack of better terminology). For four farmers, I actually think the current default is fine, but aesthetically, when there are a huge number of farmers, it just starts to look a bit weird and unrealistic. If you have any ideas, go for it! Though I really want to try to avoid having dependencies in GMSE (aside from the shiny packages needed for the GUI) -- maybe just a similar algorithm to one used in NLMR to produce an alternative to the stripes? The polygons in the example link look really cool actually. And I don't think a bit of variation is necessarily bad in terms of farm size, at least for a non-default option, though it's probably worth thinking about a bit more (obviously it would be cool to be able to set arbitrary variation in farm size with block shapes, but this is probably unrealistic for arbitrary stakeholder numbers).

bradduthie commented 4 years ago

Fingers crossed that the new R 4.0.0 doesn't result in any painful challenges to GMSE. I'll download and run all checks in the new R before submitting to CRAN (might need to be early to mid May).

jejoenje commented 4 years ago

woo R 4.0! I hadn't spotted that yet... I'm keen to run a bunch of tests with the new functions etc as above; I'll aim to do this as soon as I can (next week now, realistically).

bradduthie commented 4 years ago

One problem I'm now seeing here in the genetic algorithm is that users perceive their actions' of castration and feeding as being directly tied to lambda. This means that if simulations are run with lambda = 0 specified to make survival or reproduction dependent only on landscape consumption, then users will assume that castration and feeding have no effect (even though these actions will have an effect). Fixing this isn't difficult in the code. We would just need to specify what value should go in the paras vector to define presumed offspring production. But what this value should be is a bit more tricky. We want to this to be users' expectation of offspring production, so I am reluctant to make it something like the expected production at the current time step as calculated from the resource array (i.e., what will actually be mean reproductive output; users shouldn't know this with certainty). Maybe something like expected offspring that would be produced if the resource had consumed the whole cell's contents? For castration, this seems somewhat arbitrary but logical; for feeding, this could be reasonable as we could specify that feeding amount equals this value. What do you think @jejoenje @nbunne @AdrianBach ?

jejoenje commented 4 years ago

This means that if simulations are run with lambda = 0 specified to make survival or reproduction dependent only on landscape consumption, then users will assume that castration and feeding have no effect (even though these actions will have an effect).

I must admit, I haven't actually used the feeding or castration options myself at all yet, so I hadn't actually realised that this would be an issue. Good spot...

Fixing this isn't difficult in the code. We would just need to specify what value should go in the paras vector to define presumed offspring production.

This would then only apply IF lambda = 0, correct?

We want to this to be users' expectation of offspring production, so I am reluctant to make it something like the expected production at the current time step as calculated from the resource array (i.e., what will actually be mean reproductive output; users shouldn't know this with certainty). Maybe something like expected offspring that would be produced if the resource had consumed the whole cell's contents?

This sounds fine to me, I can't say I have much of a better idea... Would it be really unreasonable to hypothesise that a users' expectation of resource growth is always 1, i.e. the assumption is that the resource number will be the same from one year to the next? From a manager's perspective that wouldn't make sense, but from a users' perspective, perhaps it does?

for feeding, this could be reasonable as we could specify that feeding amount equals this value.

Not quite sure if I follow this... so they would always feed to the maximum?

jejoenje commented 4 years ago

@bradduthie Just as an update, I now have the development version installed in a separate R installation and my simulation scenarios seem to be running, both as they were and with the new parameter values set, too. I'll get some stuff run and will see if I can post some outputs here or elsewhere. These are 100 simulations of 100 years, both with and without varying budgets and landownership. I have not looked at the consequences of using the new "consumption" parameters yet - so at the moment it will be interesting to see what happens.

bradduthie commented 4 years ago

Sort of -- it would apply especially when lambda = 0 and stakeholders mistakenly believe that their actions have no effect. But if, e.g., a low lambda is augmented by increased birth caused by landscape consumption, then it would apply here too. A flat assumption might work. I'll try to keep things as they are for the no consumption case for now, then maybe increment the effects of presumed consumption on a single cell? Sorry, by the feeding value, I mean that users would assume that the resource had consumed a single cells worth of food (rather than, e.g., have them work out how much they are expected to consume based on their movement patterns -- but maybe this is actually better?).

bradduthie commented 4 years ago

I'm now thinking that maybe the best solution is to do the above, but release the current version tagged on the master branch (but not to CRAN). This assumes that everything looks good to you @jejoenje and that both of us can run our code with no apparent issues. That can be version 0.5.0.0, with the changes from @AdrianBach, Lovisa's crane model, and the landscape consumption included. A new version 0.6.0.0 could then include:

  1. Some sort of new landscape option with a block or polygon design (to be determined)
  2. Movement of the res_mover call to the end of the resource submodel, somewhere around L600. This addresses the decreased efficacy of scaring potentially caused by high resource movement (see my comments on Lovisa's most recent draft). It also allows resources to consume the landscape first, then find a new cell, eat, search, etc., resolving the annoyance of having resources move once first unproductively. I still think that the feeding and moving lines in the resource_feeding function should be swapped in this case.
  3. A fix for how castration and feeding work from the users' perspective, where perhaps lambda is augmented by how much survival or reproduction would be incremented by complete feeding on a cell (all real numbers allowed for this).

I feel like I'm forgetting something, but does this sound good? Anyone want to give an R function for number 1 a go? I can do numbers 2 and 3. All in all, these changes would technically make existing models run a bit differently, but almost certainly not in a way that would affect anyone's results. The version 0.6.0.0 would then require testing of course, but that could be the version sent to CRAN rather than what we have so far.

jejoenje commented 4 years ago

@bradduthie Sounds good re. releasing 0.5 on master. However, I'm just running simulations with the current dev version now, so would it make sense finish doing that? That should give us a good idea of the consequences of both the new resource consumption functions. I should be able to show some comparisons of at least population trajectories of this later today or tomorrow.

As for (1) above I'm looking into this, but I'm finding it much less straightforward to do this than I expected. As discussed all existing tesselation algorithm implementations are inside external packages, and we want to avoid dependencies. I've not found a straightforward R or C implementation of something that we could "bake" into GMSE, and it may be overkill anyway. I'll keep at it, but if this is not needed for a 0.5 release on master I think I have some time?

bradduthie commented 4 years ago

Sounds good on the simulations @jejoenje -- I'll wait until things look good on your end too. Re-running the vignettes should also help confirm that everything is working correctly (I also wrote a testthat test that loops gmse_apply, which takes few seconds but I think is worth it since there are a lot of ways for changes to the code to make looping gmse_apply go wrong).

Yeah, no hurry on a solution for v0.6.0.0 to CRAN. I'm glad I'm not the only one that finds this a bit more challenging than it seems on the surface. One thought I had was to apply solutions used to create voting districts that prevent gerrymandering.

I actually just found a video from CGP Grey that very nicely describes something called the Shortest-Splitline Algorithm. Just by replacing 'people' with 'cells', I think this might get us what we want.

jejoenje commented 4 years ago

@bradduthie I've now finished several sets of simulations, varying user and manager budgets and landownership, both using GMSE 0.4.0.13 and the dev version 0.5.0.0. You can see these here; in a nutshell, the simulations across all scenarios seem to behave in the same way in 0.5.0.0 as they did in 0.4.0.13, although clearly the parameters I chose for times_feeding, consume_surv, and consume_repr allow for rather rapid population expansion in my scenarios 2-5. I will need to address this if I will be using 0.5.0.0 with the new consumption parameters, but that is purely a concern for my implementation. Bottom line, if you compare Sections 1 and 4 in my text, the "default" parameterisation produces the same output as before, so we're all good I think.

bradduthie commented 4 years ago

Many thanks for this @jejoenje -- it's reassuring that everything seems to behave in the same way across versions. I don't think I have a handle yet on how to use the new parameter values most effectively (i.e., what we should consider 'high' versus 'low' values). Eventually we might want to build in things like time lags.

Okay, I'll merge this with the master branch soon and add a tag so that GMSE v0.5.0.0 is a milestone version that we can use, even though it will (I hope) be quickly superseded by v0.6.0.x with the added features that we have been discussing here. This next version will represent a break from complete backwards compatibility (though will, for nearly all intents and purposes, still give the same results given the same parameter values).

bradduthie commented 4 years ago

GMSE v0.5.0.0 now up on GitHub. It just occurred to me that the last thing that should be done is to update the gmse_gui with the new arguments. The GUI still works fine; I'll just leave those updates to the next version.

bradduthie commented 4 years ago

As long as we're changing things, I think that v0.6.0.0 might be a good time to re-evaluate how the users perceive scaring actions. Currently, they assume that scaring a resource will remove it from their land, and that scaring therefore has the same effect as culling. But this seems unrealistic for a farmer who owns half the landscape. I think it's reasonable to assume that people don't have access to the age, consumption, or reproductive output of a goose, e.g., but a farmer is going to know how much land they own. I think that the perceived benefit of scaring should probably be scaled to this; that is, to the probability that a scared resource lands back on an owned cell. So, for example, a farmer that owns 25 per cent of the land might only perceive the benefit of scaring to be 1 * (1 - 0.25). This can be implemented fairly easily because there are currently unused columns in the agent array, meaning that I don't need to go through the hassle of changing the array's size. It's also elegant in that if land_ownership = FALSE, then users will not see the point of scaring at all.

Adjustments to the perceived effects can be made by altering the fitness function, which I think could use a re-think anyway (killing should probably also include effects of castration, which have previously been separated from the user's perspective).

bradduthie commented 4 years ago

Okay, I think I've figured out a cool recursive way to do the shortest-splitline algorithm and break the landscape up into neat boxes fairly quickly. I'll give it a shot the next time I sit down to code.