In the last example, you noticed that some functions need quite a lot of arguments, making them hard to remember and hard to code correctly. While there exist ways to pass whole list of arguments as once, there is another problem becoming visible as soon as you try to model more than 2 combatants:
Each combatant has his own bunch of variables -at the moment only attack, defense and hitpoings, but soon far more- and sooner or later the naming of all those variables will become nasty. Imaging 10 different goblins, each having 5 variables, that make 50 variable names … There must be something like a namespace for variables where you could use the dot.notation like in modules if you refer to
randint() function of the
Stinky.attack to refer to the
attack stat value of
Such a thing exist, it is called a class.
If you never heard of classes (or object orientatoin) let's imagine you have an … mystical, magical …. goblin making machine1). This machine is wired so that it can create an individual new goblin2). Each time you start the machine, a new goblin is created. So you can create an army of goblins really fast.
Translated into computer language, the goblin creating machine is called a class. Each individual goblin produced by this machine is called an instance of this class. Each goblins has his own (personal, local) variables like attack and hitpoints. Those are called attributes (of the class instance). Each goblin can also have his own defined functions, like
flee(), those are called methods ( of the class instance). There exist also attributes (and even methods) shared by all goblins together, those are called class attributes or class methods but are not so important right now.
The nice things about using a class for goblins are:
randommodule in the class constructor. The constructor is a method with a special name that is automatic executed by python each time a new class instance is created (a new goblin is “born”)
forloop i can create 1000 individual, randomized goblins really fast.
class Monster()with all generic attributes and methods and later i can create a
class Goblin(Monster)Goblin child class inherating everything from the Monster class but adding some extra goblin-specific methods.
I found the python online documenation about classes helpful for people who already know what a class is. If you never heard of classes before, i recommend working through one of the many excellent python beginner tutorials out there, like Swaroop C. H.'s excellent free A byte of python book.
click reload on your browser if you see no source code here or visit Github.com
Stinky.attack: 5 Grunty.defense: 7 Grunty.hitpoints: 10 Grunty take 4 damage: 6 Grunty says: i am fine Grunty take 7 damage: -1 Grunty says: i am in bad shape
Did you noticed that… ?
counterthat belongs to the class, not to an instance of the class. In this example, the counter is not used, he just exist.
Now it is time to rework the Goblin Dice Duel using classes. And while i'm at it, this is a good time to randomize the goblin stat values even better. In the long run, i want this program to simulate epic battles between large armies of goblins… and i will not figure out the stats of each individual goblin myself. That's a job for a computer.
Let assume that the standard, fresh-from-the-breeding-machine-hatched goblin has on average 20 hitpoints. That means that if you grab 100 random goblins, most will have around 19, 20, or 21 hitpoints, some few elite stubborn goblins will have up to 25 hitpoints while some weaklings will have less than 17 hitpoints. Once-in a million you will find an Übergoblin with 30 hitpoints or an really unworthy creature with around 10 hitpoints.
That is, if the hitpints of goblins are normal (or gauss) distributed.
There are mathematical models to describe such an distribution and even better, the
random module of python offers you a function to generate random values following the laws of normal distribution far better than the
random.gauss() function. Let's take a look at the helpful python online documentation:
random.gauss(mu, sigma) Gaussian distribution. mu is the mean, and sigma is the standard deviation. This is slightly faster than the normalvariate() function defined below.
Wow, that is easy. Just two arguments. Let us experiment a bit, using python shell:
>>> import random >>> random.gauss(20,2) 21.229611198141817 >>> for x in range(10): print(random.gauss(20,2)) 18.731026489131313 20.061439768499362 21.576400870087564 20.467927825773376 19.312368653268773 18.24111326477667 21.357458229193927 20.376858376001735 19.461000722570816 18.468106521404675 >>> for x in range(10): print(random.gauss(20,5)) 28.446476929526845 23.715576563098807 26.584354809603987 24.661615398593913 22.951632681091176 14.820343223874811 21.071907880803966 15.610981856583557 7.884089170984463 30.044638262665586 >>>
As you can see, while all values above center around a
mu (or µ or mean) of 20 (hitpoints?), it is a huge difference (even in a small sample of only 10 numbers) if the
sigma (or Variance) is 2 or 5. With sigma 5, we got an Übergoblin ( 28 hitpoints! ) as well as pure cannon fodder (only 7 hitpoints) while with sigma 2, all hitpoints were relative close to 20.
Please note that even if you select a very small sigma, it is not impossible that you get an extreme low or high value. It is just very rare. With an big sigma however, you will see values extreme distanced from the mu far more often.
If you interested in the exact mathematic model of normal distribution, Wikipedia has some nice graphics, like this one:
Also the website Wolfram Alpha let you play around with mu and sigma in the webbrowser and generate dynamic charts for you.