EX04 - River Simulation

For this exercise, you are going to practice defining and instantiating classes to simulate a small river ecosystem with bears and fish! You will see that some “skeleton code” has been set up for you to fill in.

Part I

0. Pull the skeleton code

You will find the starter files needed by “pulling” from the course workspace repository. Before beginning:

  1. Ensure you are in your course workspace. Open the file explorer and you should see your work for the course. If you do not, open your course workspace through File > Open Recent.
  2. Open the Source Control View by clicking the 3-node (circles) graph (connected by lines) icon in your sidebar or opening the command palatte and searching for Source Control.
  3. Click the Ellipses in the Source Control pane and select “Pull” from the drop-down menu. This will begin the pulling process from the course repository. It should silently succeed.
  4. Return to the File Explorer pane and open the exercises directory. You should see it now contains another directory named EX04. If you expand that directory, you should see the starter files for the code you’ll be writing.
  5. If you do not see the EX04 directory, try once more but selecting "Pull From" and select origin in step 2.

Troubleshooting

If you’re having trouble pulling:

  • Make sure you PUSHED all of your changes to backup first!!!
  • In your terminal, type git config pull.rebase false
  • In your Visual Studio command center, select Pull From... -> Upstream -> Upstream/Head. (If not an option, do Origin -> Main.)

If you’re still having issues, please come to office hours!

1. Create Bear Class

All of the work for this part will be done in the file bear.py.

1.1 class Bear attributes

For this part, you are going to define and initialize a class to represent the Bears living by the river. In the file bear.py, you will see a class definition with the name Bear. Give it the attributes age and hunger_score which will both be integers. (You won’t use hunger_score in Part I, but it’ll be useful later!)

1.2 Bear#__init__

Within the Bear class, modify __init__ so that it initializes both self.age and hunger_score with the value 0.

2. Create Fish Class

Now, we are going to define a class to represent the fish in the river.

2.1 class Fish attributes

In the fish.py file, there will be a class definition called Fish. Like Bear, give it an age attribute, which will be an integer. (It does NOT need a hunger_score attribute.)

2.2 Fish#__init__

Within the Fish class, modify __init__ so that it initializes self.age with the value 0.

3. Create River Class

Now that you have some animals, you can create and populate a river!

3.1 class River attributes

In river.py, give the class River the following attributes:

  • day: an int that tells you what day of the river’s lifecycle you are modeling
  • bears: a list of Bear objects – each representing a different bear – to represent the river’s bear population
  • fish: a list of Fish objects to represent the river’s fish population

The __init__ method is already defined for you. It takes as parameters: self, num_fish: int, num_bears: int and does the following:

  • initializes self.fish to contain num_fish many Fish
  • initializes self.bears to contain num_bears many Bears
  • initializes self.day to be 0

3.2 Create a new River

Open the river_simulation.py module. In this file, you will be modeling daily life in this river ecosystem. Construct a River object named my_river with 10 Fish and 2 Bears. (Don’t forget to import the River class!)

3.3 view_river() method

Now, you are going to modify the existing view_river method inside your River class. Initially, this method just returns None, but you will modify it to print the status of the bear and fish populations in a given River object.

Define

Modify view_river such that:

  • It takes no parameters other than self and returns None.

  • It should print the following:

~~~ Day x: ~~~ Fish population: y Bear population: z

Where x is the current day of the river simulation, y is the number of Fish in the river, and z is the number of Bears in the river.

Call the method

At the bottom of river_simulation.py, try viewing your River object by calling my_river.view_river().

When you run python -m exercises.EX04.river_simulation, the output should be:

~~~ Day 0: ~~~ Fish population: 10 Bear population: 2

4. Simulating one day and one week in the River

Now that you have a River with Bears and Fish, you can model how their populations update day-to-day.

Bear and Fish each have their own one_day method. Modify them so they have the following behaviors:

4.1 Bear#one_day()

The method one_day() in the Bear class takes no parameters other than self and does not return anything. It should increase the value of the age attribute by 1.

4.2 Fish#one_day()

The method one_day() in the Fish class takes no parameters other than self and does not return anything. It should increase the value of the age attribute by 1.

4.3 River#one_river_week() method

The method one_river_day() in the River class is defined for you. It’ll do more once you’ve finished Part II, but notice that it calls the one_day() method for both the Fish and the Bears.

Within the River class, define a method one_river_week() that takes no parameters other than self and does not return anything. It should simply call one_river_day() seven times.

At the bottom of river_simulation.py, test out the functionality of one_river_week by calling it. (You’ll notice a pretty boring output. Don’t worry, in Part II we will model population changes!)

Part II

For Part II we will represent the circle of life… you will be simulating the life cycle of both the Bears and the Fish in the River!

1. Removing Animals

1.1 River#check_ages()

As animals reach an old age and pass away, they should be removed from the River. Modify the check_ages method so it has the following functionality: If a Fish’s age is > 3, it should be removed from fish. If a Bear’s age is > 5, it should be removed from bears. (Note that fish and bears can both live much longer than this! We just don’t want this simulation to go on for ages.)

Implementation hint: You don’t want to be removing things from a list while you’re looping through it. Instead, create a new list[Bear] and copy all surviving Bears over to that list rather than removing Bears directly from self.bears. Then update self.bears to be equal to that copied list. Do the same thing for self.fish.

1.2 River#remove_fish()

Within the River class, create a remove_fish method that has self and amount: int as parameters and returns nothing. It should remove amount many Fish from the River. You should remove the FRONTMOST Fish. (The “front” being the Fish starting at index 0.)

2. Modelling Bear’s Hunger

Now, we’ll simulate the circle of life. A bear’s gotta eat! This will involve modifying the one_day() method in the Bear class and making a new eat() method in the Bear class. Let’s do these one at a time:

2.1 Modifying Bear#one_day()

Modify the method one_day() in the Bear class such that the hunger_score attribute decreases by 1 each day.

2.2 Defining the Bear#eat() method

Now, you are going to create a new method in the Bear class called eat(). It has two parameters, self and num_fish: int and returns nothing. It should update the Bear’s hunger_score so that it increases by num_fish. (For example, if num_fish = 2, a Bear ate 2 fish, so its hunger_score should increase by 2.)

2.3 Modifying River#bears_eating()

Modify the bears_eating() method, so that, for each Bear, if there are at least 5 Fish in the river, the Bear will eat 3 Fish. This involves removing 3 Fish from the river using the remove_fish method and calling eat() with the number of fish the Bear eats.

2.4 Modifying River#check_hunger

Unfortunately, if a Bear gets hungry enough, it’ll starve. Modify the check_hunger method so that it checks the hunger_score of every Bear in the river. If hunger_score < 0, then remove the Bear from the river.

Implementation hint: You don’t want to be removing things from a list while you’re looping through it. Instead, create a new list[Bear] and copy all surviving Bears over to that list rather than removing Bears directly from self.bears. Then, update self.bears to be equal to that copied list.

3. Modelling Reproduction

Now we are going to model the reproduction of the Bears and the Fish!

3.1 Modifying River#repopulate_bears

Modify the repopulate_bears method to add the following functionality: each pair of Bear’s will produce 1 offspring. In other words, if there are 2 bears, 1 new bear will be born and added to bears. To generalize, for n bears, there will be n//2 new Bears added to bears.

3.2 Modifying River#repopulate_fish

Modify the repopulate_fish method to add the following functionality: each pair of fish will produce 4 offspring. In other words, if there are 2 fish, 4 new fish will be born and added to fish. To generalize, for n fish, there will be (n//2) * 4 new Fish added to fish.

4. Make a Backup Checkpoint “Commit”

As you make progress on this exercise, making backups is encouraged.

  1. Open the Source Control panel (Command Palette: “Show SCM” or click the icon with three circles and lines on the activity panel).
  2. Notice the files listed under Changes. These are files you’ve made modifications to since your last backup.
  3. Move your mouse’s cursor over the word Changes and notice the + symbol that appears. Click that plus symbol to add all changes to the next backup. You will now see the files listed under “Staged Changes”.
    • If you do not want to backup all changed files, you can select them individually. For this course you’re encouraged to back everything up.
  4. In the Message box, give a brief description of what you’ve changed and are backing up. This will help you find a specific backup (called a “commit”) if needed. In this case a message such as, “Progress on Exercise” will suffice.
  5. Press the Check icon to make a Commit (a version) of your work.
  6. Finally, press the Ellipses icon (…), look for “Pull/Push” submenu, and select “Push to…”, and in the dropdown select your backup repository.

5. Submit to Gradescope for Grading

Log in to Gradescope and select the assignment named “EX04 - River Simulation”. You’ll see an area to upload a zip file. To produce a zip file for autograding, return back to Visual Studio Code.

If you do not see a Terminal at the bottom of your screen, open the Command Palette and search for “View: Toggle Integrated Terminal”.

To produce a zip file for EX04, type the following command (all on a single line):

python -m tools.submission exercises/EX04

In the file explorer pane, look to find the zip file named “yy.mm.dd-hh.mm-exercises-EX04.zip”. The “mm”, “dd”, and so on, are timestamps with the current year, month, day, hour, minute. If you right click on this file and select “Reveal in File Explorer” on Windows or “Reveal in Finder” on Mac, the zip file’s location on your computer will open. Upload this file to Gradescope to submit your work for this exercise.

Autograding will take a few moments to complete. For this exercise there will be points manually graded for style – using meaningful variable names and snake_case. If there are issues reported, you are encouraged to try and resolve them and resubmit. If for any reason you aren’t receiving full credit and aren’t sure what to try next, come give us a visit in office hours!

Contributor(s): Alyssa Byrnes, Sophie Jiang, Izzi Hinks