CS计算机代考程序代写 python HW-03_OOP+ABM-STUDENT-checkpoint

HW-03_OOP+ABM-STUDENT-checkpoint

Homework Assignment #3 (Individual)¶
Simulating populations using OOP and ABM (135 pts total, 135 pts == 100%)¶

✅ Put your name here.

✅ Put your _GitHub username_ here.

Rock-Paper-Scissors(-Lizard-Spock)¶
Goal for this homework assignment¶
By now, you have learned OOP and ABM through the assignments of assembling Zoo and Superbugs, respectively. Let us use what you learned to build a simple model of population competition between three different species in a simple model built after Rock-Paper-Scissors.

This assignment is due roughly two weeks from now at 11:59 pm on Sunday, October 24. Note: Due to the proximity of the standard Friday homework deadline to the midterm, we’re giving you an extra couple days, but keep in mind there will NOT be helproom hours during the weekend, so try to start early enough to get help with the assignment!

When you’re finished, it should be uploaded into the “Homework Assignments” submission folder for Homework #3. Submission instructions can be found at the end of the notebook. The distribution of points can be found in the section headers.

At the end of the assignment, if everything went as intended, you should have a population evolution plot like the one below.

This plot should help you to determine if your code is headed in the right direction!

Part 1: Add to your Git repository to track your progress on your assignment (5 points)¶
As usual, for this assignment, you’re going to add it to the cmse202-f21-turnin repository you created in class so that you can track your progress on the assignment and preserve the final version that you turn in. In order to do this you need to

✅ Do the following:

Navigate to your cmse202-f21-turnin repository and create a new directory called hw-03.
Move this notebook into that new directory in your repository, then add it and commit it to your repository.
Finally, to test that everything is working, “git push” the file so that it ends up in your GitHub repository.

Important: Make sure you’ve added your Professor and your TA as collaborators to your “turnin” respository with “Read” access so that we can see your assignment (you should have done this in the previous homework assignment)

Also important: Make sure that the version of this notebook that you are working on is the same one that you just added to your repository! If you are working on a different copy of the noteobok, none of your changes will be tracked!

If everything went as intended, the file should now show up on your GitHub account in the “cmse202-f21-turnin” repository inside the hw-03 directory that you just created. Periodically, you’ll be asked to commit your changes to the repository and push them to the remote GitHub location. Of course, you can always commit your changes more often than that, if you wish. It can be good to get into a habit of committing your changes any time you make a significant modification, or when you stop working on the project for a bit.

Do this: Before you move on, put the command that your instructor should run to clone your repository in the markdown cell below.

# Put the command for cloning your repository here!

Part 2: Load necessary modules¶
Execute the next two Code cells to load python packages for math and visualization functions.

In [ ]:

import random
import numpy as np
import math
import matplotlib.pyplot as plt
import time
from IPython.display import display, clear_output

Part 3: Problem Statement¶
You are creating a simple ecosystem containing three types of populations (you can imagine these to be animals, bacteria or something similar) on the computer.

In your environment, there are three different animals: “Rock Animals”, “Paper Animals” and “Scissors Animals”. ==> You will create a generic Animal class and derived classes for each of the specific animals.
In our model, animals will not move from where they start.
The animals reproduce. ==> All derived objectcs will have a method of reproduce/duplicate/clone over a given period. Their offspring will appear in a neigboring spot.
Each animal has a certain energy level. If an animal reproduces, it will first lose 1 unit of energy and split into two: it will keep half of its energy for itself and give its offspring the other half. ==> You will write a reproduce method that will find an empty neighboring spot in the world, creates a clone of itself (same animal type) and assigns it to that empty spot.
The environment has a boundary. ==> No animals can exist outside of this boundary.
Animals will be able to consume each other in a cyclical way modeled after Rock-Paper-Scissors. ==> You will write code that allows them to “eat” a certain kind of different animal only. “Rock ‘eats’ (beats) scissors”, “scissors beats paper” and “paper beats rock”.
Note here your main tasks are creating a generic Animal object and an object for each animal type that derives from Animal. The code to verify the created classes and run simulations is already functioning. No need to modify them. You are encouraged to take a look of those code to ensure your objects will be compatible with them before you create the objects.

Part 3.1 Animal object (15 pt)¶
Step by step. Let’s start with creating an Animal object. The object should contain the attributes of

Energy: An energy level represented by a positive floating point number. The class should initialize this number to a starting energy that is given as an argument to the class.
Important: Add Docstrings to explain your code. Without any Docstrings, your score will be compromised. (5 pt for docstrings, 10 pt for the code)
You can find an outline of the class below. You will need to implement __init__ and get_energy(). The __init__ method has one argument (besides self) called starting_energy. You should make sure that this value is stored as an attribute called self.energy and that get_energy() returns the current value of the attribute.

Note that the class given below contains more stub functions (get_rgb, get_kind_name, can_eat, clone, eat, and reproduce). You do not have to change them at this point and do not need to provide docstrings for them right now.
Finally, create a Animal object called animal and set its initial energy to 100 .

So, to summarize, in the cell below, do this:

Implement the class constructor __init__ as outlined above
Make sure that the constructor initializes an attribute called self.energy and sets its value to starting_energy
Implement the function get_energy() to return the current energy
Add Docstrings to the overall class, the constructor and to get_energy()

In [ ]:

### Modify the code below as explained.
### Start with the class skeleton provided and implement the constructor and `get_energy()`.
### Also add Docstrings to these two functions and to the class overall (no need for docstrings for the other stub functions).

class Animal:
def __init__(self, starting_energy):
pass # IMPLEMENT THIS!

def get_energy(self):
pass # IMPLEMENT THIS!

def get_rgb(self):
pass # to be implemented by derived classes

def get_kind_name(self):
pass # to be implemented by derived classes

def can_eat(self, other_animal):
pass # to be implemented by derived classes

def clone(self):
pass # to be implemented by derived classes

def eat(self, env, neighbor_spots):
pass # will be implemented later

def reproduce(self, env, neighbor_spots):
pass # will be implemented later

### IMPLEMENT THIS: Add code here to create an Animal object called `animal` here and set its initial energy to 100.

Now do this: Now that your class is defined, create an instance of the Animal class with an energy of 100.

In [ ]:

### In this cell, do this: create an instance of the `Animal` class with an energy of 100.

Once your code is implemented, running the following cell should print “The animal has an energy of 100.”¶

In [ ]:

energy = animal.get_energy()
print(“The animal has an energy of {0}.”.format(energy))

🛑 STOP¶
Pause to commit your changes to your Git repository!

Take a moment to save your notebook, commit the changes to your Git repository using the commit message “version 1 of Animal”, and push the changes to GitHub.

Part 3.2 Specific animal objects derived from Animal (25 pt)¶
In this section you will create one of three classes. Each of the classes will be derived from the Animal class you defined previously. We will start with one of these classes first.

Create a class called Rock_Animal and derive it from Animal. This means that Rock_Animal should inherit the Animal class.
Implement (and therefore override the version from the Animal class) the following methods from Animal in your new derived object, Rock_Animal: get_rgb(self): This method should return a tuple made of three floating point numbers defining the RGB color that this animal will be represented by. For example, for red you would return (1.,0.,0.). Choose a color you like. You will use two more colors later on. You are free to choose three different colors, such as just red, green and blue, but there are other color palettes out there that might be more suitable for anyone who might be color blind. There are many resources on the internet on this, one example that might be useful is this web page. The color scale you choose will not be graded, of course.

get_kind_name(self): This method should return the name of the kind of animal. You can choose any name you like, but in this case something like “rock” might be a good idea.
can_eat(self, other_animal): This method should return a boolean value (True or False). Its argument other_animal will be another instance of a class derived from Animal. If the other animal is one that we can eat it should return True, otherwise it should return False. You might want to call get_kind_name() from this function and check the name of the other animal. If it is “scissors”, we can eat it, otherwise we cannot (“rock beats scissors”).
clone(self): This method should return a new instance of the Rock_Animal class, initialized to half the energy the current we currently have. It should also reduce the current energy of the Rock_Animal itself by half. (Thus simulating “splitting” the animal into two pieces of the same kind with half the energy as the original one/providing half of its energy to its offspring.) [Note that it is not necesary to deal with the part where animals lose “1 unit of energy” before they reproduce in clone() itself as this will be taken care of later by the reproduce() function.] ==> So clone should reduce the current energy by half then create a new Rock_Animal and inititalize that new animal to the new, halved, energy level. This new Rock_Animal should then be returned by the clone function.

(Note: We will deal with defining the eat() and reproduce() functions from the Animal class later on. These functions will be part of the Animal class, but you can ignore them for now. Specifically, they should not be part of your derived class.)

In [ ]:

### put your code of creating the derived class `Rock_Animal` in this cell

Part 3.3 Create two more animal classes (25 pt)¶
Now, create two more classes, one of them called Paper_Animal and one of them called Scissors_Animal. They should look like Rock_Animal previously with these differences:

Each of them should have a different color from each other and different from Rock_Animal.
Their names returned by get_kind_name() should be different for each class. Use “scissors” for Scissors_Animal and “paper” for Paper_Animal.
The can_eat() function needs to be updated to follow the “rock-paper-scissors” rules: rock beats (“can eat”) scissors
scissors beats (“can eat”) paper
paper beats (“can eat”) rock

Each of the clone() functions needs to return an animal object of the same kind. So make sure that clone() for Scissors_Animal actually returns a Scissors_Animal instance. The rules for energy splitting are the same as before.

In [ ]:

### put your code of creating the derived classes `Paper_Animal` and `Scissors_Animal` in this cell (10 pt)

Now run the cell below. It should finish without error. You can see it testing some of the assumption we made above and some of the intital definitions as a “unit tests”, each one a test for a small logical unit of code. If everything works as expected, none of the assert statements will produce any output. There is a print statement at the end of the cell that, once reached, will just print a message that everything worked. Each assert line below checks a certain assumption. If your classes are implemented as expected, none of them should fail. If one does fail, try to go back and correct your code above accordingly.

In [ ]:

# test 01 – create objects
rock = Rock_Animal(100)
paper = Paper_Animal(100)
scissors = Scissors_Animal(100)

# test 02 – are the classes derived from `Animal`?
assert isinstance(rock, Animal), “Rock_Animal is not derived from Animal”
assert isinstance(paper, Animal), “Paper_Animal is not derived from Animal”
assert isinstance(scissors, Animal), “Scissors_Animal is not derived from Animal”

# test 03 – make sure the energy is 100
assert abs(rock.get_energy() – 100.) < 1e-5, "The energy of the rock animal should be 100" assert abs(paper.get_energy() - 100.) < 1e-5, "The energy of the paper animal should be 100" assert abs(scissors.get_energy() - 100.) < 1e-5, "The energy of the scissors animal should be 100" # test 04 - make sure that the objects have an attribute called `energy` and that the value is the same as the value returned by `get_energy()` assert rock.energy == rock.get_energy(), "rock: self.energy is not an attribute of the class or its value is different from what `get_energy()` returns" assert paper.energy == paper.get_energy(), "paper: self.energy is not an attribute of the class or its value is different from what `get_energy()` returns" assert scissors.energy == scissors.get_energy(), "rock: self.energy is not an attribute of the class or its value is different from what `get_energy()` returns" # test 05 - make sure that the rgb colors are in a valid form assert len(rock.get_rgb()) == 3, "The RGB color for `rock` should have 3 entries" assert (rock.get_rgb()[0] >= 0.) and (rock.get_rgb()[0] <= 1.), "The R component ([0]) of the color for `rock` needs to be a value between 0 and 1" assert (rock.get_rgb()[1] >= 0.) and (rock.get_rgb()[1] <= 1.), "The G component ([1]) of the color for `rock` needs to be a value between 0 and 1" assert (rock.get_rgb()[2] >= 0.) and (rock.get_rgb()[2] <= 1.), "The B component ([2]) of the color for `rock` needs to be a value between 0 and 1" assert len(paper.get_rgb()) == 3, "The RGB color for `paper` should have 3 entries" assert (paper.get_rgb()[0] >= 0.) and (paper.get_rgb()[0] <= 1.), "The R component ([0]) of the color for `paper` needs to be a value between 0 and 1" assert (paper.get_rgb()[1] >= 0.) and (paper.get_rgb()[1] <= 1.), "The G component ([1]) of the color for `paper` needs to be a value between 0 and 1" assert (paper.get_rgb()[2] >= 0.) and (paper.get_rgb()[2] <= 1.), "The B component ([2]) of the color for `paper` needs to be a value between 0 and 1" assert len(paper.get_rgb()) == 3, "The RGB color for `scissors` should have 3 entries" assert (scissors.get_rgb()[0] >= 0.) and (scissors.get_rgb()[0] <= 1.), "The R component ([0]) of the color for `scissors` needs to be a value between 0 and 1" assert (scissors.get_rgb()[1] >= 0.) and (scissors.get_rgb()[1] <= 1.), "The G component ([1]) of the color for `scissors` needs to be a value between 0 and 1" assert (scissors.get_rgb()[2] >= 0.) and (scissors.get_rgb()[2] <= 1.), "The B component ([2]) of the color for `scissors` needs to be a value between 0 and 1" # test 06 - make sure the colors are different assert rock.get_rgb() != paper.get_rgb(), "Rock and Paper colors are the same!" assert rock.get_rgb() != scissors.get_rgb(), "Rock and Scissors colors are the same!" assert paper.get_rgb() != scissors.get_rgb(), "Paper and Scissors colors are the same!" # test 07 - make sure get_kind_name() returns a string assert isinstance(rock.get_kind_name(), str), "rock.get_kind_name() does not return a string" assert isinstance(paper.get_kind_name(), str), "paper.get_kind_name() does not return a string" assert isinstance(scissors.get_kind_name(), str), "scissors.get_kind_name() does not return a string" # test 08 - make sure get_kind_name() return *different* strings assert rock.get_kind_name() != paper.get_kind_name(), "Rock and Paper kind names are the same!" assert rock.get_kind_name() != scissors.get_kind_name(), "Rock and Scissors kind names are the same!" assert paper.get_kind_name() != scissors.get_kind_name(), "Paper and Scissors kind names are the same!" # test 09 - make sure that the "rock paper scissors" rules are followed when calling can_eat() assert not rock.can_eat(rock), "rock *can* eat rock" assert not rock.can_eat(paper), "rock *can* eat paper" assert rock.can_eat(scissors), "rock *can not* eat scissors" assert paper.can_eat(rock), "paper *can not* eat rock" assert not paper.can_eat(paper), "paper *can* eat paper" assert not paper.can_eat(scissors), "paper *can* eat scissors" assert not scissors.can_eat(rock), "scissors *can* eat rock" assert scissors.can_eat(paper), "scissors *can not* eat paper" assert not scissors.can_eat(scissors), "scissors *can* eat scissors" # test 10 - make sure that clone() returns an object of the same kind assert isinstance(rock.clone(), Rock_Animal), "rock.clone() does not return an instance of Rock_Animal" assert isinstance(paper.clone(), Paper_Animal), "paper.clone() does not return an instance of Paper_Animal" assert isinstance(scissors.clone(), Scissors_Animal), "scissors.clone() does not return an instance of Scissors_Animal" # test 11 - make sure that the energy levels returned are correct (should be 25 now) assert abs(rock.clone().get_energy() - 25.) < 1e-5, "The energy of the animal retruned by rock.clone() should be 25" assert abs(paper.clone().get_energy() - 25.) < 1e-5, "The energy of the animal retruned by paper.clone() should be 25" assert abs(scissors.clone().get_energy() - 25.) < 1e-5, "The energy of the animal retruned by scissors.clone() should be 25" # If the code gets here, everything works as excepted print("All checks passed, everything is working as expected.") The cell above uses assert to check assumptions. In the following markdown cell: Explain how assert works in python and how it can help make sure your code does the right thing. This is a new concept that you might have to do a bit of documentation reading and Google searching to understand. Remember, learning new skills independently is one of the skills we work to hone in 202! Also explain why the last test (number 09) checks for an energy of 25 instead of 50. (The initial energy is 100 and clone() returns an object that has half of the energy of the initial object.) (10 pt) ✎ Do This - Erase the contents of this cell an put your answer here. The tests related to energy use abs() and a difference between the expected and returned value and check if it is smaller than a very small number. See if you can find a reason why it doesn't just use the == operator here. Explain the reason in the markdown cell below. (5 pt) ✎ Do This - Erase the contents of this cell an put your answer here. 🛑 STOP¶ Pause to commit your changes to your Git repository! Take a moment to save your notebook, commit the changes to your Git repository using the commit message "Three Animal kinds", and push the changes to GitHub. Part 4 Run the model (30 pt)¶ We will now define a class called Environment which will be used to run the simulation. This class is pre-defined and you will not need to change it. It makes use of the "animal" classes you wrote previously. In [ ]: ## Run this cell to define the Environment class class Environment: def __init__(self, animal_kinds = [Rock_Animal, Paper_Animal, Scissors_Animal], num_animals_initial=1000, size_x=100, size_y=100, padding=20, inital_animal_energy=100): self.size_x = size_x self.size_y = size_y self.inital_animal_energy = inital_animal_energy self.animal_grid = [ ([None] * self.size_x) for row in range(self.size_y) ] self.population_history = [] self.population_colors = dict() for i in range(0,num_animals_initial): # choose a random space on the grid place_x = random.randint(padding, self.size_x-1-padding) place_y = random.randint(padding, self.size_y-1-padding) # if it is not empty, keep choosing new random spaces until we find an empty one while self.has_animal(place_x, place_y): place_x = random.randint(padding, self.size_x-1-padding) place_y = random.randint(padding, self.size_y-1-padding) # now choose one of the animal classes provided Chosen_Animal_Class = random.choice(animal_kinds) # instantiate the class to create an object and add it to the grid new_animal = Chosen_Animal_Class(starting_energy=inital_animal_energy) self.add_animal(place_x, place_y, new_animal) self.update_population_history() def update_population_history(self): new_entry_dict = dict() # make entries for all previous kinds that might have existed but are maybe now extinct for h in self.population_history: for kind_name in h.keys(): if kind_name not in new_entry_dict: new_entry_dict[kind_name]=0 for x in range(0,self.size_x): for y in range(0,self.size_y): if self.has_animal(x,y): this_animal = self.get_animal(x,y) kind_name = this_animal.get_kind_name() if kind_name not in self.population_colors: self.population_colors[kind_name] = this_animal.get_rgb() if kind_name not in new_entry_dict: new_entry_dict[kind_name]=0 new_entry_dict[kind_name]+=1 self.population_history.append(new_entry_dict) def add_animal(self, x, y, animal): self.animal_grid[y][x] = animal def remove_animal(self, x, y): self.animal_grid[y][x] = None def has_animal(self, x, y): return self.animal_grid[y][x] is not None def get_animal(self, x, y): return self.animal_grid[y][x] def is_inside_of_environment(self, x, y): if x < 0 or x >= self.size_x: return False
if y < 0 or y >= self.size_y: return False
return True

def number_total(self):
num=0
for x in range(0,self.size_x):
for y in range(0,self.size_y):
if self.has_animal(x,y):
num+=1
return num

def find_all_neighbors(self, x, y):
neighbor_spots = []
for other_x in [x-1, x, x+1]:
for other_y in [y-1, y, y+1]:
if self.is_inside_of_environment(other_x, other_y) and (not ((other_x==x) and (other_y==y))):
neighbor_spots.append( (other_x, other_y) )
return neighbor_spots

def life_cycle(self):
# make sure to go through the animals in random order

# this makes a shuffled list of animals on the grid in random order
def shuffled_animal_list():
animals = []
for x in range(0,self.size_x):
for y in range(0,self.size_y):
if self.has_animal(x,y):
the_animal = self.get_animal(x,y)
neighbor_spots = self.find_all_neighbors(x, y)
animals.append( (the_animal, neighbor_spots) )
# shuffle the list and return it
random.shuffle(animals)
return animals

# give all animals a chance to eat one of their neighbors
animals = shuffled_animal_list()
for the_animal, neighbor_spots in animals:
the_animal.eat(self, neighbor_spots)

# give all animals a chance to reproduce into empty spots next to them
animals = shuffled_animal_list()
for the_animal, neighbor_spots in animals:
the_animal.reproduce(self, neighbor_spots)

# update the statistics for plotting
self.update_population_history()

def draw_grid(self, ax):
color_grid = np.zeros((self.size_x, self.size_y, 3))

max_energy = self.inital_animal_energy

for x in range(0,self.size_x):
for y in range(0,self.size_y):
this_rgb = (0.0, 0.0, 0.0)
if self.has_animal(x,y):
the_animal = self.get_animal(x,y)
this_rgb = the_animal.get_rgb()
this_energy_mod = the_animal.get_energy()/max_energy
if this_energy_mod > 1.: this_energy_mod=1.
#this_rgb = ( this_rgb[0]*(this_energy_mod*0.8+0.2), this_rgb[1]*(this_energy_mod*0.8+0.2), this_rgb[2]*(this_energy_mod*0.8+0.2) )
color_grid[x, y, 0] = this_rgb[0]
color_grid[x, y, 1] = this_rgb[1]
color_grid[x, y, 2] = this_rgb[2]

ax.imshow(color_grid)

def draw_population(self, ax):
kinds = self.population_history[0].keys()

for kind in kinds:
this_population_array = []
for history_entry in self.population_history:
this_population_array.append(history_entry[kind])
ax.plot(range(len(this_population_array)), this_population_array, label=kind, color=self.population_colors[kind])

ax.set_ylabel(“Population”)
ax.legend()

This is a lot of code with sparse documentation. Let’s try to run it first using the next cell. Make sure this cell actually plots something. (5pts)

In [ ]:

env = Environment(animal_kinds = [Rock_Animal, Paper_Animal, Scissors_Animal],
num_animals_initial=200,
size_x=100, size_y=100,
padding=15,
inital_animal_energy=10000)

fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111)

env.life_cycle()
env.draw_grid(ax)

The output should show a plot like this:

We will now do a bit of code review of the Environment() class. All of the following written answers need to be at least a few sentences long in order to receive full credit.

Explain what is shown in the plot. (5 pt)

✎ Do This – Erase the contents of this cell an put your answer here.

Try to read and understand what the Environment() class does and how it works.

In the next cell, explain what the Environment constructor does in some detail. (You may need to remind yourself what part of the class the “constructor” is) (5 pt)

✎ Do This – Erase the contents of this cell an put your answer here.

Describe what the following 4 functions do, respectively: add_animal(), remove_animal(), has_animal(), get_animal(). (5 pt)

Again, make sure to provide the necessary detail here, including what their arguments are and what they do.

✎ Do This – Erase the contents of this cell an put your answer here.

Describe what the life_cycle() method does. (5 pt)

✎ Do This – Erase the contents of this cell an put your answer here.

Explain what the update_population_history() tries to achieve and how it does its job. (5 pt)

✎ Do This – Erase the contents of this cell an put your answer here.

Finally, describe if the simulation step performed will actually work in the current version of your code. If it does not, explain what is missing. (5 pt)

✎ Do This – Erase the contents of this cell an put your answer here.

🛑 STOP¶
Pause to commit your changes to your Git repository!

Take a moment to save your notebook, commit the changes to your Git repository using the commit message “Animals in an environment”, and push the changes to GitHub.

Part 5 Finalize the Animal class (25 pt)¶
Here, we will implement the two missing stub functions of the Animal class: eat() and reproduce()

def eat(self, neighbor_spots):
pass # will be implemented later

def reproduce(self, neighbor_spots):
pass # will be implemented later

Replace the eat() stub function with this code:

def eat(self, env, neighbor_spots):
# select only the neighboring spots that contain animals we can actually eat
neighboring_spots_with_animals_we_can_eat = []
for spot in neighbor_spots:
if env.has_animal(spot[0],spot[1]):
# only look if it has an animal, then get the other animal
other_animal = env.get_animal(spot[0],spot[1])
if self.can_eat(other_animal):
neighboring_spots_with_animals_we_can_eat.append( spot )

if len(neighboring_spots_with_animals_we_can_eat)==0:
# no animals there.. cannot eat
return

# an animal I can eat! eat it and gain its energy – Highlander rules

# choose a spot randomly
chosen_spot = random.choice(neighboring_spots_with_animals_we_can_eat)

# get the other animal and its energy
# TODO: << ... retrieve the "other" animal using `env.get_animal()` from the x-y coordinates given in `chosen_spot`, get its energy and store it in the variable `other_energy` ... >>

# eat the animal!
# TODO: << ... use the appropriate member function of the `Environment` `env` in order to remove the other animal in the spot given by `chosen_spot` ... >>

# increase our own energy
# TODO: << ... make sure to increase our own energy by the amount stored in `other_energy` ... >>

Replace the reproduce() stub function with this code:

def reproduce(self, env, neighbor_spots):
# can we reproduce? only possible if energy > 1
if self.energy <= 1: return # find an empty neighboring spot to reproduce to # make a list of neighboring spots as (x,y) tuples open_neighbor_spots = [] for spot in neighbor_spots: if not env.has_animal(spot[0],spot[1]): # if it doesn't have an animal in it already, it is available! open_neighbor_spots.append( spot ) if len(open_neighbor_spots)==0: # no space left, we cannot reproduce return # spend one unit of energy in order to reproduce # TODO: << ... Lower our own energy level by `1` ... >>

# choose one of the spots
the_neighboring_spot = random.choice(open_neighbor_spots)

# make a new animal by cloning ourself
# TODO: << ... use our own `clone()` method to create a copy (and split our energy in half). Store that clone in a variable called `the_new_animal` ... >>

# TODO: << ... Use the correct member function of the `Environment` class given in `env` to add the cloned animal (`the_new_animal`) to the environment at the x-y coordinates given by `the_neighboring_spot` ... >>

IMPORTANT NOTE: The code given above requires you to add some pieces at the points indicated by TODO.

Paste the previous version of Animal from the top of the notebook into the cell below, add the two implementations of the stub functions eat() and reproduce() given above and write the missing code replacing the TODO comment lines. (15 pt)

In [ ]:

## In this cell, paste the previous version of `Animal` from the top of the notebook into the cell below,
## add the two implementations of the stub functions `eat()` and `reproduce()` given above and finish the code by
## replacing the `TODO` comment lines with the missing parts of the implementation.

You now NEED to re-define the derived classes, since their base class has changed. Copy&paste your previous versions of Rock_Animal, Paper_Animal, and Scissors_Animal in the cell below. No changes to the derived classes should be necessary.

In [ ]:

### You now NEED to re-define the derived classes, since their base class has changed.
### Copy&paste your previous versions of `Rock_Animal`, `Paper_Animal`, and `Scissors_Animal`
### in the cell below. No changes to the derived classes should be necessary.

At this point, you should be able to run the full simulation of your environment. Run the cell below to see a simulation using 5 “life cycle” time steps. Make sure your code is working correctly and the simulation keeps updating. (10pt)

In [ ]:

env = Environment(
animal_kinds = [Rock_Animal, Paper_Animal, Scissors_Animal],
num_animals_initial=200,
size_x=100, size_y=100,
padding=15,
inital_animal_energy=10000)

## iterate over 5 time steps
for d in range(5):
clear_output(wait=True)

fig = plt.figure(figsize=(16, 8))
ax = fig.add_subplot(121)
bx = fig.add_subplot(122)

## call the roaming method and then draw here
env.life_cycle()
env.draw_grid(ax)
env.draw_population(bx)

plt.show()

time.sleep(0.001)

Your output should look somewhat like this:

Congratulations, you now have a fully working simulation!

🛑 STOP¶
Pause to commit your changes to your Git repository!

Take a moment to save your notebook, commit the changes to your Git repository using the commit message “Full Simulation”, and push the changes to GitHub.

Part 6. Running experiments. (10 pt)¶
You will now run a couple of experiments using the existing code.
First, copy the previous code cell and make it run for more than 5 steps (start with around 100-200 steps, increase the number if necessary). (5pt)

In [ ]:

### your code here

env = Environment(
animal_kinds = [Rock_Animal, Paper_Animal, Scissors_Animal],
num_animals_initial=200,
size_x=100, size_y=100,
padding=15,
inital_animal_energy=10000)

# <.... add the remaining code here ....>

Run the model several times as-is. Then in a few more runs try to experiment with the settings (try num_animals_initial and inital_animal_energy first)
Summarize the observation in your words. What do you observe? How do the three species interact with each other? What effect (if any) do the settings mentioned above have? (5 pt)

✎ Do This – Erase the contents of this cell an put your answer here.

🛑 STOP¶
Pause to commit your changes to your Git repository!

Take a moment to save your notebook, commit the changes to your Git repository using the commit message “Running experiments”, and push the changes to GitHub.

Part 7. Possible improvement. (20 pt) – EXTRA CREDIT [The total assignment score is capped at 105%]¶

Try to improve your code by making 5 instead of 3 animal species. Make them follow the rules of “Rock-Paper-Scissors-Lizard-Spock” (pictured the the left). The arrows show which sign beats which other sign. Your animal species should be able to set up accordingly. Try to implement new versions of the respective classes derived from animal and run a simulation with five instead of three species.

(Note: This game variant is also described on the Rock Paper Scissors Wikipedia page under the “Additional weapons” heading.)

Implement the 5 animal classes Rock_Animal, Paper_Animal, Scissors_Animal, Lizard_Animal, Spock_Animal, then initialize the environment below like this:

env = Environment(
animal_kinds = [Rock_Animal, Paper_Animal, Scissors_Animal, Lizard_Animal, Spock_Animal],
num_animals_initial=200,
size_x=100, size_y=100,
padding=15,
inital_animal_energy=10000)

In [ ]:

## Add your code here (10 pt)

Run the model for around 100-200 steps and see what happens. Re-run it a few times. How do the results differ from 3-species version? (5 pt)

In [ ]:

# Run the model for around 100-200 steps and see what happens. Re-run it a few times. Put your code here.

✎ Do This – Erase the contents of this cell an put your answer here.

Run one model for even more steps (around 500 to 1000 more steps depending on how long you are willing to watch it for). Describe if there are any additional observations. (5 pt)

In [ ]:

# Run one model for even more steps (around 500 to 1000 depending on how long you are willing to watch it for). Put your code here.

✎ Do This – Erase the contents of this cell an put your answer here.

🛑 STOP¶
Pause to commit your changes to your Git repository!

Take a moment to save your notebook, commit the changes to your Git repository using the commit message “Assignment complete”, and push the changes to GitHub.

Assignment wrap-up¶
Please fill out the form that appears when you run the code below. You must completely fill this out in order to receive credit for the assignment!

In [ ]:

from IPython.display import HTML
HTML(
“””

“””
)

Congratulations, you’re done!¶
Submit this assignment by uploading it to the course Desire2Learn web page. Go to the “Homework Assignments” folder, find the dropbox link for Homework #3, and upload it there.

© Copyright 2021, Department of Computational Mathematics, Science and Engineering at Michigan State University