Using the Debugger
Find the Bug
Using the debugger is a good way to figure out what is causing bugs in your program. As an example, here is a small program that has a bug in it. The program comes up with a random addition problem for the user to solve. In the interactive shell window, click on File, then New Window to open a new file editor window. Type this program into that window, and save the program as buggy.py.
buggy.py
1. import random 2. number1 = random.randint(1, 10) 3. number2 = random.randint(1, 10) 4. print('What is ' + str(number1) + ' + ' + str(number2) + ' ? ' ) 5. answer = input() 6. if answer == number1 + number2: 7. print('Correct!') 8. else: 9. print('Nope! The answer is ' + str(number1 + number2))
Type the program in exactly as it is above, even if you can already tell what the bug is. Then trying running the program by pressing F5. This is a simple arithmetic game that comes up with two random numbers and asks you to add them. Here's what it might look like when you run the program:
What is 5 + 1?
6
Nope! The answer is 6
That's not right! This program has a semantic bug in it. Even if the user types in the correct answer, the program says they are wrong.
You could look at the code and think hard about where it went wrong. That works sometimes. But you might figure out the cause of the bug quicker if you run the program under the debugger. At the top of the interactive shell window, click on Debug, then Debugger (if there is no check already by the Debugger menu item) to display the Debug Control window. In the Debug Control window, make sure the all four checkboxes (Stack, Source, Locals, Globals) are checked. This makes the Debug Control window provide the most information. Then press F5 in the file editor window to run the program under the debugger.
The debugger starts at the import random line. Nothing special happens here, so just click Step to execute it. You should see the random module at the bottom of the Debug Control window in the Globals area.
Click Step again to run line 2. A new file editor window will pop open. Remember that the randint() function is inside the random module. When you stepped into the function, you stepped into the random module because that is where the randint function is. The functions that come with Python's modules almost never have bugs in their code, so you can just click Out to step out of the randint() function and back to your program. After you have stepped out, you can close the random module's window.
Line 3 is also a call to the randint() function. We don't need to step through this code, so just click Over to step over this function call. The randint() function's code is still executed, it is just executed all at once so that we don't have to step through it.
Line 4 is a print() call to show the player the random numbers. But since we are using the debugger, we know what numbers the program will print even before it prints them! Just look at the Globals area of the Debug Control window. You can see the number1 and number2 variables, and next to them are the integer values stored in those variables. When I ran the debugger, it looked like this:
The number1 variable has the value 9 and the number2 variable has the value 10. When you click Step, the program will display the string in the print() call with these values. (Of course, we use the str() function so that we can concatenate the string version of these integers.)
Clicking on Step on line 5 will cause the debugger to wait until the player enters a response. Go ahead and type in the correct answer (in my case, 19) into the interactive shell window. The debugger will resume and move down to line 6.
Line 6 is an if statement. The condition is that the value in answer must match the sum of number1 and number2. If the condition is True, then the debugger will move to line 7. If the condition is False, the debugger will move to line 9. Click Step one more time to find out where it goes.
The debugger is now on line 9! What happened? The condition in the if statement must have been False. Take a look at the values for number1, number2, and answer. Notice that number1 and number2 are integers, so their sum would have also been an integer. But answer is a string. That means that the answer == number1 + number2 condition would have evaluated to '19' == 19. A string value and an integer value will always not equal each other, so the condition would have evaluated to False.
That is the bug in the program. The bug is that we use answer when we should be using int(answer). Go ahead and change line 6 to use int(answer) == number1 + number2 instead of answer == number1 + number2, and run the program again.
What is 2 + 3?
5
Correct!
This time, the program worked correctly. Run it one more time and enter a wrong answer on purpose to make sure the program doesn't tell us we gave the correct answer. We have now debugged this program. Remember, the computer will run your programs exactly as you type them, even if what you type is not what you intend.
Break Points
Stepping through the code one line at a time might still be too slow. Often you will want the program to run at normal speed until it reaches a certain line. You can do this with break points. A break point is set on a line when you want the debugger to take control once execution reaches that line. So if you think there is a problem with your code on, say, line 17, just set a break point on line 17 and when execution reaches that line, the debugger will stop execution. Then you can step through a few lines to see what is happening. Then you can click Go to let the program execute until it reaches the end (or another break point).
To set a break point, right-click on the line that you want a break point on and select "Set Breakpoint" from the menu that appears. The line will be highlighted with yellow to indicate a break point is on that line. You can set break points on as many lines as you want. To remove the break point, click on the line and select "Clear Breakpoint" from the menu that appears.
Example of Using Break Points
Let's try debugging a program with break points. Here is a program that simulates coin flips by calling random.randint(0, 1). Each time this function call returns the integer 1, we will consider that "heads" and increment a variable called heads. We will also increment a variable called flips to keep track of how many times we do this "coin flip".
The program will do "coin flips" one thousand times. This would take a person over an hour to do, but the computer can do it in one second! Type in the following code into the file editor and save it as coinFlips.py. You can also download this code from http://inventwithpython.com/coinFlips.py
coinFlips.py
This code can be downloaded from http://inventwithpython.com/coinFlips.py
If you get errors after typing this code in, compare it to the book's code with the online
diff tool at http://inventwithpython.com/diff or email the author at
al@inventwithpython.com
1. import random 2. print('I will flip a coin 1000 times. Guess how many times it will come up heads. (Press enter to begin)') 3. input () 4. flips = 0 5. heads = 0 6. while flips < 1000: 7. if random.randint(0, 1) == 1: 8. heads = heads + 1 9. flips = flips + 1 10 . 11. if flips == 900: 12. print('900 flips and there have been ' + str (heads) + ' heads.') 13. if flips == 100: 14. print('At 100 tosses, heads has come up ' + str (heads) + ' times so far.') 15. if flips == 500: 16. print('Half way done, and heads has come up ' + str(heads) + ' times.') 17. 18. print() 19. print('Out of 1000 coin tosses, heads came up ' + str (heads) + ' times!') 20. print('Were you close?')
The program runs pretty fast. It probably spent more time waiting for the user to press the Enter key than it did doing the coin flips. Lets say we wanted to see it do coin flips one by one. On the interactive shell's window, click on Debug and then Debugger at the top menu to bring up the Debug Control window. Then press F5 to run the program.
The program starts in the debugger on line 1. Press Step three times in the Debug Control window to execute the first three lines. You'll notice the buttons become disabled because the input() function has been called and the interactive shell window is waiting for the player to type something. Click on the interactive shell window and press Enter. (Be sure to click beneath the text in the shell window, otherwise IDLE might not receive your keystrokes.) After entering text for the input() call, the Step buttons will become enabled again.
You can click Step a few more times, but you'll find that it would take quite a while to get through the entire program. Instead, set a break point on lines 12, 14, and 16 (Figure 7.6).
After setting the breakpoints, click Go in the Debug Control window. The program will run at its normal speed until it reaches flip 100. On that flip, the condition for the if statement on line 13 is True. This causes line 14 (where we have a break point set) to execute, which tells the debugger to stop the program and take over. Look at the Debug Control window in the Globals section to see what the value of flips and heads are.
Click Go again and the program will continue until it reaches the next break point on line 16. Again, see how the values in flips and heads have changed. You can click Go one more time to continue the execution until it reaches the next break point.
And if you click Go again, the execution will continue until the next break point is reached, which is on line 12. You probably noticed that the print() functions on lines 12, 14 and 16 are called in a different order than they appear in the source code. That is because they are called in the order that their if statement's condition becomes True. Using the debugger can help make it clear why this is.
Summary
Writing programs is only part of the work for making games. The next part is making sure the code we wrote actually works. Debuggers let us step through the code one line at a time, while examining which lines execute (and in what order) and what values the variables contain. When this is too slow, we can set break points and click Go to let the program run normally until it reaches a break point.
Using the debugger is a great way to understand what exactly a program is doing. While this book provides explanations of all the games in it, the debugger can help you find out more on your own.