To begin, we’re going to make a simple dice roller application. We want a page that, given a number of dice, a number of sides, and a number of rolls, returns a list of random numbers in the proper distribution. For example, rolling 2 six-sided dice (2d6) is not the same as rolling a 12-sided die, since when rolling 2, 6 is much more likely than, say, 2 or 3, and 1 is impossible.
For this, we need to be able to generate random numbers. Python’s standard library gives us the useful function
random.randint(lower, upper) from the
random module, which will give us a
A fair six sided die will only ever give an integer between 1 and 6, evenly distributed:
We could make a dice roller like this:
and it would work just fine, printing out random numbers every time it was run. Imagine, however, that we wanted to do more than one die of each type. This requires a little arithmetic.
Two six sided dice do not have an even chance of producing a number between 1 and 12; rather, it is impossible for them to produce 1 (as each die’s minimum is 1, and there are two of them), and it is much more likely that they will produce 7 than 12, because there are many combinations of rolls that produce 7, and only one that produces 12.
We can solve this simply by adding together calls to
print("3d6:", random.randint(1,6) + random.randint(1,6) + random.randint(1,6))
This totally works and has the correct distribution, but it’s kind of gross and requires a lot of retyping. This is where we go from simple technical knowledge to properly decomposing the problem, and you should try to do this without looking at the next answer: how would we modularize this solution, to be able to deal with and number of dice of any geometry (any number of sides)?
Don’t be afraid to try this on your own!
We could make a more general dice-roller with a function which takes as input the number of dice and the number of sides. At first, let’s deal with only the number of sides:
" Simulate rolling a single die with number_of_sides sides"
import random # We need randint from the random module
# This is exactly what we were doing by hand before: a random number between 1 and the number of sides
random_number = random.randint(1, number_of_sides)
# Give back the number we generated
We now, however, have the problem of adding multiple dice. Again, we could do
roll_die(6) + roll_die(6) + roll_die(6), but this is only marginally better; it looks much cleaner, but doing 1000d6 would still be somewhat problematic.
Luckily, if computers are good at one thing, it’s repeating instructions over and over. All we need to do is make a variable to keep track of the total, just like a person would do on a bit of paper if they had to roll a die a bunch of times. This is called an accumulator, because it’s accumulating a value over time, and that’s what I’ll call the variable. As with all variables, though, you can call it anything you like.
def roll_dice(number_of_dice, number_of_sides):
" Return a random number in the same distribution as rolling number_of_dice dice of number_of_sides sides "
import random # we need randint from the random module
accumulator = 0 # This variable will "accumulate" a value as the loop runs
for roll_number in range(number_of_dice):
# We don't actually use roll_number for anything, it's just a placeholder
# You could use it for debugging messages if you want
accumulator += roll_die(number_of_sides)
First, this isolates the importing of the random module to where it belongs – we only use functions from random in this function, so the rest of the program doesn’t need to know about it.
Second, this function is very general. We could write
print(roll_dice(3, 6)) to achieve the same result as above, or
print(roll_dice(1024, 6)), or even
print(roll_dice(1, 15)); the computer doesn’t care that fair 15-sided dice are physically impossible.
Decomposing and generalizing problems problems is a very important skill – much more important, in fact, understanding any individual language.
In the next post, we’ll create a simple web service that rolls dice for anyone who accesses it.