Tutorial 6: Digital Pets
Setup: before you start
Create a new empty project in Spyder, and call it “Tutorial_6”.
Now create a new program file, trobble.py. All the classes and functions you write have to be added to this file.
Context and Objectives
Since the time when we were young is the greatest of all times, we will today relive the golden age when the CSE101 staff members were young. Some of us have fond memories of digital pets that were fashionable at that point, see e.g. these. You might not know the principle (after all, you were not even born then), so let us give a quick introduction: digital pets were small roughly egg-shaped toys that simulated real pets with a small computer chip and a tiny black and white screen. One had to “feed” them regularly, clean up behind them, and generally take care of their well-being.
In this tutorial, we will create a simplistic version of this by creating sweet little pets that we call trobbles.
Skills we will practice today: writing simple classes, creating objects.
Exercises
Exercise 1: A new pet is born
We will create several trobbles later on, each of them with their own
properties. Consequently, it makes sense to represent each trobble as an
object of a common class which will be called
Trobble
.
In this exercise, we will define the Trobble
class, to
prepare the basic functionalities of our Trobble
objects.
Create a new class Trobble
in your
trobble.py file. (Note: the class name
Trobble
starts with an upper-case letter, but it is defined
in module trobble
from the file trobble.py, whose
names start with a lower-case letter!)
Your class should begin as follows. Note how the docstring describes the data contained in each Trobble object.
class Trobble:
"""Trobbles: simplified digital pets.
Data Attributes:
name -- the Trobble's name.
sex -- 'male' or 'female'.
age -- a non-negative integer
health -- an integer between 0 (dead) and 10 (full health) inclusive
hunger -- a non-negative integer (0 is not hungry)
"""
Add an __init__()
method to the
Trobble
class. This method has three parameters:
self
(the object itself), name
, and
sex
. It initializes each new Trobble object’s data
attributes with the given values:
name
is a string containing the name of theTrobble
instance, to be initialized by the parametername
,sex
is one of the two strings'male'
or'female'
. (Note that this binary perspective on sexes is a gross oversimplification that we make here to keep things manageable.)
Additionally, there are a few more data attributes that you should
initialize as follows, in the __init__
method:
health
encodes the health of theTrobble
instance as an integer between0
(the instance is dead!) and10
. The initial value forhealth
is10
.age
is the age of the instance, starting at0
.hunger
encodes how hungry the instance is, starting from (and initialized to)0
and unbounded.
Test your Trobble
class. For example,
you should see the following behaviour in the console
In [1]: t1 = Trobble('Dave', 'male')
In [2]: t1.name
Out[2]: 'Dave'
In [3]: t1.sex
Out[3]: 'male'
In [4]: t1.age
Out[4]: 0
In [5]: t1.health
Out[5]: 10
In [6]: t1.hunger
Out[6]: 0
Upload your file trobble.py
:
Exercise 2: Showing what we got
As we have seen in the tests for the last exercise, checking one data
attribute after the other to see the status of a Trobble
is
not very convenient. To simplify this, we will now add a
__str__()
method that produces a string representing the
instance.
Add a __str__()
method to your
Trobble
class. It should start as follows:
def __str__(self):
pass # remove this line and replace with your own code
The strings that your __str__
returns should be in the
form
'_name_: _sex_, health _health_, hunger _hunger_, age _age_'
,
where _name_
, _sex_
, _health_
,
_hunger_
, and _age_
are the values of the data
attributes with the same name in the active Trobble
object.
Test your __str__()
method. For
example, in the ipython console you should have
In [7]: t1 = Trobble('Dave', 'male')
In [8]: print(t1)
Dave: male, health 10, hunger 0, age 0
In [9]: t1.sex = 'female'
In [10]: print(t1)
Dave: female, health 10, hunger 0, age 0
In [11]: t1.name = 'Eve'
In [12]: print(t1)
Eve: female, health 10, hunger 0, age 0
Upload your file trobble.py
:
Exercise 3: Growing older
While we can now encode Trobble
s as objects, so far they
are rather boring because they are just static and do not change unless
we change their attributes manually. In this exercise we will make them
change over time. But instead of using real time (as is the case for the
toys we are trying to emulate), we will work with turns.
To this end, add a method next_turn()
to the class Trobble
, which should start as follows:
def next_turn(self):
"""End the turn for the instance and recompute the attribute values
for the next turn.
"""
When next_turn()
is called, we first check if the
Trobble
is still alive, i.e., if its health
is
positive. If not, we change nothing. Otherwise,
- the age of a
Trobble
instance increases by1
, - the
hunger
increases by the (new)age
of the instance (for reasons no one really understandsTrobbles
have to eat more once they get older), - afterwards, for every full
20
hunger thehealth
decreases by1
, e.g. it decreases by1
for20
hunger, by2
for40
, not at all for19
, and by1
for39
.
Remember that the health of a Trobble
can never be negative, so whenever the rules above would give a negative
health, we set the health to 0
instead.
Test your next_turn()
method. In the
ipython console you might have
In [13]: t1 = Trobble('Dave', 'male')
In [14]: print(t1)
Dave: male, health 10, hunger 0, age 0
In [15]: for i in range(13):
...: print(f'Turn {i} -> {t1}')
...: t1.next_turn()
...:
Turn 0 -> Dave: male, health 10, hunger 0, age 0
Turn 1 -> Dave: male, health 10, hunger 1, age 1
Turn 2 -> Dave: male, health 10, hunger 3, age 2
Turn 3 -> Dave: male, health 10, hunger 6, age 3
Turn 4 -> Dave: male, health 10, hunger 10, age 4
Turn 5 -> Dave: male, health 10, hunger 15, age 5
Turn 6 -> Dave: male, health 9, hunger 21, age 6
Turn 7 -> Dave: male, health 8, hunger 28, age 7
Turn 8 -> Dave: male, health 7, hunger 36, age 8
Turn 9 -> Dave: male, health 5, hunger 45, age 9
Turn 10 -> Dave: male, health 3, hunger 55, age 10
Turn 11 -> Dave: male, health 0, hunger 66, age 11
Turn 12 -> Dave: male, health 0, hunger 66, age 11
Notice that Dave starts losing health from Turn 6, and (sadly) dies on Turn 11.
Upload your file trobble.py
:
Exercise 4: Caring for your Trobble
Our Trobble
s now change over time, but they only get
hungry, then sick, and ultimately they die. While this may be realistic,
it is perhaps a little depressing for a pet. So let’s make them live
longer now.
Exercise 4a: Feeding
As a start, add a new method feed()
that decreases the hunger by 25
, but never below
0
. Your method should start with
def feed(self):
"""Feed the Trobble instance to decrease the hunger by 25
with a minimum value of 0.
"""
Exercise 4b: Curing
Afterwards, to cure sick Trobble
s, add a method
cure()
that increases the health
by
5
up to the maximum of 10
. The method should
start with
def cure(self):
"""Increase the health of the instance by 5 up to the maximum of 10.
"""
Exercise 4c: Having fun
To keep healthy, having fun is also important. Thus, write a method
party()
which increases the health
by
2
up to the maximum of 10
. However, partying
will make you hungry, so party()
will increase the hunger
by 4.
def party(self):
"""Increase the health of the instance by 2 up to the maximum of 10
and increase the hunger by 4.
"""
Exercise 4d: Checking
Now our pets can live longer. So let’s add a method to verify if they are still alive.
Add a method is_alive
that returns
True
if the health of the instance is positive, and
False
otherwise. Your method should begin with
def is_alive(self):
"""Return True if the health of the instance is positive,
otherwise False.
"""
Test the methods you have added in this exercise. In the ipython console you might have
In [16]: t1 = Trobble('Dave', 'male')
In [17]: print(t1)
Dave: male, health 10, hunger 0, age 0
In [18]: for i in range(7):
...: t1.next_turn()
...:
In [19]: print(t1)
Dave: male, health 8, hunger 28, age 7
In [20]: t1.feed()
In [21]: print(t1)
Dave: male, health 8, hunger 3, age 7
In [22]: t1.cure()
In [23]: print(t1)
Dave: male, health 10, hunger 3, age 7
In [24]: t1.party()
In [25]: print(t1)
Dave: male, health 10, hunger 7, age 7
In [26]: for i in range(20):
...: t1.next_turn()
...:
In [27]: print(t1)
Dave: male, health 0, hunger 84, age 14
In [28]: t1.is_alive()
Out[28]: False
Upload your file trobble.py
:
Exercise 5: Playing with Trobbles
After all this hard work, we deserve a break now to play with the
pets we have created. To this end, copy the following code to your file
trobble.py
, separately from the class definition
(that is, make sure that it is not part of the indented code block
defining the class Trobble
).
def get_name():
return input('Please give your new Trobble a name: ')
def get_sex():
= None
sex while sex is None:
= 'Is your new Trobble male or female? Type "m" or "f" to choose: '
prompt = input(prompt)
choice if choice == 'm':
= 'male'
sex elif choice == 'f':
= 'female'
sex return sex
def get_action(actions):
while True:
= f"Type one of {', '.join(actions.keys())} to perform the action, or stop to quit the game: "
prompt = input(prompt)
action_string if action_string == 'stop':
print('Thanks for having played with Trobbles!')
return
if action_string not in actions:
print('Unknown action!')
else:
return actions[action_string]
def play():
= get_name()
name = get_sex()
sex = Trobble(name, sex)
trobble = {'feed': trobble.feed, 'cure': trobble.cure}
actions while trobble.is_alive():
print('You have one Trobble named ' + str(trobble))
= get_action(actions)
action if action is None:
return
action()
trobble.next_turn()print(f'Unfortunately, your Trobble {trobble.name} has died at the age of {trobble.age}')
Exercise 5a: Play
You can play by calling the function play()
. Note that
you can hit ctrl-C
to stop playing at any point. After
having played a little with some Trobble
s, try to
understand what is happening in the code you have just copied. In
particular, what is the role of action
?
Exercise 5b: Party
Additionally, add the method party()
to
play()
as an action.
Exercise 5c: Celebrate some birthdays!
Each time your Trobble
turns 10, 20, 30, … years,
congratulate by printing “Happy Birthday {trobble.name}!” and give him
some food as a present, thus, decrease the hunger by 5. Note: do
not congratulate when your trobble just has been created and is
0 years old!
Test the methods you have added in this exercise. In the ipython console you might have
In [29]: play()
Please give your new Trobble a name: Lily
Is your new Trobble male or female? Type "m" or "f" to choose: f
You have one Trobble named Lily: female, health 10, hunger 0, age 0
Type one of feed, cure, party to perform the action: party
You have one Trobble named Lily: female, health 10, hunger 5, age 1
Type one of feed, cure, party to perform the action: feed
You have one Trobble named Lily: female, health 10, hunger 2, age 2
Type one of feed, cure, party to perform the action: party
You have one Trobble named Lily: female, health 10, hunger 9, age 3
Type one of feed, cure, party to perform the action: party
You have one Trobble named Lily: female, health 10, hunger 17, age 4
Type one of feed, cure, party to perform the action: feed
You have one Trobble named Lily: female, health 10, hunger 5, age 5
Type one of feed, cure, party to perform the action: party
You have one Trobble named Lily: female, health 10, hunger 15, age 6
Type one of feed, cure, party to perform the action: party
You have one Trobble named Lily: female, health 9, hunger 26, age 7
Type one of feed, cure, party to perform the action: feed
You have one Trobble named Lily: female, health 9, hunger 9, age 8
Type one of feed, cure, party to perform the action: feed
You have one Trobble named Lily: female, health 9, hunger 9, age 9
Type one of feed, cure, party to perform the action: party
You have one Trobble named Lily: female, health 9, hunger 23, age 10
Happy Birthday Lily!
Type one of feed, cure, party to perform the action: partey
Unknown action!
Upload your file trobble.py
:
Exercise 6: Be fruitful, and multiply
Trobble
s are social animals, and they feel lonely if
they have no other animals of their species around them. But since
creating them by hand is rather annoying, why not simply let them
procreate? Fortunately, Trobble
s are very fertile: whenever
a female and a male specimen are left alone, they create a new
Trobble
assuming that both potential parents are old enough
to have offspring, which is starting from the age of 4
.
Write a function
mate(trobble1, trobble2, name_offspring)
that, given two
living Trobble
s and a name for the potential offspring,
checks if the prerequisites are met to create a new Trobble
and if so returns a new pet with the name name_offspring
.
For reasons no one really understands, the offspring will always have
the same sex as the first argument trobble1
.
Your function should start as follows:
def mate(trobble1, trobble2, name_offspring):
"""Check if the given Trobbles can procreate and if so return a new
Trobble that has the sex of trobble1 and the name 'name_offspring'.
Otherwise, return None.
"""
Warning: This function is not a method of
the Trobble
class, so make sure that it is defined
outside the class Trobble
!
Test your function mate
. For example,
in the ipython console you should have
In [30]: t1 = Trobble('Dave', 'male')
In [31]: t2 = Trobble('Eve', 'female')
In [32]: c1 = mate(t1, t2, 'Fred')
In [33]: print(c1)
None
In [34]: t1.age = 10
In [35]: t2.age = 10
In [36]: c1 = mate(t1, t2, 'Fred')
In [37]: print(c1)
Fred: male, health 10, hunger 0, age 0
In [38]: c2 = mate(t2, t1, 'Gerda')
In [39]: print(c2)
Gerda: female, health 10, hunger 0, age 0
In [40]: c3 = mate(t1, t1, 'Jon')
In [41]: print(c3)
None
Upload your file trobble.py
:
Optional Exercise 7: More Trobbles, more troubles
Since now we can make more Trobble
s from some others, we
can make the game of Exercise 5 more interesting.
To this end, add a function multi_play()
that lets you
play a game as in play()
, but starts out with two
Trobble
s, one male and one female. In each round, you can
feed or cure one of the current pets individually or use the function
mate
on two of them to create more pets. How many living
pets can you have at the same time?
Try to think of different ways to make the game more interesting. For example, you could:
- try changing the basic health/hunger mechanics;
- try adding a mood attribute to the trobbles (which might depend on health, hunger, breeding, or anything else);
- try including more interactions.