O'Reilly logo

Head First Learn to Code by Eric Freeman

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. 3 booleans, decisions, and loops: Decisive Code

Image

Have you noticed how, so far, our programs aren’t very, well, interesting? That is, all our code has strictly been a set of statements the interpreter evaluates from top to bottom—no twists in the plot, no sudden turns, no surprises, no independent thinking. For code to be more interesting, it needs to make decisions, to control its own destiny, and to do things more than once straight through. And in this chapter that’s exactly what we’re going to learn to do. Along the way we’ll learn about the mysterious game called shoushiling, meet a character named Boole, and see how a data type with only two values could be worth our time. We’re even going to learn how to deal with the dreaded infinite loop. Let’s get started!

Note

We may even create one just for the fun of it!

Would you like to play a game?

Passed down from the ancient Chinese Han dynasty, the game shoushiling has been used to settle court case decisions, to decide multimillion-dollar deals, and perhaps most importantly, to determine who gets to sit in the front seat of the car.

Today you know the game as Rock, Paper, Scissors, and we’re going to implement it so that you can play the game against a rather tough opponent: your computer.

Image

How Rock, Paper, Scissors works

If you’re never heard of Rock, Paper, Scissors we’re going to go over its simple rules now; and if you do know the game, this will be a good review for you. The game is played by two players, who each, upfront, secretly choose either rock, paper, or scissors. The two players then typically count out loud to three (or shout “rock-paper-scissors!”) and then show their choice through their hand position, which is either, you guessed it, a rock, paper, or scissors. The winner can be determined by this chart:

Image

How you’re going to play against the computer

Given the computer doesn’t have hands, we’ll have to change the way the game works, at least a little—what we’ll do is have the computer preselect its choice of rock, paper, or scissors, but not tell us. We’ll then enter our choice, and the computer will compare the two before revealing the winner.

It helps to see an example of how the game is going to be played. Below in the Python Shell you’ll find a few rounds of Rock, Paper, Scissors being played that show each possible outcome: the user wins, the computer wins, and a draw.

Image

First, a high-level design

The first thing we need to do is figure out the flow of the game. Let’s put our pseudocode skills to use by reviewing this high-level design for the game. Notice we’ve added something new this time too: a diagram that helps map out the flow of the game in the form of a flowchart.

Here’s the basic idea:

  1. User starts the game.

    1. The computer determines what its choice is going to be: rock, paper, or scissors.

  2. Game play begins.

    1. Get the user’s choice.

    2. Examine the user’s choice. If it is invalid (not rock, paper, or scissors), go back to step 2A.

    3. Determine who wins by the rules of the game.

      If it is the same as the computer’s, set the winner to a tie and move on to step 3.

  3. Game finishes.

    Tell the user who won along with what the computer’s choice was.

Now we have a high-level idea of the kinds of things the program needs to do. Next we’ll dig into each step and figure out a few more details.

Image

The computer’s choice

Looking at our high-level design, the first thing we need to do is have the computer make its choice in the game; that is, it needs to choose rock, paper, or scissors. To make the game interesting, that choice should be random and not predictable by the user.

Making random choices is a task many programs need to perform, and you’ll find practically every programming language provides a way to generate random numbers. Let’s see how we can get a random number in Python, and how we turn that into a choice of rock, paper, or scissors.

Image

How to generate a random number

Python ships with a lot of prebuilt code—that is, code you don’t have to write yourself. You’ll often find prebuilt code supplied in the form of a module (sometimes called a library), and we’ll be discussing modules in detail later in the book. But for now, we’d like to use the random module, and to do that we import it into our code using Python’s import statement.

Here’s how we do that:

Image

After you’ve imported the random module, you’re all ready to make use of the many random functions it provides. We’re going to use just one of them for now:

Note

We’re going to dive into all the specifics of this notation later in the book, but for now, just take it all in.

Image

How to use the random number

Alright, we know how to generate a random number of either 0, 1, or 2, and we’re going to use that number to represent the computer’s game choice. So, if we generate a 0, then the computer’s choice is rock; if it’s a 1, it will be paper; and if it’s 2, the choice is scissors. Let’s write that code:

Image

there are no Dumb Questions

Q: How are random numbers going to help?

A: Think of generating a random number like throwing the dice. In this case we have three choices (rock, paper, scissors), so generating a random number of 0, 1, or 2 is sort of like having a dice with three sides. Once we generate a random number, we’ll then associate that number with our choices, so 0 = rock, 1 = paper, and 2 = scissors.

Q: Why do you start with 0 for the random numbers? Why not generate the numbers 1, 2, and 3? That makes more sense.

A: Ah, not to a computer scientist. Programmers usually think of sequences of numbers starting at zero. This will start to feel more natural (and sensical) as you see it used in a variety of ways in code. For now, just go with the flow.

Q: Are random numbers truly random?

A: No, random numbers generated by a digital computer are pseudorandom, meaning not truly random. Pseudorandom numbers, at some level, have patterns that are predictable, whereas truly random numbers do not. To generate true random numbers, we have to make use of natural phenomena like radioactive decay—not a very convenient method for everyday use. For most programming applications, though, pseudorandom numbers are generally sufficient.

Q: So import gives me a way to access Python code written by someone else?

A: Python developers take useful code and make it available in modules. By using import, you can make use of their code and use it along with your own. For instance, the random module includes many functions you can use to generate random numbers. Right now we’re just making use of the randint function, but we’ll be seeing more of this module as the book progresses.

Taking things further...

By using the random module we’ve now implemented a way for the computer to randomly make its choice, but it’s a little unsatisfying. Why? Well, our goal was to have the computer choose rock, paper, or scissors, and we’ve done that by mapping those choices to the integers 0, 1, and 2, but wouldn’t it be nicer if we had a variable that was set to a string "rock", "paper", or "scissors" instead? Let’s make that happen. But to do that we’re going to have to step back and learn about how to make decisions in Python.

Image

True? Or False?

Python makes decisions by asking questions with yes or no answers, only in Python we call those true or false answers. The questions themselves are just expressions, similar to the expressions you’ve already learned, but instead of evaluating to strings or integers or floating-point numbers, they evaluate to True or False. Here’s an example:

Image

You can assign the result of this expression to a variable as well, and you can even print it if you want.

Image
Image

The values True and False belong to their own data type, the Boolean type. Let’s take a look at it...

Introducing the Boolean type

Oh, forgive us, we’ve been talking about a brand new data type, but we haven’t formally introduced you. The Boolean data type is a simple one; it has only two values, and, as you can guess, they are True and False.

Image

You can treat Booleans like any other type in that you can store them in a variable, print them, or use them in expressions. Let’s get some practice in with them, and then we’re going to see how to use them to make decisions.

Making decisions

Now that we know about Boolean expressions and relational operators, like > and < and ==, we can use them to make decisions in code. To do that we use the if keyword combined with a Boolean expression. Here’s an example:

Image

But we don’t have to stop there: we can supply an alternative set of statements to execute if the conditional expression is False.

Image

Decisions and more decisions

But there’s more: we can even set up a whole series of conditions, by using the elif keyword. Admittedly, elif is a strange keyword, but it’s just a contraction of “else if,” so don’t let it throw you. Let’s see how elif works:

Image
Image

Back to Rock, Paper, Scissors

We’re still finishing up the first stage of our Rock, Paper, Scissors game. Remember, before our Boolean diversion, we wanted to improve our code so that the computer could pick a string, "rock", "paper", or "scissors", instead of a number 0, 1, or 2. Now that you’ve learned how to use Python’s if statement, you’re all set to do that. What we’re going to do now is write some code that, depending on the value of random_choice, sets a new variable, computer_choice, to one of those three strings.

Image
Image

Getting the user’s choice

Now that we have the computer’s choice, it’s time to get the user’s choice. After Chapter 3, you’re a pro at getting user input. Let’s start by prompting the user and storing the response in a variable called user_choice.

Image
Image
Image

Good eye.

We said there were a lot of other useful functions in the random module, and there are. One of those is the function choice, and here’s how it works:

Image

So now that you mention it, this is exactly what we’d want to use if we were to implement this again because this approach uses less code and is more readable. That said, if we’d used this from the beginning of this chapter, we would have had no reason to talk about decisions, or Boolean values, or relational operators, or conditionals, or data types...well, you see the point.

But we’re glad you asked because choice is a great function to use to do just this kind of thing, especially after the next chapter when you totally understand lists.

NOTE: for those Type A’s out there dying to update their code to use random.choice, go for it, if you must. All you need to do is replace every line between the import and the input statement with the code above. That said, this isn’t necessary at this point, but you know how you are, so we’re letting you know.

Taking a look at the user’s choice

Now that we’ve got the user’s choice, let’s examine it. According to our flowchart, we’ve got three possibilities:

Inline The user and the computer made the same choice, and we have a tie.
Inline The user and the computer made different choices, and we need to determine who won.
Inline The user entered an invalid choice, and needs to enter another choice.
Note

This possibility is where the user enters a word that isn’t rock, paper, or scissors; we’re going to come back and handle this case a bit later.

Image

We’ll tackle these in order (saving the last one for a bit later in the chapter), but first, let’s observe that no matter who wins, or if there is a tie, we need some kind of variable to hold that information. So let’s use a variable, winner, that will hold the outcome of the game, which will be either ‘Tie’, ‘User’, or ‘Computer’. Create that variable and give it an initial value like this:

Image

Here we’ve assigned the empty string to the new variable winner as an initial value. An empty string is a string that has no characters in it (yet it’s still a string). You might think of it like this: a laundry basket is still a laundry basket even if it currently has no laundry in it. Right? You’ll find this kind of thing pops up all over programming languages: empty strings, empty lists, empty files, and so on. For us, setting winner to an empty string gives us a way to indicate that winner is going to be a string, even if we’re not yet in a position to put any meaningful characters in it (because we haven’t computed the outcome).

Note

Although there’s nothing wrong with this approach, later in the book we’ll see an alternative, that, for Python, is a better way to provide an initial value for winner.

Now that we’ve created the winner variable to hold the outcome of the game, let’s proceed with implementing the possibilities at the top of the page. Looking at the first item above, where the user and the computer make the same choice, we’ll need to set winner to ‘Tie’. To do that, we need to first write the code to compare the user’s and computer’s choices, and, again, if they are the same, then we’ll set our new winner variable to ‘Tie’.

Adding the code to detect a tie

We’ve got a new variable to add, winner, and we’ve got some new code that compares the user’s and computer’s choice to see if they are the same, in which case we have a tie. Let’s take a look at all the code together:

Image

Who won?

So now that we’ve written the code to deal with a tie, we’re ready for the interesting part of the code: figuring out who won. We already have everything we need to decide a winner—we’ve got the computer’s choice in the variable computer_choice, and we’ve got the user’s choice in the variable user_choice. So what we need at this point is to figure out the logic of determining who won. To do that it really pays to study our Rock, Paper, Scissors diagram to see if we can break the process of determining the winner down into a simple set of rules. Here’s another insight too: if we pick a side, say, by figuring out the ways the computer can win, then we know if the computer doesn’t win, the user does. That can really simplify our logic because we only need to look at one set of cases.

So with that in mind, let’s take a look at all the cases where the computer wins:

Image
Image
Image

How to implement the game logic

As you can see there are three ways the computer can win, and for each way we have to test two conditions, like “did the computer choose paper?” AND “did the user choose rock?” But, so far, in our coding, we’ve never had to test two conditions at once. That said, we do know how to test for a single condition, like, if the computer chose paper:

Image
Image

And if the user chose rock:

Image

But how do we test for both conditions?

To do that we can use a Boolean operator. It sounds fancy, but Boolean operators are just a way to combine Boolean expressions together, and, for now, there are only three of them to know about: and, or, and not.

Note

We’ll see one additional Boolean operator in a bit.

To test if the computer chose paper AND the user chose rock, we can use the and Boolean operator and combine our expressions, like this:

Image

And we can use this Boolean expression with an if statement:

Image

More about Boolean operators

As you’ve already seen, the and operator is True if, and only if, both of its conditions (or we can call them operands) are True. But what about or and not; how do they work? Like and, or is used to combine two Boolean values or expressions, and is True if either of those values or expressions evaluates to True.

Image

The not operator, on the other hand, when placed before any single Boolean value or expression, gives you the opposite of the Boolean value—in other words, if not’s operand evaluates to True then not evaluates to False, and if its operand is False then not evaluates to True. We like to say that not negates its operand.

Image

Display the winner

Now it’s time to display the winner. If you look at the sample output again, either the user or the computer wins, or there’s a tie.

Image
Image

Let’s first take care of the code to handle the tie. Looking at our existing code, if there’s a tie then the winner variable will be assigned to the value ‘Tie’. So, let’s set up a condition for this case:

Image

If there isn’t a tie, we need to announce the winner, which is conveniently stored in the winner variable.

Image

Got documentation?

It’s a good time to step back and look at all the code you’ve written. There’s actually enough code that if you revisited it in the future you might have to remind yourself of what each piece does and how it all fits together. You might also have to study the code to remember the design decisions you made and why you made them.

Also notice that the code has an inherent structure and is pretty well organized in that it’s divided into pieces that handle the parts of our algorithm (or the actions in the corresponding flowchart). Let’s mark these sections and also add some notes to remind us in the future of how all this works.

Note

It’s also handy to document your code for anyone else who might want to take a look at it, like another programmer.

Image

But isn’t this silly that we’re documenting this code in a book? After all, you’ve got real, live code on your computer. Why don’t we document the actual code so the documentation is right there when you need it? Let’s see how to do that.

How to add comments to your code

Note

Comments are one form of documentation; later in the book we’ll look at help documentation, which is meant for coders who just want to use your code, not necessarily understand it.

With Python, and pretty much any programming language, you can add human-readable comments right into your code. To add a comment with Python, type a hash character (#) on any line, and then your comment. Python will conveniently ignore anything you type after the hash. With comments, the general idea is to add remarks to your code that are going to be read by you, or other programmers, with the goal of providing additional information about the design, structure, or approach you used in the code. Let’s look at an example:

Image

We need to finish that game!

You realize that we haven’t quite finished our game, right? Check out the To Do list: we haven’t dealt with that possibility of invalid user input. Now the user is supposed to enter “rock” or “scissors” or “paper,” but they might not; they might mistype, like “scisors,” or they might just be troublemakers who decide to enter “dog,” “hammer,” or “no.” So, when you’re creating an app or program that’s going to be used by actual people, you want to keep in mind that they often make mistakes, and your code needs to deal with that.

Image

So, let’s deal with it.

But first we have to figure out how we want the game to behave when the user enters an invalid answer. Looking back at the flowchart, our original intent was to have the program reprompt the user if the input was invalid.

Image

Perhaps something like this:

Image

Users often make mistakes. Make sure your code anticipates and handles these mistakes—even if the only user is you.

We can always make it more elaborate later, but for now we’ll just reprompt the user until we get a valid input.

Are you ready to get this coded and finish this game? We just need to make sure we know how to approach coding two aspects of this:

  1. How do we detect invalid input?

  2. How do we continually prompt the user until we get a valid answer?

How do we know if the user’s choice is invalid?

How do we detect if the user’s input is invalid? Well, you probably know we’re going to make use of our new Boolean logic skills, but what does an expression that detects invalid answers look like? Sometimes it’s good to just talk things out: we know if the user’s choice is invalid if:

Image

Checking out and cleaning up the expression

Hopefully your Boolean expression in the last Sharpen exercise was close to our solution. Here it is again, this time as part of an if statement:

Image

That looks like a perfectly acceptable statement. But sometimes really long lines like this are quite unwieldy once we start typing them into an editor, or if we have to go back and read them later. It would be nice if we could reformat the statement a bit and make it look more like:

Image

The only problem is when we try to break the code into more than one line, Python complains about our syntax.

There is another way—we can wrap a set of parentheses around the expression, like this:

Image

And Python is just fine with the reformatting of the code.

Okay, now that we know how to detect an invalid user choice, we still need to figure out how to reprompt the user. Let’s spend a little time thinking through how that might work....

Image
Image

No, but we could, and you bring up a good point. First of all, what is the issue here? Well, the strings ‘rock’ and ‘ROCK’, for example, are different strings because Python treats strings as case sensitive. In other words, in Python (and almost every programming language), the following equality test would evaluate to False:

Image

So if the user enters Rock instead of rock, right now our code would say that entry was invalid (and our logic code, for that matter, wouldn’t know what to do with Rock).

That said, the suggestion does seem very reasonable—after all, if you enter the word rock no matter the capitalization, it should count as a valid answer.

So what do we do? Well, we could just add in additional logic to test all permutations of upper- and lowercase letters for the words rock, paper, and scissors, and that would work. However, it would make our code very complex, and there are better ways to approach this problem that we’re going to learn about later in the book.

But right now, let’s just assume that the user needs to enter an answer in lowercase, and we’ll point out how this could have been more easily solved when we get to it later in the book.

How to continually prompt the user

Our first attempt failed. We tried to test the user input and then if it wasn’t valid, prompt again. The problem is, this solution only works once. If the user enters “rocknroll” on the second try, then that string will be accepted as the valid user input.

Now we could keep adding if statements for a second and third and fourth try, but that would lead to a coding mess, and our requirements are to reprompt the user as many times as it takes.

The problem is, given our Python knowledge, we only know how to do things once. What we really need to be able to do is write code in a way that it can repeat over and over, as many times as needed. We need a way to do things more than once.

Image
Image

Doing things more than once

You do a lot of things more than once:

Lather, rinse, repeat

Wax on, wax off

Keep turning the pages of the book, until it’s done.

Of course you’ll often need to do things in code more than once, and Python gives you a couple of ways to repeatedly execute code in a loop using its while and for statements. We’ll look at both of these ways of looping, but let’s focus on while for now.

We’ve talked a lot about expressions that evaluate to Boolean values, like scoops > 0, and these kinds of expressions are the key to the while statement. Here’s how the while statement works:

Image

How the while loop works

Seeing as this is your first while loop, let’s trace through a round of its execution to see exactly how it works. Notice we’ve added a declaration for the variable scoops at the top of the code, and initialized it to the value 5.

Now let’s start executing this code. First we set scoops to 5.

 scoops = 5
 while scoops > 0:
     print(‘Another scoop!’)
     scoops = scoops - 1
 print(“Life without ice cream isn’t the same.”)

A note from readers who have read this before you: read the next several pages slowly and carefully. There’s a lot to take in and you really want to get how this works into your brain.

After that we encounter the while statement. When we evaluate a while statement, the first thing we do is evaluate the conditional to see if it’s True or False.

Image

Because the conditional is True, we start executing the block of code. The first statement in the body prints the string "Another scoop!" to the shell.

Image

The next statement subtracts one from the number of scoops and then sets scoops to that new value, 4.

Image

That’s the last statement in the block, so we loop back up to the conditional and start over again.

Image

Evaluating our conditional again, this time scoops is 4. But that’s still more than zero.

Image

Once again we write the string "Another scoop!" to the shell.

Image

The next statement subtracts one from the number of scoops and sets scoops to that new value, which is 3.

Image

That’s the last statement in the block, so we loop back up to the conditional and start over again.

Image

Evaluating our conditional again, this time scoops is 3. But that’s still more than zero.

Image

Once again we write the string "Another scoop!" to the shell.

Image

And as you can see, this continues. Each time we loop, we decrement (reduce scoops by 1), write another string to the shell, and keep going.

Image

And continues...

Image

Until the last time...this time something’s different. scoops is 0, and so our conditional evaluates to False. That’s it, folks; we’re not going to go through the loop anymore, and we’re not going to execute the block. This time, we bypass the block and execute the statement that follows it.

Image

Now we execute the other print statement, and write the string "Life without ice cream isn’t the same". We’re done!

Image

How to use while to prompt the user until you get a valid choice

Now that you know how to use while, you’re all ready to get this code reprompting the user. To do that we just need to make a couple simple changes to the previous attempt: we’re going to start by initializing user_choice to the empty string, and then we’re going to replace the if keyword with while.

Like this:

Image
Image

Congratulations on coding your first game!

What’s the best thing to do after coding your new game? Play a few rounds, of course! Sit back, relax, and let everything in this chapter sink in as you try to defeat the computer at Rock, Paper, Scissors. Of course, you’re not quite done yet—you’ve still got the extra credit, the bullet points, and a crossword to do, but take some time and enjoy the game first.

Image

Beware of the The Dreaded Infinite Loop

Before we wrap up this chapter we need to talk about infinite loops. You see, when you write code without loops it just goes straight through—you know it’s going to end, someday. But with loops, things get more interesting.

Let’s say you’ve just written your latest code, you feel good about it, and you confidently run it. What happens next? Nothing. Any output slows to a crawl. Your program seems to be doing something, but what, you’re not quite sure. Whatever it is, it’s taking a long time.

You just encountered an infinite loop—that is, a loop that is looping and looping and is never going to end, ever.

It’s easier to get into this situation than it sounds. In fact, sooner or later you’re going to encounter one, so it might as well be now. Let’s create one:

Image

So what do you do when you have an out-of-control program running on your computer? If you’re using IDLE, simply close the shell window to terminate the program. If you’re using your computer’s command line, then typically a tap of Control+C (Ctrl+C on some keyboards) will terminate the program as well.

And what do you do with your code? Well, infinite loops are logic errors. You’ve create some logic that never lets the loop end, so examine the conditional of your loop (or loops) and trace through the execution of your code until you determine what about the conditional logic is wrong. In our case, we simply need to rewrite the counter + 1 as counter - 1, so that the code counts down.

Image

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required