Goals ¶
Completing this assignment will help you practice:
- Working with classes and instances, including concepts like
selfand special methods - Understanding and modifying existing code
- Debugging existing code
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 Lab 4 we made a population simulation. The code was complex, in part because the behavior of each individual 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.
Step 0: Understanding ¶
Step 1: Individual class ¶
On your Shell, try creating an Individual 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.
Step 2: Plotting ¶
Step 3: Counting births ¶
Step 4: 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. The average stays pretty flat… is that right?
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(), usestats_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 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 individuals 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.
Step 5: Ensuring that newborns are newborns ¶
Observe that the offspring age printouts are now all 0, and that your age curve drops over time. Success. 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.
Step 6: Add dying of poor health ¶
The code is missing one feature that it had in Lab 4: individuals had a health that randomly changed each time step, and individuals 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_INDIVIDUALS.
Grading ¶
2 points for each step, 2 points for good variable names, comments, and docstrings.