Homework 6: Population Simulation with Classes

Goals

Completing this assignment will help you practice:

Why? You’ll often find yourself working with code written by someone else and wanting to extend it, while cleaning it up, documenting it, and fixing some bugs. That’s exactly what we’ll be practicing here! At the same time, you’ll practice with some object-based programming concepts.

Strategy: Even though Step 0 doesn’t have much to “turn in”, I recommend spending some time there. You don’t need to understand every symbol of every line of code, but pay attention to the specific things that are noted there.

Background

Back in Homework 4 we made a population simulation. The code was complex, in part because the behavior of each animal was not encapsulated. In this homework, we’re going to adapt that simulation to use classes.

We’ve actually already done most of the work for you, but there’s still a few things we didn’t get to adding yet, so you’ll do the rest!

Step 0: Understanding

Step 1: __str__ special method

On your Shell, try creating an Animal instance, assigned to a variable named animal1. Then run print(animal1). Notice that the output doesn’t give you any useful information about that animal.

Checklist:

Step 2: Add dying of poor health

The code is missing one feature from the previous version: animals had a health that randomly changed each time step, and animals with negative health died. Let’s add this logic back.

To test this code, temporarily initialize all health values to a very negative number. Check that the population becomes empty in a single time step, and that the old-age counter plus the poor-health counter sum to NUM_INITIAL_ANIMALS.

CHecklist:

Step 3: Reproduction

Checklist:

Step 4: Counting births

Checklist:

Step 5: Plotting age (and finding a bug!)

The update_population method in DayStats gets called after every time step to log stats about the current population. Currently it only logs the size of the population.

Notice that the plot now includes the average age also. You might not realize it from first glance, but that plot suggests that we have a bug in the simulation code.

Note: this is hard to see with the default scaling since all of the plots are on the same y scale. So: instead of stats_df.plot(), use stats_df.plot(subplots=True) – and make your plot window (by dragging the window borders) so you can see the details of the average age curve.

Temporarily set CHANCE_OF_REPRODUCING to a high value like 1.0. Set NUM_ITERATIONS down to something like 10. Observe the population explosion. With such rapid growth, most of the population should be very young… but it instead stays relatively constant, or even increases. Something is broken.

Debugging simulations can be quite tricky. Testing extremes like this is one important strategy to have in your tool belt. There’s no substitute for thinking about how things should act in your domain!

Temporarily add a print() statement in the Animal constructor to look at the age of each new offspring. Now do you see the problem?

Fortunately for you, in this case I’ve debugged it for you: the simulation initially creates animals with a range of ages… but then uses the same code to create offspring. So offspring end up with a wide range of age values instead of 0.

Checklist:

Step 6: Ensuring that newborns are newborns

Observe that the offspring age printouts are now all 0, and that your age curve drops over time. Success. Now remove the debugging stuff. Remove the temporary print(offspring.age) that you added earlier. And set CHANCE_OF_REPRODUCING back to 0.35 and NUM_ITERATIONS back to 50.

Checklist:

Step 7 (optional): Try something!

Grading

2 points for each non-optional step, 2 points for good variable names, comments, and docstrings.