Россия |
Bagels
Topics Covered In This Chapter:
- Hard-coding
- Augmented Assignment Operators, +=, -=, *=, /=
- The random.shuffle() Function
- The sort() List Method
- The join() List Method
- String Interpolation (also called String Formatting)
- Conversion Specifier %s
- Nested Loops
In this chapter you will learn a few new methods and functions that come with Python. You will also learn about augmented assignment operators and string interpolation. These concepts don't let you do anything you couldn't do before, but they are nice shortcuts that make typing your code easier.
Bagels is a simple game you can play with a friend. Your friend thinks up a random 3-digit number with no repeating digits, and you try to guess what the number is. After each guess, your friend gives you clues on how close your guess was. If the friend tells you "bagels", that means that none of the three digits you guessed is in the secret number. If your friend tells you "pico", then one of the digits is in the secret number, but your guess has the digit in the wrong place. If your friend tells you "fermi", then your guess has a correct digit in the correct place. Of course, even if you get a pico or fermi clue, you still don't know which digit in your guess is the correct one.
You can also get multiple clues after each guess. Say the secret number is 456, and your guess is 546. The clue you get from your friend would be "fermi pico pico" because one digit is correct and in the correct place (the digit 6), and two digits are in the secret number but in the wrong place (the digits 4 and 5).
Sample Run
I am thinking of a 3-digit number. Try to guess what it is. Here are some clues: When I say: That means: Pico One digit is correct but in the wrong position. Fermi One digit is correct and in the right position. Bagels No digit is correct. I have thought up a number. You have 10 guesses to get it. Guess #1:
123
Fermi Guess #2:
453
Pico Guess #3:
425
Fermi Guess #4:
326
Bagels Guess #5:
489
Bagels Guess #6:
075
Fermi Fermi Guess #7:
015
Fermi Pico Guess #8:
175
You got it! Do you want to play again? (yes or no)
no
Bagel's Source Code
bagels.py
This code can be downloaded from http://inventwithpyt.hon.com/bagels.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
1. import random 2. def getSecretNum(numDigits): 3. # Returns a string that is numDigits long, made up of unique random digits. 4. numbers = list(range(10)) 5. random.shuffle(numbers) 6. secretNum = '' 7. for i in range(numDigits): 8. secretNum += str(numbers[i]) 9. return secretNum 10. 11. def getClues(guess, secretNum): 12. # Returns a string with the pico, fermi, bagels clues to the user. 13. if guess == secretNum: 14. return 'You got it!' 15. 16. clue = [] 17. 18. for i in range(len(guess)): 19. if guess[i] == secretNum[i]: 20. clue.append('Fermi') 21. elif guess[i] in secretNum: 22. clue.append('Pico') 23. if len(clue) == 0: 24. return 'Bagels' 25. 26. clue.sort() 27. return ' '.join(clue) 28. 29. def isOnlyDigits(num): 30. # Returns True if num is a string made up only of digits. Otherwise returns False. 31. if num == '': 32. return False 33. 34. for i in num: 35. if i not in '0 1 2 3 4 5 6 7 8 9'.split(): 36. return False 37. 38. return True 39. 40. def playAgain(): 41. # This function returns True if the player wants to play again, otherwise it returns False. 42. print('Do you want to play again? (yes or no)') 43. return input().lower().startswith('y') 44. 45. NUMDIGITS = 3 46. MAXGUESS = 10 47. 48. print('I am thinking of a %s-digit number. Try to guess what it is.' % (NUMDIGITS)) 49. print('Here are some clues:') 50. print('When I say: That means:') 51. print(' Pico One digit is correct but in the wrong position.') 52. print(' Fermi One digit is correct and in the right position.') 53. print (' Bagels No digit is correct.') 54 . 55. while True: 56. secretNum = getSecretNum(NUMDIGITS) 57. print('I have thought up a number. You have %s guesses to get it.' % (MAXGUESS)) 58 . 59. numGuesses = 1 60. while numGuesses <= MAXGUESS: 61. guess = '' 62. while len(guess) != NUMDIGITS or not isOnlyDigits (guess): 63. print('Guess #%s: ' % (numGuesses)) 64. guess = input() 65 . 66. clue = getClues(guess, secretNum) 67. print(clue) 68. numGuesses += 1 69 . 70. if guess == secretNum: 71. break 72. if numGuesses > MAXGUESS: 73. print('You ran out of guesses. The answer was %s.' % (secretNum)) 74 . 75. if not playAgain() : 76. break
Designing the Program
Here is a flow chart for this program. The flow chart describes the basic events of what happens in this game, and in what order they can happen:
And here is the source code for our game. Start a new file and type the code in, and then save the file as bagels.py. We will design our game so that it is very easy to change the size of the secret number. It can be 3 digits or 5 digits or 10 digits. We will do this by using a constant variable named NUMDIGITS instead of hard-coding the integer 3 into our source code.
Hard-coding means writing a program in a way that it changing the behavior of the program requires changing a lot of the source code. For example, we could hard-code a name into a print() function call like: print('Hello, Albert'). Or we could use this line: print('Hello, ' + name) which would let us change the name that is printed by changing the name variable while the program is running.
How the Code Works: Lines 1 to 9
At the start of the program we import the random module and also create a function for generating a random secret number for the player to guess. The process of creating this number isn't hard, and also guarantees that it only has unique digits in it.
1. import random
This game imports the random module so we can use the module's random number functions.
Shuffling a Unique Set of Digits
2. def getSecretNum(numDigits): 3. # Returns a string that is numDigits long, made up of unique random digits. 4. numbers = list(range(10)) 5. random.shuffle(numbers)
Our first function is named getSecretNum(), which will generate the random secret number. Instead of having the code only produce 3-digit numbers, we use a parameter named numDigits to tell us how many digits the secret number should have. (This way, we can make the game produce secret numbers with four or six digits, for example, just by passing 4 or 6 as numDigits.)
You may have noticed that the return value of our call to range() was in turn passed to a function called list(). The list() function returns a list value of the value passed to it, much like the str() function returns a string form or the int() function returns an integer form. The reason we do this is because the range() function technically does not return a list but something called an iterator object. Iterators are a topic that you don't need to know at this point, so they aren't covered in this book.
Just about every time we use the range() function it is in a for loop. Iterators are fine to use in for loops (just like lists are), but if we ever want to store a list of integers in a variable, be sure to convert the return value of range() to a list with the list() function first. (Just like we do on line 4.)