Harsh islands

Let's add harsh islands where, each turn, weather conditions change, killing animals below a certain level of skills. Hypothetically, that should add space for the luckier and more skillful ones, which, in turn, will result in a natural selection phenomenon—as baby's survival skill is dependent on the parent's survival skill, the survival skill of the entire population, on average, will start to grow. That's the theory—let's check if that will work, and if it will, how fast this improvement will be.

The good news is, we don't need to add much code. We only need to introduce this new type of island by inheriting from the original code and add just a few lines of code. Take a look at the following snippet. Here, we're inheriting from the Island object. As we have to add one more value to the initialization class, we use **kwargs and run the super method. By doing so, we are executing the Island object's initialization with all the corresponding values (passed via kwargs), but also storing the env_range as the object's attribute. This parameter will define the bounds of how harsh or peaceful the weather can be:

class HarshIsland(Island):
'''same as Island, except
has harsh conditions within [e_min,e_max] interval.
Rabbits with survival skill below the condition die at the
beginning of the epoch
'''

def __init__(self, env_range, **kwargs):
self.env_range = env_range
super().__init__(**kwargs)

One caveat is that the init function is now less readable and transparent—we won't see the original parameters upon calling the help function. It will suffice for now, but we shall keep that in mind for future class design.

Next, we will introduce the weather function. We compute the random integer within the range we passed earlier, representing weather severity, and keep only the animals with a survival skill equal to or greater than the result in the following code:

    def _compute_env(self):
condition = random.randint(*self.env_range)
self.animals = [a for a in self.animals if a.survival_skill >=
condition]

Finally, we need to somehow incorporate this function into the flow. Here, we can again reuse super—all we need is to override the _simulate method, adding the function we just wrote—and only then execute the original simulation:

    def _simulate(self):
self._compute_env()
super()._simulate()

But why are we overriding the existing method? Because it is already in use by the other methods—namely compute_epoches. Once we overwrote the method, compute_epoch will run it instead. In other words, our HarshIsland is ready.

You might need to restart the Notebook to be able to import new code from the static .py files if you change them after the first import.

Now we can import this new class and run a simulation over hundreds of instances in the Notebook. As before, we specified the parameters (exactly the same, but with the addition of the env_range parameter). Everything else is also the same—except, of course, that we initiate the new class instead of the old one:

from animals import HarshIsland

params = {'initial_pop':10, 'max_pop':100, 'env_range':[20,80]}
years, N_islands = 15, 1000

h_islands = [HarshIsland(**params) for _ in range(N_islands)]
h_stats = [ island.compute_epoches(years) for island in h_islands]

It seems that the code runs without any issues. But what do we get as a result? And did the animals survive? Comparing statistics for 200 islands is tough. In order to have at least some understanding, we need to visualize them in charts in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.210.133