PROJECT 3
Guess what? You’re going to create a game where the computer chooses a number and the player guesses the number.
This project teaches you how to get input from the user and how numbers work in Python (they’re different from strings). You create a random number and in the process reuse someone else’s code in your own projects. You also do a little bit of debugging and tracking down logical errors.
In the guessing game, Python thinks of a random number. The player guesses the number.
If you did Project 2, you already know how to tell the player something — that’s what print is for. However, what you don’t know about the game at the moment is how to get Python to think of a random number, or how to get a guess from the player.
In practice, your program will always need some kind of input from the user. You’re still using the command line right now, so text is the only way to get input. You get text from a user by using a special built-in called raw_input. It waits for the user to type something and returns to Python what the user types.
Open a Python (command line) prompt by clicking the Python (command line) application that you pinned to your Start menu in Project 1 and try it now:
Press Enter.
You get a blank line.
What you typed in Step 1 will echo back to you:
>>> raw_input()
I am typing a response. Python won't see this until I press Enter.
"I am typing a response. Python won't see this until I press Enter."
When you type your response and press Enter, whatever you typed is echoed back to the terminal surrounded by single or double quotes. Python was echoing your literals the same way when you entered literals in Project 2. Does this give you an idea how to capture input?
You can make raw_input() friendlier by giving it a literal. raw_input() prints the literal to the screen before getting the player's response. In this code, "What is your guess?" is the literal.
>>> raw_input("What is your guess?")
What is your guess?17
'17'
Python does exactly what you tell it to do, and in this case that’s not so great. It doesn’t insert a space between your string prompt and where the player starts typing. To make it friendlier still, insert your own space at the end of the prompt.
>>> raw_input("What is your guess? ")
What is your guess? 17
'17'
The literal name is between the parentheses. You could replace the name with a variable pointing to a literal:
>>> prompt = 'What is your guess? '
>>> raw_input(prompt)
What is your guess? 17
'17'
How do you capture what the user types? Well, you do the same as you do for any other literal — you name it. In the following code, it’s named players_guess.
>>> prompt = 'What is your guess? '
>>> players_guess = raw_input(prompt)
What is your guess? 17
>>>
>>> players_guess
'17'
The number 17 isn’t echoed immediately because it has been stored in the variable named players_guess. That means you can use that response later in your program.
Python uses a special symbol to test whether two things are equal. That symbol is two equals signs together, like this: ==. To test whether the name a contains the value 1 you would type: a == 1. Try it yourself:
>>> a = 1
>>> a == 1
True
>>> a = 2
>>> a == 1
False
Just to reinforce the point that = means an assignment, try these:
>>> 1 == 1
True
>>> 1 == 2
False
>>> 1 = 2
File "<stdin>", line 1
SyntaxError: can't assign to literal
The first and third lines of code ask Python if 1 is equal to 1 (1 == 1) and if 1 is equal to 2 (1 == 2). In the fifth line you’re telling Python to take the value 2 and store it in the value 1 (1 = 2).
You get a syntax error because 1 isn’t (and can’t be) the name of a variable. You can see that the error text explains that Python can't assign to literal.
When Python sees ==, it compares the value on the left against the value on the right. If they’re equal, Python replaces the entire statement with the value True. If they aren’t equal, Python replaces the entire statement with the value False.
The equals sign and doubled equals sign are called operators. They work, or operate, on the things on either side of them. Table 3-1 has more common operators. Whenever you want to compare, add, subtract, and so on, use an operator. You need them a lot.
Table 3-1 Common Python Operators
Operator |
Name |
Effect |
Examples |
+ |
Plus |
Add two numbers. Join two strings together. |
Add: >>> 1+1 2 Join: >>> 'a'+'b' 'ab' |
– |
Minus |
Subtract a number from another. Can’t use for strings. |
>>> 1-1 0 |
* |
Times |
Multiply two numbers. Make copies of a string. |
Multiply: >>> 2*2 4 Copy: >>> 'a'*2 'aa' |
/ |
Divide |
Divide one number by another. Can’t use for strings. Python uses / because there’s no ÷ on your keyboard. |
Ermagaaard! It’s complicated. See the next section. |
% |
Remainder (Modulo) |
Give the remainder when dividing the left number by the right number. Formatting operator for strings. (See Project 8.) |
>>> 10%3 1 |
** |
Power |
x**y means raise x to the power of y. Can’t use for strings. |
>>> 3**2 9 |
= |
Assignment |
Assign the value on the right to the variable on the left. |
>>> a = 1 |
== |
Equality |
Is the left side equal to the right side? True if so; is False otherwise. |
>>> 1 == 1 True >>> 'a' == 'a' True |
!= |
Not equal |
Is the left side not equal to the right side? True if so; is False otherwise. |
>>> 1 != 1 False >>> 1 != 2 True >>> 'a' != 'A' True |
> |
Greater than |
Is the left side greater than the right side? >= means greater than or equal to |
>>> 2 > 1 True |
< |
Less than |
Is the left side less than the right side? <= means less than or equal to |
>>> 1 < 2 True |
& (or and) |
And |
Are both left and right true? Usually for complex conditions where you want to do something if everything is true: while im_hungry and you_have_food: |
>>> True & True True >>> True and False False >>> True & (1 == 2) False |
| (or or) |
Or |
Is either left or right true? Usually for complex conditions where you want at least one thing to be true: while im_bored or youre_bored: |
>>> True | False True >>> True or False True >>> False | False False >>> (1 == 1) | False True |
Division in Python 2.7 is a little complicated. Python tries to be helpful when you divide, but usually it hinders more than helps. The reason is that Python changes the answer if one of your numbers has a decimal point in it.
Python treats decimal numbers — called floats, short for floating point numbers — differently from numbers that don’t have decimals. (A number without a decimal is called an integer, a whole number, or an int.) If you try to divide an integer by an integer in Python 2.7, the answer is rounded down to the nearest integer. Ironic, since it’s Python 2.7 and all.
When you want to divide something in Python, use the / operator, like this:
>>> 3/2
1
>>> -3/2
-2
You didn’t get 1.5 or -1.5. You got 1 and -2. You get -2 because Python rounds the negative number -1.5 down and the next integer less than -1.5 is -2.
In this exercise make sure you add a dot (a period, full stop, decimal point — whatever you want to call it) after either the 3 or the 2 (or after both):
>>> 3/2.
1.5
If the number’s an integer and is stored in a variable, you don’t have anywhere to put your decimal point. In that case, use the float() built-in to convert it to a float, like you see here with 3/float(a):
>>> a=2
>>> 3/a
1
>>> 3/float(a)
1.5
>>>
Remember this code (you don’t need to retype this):
>>> prompt = 'What is your guess? '
>>> raw_input(prompt)
What is your guess? 17
'17'
When Python echoes the input back, the input is in single or double quotes. This should be a flag to you that Python sees these responses as strings. Python doesn’t even see the '17' as a number.
A quick way to test whether Python thinks something is a number is by trying to add a number to it:
>>> a=1
>>> a+1
2
First you store 1 in a. Then you add 1 to it. No problem, because a has a number in it. However, when you try to add 1 to players_guess:
>>> players_guess = raw_input(prompt)
What is your guess? 17
>>> players_guess+1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects
Problem! Something’s wrong with players_guess. Python tells you that it “cannot concatenate 'str' and 'int' objects”. Python isn’t even trying to add them — concatenate means to join them. Confirm what players_guess is by printing it out:
>>> players_guess
'17'
As you expect, players_guess is '17'. (Note the single quotes.) The ones around 17 keep Python from understanding it as a number.
Say that the computer has come up with 17. To compare the variable players_input to this number, follow these steps:
>>> computers_number = 17
>>> computers_number == players_guess
False
This code — computers_number == players_guess — asks if 17 is equal to '17'. At the moment, the comparison is False. That's because the player's guess is still stored as a string and a string is never equal to a number. So the comparison will always be False.
>>> computers_number == int(players_guess)
True
The int built-in takes a string that has a whole number and changes it into something Python recognizes as a number. The int() built-in will fail if the string has a decimal number or doesn't have a number. Empty spaces before and after the number are okay.
Here are some examples:
>>> int('1.0')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '1.0'
>>> int('1 fine day')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '1 fine day'
Finally, int can deal with extra spaces around an otherwise valid number:
>>> int(' 17 ')
17
When you can compare the guess with the answer, you can print a response for the player.
Check out this block of code:
>>> if computers_number == int(players_guess):
… print("Correct!")
…
Correct!
The if statement is here in all its glory. This if is another one of Python’s keywords. The statement is followed immediately by a condition: Is computers_number equal to players_guess after it’s converted to an integer? This is followed by a colon, also known as “Hey Python, expect a code block.”
If the condition is true, Python executes (runs) the code block. If it isn’t true, Python skips the code block and continues execution at the next statement after the end of the code block. You can think of if like an if … then phrase in English (minus the then).
>>> if 1==2:
… print("Correct!")
…
>>> if 1==1:
… print("Correct!")
…
Correct!
In the first example, since 1 isn’t equal to 2, the print statement ("Correct!") was skipped. Nothing happened. If there were more to the code block, the whole code block would be skipped. In the second example, 1 is equal to 1, so the print statement is executed. You get a big, juicy Correct!
What happens if the player guesses the wrong answer? Python’s got your back on that too.
In the following code block, you see the if keyword. The else keyword changes the operation of the if keyword. Unlike if, else doesn’t take a condition. It’s always executed when a preceding if code block isn’t executed. You use if/else where you want to execute one of two code blocks, but not both.
The following code continues from where you left off. It assumes that computers_number = 17 and players_guess = '17'.
>>> if computers_number == int(players_guess)+1:
… print('Correct!')
… else:
… print('Wrong! Guess again')
…
Wrong! Guess again
In this code I added +1 to make the values different on purpose. Otherwise, the else: code block won’t execute.
You can see that else is followed by a colon and — not much of a surprise here — a code block. You know it’s a code block because it’s indented by four spaces from the indent level of the else. (Speaking of indentation: Line up the beginning of the else keyword with the beginning of the if keyword before it.)
If the if condition is True, then its code block is executed and the code block following the else is ignored. The keyword else means “Otherwise, do this stuff.” If the condition is False, then the code block following the if is ignored and the one after the else is executed.
Even this doesn’t say why the guess is wrong. You have to show players whether the guess is too high or too low.
What you need is a test. (I know, a test is probably the last thing you really need.) The test tells players whether they’re right on, too low, or too high.
>>> if computers_number == int(players_guess):
… print('Correct!')
… elif computers_number > int(players_guess):
… print('Too low')
… else:
… print('Too high')
…
Correct!
For example, suppose you want to do different things if a is equal to 1, 2, and 3 respectively. You can use the elif structure to test each individually:
>>> a = 3
>>> if a == 1:
… print('a is 1!')
… elif a == 2:
… print('a is 2!')
… elif a == 3:
… print('a is 3!')
… else:
… print("I don't know what a is")
…
a is 3!
Test that this elif structure works. Tell the player when the guess is too high or too low:
>>> computers_number = 16
>>> if computers_number == int(players_guess):
… print('Correct!')
… elif computers_number > int(players_guess):
… print('Too low')
… else:
… print('Too high')
…
Too high
>>> computers_number = 18
>>> if computers_number == int(players_guess):
… print('Correct!')
… elif computers_number > int(players_guess):
… print('Too low')
… else:
… print('Too high')
…
Too low
Retype that. Yes, really. It’s tedious but life will get easier in Project 4. Other things to notice about this code:
Now you know how to ask for the player’s guess, how to convert (change) the answer into a number, and how to compare the player’s answer to the computer’s number.
You still need to set something up so that the computer keeps asking if the player doesn’t guess the number. You do this by
>>> computers_number = 17
>>> prompt = 'What is your guess? '
>>> while True:
… players_guess = raw_input(prompt)
… if computers_number == int(players_guess):
… print('Correct!')
… break
… elif computers_number > int(players_guess):
… print('Too low')
… else:
… print('Too high')
…
What is your guess? 3
Too low
What is your guess? 93
Too high
What is your guess? 50
Too high
What is your guess? 30
Too high
What is your guess? 20
Too high
What is your guess? 10
Too low
What is your guess? 19
Too high
What is your guess? 16
Too low
What is your guess? 18
Too high
What is your guess? 17
Correct!
The break statement is bold here so you can see it easily. The break statement allows you to exit any loop that Python’s already in, regardless of conditions on the enclosing loops. For example, say you have a whole heap of colors and want to see if the red is one of them. You’d set up a loop to run through all the colors. But if you found red halfway through (or even on the first try), there’d be no point continuing. You could use break to stop the loop.
>>> break
File "<stdin>", line 1
SyntaxError: 'break' outside loop
If you have a loop inside a loop, the break applies to the loop level that the break appears in (or its relevant flow control statement, because it’s almost always subject to a condition).
Here are some examples of how break works. In the next code excerpt:
>>> for i in range(3):
… for j in range(3):
… print(str(i)+", "+str(j))
… if i == 1:
… break
0, 0
0, 1
0, 2
1, 0
2, 0
2, 1
2, 2
To break the outer loop, you need to be in the outer loop’s code block. In particular, the code if i == 1: is indented by four spaces, not eight as in the previous example.
Here, the outer loop stops when i is 1. The outer loop never makes it to 2:
>>> for i in range(3):
… for j in range(3):
… print(str(i)+", "+str(j))
… if i == 1:
… break
0, 0
0, 1
0, 2
1, 0
1, 1
1, 2
Take a moment to think about what’s happening with these loops, and try some of your own.
The player’s guessing numbers. How do you get Python to think of a number?
The random integer feature randint is from the random module. It gives you a random number between two numbers that you put in the parenthesis that follows randint. The number will include the lowest or highest number. If you want a number between 6 and 10 (inclusive), use random.randint(6,10).
>>> import random
>>> random.randint(1,100)
67
>>> help(random.randint)
Because this is a new feature, type help(random.randint) at the Python prompt and read what it tells you. If you don’t get back the >>> prompt, press q when you’re finished.
>>> random.randint(1,100)
15
>>> random.randint(1,100)
72
>>> random.randint(1,100)
25
>>> random.randint(1,100)
36
>>> random.randint(1,100)
90
>>> random.randint(1,100)
81
>>> random.randint(1,100)
23
Simple, huh? See how the number is changing?
But you can’t just up and use random.randint like you can with built-ins such as raw_input and str. To use random.randint, you must tell Python that you want to use a function from the random module — import random.
This code example uses a new keyword. The import statement makes a module (in this case the random module) available to Python. You can use all the features in the module after you import it.
You have already met some features — int, range, and raw_input. But you didn’t need to import anything to use them because they’re built in to Python (which is why they’re called built-ins). They’re always available when Python is running.
The random module isn’t a built-in, so it isn’t always available. You get an error if you start a new Python prompt and try random.randint(1,100):
Python 2.7.3 (default, Apr 14 2012, 08:58:41) [GCC] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> random.randint(1,100)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'random' is not defined
The random module does come with Python, but unlike the built-ins, it isn’t loaded automatically. The random module is part of the Python standard library. If you need it, you must import it.
Why can’t Python just automatically load everything? Because it would make programming boring. Every time you ran a program, you’d need to wait while it found and loaded all the modules in the standard library.
Besides giving you access to the standard library, the import statement also lets you use modules that someone else has written (called third-party modules). To use those, you have to download and install them on your computer.
The dot syntax is another important part of what’s going on in this line of code: >>> random.randint(1,100). You put the random module and the randint feature together with a dot.
The built-ins you saw earlier were referred to directly by their names. This way of referencing (called a namespace) turns out to be one honking great idea.
If you always refer to these features by their names without identifying their modules, then you’d have trouble. What if you had two modules with a feature of the same name? It would be like everyone in class being named Jaden. Python would have the same trouble if you didn’t identify a feature by its module name and its feature name.
It’s time to finish off the guessing game. The final touches on the guessing game turn out to be pretty straightforward. At the start of the program you
When you add these changes to the code, you get this. The first three lines are new:
>>> import random
>>>
>>> computers_number = random.randint(1,100)
>>> prompt = 'What is your guess? '
>>> while True:
… players_guess = raw_input(prompt)
… if computers_number == int(players_guess):
… print('Correct!')
… break
… elif computers_number > int(players_guess):
… print('Too low')
… else:
… print('Too high')
…
What is your guess? 24
Too low
What is your guess? 86
Too low
What is your guess? 94
Too low
What is your guess? 98
Too high
What is your guess? 96
Too high
What is your guess? 95
Correct!
This project covered: