Python Jumpstart by Building 10 Apps Transcripts
Chapter: App 7: Wizard Battle App
Lecture: Creating the creature hierarchy
Login or
purchase this course
to watch this video and the rest of the course contents.
0:00
So let's go over here and first address this problem we kind of would like to use the wizard specialization,
0:07
the wizard class as any other fighter in our game. And the commonality that we need for any kind of fighter is of course that it is a creature, right?.
0:19
And we just saw that we can use inheritance up here like so and say this wizard is a creature. Now, this is little annoying,
0:27
it says we don't really know what creature is and the problem is creature is defined after the wizard class, so there is two possible fixes here,
0:38
one fix could do this and just change the order, put the base class first, the other fix could be to possibly put the creature in its own module,
0:47
a wizard in certain module and then they could just import them int he order they need.
0:51
Now notice down here, there is some extreme duplication in the init for the creature we have a name and a level,
0:56
and we are storing those, in the wizard we have a name and a level and we are storing those as well.
1:01
This is not good first of all but there is two ways we can fix it, one way we could just say we are going to have this init method here
1:09
and we could say super and this will give us access to the direct methods on creature, we could say __init__() and we can say name and level,
1:19
and now the warning will go away, right, and we are actually passing this off, so when you call this method, by creating a wizard
1:26
you really just setting up the wizards properties by delegating or pushing this onward to this method. That's really good.
1:33
If we were doing additional stuff like more wizard stuff here, with the proper spelling, that would be cool, we would totally do this.
1:43
But, if all we are going to do is delegate up the inheritance hierarchy there. There is no reason to even have this method.
1:50
So we don't even have to write an init method and we can just inherit this one if you will. so now let's try to run our game and see if it will work.
2:00
Ok, a tiger of level 12 appears, let's just look around until we see the wizard again. Ok, wow there is a lot of tigers in a row.
2:09
So the evil wizard appears and we are going to try to attack it, remember, when we tried to get its defensive roll before it didn't have one,
2:15
boom, now it rolled 4000 in its defensive roll, we were defeated, but by making the wizard class derived from the creature class
2:26
we now can treat the wizard as any other player in the game, not just the hero. In fact, we can go over here and fix this up again
2:34
so here we say creature get defensive roll, and now because the wizard itself is a creature we can say self.get defensive roll as well,
2:43
and the game should play exactly the same. Perfect, so our wizards can now be creatures, let's work on small animal.
2:51
So let's define another class, over here, in the actors, and again, make some space for a minute, so we'll say class in memory just to define a class
3:01
by saying class, class name, cap words and hen again we want this small animal to be a special type of creature and we could just say pass,
3:09
and if that is all we do this stuff will start working. Ok, we have a toad and a bat, of course, one minor problem,
3:16
remember the top the way we imported, the actors, we now also need to import the small animal, from here.
3:23
Ok, now we should be able to treat these let's run away from that, a toad of level 1,
3:28
this is a small animal, and perfect, it behaves just like a creature but there is not a lot going on, with this small animal yet, right,
3:36
there is nothing special about it, it literally just does everything a creature does other than it does have its own type
3:43
and so when we say what type of object is this in memory, we know that is a small animal,
3:50
you could actually ask is this a type that is derived from a small animal or is it just a plain creature.
3:55
We could use that for something, but we don't really in this game. I guess the thing we can do to make this small animals special is
4:01
maybe it's a little extra weak, so down here we can change this get defensive roll,
4:07
in the creature class, it has a get_defensive_roll() and it just does this, let me copy the whole thing, and we can actually change how this works ok,
4:17
so we can come over here and say the small animal has all the behaviors and everything from the creature
4:22
except it's going to have its own version of get _defensive_roll(). Now, some programming languages when you are doing inheritance
4:29
you have to have special words like override or virtual or these sorts of things, Python doesn't have that, we just have one method
4:36
it's called get_defensive_roll(), if we just say it in the other classes something replaces it.
4:40
If we leave it this way, things should just still work again other than the wizard is not what we are looking for, but the bat is, perfect.
4:48
But let's say we want to change this, let's say we are going to come over here and say I'll call this base roll,
4:57
and then we want to return base roll maybe divided by 2, ok so it's a really small creature it's really weak,
5:05
so we could do this, let's find something, ok, there is a bat and the bat rolls 3.0. Ok, so it probably really rolled 2 but it's level 3
5:15
so I got 6 so then we cut that down to 3.0. So now we specialize our small creature to be
5:21
a little easier to defeat than the others, well quite a bit really. However, this part of code, is literally this base get_defensive_roll().
5:33
Ok, so we can actually change this, we can say you know what,
5:37
it could be the super, go to the creature class and we can call it it ge_ defensive_roll(), and that way as this evolves over time
5:44
maybe we decide to switch to a 15 sided dice or something like that we don't have to maintain the small animal,
5:51
it just does whatever the base one does, and it divides it by 2. So let's run this one more time, make sure it's still working,
5:56
ok wizard, wizard, tiger, wizard, can I get a bat please, ok, there is a bat, perfect, look bat rolls 3.0,
6:06
let's do it one more time, there is a toad we just missed it, ok, toad of level 1, the toad rolled like an 8 and we cut it in half.
6:16
So notice how we are using the super to grab the base behavior but then we are replacing it with our own method
6:24
so that small creatures have a different style, different algorithm, for their defensive rolls. Last thing is let's create a dragon,
6:33
because I know you've been waiting to create a dragon they are so awesome. I want a black one, right so we'll create a dragon and again,
6:38
this is going to be a creature now in the small animal and the wizard case, we have said there is no reason to have a specialized init method,
6:47
basically the setup in the data that the wizard and the small animal use is the same as the creature, but the dragon, I am going to change this,
6:55
I want the dragon to have a scaliness and an indicator whether it breaths fire or not.
7:01
So what we are going to do is we are going to define a __init__() and it's going to have to take all the data
7:07
or at least somehow supply the data that the creature is going to need, so name and level, and then I am going to have a scaliness and a breaths_fire,
7:17
this will be a boolean to say it does or does not breath fire. and then the first thing that we should do is
7:22
we should set up the creature feature so we'll say super.__init__(), name and level, and then we need to assign to the dragon
7:33
whether or not his scaliness and whether or not it breaths fire. So we can just say add this parameter here
7:38
and let PyCharm rock it out for us, thank you PyCharm. So now our dragon has different data, different features,
7:45
and it's probably going to have a different defensive roll, that's going to take those into account, so here we can say we have the base roll
7:51
but our dragon it's going to have a multiplier, so let me take this chance to show you a slightly different way
7:58
of doing if statements or conditional test here, so we'll have a fire modifier, now if they do not breath fire
8:08
we are just going to have 1.0, we are going to multiply this by the base roll,
8:11
so if the dragon does not breath fire we are going to say 1 but if it does, maybe we'll have a factor of 5 or something,
8:19
I mean that's a pretty serious defensive measure that it literally can breath fire. I could write this, let me say none and I'll say
8:27
if self.breaths_fire, the fire_modifier = 5 maybe this could just be 1 but let me put it this way just an else statement,
8:39
so it's a little more clear, so else fire_modifier = 1, now this bit right here, we are using all these lines of code to do this,
8:47
we could actually write this in a much nicer way, we'll say fire_modifier = and I can put this
8:54
all in one line in this condensed if statement the Python supports, let me just sketch out the placeholder
9:00
so value if true say if some test else value if false, right, so if we use this the value if- let's do the test first,
9:12
so the test is going to be if breaths_fire, self breaths_fire, so if it does breath fire, I would like the modifier to be 5,
9:22
but if it does not breath_fire, I would like the modifier to be 1, ok, so let me put this like so,
9:28
so now this will actually let us in one nice little condensed line, specify the fire modifier, the other thing is the scaliness factor,
9:37
let's say this i s a number between 1 and 100, and it has like a 10% effect on here, so we'll have something like this,
9:45
scale_modifier = scaliness self.scaliness, divided by 10, right, so if it's a 100 it multiply it by 10, if it 1 it actually takes it down a little bit,
10:00
so then we are going to return base_roll times fire_modifier times scale_modifier. Ok, now let's go back to our program here
10:11
and let me just put this back for a minute, that's how it was before we started to talk about these dragons,
10:17
so if I try to run it, it's not going to turn out so well, first of all it says dragon is not defined right there,
10:22
that's because I again forgot to import it, it's kind of why I like the name spaces you don't have to keep doing this, but it's all good.
10:30
So now we have our dragon, but notice PyCharm is already telling us hummm? not so good, but it won't run it will say you are missing
10:35
this parameter of scaliness and breaths_fire right there, so let's say that this dragon has a scaliness of 50, no let's say 75,
10:46
that's a pretty serious scaliness and it does breath_fire, so this dragon is now a much harder thing to fight, all right,
10:53
so let's look around, so we see- let's run away until we see something that is a dragon.
11:02
Toads, dragon, ok dragon at level 50 and what is our level, 75, so we should have a pretty good chance to beat it on the previous algorithm,
11:11
but this is a very scaly fire breathing level 50 dragon chances are not good, let's see what happens, let's fight it.
11:18
Ok, we rolled a 150 it rolled 11250, I would say we've been defeated, the thing is even worse than evil wizard now.
11:28
So let's go back and see how inheritance has helped here, so we have a small animal which is a toad
11:35
and the fact that it's a small animal it behaves like a creature but it actually is easier to defeat
11:40
because it has a different wimpier get saving or defensive roll method. Creature this is just a standard creature, the dragon,
11:48
actually is much harder to defeat it has additional data that it uses to define how it defends when it's attacked
11:56
and now it has a scaliness protective layer and it breaths fire it takes all that into account during this battle
12:03
but the wizard doesn't have to know it's a dragon, it can still treat it like a creature.
12:08
And yet it behaves differently, finally the wizard can also behave like just another creature in the game because it derives from creature.
12:16
We do this derivation by just defining the creature class and then we say the wizard is a creature, here we have an additional method
12:24
that's not in the creature class called attack, PyCharm we can collapse those, and then the small animal
12:30
it has this simpler wimpier defensive roll and notice that PyCharm is showing you this overwrites a method in creature
12:36
so just be aware of that, that's actually pretty awesome. The dragon has its own initialization method
12:43
because it has to take additional data and do additional setup but it delegates to the base class for the sort of base class setup
12:50
and it has a different defensive roll that not just has a different algorithm but takes into account the fact that it has different data
12:56
like the scale modifier which apparently I misspelled. So this just gives you a little taste of object oriented programming
13:04
but the types of applications that you can build are seriously powerful and it's a lot of fun to think about solving problems this way.