- [Narrator] In this video, we're going to
continue our discussion of simulation
with a game of chance, a dice game
called "Craps" that you'll find
played in casinos around the world.
Now the basic rules of the game are as follows.
We're going to start by rolling two six-sided dice
with the values one through six on the different faces.
We're going to calculate the sum
of the two upward faces of the dice,
and if that sum is seven or 11 on the first roll,
the player wins.
If the sum is two, three, or 12 on the first roll,
the player loses.
And if the sum is any of the other possible totals,
that is known as your point.
And in order to win going forward,
you have to keep rolling the dice
and you must make your point,
which is actually where that expression comes from.
So if you roll your point before you roll a seven
on the second and subsequent rolls of the dice,
then you win.
If you roll a seven before rolling your point,
you lose.
So before we get into the actual script,
which uses many of the techniques we've introduced so far,
and introduces a couple of additional ones,
let's switch over to a terminal window here
and execute the script a couple of times
so you can see it in action.
So here you can see, I'm in my lesson four folder,
the scripts that are part of this lesson are listed here,
we're going to be looking at figure 04_02.py
in just a moment.
I'm about to execute that with the iPython command.
Now when I do execute that, in this first case,
it happens to be that we lost on the first roll.
A two three or 12 on the first roll is a loss,
so we display "Player loses."
Let me recall that command and execute it again.
I got the exact same result but with
two different dice faces in this case.
And let's run it again.
So far that's interesting that we've only
had to roll once each time, we won this time.
Let's see if we can get a roll of several.
We lost on one roll that time.
There we go.
All right so in this case, what we had
is three plus six is nine on the first roll,
so our point is nine.
It's not a seven, 11, two, three, or 12.
And then we had a whole bunch of rolls
before we eventually rolled a seven, before rolling a nine.
So we lost in that case.
Let's see if we can get a win after a couple of rolls.
In this case we had two rolls, but we again lost
because we rolled a seven before the point.
So far we're losing a lot, which is why
you don't wanna gamble.
Let's see if we can run this a couple more times.
Oh wow, that's a really long one.
Very rare that you see this many rolls in a row,
and then get your point before rolling a seven,
because seven is the most common sum of the dice
that you'll encounter.
So now that we've seen the script in action,
let's go ahead and take look at the source code
that implements this example.
So as we did in the interactive iPython session,
in order to use the random number generator,
we do have to "import random" first,
so we see that up at the top of the script here
after our comment that says the file name
and our docstring that says the purpose of the script.
Now for this particular program,
we've defined two functions that we're going
to use in the script down below.
And when you're writing a script,
any functions that you're going to call
have to be defined before the statements
that are actually going to call them are executed.
So what we've done here is defined two functions,
one called "roll dice," and you can probably
guess based on its name what it's going to do,
but it's going to roll two dice
by randomly selecting numbers from one through seven,
and then we're going to introduce
a new concept in this example,
which is returning more than one value
at a time from a function.
In particular, we're going to roll two dice
and return both of those die values back to the caller.
We also have a "display dice" function,
which is going to receive what is known as a "tuple,"
containing the two dice.
And we'll talk more about tuples in just a moment.
And it is going to pick apart the two die values
and display the contents.
So again, I'm just giving you a kind of
summary of the script first, and then we'll go ahead
and look at the details.
The actual logic of the application starts here at line 16,
where we're going to roll the dice,
display their values, calculate their sum,
and then work through whether we won, lost,
or need to continue rolling.
If we need to continue rolling,
the "while" loop will do that,
and then eventually we'll win or lose
and display the results.
So let's go back to the top and take a look
at this in more detail.
So the "roll dice" function, first of all
I want to point out the naming convention once again.
If you have a multi-word name for a function,
you're supposed to define it with all lower case letters
and separate the words by an underscore.
That's per the style guide, so that's what we've done here.
And this particular function has no arguments,
so we have an empty set of parentheses.
It's simply going to perform a task
and give us back a result.
The colon introduces the block that belongs to the function,
that is the set of statements to execute
when the function gets called.
And as you see here, each of the statements in the body
is indented the same number of spaces.
Per convention, per style, in the style guide,
we start our function's body with a docstring
stating the purpose of the function,
so "Roll two dice and return their face values as a tuple."
So let's talk about that a tuple is for a moment here.
In the preceding lesson, we introduced
the concept of a "list" in Python,
which is simply a collection of values.
As you'll see in lesson five,
a list is actually a modifiable entity,
meaning it can grow and shrink
by adding and removing elements,
and you can change the values of individual elements
within a list as well.
A tuple, on the other hand,
is an immutable sequence of values.
That is, once you create a tuple,
you cannot remove elements from it
or add more elements to it.
So it's a great way to basically
group together some related data,
so that you can pass it around in a Python program.
So with that said, inside the body
of our "roll dice" function here,
we are using the random module's random range function
to produce a random number from one
up to but not including six,
and again those will be integer values.
We call "randrange" twice, which gives us
two separate random values in that range,
and we store those in die one and die two,
and then our "return" statement,
which is going to give back the result of this function,
defines a tuple.
This is called packing the tuple
with the values that you want it to hold.
The parentheses are not required.
What defines a tuple is simply
a comma-separated list of values,
and maybe I shouldn't use the word "list"
in that particular context.
I always tend to use parentheses for readability,
and many programmers do, and there are a couple of cases,
syntactically in Python where those parentheses are required
and if you don't use them you'd wind up with a syntax error.
So I just got in the habit of always including parentheses
for the tuples that I was defining.
So that's how we're going to return
more than one item at a time.
In reality, the one item we are returning
is a tuple that contains two values.
So in reality, every function still returns one value,
but you can pack together a bunch of items
and return them from a function, as shown here.
You can also do that, by the way,
with other data structures like the list data structure
that we talked about in the preceding lesson.
Now our "display dice" function is going to take,
as its argument, what we are calling "dice,"
and as you're about to see, "dice" is going to be the tuple
that we return from "roll dice"
so we're going to receive that as one item,
one object if you will.
And then as you can see, this function's job
is to display one roll of the two dice.
This is the key statement in the "display dice" function,
this is called "unpacking" the tuple.
So "dice" is going to be the tuple
containing the two die values,
and when you assign a tuple
to a comma delimited list of variable names,
it unpacks each of the values.
And there is a requirement here
that the number of variables on the left side
of the equal sign must match up
with the number of elements in the tuple.
So because we know that we're packing the tuple
representing the dice with two values,
we know we must use two separate variables
on the left side of the assignment
to unpack the two dice.
And once we've done that, we're using a format string here
to display that the player rolled.
So we insert the value of die one,
and we insert the value of die two,
and then we use that "sum" function
that we introduced in the preceding lesson,
which is built in to Python, to say,
"Here's a sequence that contains a couple of integers,"
it's called "dice," it's the argument that we received,
"Walk through that sequence and add up the values."
So rather than us calculating die one plus die two,
we're just using the build in function "sum"
to do that for us.
So now that we've defined these two functions,
we can start to take a look at the logic of the program.
The first thing we need to do when we're playing the game
is to roll the dice.
And as you can see here, we call "roll dice."
Now up above, we just saw that "roll dice" returns a tuple
containing two die values.
And as you can see here, we're storing that
into a variable called "die values."
So if we were doing this in command line
interactive mode with iPython,
we could go ahead and check the type of die values here
and immediately see that it's in fact a tuple.
We could certainly do that here in the script as well
but it's not interactive so we wouldn't
see the results as I type the code here,
that's why I'm just telling you about it at the moment.
So "die values," we then want to display,
so we can see what we rolled
on that initial roll of the dice.
So we take that tuple and pass it off to "die values."
Then we calculate the sum of the dice here
by calling the built in "sum" function once again,
and now we can check to see whether we won the game,
lost the game, or need to continue rolling.
So we have a couple of new features
that we're presenting to you in this example,
one of which was this concept of a tuple.
Another is the "in" operator.
The "in" operator takes the value
on the left side of the operator
and checks whether it's inside the sequence
on the right side of the operator.
And once again we're using a tuple here.
So we have a tuple containing the values
that allow you to win on the first roll, seven and 11.
If the sum of the dice is in this tuple,
then we know we won the game.
So if the sum of the dice is seven or 11,
then we're going to set game status to the string "Won"
to indicate that we won the game.
Otherwise, if the sum of the dice
is in the tuple two, three, or 12,
then we know we lost the game.
So if this condition is true,
we're going to set game status to "Lost."
And for all other sums of the dice,
we're going to set our game status variable to "Continue,"
indicating that we need to keep rolling the dice,
and we're going to create a variable called "my point"
in which we're going to store the sum of the dice.
Now for those of you who are coming
from other programming languages into Python,
as you look at this you see we're defining
a variable called "game status" in the different suites
of the "if, elif, else" statement.
We're also defining "my point" in the "else" part
in a C-based language like C, C++, Java, C#,
when you define variables in the block
of an "if, the "elseif," or "elif"
depending on what language you're looking at,
or "else" in those other languages,
that variable would be known only within that block of code.
That is not the case in Python.
When I define these variables locally
within the suites of the "if, elif, else" statement,
they are still known after the statement finishes executing.
So I can use those variables going forward down below.
And that's a very big different between Python
and some of those other programming languages.
So if we get to the "else" part,
we're also going to print out what that sum of the dice was,
so you know what you need to roll to win going forward.
Now the "while" loop is going to keep executing
as long as the value of our "game status" variable
remains equal to "Continue," the string "Continue."
And at that point we need to roll the dice again,
so one of the reasons we defined "roll dice"
was so that we didn't have to duplicate that code
in multiple locations throughout the program's logic.
So we roll the dice and display the new sum of the dice,
and what the actual individual die values were.
Then we calculate the sum of the dice
so that we can check whether we won the game
or lost the game on this subsequent roll.
If the sum of the dice is equal to "my point,"
that would mean we won, in which case
we set "game status" accordingly.
Otherwise if the sum of the dice is equal to seven,
that would mean we lost.
So we set "game status" accordingly.
And if we did not modify "game status"
as a result of this "if, elif" statement,
meaning it was not the point
and it was not a seven that we just rolled,
we wind up back at the top of the loop,
to do the next roll of the dice.
And then finally, once we do win or lose,
we check at the end of the game here
to see if "game status" is equal to "Won,"
in which case we will display "Player wins"
otherwise we display "Player loses."
So just summarizing a little bit
of what we looked at in this example,
this was our first example with multiple functions defined,
but the key new features that we were introducing
in this example were the ability to return multiple values
from a function by packing them into a tuple,
the ability to unpack a tuple into individual variables
so that you could use the values within the tuple,
and this new "in" operator which we had not seen previously,
which allows us to check whether a value is in a sequence.
And that will work, by the way, with tuples.
It will also work with lists and other sequences
as we work our way forward in Python
and learn other data structures.