We included lots of challenges throughout Captain Code, sometimes multiple per chapter. If you need some help, you’ll find solutions to the challenges presented here. Click on any challenge below to expand it.
Just keep in mind that there is never one solution to any coding challenge, so if your solutions look different but produce the desired result, that’s ok.
Part 1: It’s All Fun & Games
Challenge 2.1
So, here is your first Challenge. Hello4.py created two variables, firstName and lastName, and then combined them into a new variable, named fullName. Modify that code so that it asks the user for a first name and last name instead of using the hard-coded values.
Here’s a hint: You only need to change the first two lines of code so that each line uses an input() function. Can you figure this one out?
firstName = input("What is your first name? ") lastName = input("What is your last name? ") fullName = firstName + " " + lastName print("Hello, my name is", fullName, "and I'm a coder!")
Challenge 2.2
Make your Mad Lib interesting by prompting for at least 15 different words. And then personalize it. At the start, when you provide instructions, ask the user for their name, and then use that in the instructions to create a more personalized experience.
print("Hello, please answer the following prompts.") print() animal = input("Enter an animal: ") name = input("Enter a name: ") adjective1 = input("Enter an adjective: ") color1 = input("Enter a color: ") adjective2 = input("Enter an adjective: ") noun1 = input("Enter a noun: ") noun2 = input("Enter a noun: ") noun3 = input("Enter a noun: ") adjective3 = input("Enter an adjective: ") color2 = input("Enter a color: ") noun4 = input("Enter a noun: ") verb = input("Enter a verb: ") adjective4 = input("Enter an adjective: ") color3 = input("Enter a color: ") animal2 = input("Enter an animal: ") print("Thank you. Here is your story.") print() print("I have a pet", animal, "named", name, ".") print("He is", adjective1, ",", color1, ", and", adjective2, ".") print(name, "eats", noun1, ",", noun2, ", and", noun3, ".") print("His favorite toy is a", adjective3, color2, noun4, ".") print("He likes to", verb, "with his friend the", color3, adjective4, animal2)
Challenge 3.1
Ok, this one is a little harder, but you can do it, promise! See that list with the five animals? Write code that creates two lists, one with animals, like this:
animals=["ant","bat","cat","dog","eel"]
You can use your own list of animals, and you can have more than five (the more the better).
Then create a similar list of adjectives, things like big, green, smelly, cute, and so on. (Again, the more, the better. And it doesn’t matter whether or not your two lists have the same number of items.)
Then pick a random adjective and a random animal and save each to a variable. (You’ll need two variables: one for your animal and one for your adjective.) Then print() the choices so that the output is something like I have a cute eel. Each time you run the app, you’ll get a different combination.
import random animals=["ant","bat","cat","dog","eel"] adjectives=["big","small","cute","smelly","shiny"] choiceAnimal = random.choice(animals) choiceAdjective = random.choice(adjectives) print("I have a", choiceAdjective, choiceAnimal)
Challenge 3.2
Most dice we use have 6 sides, but some games use dice with more sides. And, actually, Ancient Greeks and Romans used dodecahedron shaped dice, which have 12 sides! So, just in case you ever run into an ancient Greek or Roman, write code that rolls a 12-sided die.
# Imports import random # Roll the die die = random.randrange(1, 13) # Display value of dodecahedron die! print("You rolled a", die, "!")
Challenge 4.1
Modify Date2.py to also display the current time. The properties you want are called hour and minute.
# Imports import datetime # Get today's date today = datetime.datetime.now() # Print today's year, month, day, hour, and minute print("The year is", today.year) print("The month is", today.month) print("The day is", today.day) print("The hour is", today.hour) print("The minute is", today.minute)
Challenge 5.1
You wouldn’t want someone else with the same name getting the same advantage. How could you modify this code to make it a little more private? You could require the name to be typed a specific way (all lowercase maybe), or have a space or two at the end. Come up with an option and modify the if statement to check for it.
# Imports import random # Ask the user for their name name=input("What is your name?: ") print("Hello", name, "let's play Rock, Paper, or Scissors") # Computer picks one cChoice = random.choice("RPS") # Get user choice print("Rock, Paper, or Scissors?") uChoice=input("Enter R, P, S: ").upper().strip() # TOP SECRET CODE - Modified for secrecy! # ('$' and space before and after) if name == " $hmuel ": if uChoice == "R": cChoice = "S" elif uChoice == "P": cChoice = "R" elif uChoice == "S": cChoice = "P" # Compare choices if cChoice == uChoice: print("It's a tie!") elif uChoice == "R" and cChoice == "P": print("You picked rock, computer picked paper. You lose.") elif uChoice == "P" and cChoice == "R": print("You picked paper, computer picked rock. You win.") elif uChoice == "R" and cChoice == "S": print("You picked rock, computer picked scissors. You win.") elif uChoice == "S" and cChoice == "R": print("You picked scissors, computer picked rock. You lose.") elif uChoice == "P" and cChoice == "S": print("You picked paper, computer picked scissors. You lose.") elif uChoice == "S" and cChoice == "P": print("You picked scissors, computer picked paper. You win.") else: print("Not very good at listening to instructions. Huh?")
Challenge 6.1
range() takes an optional third argument—a step. If you specify range(1, 11, 2), the loop counter will increase by 2 each time, so the loop will run 5 times instead of 10 (for 1, 3, 5, 7, and 9). Try to create a loop that displays the numbers 10, 20, 30, all the way to 100.
# Loop from 10 to 100, counting by 10 for i in range(10, 101, 10): # Display i in each loop iteration print(i)
Challenge 6.2
As you have seen, Encrypt.py and Decrypt.py are almost identical. In truth, they should have been the same program. We just separated them to make the code a little simpler.
action = input("Encrypt or decrypt? Enter E or D: ")
Then, in your code, you can use if statements to select the encrypt or decrypt versions of the code, based on action being E or D.
# ASCII range of usable characters - anything out of this range could throw errors asciiMin = 32 # Represents the space character - " " asciiMax = 126 # Represents the tilde character - "~" # Secret key key = 314159 # Top secret! This is the encryption key! key = str(key) # Convert to string so can access individual digits # Determine if encrypting or decrypting action = input("Encrypt or decrypt? Enter E or D: ") # Get input message (either encrypted message to decrypt or message to be encrypted) if action == 'E': message = input("Enter message to be encrypted: ") elif action == 'D': message = input("Enter message to be decrypted: ") # Initialize variable for encrypted message messEncr = "" # Only run loop if entered E or D if action in ['D', 'E']: # Loop through message for index in range(0, len(message)): # Get the ASCII value for this character char = ord(message[index]) # Is this character out of range? if char < asciiMin or char > asciiMax: # Yes, not safe to encrypt, leave as is messEncr += message[index] else: # Safe to encrypt or decrypt this character if action == 'E': # Encrypt and shift the value as per the key ascNum = ord(message[index]) + int(key[index % len(key)]) # If shifted past range, cycle back to the beginning of the range if ascNum > asciiMax: ascNum -= (asciiMax - asciiMin + 1) elif action == 'D': # Decrypt and shift the value as per the key ascNum = ord(message[index]) - int(key[index % len(key)]) # If shifted past range, cycle back to the end of the range if ascNum < asciiMin: ascNum += (asciiMax - asciiMin + 1) # Convert to a character and add to output messEncr = messEncr + chr(ascNum) # Display result print("Encrypted message:", messEncr) else: print("You must ener E or D only.")
Challenge 7.1
Double challenge for you this time.
First, look at the final print() statement. It displays the sorted list, but the output doesn’t look that good. So change that output to use a for loop printing the sorted animals one per line.
Second, make sure the user doesn’t type an animal already in the list. How? Refer back to Chapter 6 if you need a reminder of how to check if an item is in a list. Then modify the if statement so that in addition to checking for the length of the input, it also checks to ensure that the item is not already in the list. Your condition will have two parts, and you’ll want to use and to join them.
# Create the empty animals array animals = [] # Variable for input userInput = " " # Give instructions to user print("I can sort animals for you.") print("Enter your animals, one at a time.") print("When you are done just press Enter.") # Loop until get an empty string while userInput != "": # Get input userInput=input("Enter an animal, leave empty to end: ").strip() # Make sure it is not empty or duplicate # Note, the 'not' says "if userInput" isn't in the animals array" # See later in Chapter 7 for more information if len(userInput) > 0 and userInput not in animals: # It's not empty, add it animals.append(userInput) # Sort data animals.sort() # Display the list - formatted for animal in animals: print(animal)
Challenge 7.2
This one is tricky, but you can do it. Can you provide more feedback? Instead of always displaying Too low
or Too high
, can you display Too low
or Too high
if they are close and Much too high
and Much too low
if they are way off? Think about it.
# Guess the number between a specified range. # User is told if the number guess is too high or too low. # Game tells the user how many guesses were needed # Imports import random # Define variables guesses = 0 # To keep track of how many guesses numMin = 1 # Start of number range numMax = 100 # End of number range userInput = "" # This holds the user's input userGuess = 0 # This holds the user's input as a number farOff = 15 # This is how far off will be considered way off # Generate random number randNum = random.randrange(numMin, numMax+1) # Instructions for user print("I am thinking of a number between", numMin, "and", numMax) print("Can you guess the number?") # Loop until the user has guessed it while randNum != userGuess: # Get user guess userInput=input("Your guess: ").strip() # Make sure the user typed a valid number if not userInput.isnumeric(): # Input was not a number print(userInput, "is not a valid number!") else: # Input was a number, good to proceed # Increment guess counter guesses=guesses+1 # Convert the input text to a number userGuess=int(userInput) # Check the number if userGuess < numMin or userGuess > numMax: print(userGuess, "is not between", numMin, "and", numMax) elif userGuess + farOff < randNum: print("Way too low. Try again.") elif userGuess < randNum: print("Too low. Try again.") elif userGuess - farOff > randNum: print("Way too high. Try again.") elif userGuess > randNum: print("Too high. Try again.") else: print("You got it in", guesses, "tries") # Goodbye message print("Thanks for playing!")
Challenge 9.1
On second thought, we handled the user input badly in this code. Why? If the user entered too many characters, we opted to use the first of them and ignore the rest. That works. But what if the user enters no characters at all? That’s a situation we didn’t plan for! Oops!
No worries, that’s why coders write version 2 (or version 1.1, you get the idea) of their apps. So, update the code so that it catches all invalid input lengths (too long or too short). You can do this with a while loop, like this:
currGuess = ""
while len(currGuess) != 1:
# Imports import random # Variables maxLives = 7 # Maximum tried maskChar = "_" # Mask character livesUsed = 0 # Try counter guessedLetters = [] # To store guesses wordLetters = [] # All of the letters in the word victory = False # Will be True if word is guessed #Game words gameWords = ["anvil", "boutique", "cookie", "fluff", "jazz", "pneumonia", "sleigh", "society", "topaz", "tsunami", "yummy", "zombie", "rhythm"] # Pick the word for the game gameWord = random.choice(gameWords) # Get all the letters in this word for letter in gameWord: # Make sure we don't have this one in the list letterInList = False # Loop through the letters for wl in wordLetters: # Check each one if wl == letter: # We have this one letterInList = True # If we don't have it yet if not letterInList: # Add it to the list wordLetters.append(letter) # Now sort the list wordLetters.sort() # Actual game starts here # Loop until out of tries or guessed word correctly while livesUsed < maxLives and victory == False: # First we need to mask the word # Start with an empty string displayWord = "" # Loop through word for letter in gameWord: # Has this letter been guessed? if letter in guessedLetters: # This one has been guessed so add it displayWord += letter else: # This one has not been been guessed so mask it displayWord += maskChar # Display masked word print(displayWord) # Next we need to display any letters already guessed # Are there any guessed letter if len(guessedLetters) > 0: # There are, start with an empty string youTried="" # Add each guessed letter for letter in guessedLetters: youTried += letter # Display them print("You tried:", youTried) # Get a guess currGuess = "" # Make sure it's just one character while len(currGuess) != 1: currGuess = input("Guess a letter ").lower() # Don't allow repeated guess if currGuess in guessedLetters: print("You already guessed", currGuess) # If letter wasn't repeated else: # Save it to guessed letter list guessedLetters.append(currGuess) # And sort it guessedLetters.sort() # Is it a correct guess? if currGuess in gameWord: # Correct answer print ("Correct") # This letter has been guessed # Remove it from word letters wordLetters.remove(currGuess) else: # Incorrect answer print ("Nope") # One more life used livesUsed += 1 # A little space to make it more readable print() # Check if user won (no more letters to guess) if len(wordLetters) == 0: # Yep! victory = True else: # Display remaining lives print (maxLives-livesUsed, "tries left") # Game play is finished, display results if victory: # If won print ("You win,", gameWord, "is correct!") else: # If lost print ("You lose, the answer was:", gameWord)
Challenge 9.2
This one is a fun one. In the current game, we display the number of lives left, like this:
# Display remaining lives
print (maxLives-livesUsed, "tries left")
Can you replace that code to actually display a Hangman picture? You can use simple characters like | and / to draw one. For example, this code would print the picture at the start of the game, with no incorrect guesses yet:
print(" |---------")
print(" | / |")
print(" |/ |")
print(" |")
print(" |")
print(" |")
print(" |")
print(" |")
print(" |")
print("---")
Start with this and create the pictures needed for each wrong guess.
You’ll need an if statement to decide which picture to show.
And, here’s a tip. If you plan your print() and if statements carefully, you can do this without having to create a different picture for each number of lives. You can have one picture and change what gets shown on each line based on the number of lives left.
Oh, watch for backslash characters (the \ character). That’s a special character in Python. If you actually want to display \ as part of your hangman, you’ll want to type \ instead (you type two backslashes, but Python will display just one).
# Imports import random # Variables maxLives = 7 # Maximum allowed tries maskChar = "_" # Mask character livesUsed = 0 # Try counter guessedLetters = [] # List to store guesses #Game words gameWords = ["anvil", "boutique", "cookie", "fluff", "jazz", "pneumonia", "sleigh", "society", "topaz", "tsunami", "yummy", "zombie"] # Pick the word for the game gameWord = random.choice(gameWords) # Start the display with a fully masked word displayWord = maskChar * len(gameWord) # Actual game starts here # Loop until guessed word correctly or out of lives while gameWord != displayWord and livesUsed < maxLives: # First display the masked word print(displayWord) # Next we need to display any letters already guessed # Lists don't display nicely, so let's create a string # Are there any guessed letters? if len(guessedLetters) > 0: # There are, start with an empty string youTried="" # Add each guessed letter for letter in guessedLetters: youTried += letter # Display them print("You tried:", youTried) # Display remaining lives print (maxLives-livesUsed, "tries left") # Display hangman based on lives left print(" |---------") print(" | / |") print(" |/ |") if livesUsed >= 1: print(" | ( )") else: print(" |") if livesUsed == 2 or livesUsed == 3: print(" | |") elif livesUsed == 4: print(" | /|") elif livesUsed >= 5: # A product of escape characters (anything after a '\') means that to print a '\', you type '\\' print(" | /|\\") else: print(" |") if livesUsed == 2 or livesUsed == 3: print(" | |") elif livesUsed == 4: print(" | / |") elif livesUsed >= 5: # A product of escape characters (anything after a '\') means that to print a '\', you type '\\' print(" | / | \\") else: print(" |") if livesUsed >= 3: print(" | |") else: print(" |") if livesUsed == 6: print(" | /") elif livesUsed == 7: print(" | / \\") else: print(" |") if livesUsed == 6: print(" | /") elif livesUsed == 7: print(" | / \\") else: print(" |") if livesUsed == 6: print(" | /") elif livesUsed == 7: print(" | / \\") else: print(" |") print("---") # A little space to make it more readable print() # Get a guess currGuess = input("Guess a letter ").lower() # Make sure it's just one character while len(currGuess) != 1: currGuess = input("Guess a single letter ").lower() # Don't allow repeated guess if currGuess in guessedLetters: print("You already guessed", currGuess) else: # This is a new guess, save to guessed letter list guessedLetters.append(currGuess) # And sort the list guessedLetters.sort() # Update mask # Start with an empty string displayWord = "" # Loop through word for letter in gameWord: # Add letter or mask as needed # Has this letter been guessed? if letter in guessedLetters: # This one has been guessed so add it displayWord += letter else: # This one has not been been guessed so mask it displayWord += maskChar # Is it a correct guess? if currGuess in gameWord: # Correct answer print ("Correct") else: # Incorrect answer print ("Nope") # One more life used livesUsed += 1 # A little space to make it more readable print() # Game play is finished, display results if displayWord == gameWord: # If won print ("You win,", gameWord, "is correct!") else: # If lost print ("You lose, the answer was:", gameWord) # Display final hangman print(" |---------") print(" | / |") print(" |/ |") if livesUsed >= 1: print(" | ( )") else: print(" |") if livesUsed == 2 or livesUsed == 3: print(" | |") elif livesUsed == 4: print(" | /|") elif livesUsed >= 5: # A product of escape characters (anything after a '\') means that to print a '\', you type '\\' print(" | /|\\") else: print(" |") if livesUsed == 2 or livesUsed == 3: print(" | |") elif livesUsed == 4: print(" | / |") elif livesUsed >= 5: # A product of escape characters (anything after a '\') means that to print a '\', you type '\\' print(" | / | \\") else: print(" |") if livesUsed >= 3: print(" | |") else: print(" |") if livesUsed == 6: print(" | /") elif livesUsed == 7: print(" | / \\") else: print(" |") if livesUsed == 6: print(" | /") elif livesUsed == 7: print(" | / \\") else: print(" |") if livesUsed == 6: print(" | /") elif livesUsed == 7: print(" | / \\") else: print(" |") print("---")
Challenge 10.1
Want to make this a bit more interesting? Asking the user for year, month, and date (or hard coding those values) makes the math easier. But, in truth, you only need month and day, as you can figure out the year yourself: It is either this year if the birthday has not occurred yet
or next year if it has.
So update the code to prompt for a month and day and do the math to figure out the year.
import datetime # Get today today = datetime.datetime.now() # Get birthday from user mm = int(input("Enter birth month as number: ")) dd = int(input("Enter day of month of birthday: ")) # Check if currently is birthday if today.month == mm and today.day == dd: print("Happy birthday!") else: # Check if birthday has passed this year. Set year for birthday accordingly # This if asks "if current month is later, or current month is same and day is later" if today.month > mm or (today.month == mm and today.date > dd): yy = today.year + 1 else: yy = today.year print("You birthday is in", datetime.datetime(yy, mm, dd) - today)
Challenge 10.2
Want to make it more interesting? Here are some ideas:
- Ask the user to rate the service and pick a tip amount for them based on the reply. You can use 15% for average service, 20% (or more) for great service, and 10% (or less—maybe even 0%) for poor service.
- Another enhancement would be to help the user split the bill. Ask them how many diners there were and then tell the user how much each needs to pay.
# Enter the bill and rate the service billAmt = float(input("Enter amount of the bill: ")) num = int(input("How was the service (1 - 5 stars): ")) # How many diners diners = int(input("How many diners: ")) # Choose tip based on stars if num == 2: tipPnt = 10 elif num == 3: tipPnt = 15 elif num == 4: tipPnt = 18 elif num == 5: tipPnt = 22 else: tipPnt = 0 # Set tip based on bill and total based on tip and bill tip = tipPnt/100 * billAmt total = billAmt + tip # Display results # Rounding is optional, but 'round(num, 2)' will return num rounded to two decimal places # If you round, some of the decimals may not work out well print("The bill is $", round(billAmt, 2), "and the tip is $", round(tip, 2), "totaling to $", round(total, 2)) # How much must each person pat print("Each diner must pay $", round(billAmt/diners, 2))
Challenge 10.3
Ok, heads-up, this one is tricky. But, we have faith in you.
Have you ever seen websites that give you password rules? They’ll say something like “Passwords must be at least 8 characters in length and have at least 1 digit and 1 special character.”
So, suppose the user says yep, I want uppercase, lowercase, digits, and special characters in my password. Easy, you pick random characters and build a password. Right?
Well, if you pick random characters from all the options, there is no guarantee that you’ll get a digit or a special character. Actually, you may not even get letters at all. You could end up with just digits or special characters.
Ideally, if the user says they want digits, you’ll make sure that there is at least 1 digit. Same for special characters. So, how could you modify the code to do this?
# Generate a password based on options provided by user. # Minimum length is based on options selected. # To handle required options the code always picks at # least one of each option type. All other characters are # random from the full allowed set and then shuffled. # Imports import random, string # Function to prompt for an option # Pass it text for input() prompt # Returns True or False def promptOption(prompt): # Initialize result result = False opt = " " # User must choose Y or N while not opt in "YN": opt = input(prompt).strip().upper() # If user entered Y then set result to True if opt == "Y": result =True # Return it return result # Function to prompt for password length # Pass it the minimum allowed # Returns a length def promptLength(minLen): # Make sure minimum length isn't 0, if it is then make it 1 if minLen == 0: minLen = 1 # Initialize result result = 0 # Build prompt prompt = "How long should the password be? (Min=" + str(minLen) + "): " # Loop until have a valid length while result < minLen: # Prompt for length p=input(prompt).strip() # Make sure it's a valid number if p.isnumeric(): result=int(p) # Return it return result # Password chars list (stores the randomly generated characters) # We store them in a list instead of a string as lists are easier to shuffle pwList = [] # Characters to use (start with lowercase only) # We'll add to this based on options selected pwChars = string.ascii_lowercase # Minimum password length is based on selected options pwMinLen = 0 # Welcome message print("I'll help you generate a great password. Ready?") # Get options # Uppercase if promptOption("Include uppercase characters? [Y/N] "): # Increment minimum length pwMinLen += 1 # Make sure have at least one uppercase pwList.append(random.choice(string.ascii_uppercase)) # Add to allowed chars pwChars += string.ascii_uppercase # Digits if promptOption("Include digits? [Y/N] "): # Increment minimum length pwMinLen += 1 # Make sure have at least one digit pwList.append(random.choice(string.digits)) # Add to allowed chars pwChars += string.digits # Symbols if promptOption("Include symbols? [Y/N] "): # Increment minimum length pwMinLen += 1 # Make sure have at least one uppercase pwList.append(random.choice(string.punctuation)) # Add to allowed chars pwChars += string.punctuation # Get desired length length = promptLength(pwMinLen) # Loop and generate password characters while len(pwList) < length: # Add next character pwList.append(random.choice(pwChars)) # Randomize the order, this is to make sure that any required # characters are not always first random.shuffle(pwList) # Loop through list and build the result string result = "" for c in pwList: result += c # Display password print("Your password is:", result)
Challenge 11.1
Superheroes often need to travel great distances, and depending on where they go they’ll need to measure those distances in miles or kilometers. Create two functions:
- miles2km() accepts a distance in miles and returns that distance in kilometers.
- km2miles() does the reverse, accepting a distance in kilometers and returning it in miles.
Each of these functions can be written in just two lines of code.
The first is the def that defines the function and argument, and the second performs the calculation and returns it.
And to save you time, there are 1.6 kilometers in a mile, and .6 miles in a kilometer (rounded to keep things simple).
# Convert mi to km def miles2km(num): return num * 1.6 # Convert km to mi def km2miles(num): return num * 0.6 # Use functions miles = int(input("Enter a value in miles: ")) print(miles, "miles = ", miles2km(miles), "km") km = int(input("Enter a value in km: ")) print(km, "km = ", km2miles(km), "miles")
Part 2: On an Adventure
Challenge 13.1
Externalize all of the strings in your application. At a minimum, externalize the display text. If you’d like, you can even externalize option prompts and any other displayed text.
Main.py
########################################## # Space Adventure # by Ben & Shmuel ########################################## # Imports import Strings # Welcome the player def doWelcome(): # Display text print(Strings.get("Welcome")) # Location: Start def doStart(): # Display text print(Strings.get("Start")) # Prompt for user action choice=" " while not choice in "PSBR": print("You can:") print("P = Examine boulder pile") print("S = Go to the structure") print("B = Walk towards the beeping") print("R = Run!") choice=input("What do you want to do? [P/S/B/R]").strip().upper() # Perform action if choice == 'P': doBoulders() elif choice == 'S': doStructure() elif choice == 'B': doBeeping() elif choice == 'R': doRun() # Location: Boulders def doBoulders(): # Display text print(Strings.get("Boulders")) # Go back to start doStart() # Location: Structure def doStructure(): # Display text print(Strings.get("Structure")) # Prompt for user action choice=" " while not choice in "SDBR": print("You can:") print("S = Back to start") print("D = Open the door") print("B = Walk towards the beeping") print("R = Run!") choice=input("What do you want to do? [S/D/B/R]").strip().upper() # Perform action if choice == 'S': doStart() elif choice == 'D': doStructureDoor() elif choice == 'B': doBeeping() elif choice == 'R': doRun() # Location: Structure door def doStructureDoor(): # Display text print(Strings.get("StructureDoor")) print(Strings.get("StructureDoorNoKey")) # Prompt for user action choice=" " while not choice in "SR": print("You can:") print("S = Back to structure") print("R = Run!") choice=input("What do you want to do? [S/R]").strip().upper() # Perform action if choice == 'S': doStructure() elif choice == 'R': doRun() # Location: Explore beeping def doBeeping(): pass # Player ran def doRun(): # Display text print(Strings.get("Run")) # Dead, game over gameOver() # Game over def gameOver(): print(Strings.get("GameOver")) # Actual game starts here # Display welcome message doWelcome() # Game start location doStart()
Strings.py
############################################ # Strings.py # Externalized strings ############################################ def get(id): if id == "Welcome": return ("Welcome adventurer!\n" "You wake in a daze, recalling nothing useful.\n" "Stumbling, you reach for the door, it opens in " "anticipation.\nYou step outside. Nothing is " "familiar.\nThe landscape is dusty, vast, tinged " "red, barren.\nYou notice that you are wearing " "a spacesuit. Huh?") elif id == "Start": return ("You look around. Red dust, a pile of boulders, " "more dust.\nThere's an odd octagon shaped " "structure in front of you.\nYou hear beeping " "nearby. It stopped. No, it didn't.") elif id == "Boulders": return ("Seriously? They are boulders.\n" "Big, heavy, boring boulders.") elif id == "Structure": return ("You examine the odd structure.\n" "Eerily unearthly sounds seem to be coming from " "inside.\nYou see no doors or windows.\nWell, that " "outline might be a door, good luck opening it.\n" "And that beeping. Where is it coming from?") elif id == "StructureDoor": return ("The door appears to be locked.\nYou see a small " "circular hole. Is that the keyhole?") elif id == "StructureDoorNoKey": return ("You move your hand towards it, it flashes blue " "and closes!\nWell, that didn't work as planned.") elif id == "Run": return ("You run, for a moment.\n" "And then you are floating. Down down down.\n" "You've fallen into a chasm, never to be seen " "again.\nNot very brave, are you?") elif id == "GameOver": return "Game over!" else: return ""
Challenge 14.1
You know what the challenge is this time: Refactor your game. Update every single location function in your game to use the new getUserChoice()
function.
########################################## # Space Adventure # by Ben & Shmuel ########################################## # Imports import Strings import Utils # Welcome the player def doWelcome(): # Display text print(Strings.get("Welcome")) # Location: Start def doStart(): # Display text print(Strings.get("Start")) # What can the player do? choices = [ ["P", "Examine pile of boulders"], ["S", "Go to the structure"], ["B", "Walk towards the beeping"], ["R", "Run!"] ] # Prompt for user action choice = Utils.getUserChoice(choices) # Perform action if choice == 'P': doBoulders() elif choice == 'S': doStructure() elif choice == 'B': doBeeping() elif choice == 'R': doRun() # Location: Boulders def doBoulders(): # Display text print(Strings.get("Boulders")) # Go back to start doStart() # Location: Structure def doStructure(): # Display text print(Strings.get("Structure")) # What can the player do? choices = [ ["S", "Back to start"], ["D", "Open the door"], ["B", "Walk towards the beeping"], ["R", "Run!"] ] # Prompt for user action choice = Utils.getUserChoice(choices) # Perform action if choice == 'S': doStart() elif choice == 'D': doStructureDoor() elif choice == 'B': doBeeping() elif choice == 'R': doRun() # Location: Structure door def doStructureDoor(): # Display text print(Strings.get("StructureDoor")) print(Strings.get("StructureDoorNoKey")) # What can the player do? choices = [ ["S", "Back to structure"], ["R", "Run!"] ] # Prompt for user action choice = Utils.getUserChoice(choices) # Perform action if choice == 'S': doStructure() elif choice == 'R': doRun() # Location: Explore beeping def doBeeping(): pass # Player ran def doRun(): # Display text print(Strings.get("Run")) # Dead, game over gameOver() # Game over def gameOver(): print(Strings.get("GameOver")) # Actual game starts here # Display welcome message doWelcome() # Game start location doStart()
Challenge 14.2
Back in Chapter 11, you created a wonderful inputNumber()
function. That will be useful in your game, so copy it into Utils.py
.
Utils.py
############################################ # Utils.py # Utility functions ############################################ # getUserChoice() # Displays a list of options, prompts for an option, and returns it # Pass it a list of lists in format [["Letter","Display Text"]] # Example: [["A","Option A"],["B","Option B"],["C","Option C"]] # Returns selected letter def getUserChoice(options): # Create a variable to hold valid inputs validInputs="" # Loop through the options for opt in options: # Add this one to the valid letters list validInputs+=opt[0] # And display it print(opt[0], "-", opt[1]) # Create the prompt prompt="What do you want to do? [" + validInputs + "]: " # Initialize variables choice="" done=False # Main loop while not done: # Get a single upper case character choice=input(prompt).strip().upper() # If the user entered more then 1 character if len(choice) > 1: # Just use the first choice=choice[0] # Do we have 1 valid input? if len(choice) == 1 and choice in validInputs: # We do, outa here! done = True # Return the selected option return choice # inputNumber() # Numeric input function def inputNumber(prompt): # Input variable inp = "" # Loop until variable is a valid number while not inp.isnumeric(): # Prompt for input inp = input(prompt).strip() # Return the number return int(inp)
Challenge 14.3
You know what else would make a great function? You are often going to need to ask the user to make a yes-or-no choice. Things like Do you want to pick up the weapon? or D
o you give up? or Do you need help?
You could have a while loop in your code and use input()
to get a Y
or N
from the user. But, nah. You could also probably use getUserChoice()
. But that’s a little convoluted.
So, create a new function in Utils.py
called inputYesNo()
. You’d call it like this:
pickUpGun=inputYesNo("Do you want to pick up the gun?")
inputYesNo()
would display the passed text, prompt the user, and return a result.
You can use inputNumber()
as a starting point for this one.
Utils.py
############################################ # Utils.py # Utility functions ############################################ # getUserChoice() # Displays a list of options, prompts for an option, and returns it # Pass it a list of lists in format [["Letter","Display Text"]] # Example: [["A","Option A"],["B","Option B"],["C","Option C"]] # Returns selected letter def getUserChoice(options): # Create a variable to hold valid inputs validInputs="" # Loop through the options for opt in options: # Add this one to the valid letters list validInputs+=opt[0] # And display it print(opt[0], "-", opt[1]) # Create the prompt prompt="What do you want to do? [" + validInputs + "]: " # Initialize variables choice="" done=False # Main loop while not done: # Get a single upper case character choice=input(prompt).strip().upper() # If the user entered more then 1 character if len(choice) > 1: # Just use the first choice=choice[0] # Do we have 1 valid input? if len(choice) == 1 and choice in validInputs: # We do, outa here! done = True # Return the selected option return choice # inputNumber() # Numeric input function def inputNumber(prompt): # Input variable inp = "" # Loop until variable is a valid number while not inp.isnumeric(): # Prompt for input inp = input(prompt).strip() # Return the number return int(inp) # inputYesNo() #User picks Yes or No, return True or False def inputYesNo(text): #Loop until while True: #Display prompt x=input(text + " [Y/N]").upper() #Check response if x in ["Y", "YES"]: return True elif x in ["N", "NO"]: return False
Challenge 15.1
You now have everything you need to define a complete inventory system. Identify places in your game where you’ll use additional inventory items. Add them to the inventory and create the appropriate wrapper functions.
Inventory.py
############################################ # Inventory.py # Inventory system ############################################ inv = { "StructureKey": False, "Coins": 0, "LaserBlaster": False, "QuantumGrenades": 0, "PlasmaShield": False, "GalacticMap": False, } # Add key to inventory def takeStructureKey(): inv["StructureKey"] = True # Remove key from inventory def dropStructureKey(): inv["StructureKey"] = False # Does the player have the key? def hasStructureKey(): return inv["StructureKey"] # Add coins to inventory def takeCoins(coins): inv["Coins"] += coins # Remove coins from inventory def dropCoins(coins): inv["Coins"] -= coins # How many coins does the player have? def numCoins(): return inv["Coins"] # Add laser blaster to inventory def takeLaserBlaster(): inv["LaserBlaster"] = True # Remove laser blaster from inventory def dropLaserBlaster(): inv["LaserBlaster"] = False # Does the player have the laser blaster? def hasLaserBlaster(): return inv["LaserBlaster"] # Add grenades to inventory def takeGrenades(grenades): inv["QuantumGrenades"] += grenades # Remove grenades from inventory def dropGrenades(grenades): inv["QuantumGrenades"] -= grenades # How many grenades does the player have? def numGrenades(): return inv["QuantumGrenades"] # Add plasma shield to inventory def takePlasmaShield(): inv["PlasmaShield"] = True # Remove plasma shield from inventory def dropPlasmaShield(): inv["PlasmaShield"] = False # Does the player have the plasma shield? def hasPlasmaShield(): return inv["PlasmaShield"] # Add galactic map to inventory def takeGalacticMap(): inv["GalacticMap"] = True # Remove galactic map from inventory def dropGalacticMap(): inv["GalacticMap"] = False # Does the player have the galactic map? def hasGalacticMap(): return inv["GalacticMap"] # Display inventory def display(): print("*** Inventory ***") print("You have", numCoins(), "coins") if hasStructureKey(): print("You have a key that flashes blue") if hasGalacticMap(): print("You have the galactic map") if hasPlasmaShield(): print("You have the plasma shield") if hasLaserBlaster() == False and numGrenades() == 0: print("You have no weapons") if hasLaserBlaster(): print("You have the last blaster") if numGrenades() > 0: print("You have", numGrenades(),"quantum grenades") print("*****************")
Challenge 16.1
Our player class has a name property that you can use to personalize the game. Update your doWelcome()
function to ask the player for their name and save it to the player class. You can do something as simple as p.name=input()
, or you can create a method in player to setName()
. Either way works.
Then use p.getName()
in your code to display personalized messages.
Oh, you’ll also want to personalize the text returned by get()
in Strings.py, too. One simple way to do this is to pass p.getName()
as a second argument to the get()
method, and use that when you construct the text to be displayed.
Main.py
########################################## # Space Adventure # by Ben & Shmuel ########################################## # Imports import Strings import Utils import Inventory as inv import Player # Create player object p = Player.player() # Welcome the player def doWelcome(): # Get player name p.setName(input("Identify yourself: ")) # Display text print(Strings.get("Welcome", p.getName())) # Location: Start def doStart(): # Display text print(Strings.get("Start", p.getName())) # What can the player do? choices = [ ["P", "Examine pile of boulders"], ["S", "Go to the structure"], ["B", "Walk towards the beeping"], ["R", "Run!"], ["I", "Inventory"] ] # Prompt for user action choice = Utils.getUserChoice(choices) # Perform action if choice == 'P': doBoulders() elif choice == 'S': doStructure() elif choice == 'B': doBeeping() elif choice == 'R': doRun() elif choice == "I": inv.display() doStart() # Location: Boulders def doBoulders(): # Track this visit p.visitBoulder() # Display text if p.getBoulderVisits() == 1: print(Strings.get("Boulders", p.getName())) elif p.getBoulderVisits() == 3: print(Strings.get("BouldersKey", p.getName())) inv.takeStructureKey() else: print(Strings.get("Boulders2", p.getName())) # Go back to start doStart() # Location: Structure def doStructure(): # Display text print(Strings.get("Structure", p.getName())) # What can the player do? choices = [ ["S", "Back to start"], ["D", "Open the door"], ["B", "Walk towards the beeping"], ["R", "Run!"] ] # Prompt for user action choice = Utils.getUserChoice(choices) # Perform action if choice == 'S': doStart() elif choice == 'D': doStructureDoor() elif choice == 'B': doBeeping() elif choice == 'R': doRun() # Location: Structure door def doStructureDoor(): # Display text print(Strings.get("StructureDoor", p.getName())) if inv.hasStructureKey(): print(Strings.get("StructureDoorKey", p.getName())) else: print(Strings.get("StructureDoorNoKey", p.getName())) # What can the player do? choices = [ ["S", "Back to structure"], ["R", "Run!"] ] # Does user have the key? if inv.hasStructureKey(): # Yep, add unlock to choices choices.insert(0, ["U","Unlock the door"]) # Prompt for user action choice = Utils.getUserChoice(choices) # Perform action if choice == 'S': doStructure() elif choice == 'R': doRun() elif choice == 'U': doEnterStructure() # Location: Explore beeping def doBeeping(): pass # Location: Enter structure def doEnterStructure(): pass # Player ran def doRun(): # Display text print(Strings.get("Run", p.getName())) # Dead, game over gameOver() # Game over def gameOver(): print(Strings.get("GameOver", p.getName())) # Actual game starts here # Display welcome message doWelcome() # Game start location doStart()
Strings.py
############################################ # Strings.py # Externalized strings ############################################ def get(id, name="Adventurer"): if id == "Welcome": return ("Welcome " + name + "!\n" "You wake in a daze, recalling nothing useful.\n" "Stumbling you reach for the door, it opens in anticipation.\n" "You step outside. Nothing is familiar.\n" "The landscape is dusty, vast, tinged red, barren.\n" "You notice that you are wearing a spacesuit. Huh?") elif id == "Start": return ("You look around. Red dust, a pile of boulders, more dust.\n" "There's an odd octagon shaped structure in front of you.\n" "You hear beeping nearby. It stopped. No, it didn't.") elif id == "Boulders": return ("Seriously " + name + "? They are boulders.\n" "Big, heavy, boring boulders.") elif id == "Boulders2": return ("What's with you and boulders?\n" "They are still big, heavy, boring boulders.") elif id == "BouldersKey": return ("You look closer. Was that a blue flash?\n" "You reach between the boulders and find ...\n" "It looks like a key, it occasionally flashes blue.") elif id == "Structure": return ("You examine the odd structure.\n" "Eerily unearthly sounds seem to be coming from inside.\n" "You see no doors or windows.\n" "Well, that outline might be a door, good luck opening it.\n" "And that beeping. Where is it coming from?") elif id == "StructureDoor": return ("The door appears to be locked.\n" "You see a small circular hole. Is that the keyhole?") elif id == "StructureDoorNoKey": return ("You move your hand towards it, it flashes blue and closes!\n" "Well, that didn't work as planned, " + name + ".") elif id == "StructureDoorKey": return ("You look at the key you are holding.\n" "It is flashing blue, as is the keyhole.") elif id == "Run": return ("You run, for a moment.\n" "And then you are floating. Down down down.\n" "You've fallen into a chasm, never to be seen again.\n" "Not very brave, are you, " + name + "?") elif id == "GameOver": return "Game over!" else: return ""
Challenge 16.2
When a player runs, they die (method doRun()
). Change the code so that when they run, they lose a life. (Hint: we have a method in the class that does just this.) Then use isAlive()
in an if statement. If the player is still alive, send them to doStart()
to continue playing; if not, use gameOver()
.
########################################## # Space Adventure # by Ben & Shmuel ########################################## # Imports import Strings import Utils import Inventory as inv import Player # Create player object p = Player.player() # Welcome the player def doWelcome(): # Display text print(Strings.get("Welcome")) # Location: Start def doStart(): # Display text print(Strings.get("Start")) # What can the player do? choices = [ ["P", "Examine pile of boulders"], ["S", "Go to the structure"], ["B", "Walk towards the beeping"], ["R", "Run!"], ["I", "Inventory"] ] # Prompt for user action choice = Utils.getUserChoice(choices) # Perform action if choice == 'P': doBoulders() elif choice == 'S': doStructure() elif choice == 'B': doBeeping() elif choice == 'R': doRun() elif choice == "I": inv.display() doStart() # Location: Boulders def doBoulders(): # Track this visit p.visitBoulder() # Display text if p.getBoulderVisits() == 1: print(Strings.get("Boulders")) elif p.getBoulderVisits() == 3: print(Strings.get("BouldersKey")) inv.takeStructureKey() else: print(Strings.get("Boulders2")) # Go back to start doStart() # Location: Structure def doStructure(): # Display text print(Strings.get("Structure")) # What can the player do? choices = [ ["S", "Back to start"], ["D", "Open the door"], ["B", "Walk towards the beeping"], ["R", "Run!"] ] # Prompt for user action choice = Utils.getUserChoice(choices) # Perform action if choice == 'S': doStart() elif choice == 'D': doStructureDoor() elif choice == 'B': doBeeping() elif choice == 'R': doRun() # Location: Structure door def doStructureDoor(): # Display text print(Strings.get("StructureDoor")) if inv.hasStructureKey(): print(Strings.get("StructureDoorKey")) else: print(Strings.get("StructureDoorNoKey")) # What can the player do? choices = [ ["S", "Back to structure"], ["R", "Run!"] ] # Does user have the key? if inv.hasStructureKey(): # Yep, add unlock to choices choices.insert(0, ["U","Unlock the door"]) # Prompt for user action choice = Utils.getUserChoice(choices) # Perform action if choice == 'S': doStructure() elif choice == 'R': doRun() elif choice == 'U': doEnterStructure() # Location: Explore beeping def doBeeping(): pass # Location: Enter structure def doEnterStructure(): pass # Player ran # This function changed to accommodate lives left def doRun(): # Display text print(Strings.get("Run")) # Player died, lose a life p.died() # Are there any lives left? if p.isAlive(): # Back to the start doStart() else: # Dead, game over gameOver() # Game over def gameOver(): print(Strings.get("GameOver")) # Actual game starts here # Display welcome message doWelcome() # Game start location doStart()
Challenge 17.1
Now that you know how to color your output, go ahead and color the whole game.
Main.py
########################################## # Space Adventure # by Ben & Shmuel ########################################## # Imports import Strings import Utils import Inventory as inv import Player from colorama import init, Fore # Create player object p = Player.player() # Initialize colorama init() # Welcome the player def doWelcome(): # Display text print(Fore.GREEN+Strings.get("Welcome")) # Location: Start def doStart(): # Display text print(Fore.GREEN+Strings.get("Start")) # What can the player do? choices = [ ["P", "Examine pile of boulders"], ["S", "Go to the structure"], ["B", "Walk towards the beeping"], ["R", "Run!"], ["I", "Inventory"] ] # Prompt for user action choice = Utils.getUserChoice(choices) # Perform action if choice == 'P': doBoulders() elif choice == 'S': doStructure() elif choice == 'B': doBeeping() elif choice == 'R': doRun() elif choice == "I": inv.display() doStart() # Location: Boulders def doBoulders(): # Display text if p.getBoulderVisits() == 0: print(Fore.GREEN+Strings.get("Boulders1")) elif p.getBoulderVisits() == 2: print(Fore.GREEN+Strings.get("BouldersKey")) inv.takeStructureKey() else: print(Fore.GREEN+Strings.get("Boulders2")) p.visitBoulder() # Go back to start doStart() # Location: Structure def doStructure(): # Display text print(Fore.GREEN+Strings.get("Structure")) # What can the player do? choices = [ ["S", "Back to start"], ["D", "Open the door"], ["B", "Walk towards the beeping"], ["R", "Run!"] ] # Prompt for user action choice = Utils.getUserChoice(choices) # Perform action if choice == 'S': doStart() elif choice == 'D': doStructureDoor() elif choice == 'B': doBeeping() elif choice == 'R': doRun() # Location: Structure door def doStructureDoor(): # Display text print(Fore.GREEN+Strings.get("StructureDoor")) if inv.hasStructureKey(): print(Fore.GREEN+Strings.get("StructureDoorKey")) else: print(Fore.RED+Strings.get("StructureDoorNoKey")) # What can the player do? choices = [ ["S", "Back to structure"], ["R", "Run!"] ] # Does user have the key? if inv.hasStructureKey(): # Yep, add unlock to choices choices.insert(0, ["U","Unlock the door"]) # Prompt for user action choice = Utils.getUserChoice(choices) # Perform action if choice == 'S': doStructure() elif choice == 'R': doRun() elif choice == 'U': doEnterStructure() # Location: Explore beeping def doBeeping(): pass # Location: Enter structure def doEnterStructure(): pass # Player ran def doRun(): # Display text print(Fore.GREEN+Strings.get("Run")) # Dead, game over gameOver() # Game over def gameOver(): print(Fore.GREEN+Strings.get("GameOver")) # Actual game starts here # Display welcome message doWelcome() # Game start location doStart()
Utils.py
############################################ # Utils.py # Utility functions ############################################ from colorama import Fore # getUserChoice() # Displays a list of options, prompts for an option, and returns it # Pass it a list of lists in format [["Letter","Display Text"]] # Example: [["A","Option A"],["B","Option B"],["C","Option C"]] # Returns selected letter def getUserChoice(options): # Create a variable to hold valid inputs validInputs="" # Loop through the options for opt in options: # Add this one to the valid letters list validInputs+=opt[0] # And display it print(Fore.YELLOW+opt[0], "-", opt[1]) # Create the prompt prompt="What do you want to do? [" + validInputs + "]: " # Initialize variables choice="" done=False # Main loop while not done: # Get a single upper case character choice=input(prompt).strip().upper() # If the user entered more then 1 character if len(choice) > 1: # Just use the first choice=choice[0] # Do we have 1 valid input? if len(choice) == 1 and choice in validInputs: # We do, outa here! done = True # Return the selected option return choice
Inventory.py
############################################ # Inventory.py # Inventory system ############################################ # Imports from colorama import Fore inv = { "StructureKey": False, "Coins": 0 } # Add key to inventory def takeStructureKey(): inv["StructureKey"] = True # Remove key from inventory def dropStructureKey(): inv["StructureKey"] = False # Does the player have the key? def hasStructureKey(): return inv["StructureKey"] # Add coins to inventory def takeCoins(coins): inv["Coins"] += coins # Remove coins from inventory def dropCoins(coins): inv["Coins"] -= coins # How many coins does the player have? def numCoins(): return inv["Coins"] # Display inventory def display(): print(Fore.CYAN+"*** Inventory ***") print(Fore.CYAN+"You have", numCoins(), "coins") if hasStructureKey(): print(Fore.CYAN+"You have a key that flashes blue") print(Fore.CYAN+"*****************")
Part 3: Hit the Road
Challenge 19.1
Try changing the window size passed to set_mode()
. Make it bigger, smaller, wider … you get the idea.
We defined three colors. Try changing fill()
to use RED
instead of WHITE
. And then create your own color variables and use them. Remember, you can use any values between 0
and 255
for each of the RGB values. You may want to use just 0
and 255
as you play with the colors. (That still gives you 8 combinations to try.)
# Imports import sys import pygame from pygame.locals import * # Game colors # A bunch have been added! BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) ORANGE = (255, 127, 0) YELLOW = (255, 255, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) INDIGO = (75, 0, 130) VIOLET = (148, 0, 211) # Main game starts here # Initialize PyGame pygame.init() # Initialize frame manager clock = pygame.time.Clock() # Set frame rate clock.tick(60) # Set caption bar pygame.display.set_caption("Crazy Driver") # Initialize game screen screen = pygame.display.set_mode((1000, 600)) # Update Window size here # Set background color screen.fill(VIOLET) # Select color of choice here # Update screen pygame.display.update() # Main game loop while True: # Check for events for event in pygame.event.get(): # Did the player quit? if event.type == QUIT: # Quit pygame pygame.quit() sys.exit() # Update screen pygame.display.update()
Challenge 20.1
We’re only using images right now, and thus we have just an Images folder. But it’s good to plan for the future. Assume that you have Sound and Videos folders and create variables for each of those. You should be able to create each with a single line of code.
# Imports import sys import os import pygame from pygame.locals import * # Build game paths GAME_ROOT_FOLDER=os.path.dirname(__file__) IMAGE_FOLDER=os.path.join(GAME_ROOT_FOLDER, "Images") SOUND_FOLDER=os.path.join(GAME_ROOT_FOLDER, "Sound") VIDEO_FOLDER=os.path.join(GAME_ROOT_FOLDER, "Video") # Game colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) # Main game starts here # Initialize PyGame pygame.init() # Initialize frame manager clock = pygame.time.Clock() # Set frame rate clock.tick(60) # Set caption bar pygame.display.set_caption("Crazy Driver") # Initialize game screen screen = pygame.display.set_mode((500, 800)) # Set background color screen.fill(WHITE) # Update screen pygame.display.update() # Main game loop while True: # Check for events for event in pygame.event.get(): # Did the player quit? if event.type == QUIT: # Quit pygame pygame.quit() sys.exit() # Update screen pygame.display.update()
Challenge 20.2
We provided three different enemy cars for you to use. We’ll actually use all three in later chapters, but for now, you can experiment with them. Change the code so that the enemy car that gets placed at the top of the screen is Enemy2
or Enemy3
.
# Imports import sys, os, random import pygame from pygame.locals import * # Game colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) # Build game paths GAME_ROOT_FOLDER=os.path.dirname(__file__) IMAGE_FOLDER=os.path.join(GAME_ROOT_FOLDER, "Images") # Main game starts here # Initialize PyGame pygame.init() # Initialize frame manager clock = pygame.time.Clock() # Set frame rate clock.tick(60) # Set caption bar pygame.display.set_caption("Crazy Driver") # Load images IMG_ROAD = pygame.image.load(os.path.join(IMAGE_FOLDER, "Road.png")) IMG_PLAYER = pygame.image.load(os.path.join(IMAGE_FOLDER, "Player.png")) IMG_ENEMY = pygame.image.load(os.path.join(IMAGE_FOLDER, "Enemy3.png")) # Code change is here! # Initialize game screen screen = pygame.display.set_mode(IMG_ROAD.get_size()) # Create game objects # Calculate initial player position h=IMG_ROAD.get_width()//2 v=IMG_ROAD.get_height() - (IMG_PLAYER.get_height()//2) # Create player sprite player = pygame.sprite.Sprite() player.image = IMG_PLAYER player.surf = pygame.Surface(IMG_PLAYER.get_size()) player.rect = player.surf.get_rect(center = (h, v)) # Enemy # Calculate initial enemy position hl=IMG_ENEMY.get_width()//2 hr=IMG_ROAD.get_width()-(IMG_ENEMY.get_width()//2) h=random.randrange(hl, hr) v=0 # Create enemy sprite enemy = pygame.sprite.Sprite() enemy.image = IMG_ENEMY enemy.surf = pygame.Surface(IMG_ENEMY.get_size()) enemy.rect = enemy.surf.get_rect(center = (h, v)) # Main game loop while True: # Place background screen.blit(IMG_ROAD, (0,0)) # Place player on screen screen.blit(player.image, player.rect) # Place enemy on screen screen.blit(enemy.image, enemy.rect) # Check for events for event in pygame.event.get(): # Did the player quit? if event.type == QUIT: # Quit pygame pygame.quit() sys.exit() # Update screen pygame.display.update()
Challenge 21.1
Game speed is controlled by the moveSpeed
variable. It specifies how many pixels the enemy should advance by and how many pixels the player sprite moves with each left or right arrow key press.
Try changing the moveSpeed
value. You can try smaller numbers and larger numbers. Get a feel for how changing this value impacts game play.
# Imports import sys, os, random import pygame from pygame.locals import * # Game colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) # Build game paths GAME_ROOT_FOLDER=os.path.dirname(__file__) IMAGE_FOLDER=os.path.join(GAME_ROOT_FOLDER, "Images") # Game variables moveSpeed = 10 # Change is here! # Main game starts here # Initialize PyGame pygame.init() # Initialize frame manager clock = pygame.time.Clock() # Set frame rate clock.tick(60) # Set caption bar pygame.display.set_caption("Crazy Driver") # Load images IMG_ROAD = pygame.image.load(os.path.join(IMAGE_FOLDER, "Road.png")) IMG_PLAYER = pygame.image.load(os.path.join(IMAGE_FOLDER, "Player.png")) IMG_ENEMY = pygame.image.load(os.path.join(IMAGE_FOLDER, "Enemy.png")) # Initialize game screen screen = pygame.display.set_mode(IMG_ROAD.get_size()) # Create game objects # Calculate initial player position h=IMG_ROAD.get_width()//2 v=IMG_ROAD.get_height() - (IMG_PLAYER.get_height()//2) # Create player sprite player = pygame.sprite.Sprite() player.image = IMG_PLAYER player.surf = pygame.Surface(IMG_PLAYER.get_size()) player.rect = player.surf.get_rect(center = (h, v)) # Enemy # Calculate initial enemy position hl=IMG_ENEMY.get_width()//2 hr=IMG_ROAD.get_width()-(IMG_ENEMY.get_width()//2) h=random.randrange(hl, hr) v=0 # Create enemy sprite enemy = pygame.sprite.Sprite() enemy.image = IMG_ENEMY enemy.surf = pygame.Surface(IMG_ENEMY.get_size()) enemy.rect = enemy.surf.get_rect(center = (h, v)) # Main game loop while True: # Place background screen.blit(IMG_ROAD, (0,0)) # Place player on screen screen.blit(player.image, player.rect) # Get keys pressed keys = pygame.key.get_pressed() # Check for LEFT key if keys[K_LEFT] and player.rect.left > 0: # Move left player.rect.move_ip(-moveSpeed, 0) # Make sure we didn't go too far left if player.rect.left < 0: # To far, fix it player.rect.left = 0 # Check for RIGHT key if keys[K_RIGHT] and player.rect.right < IMG_ROAD.get_width(): # Move right player.rect.move_ip(moveSpeed, 0) # Make sure we didn't go too far right if player.rect.right > IMG_ROAD.get_width(): # To far, fix it player.rect.right = IMG_ROAD.get_width() # Place enemy on screen screen.blit(enemy.image, enemy.rect) # Move enemy downwards enemy.rect.move_ip(0, moveSpeed) # Check didn't go off edge of screen if (enemy.rect.bottom > IMG_ROAD.get_height()): # Calculate new random location hl=IMG_ENEMY.get_width()//2 hr=IMG_ROAD.get_width()-(IMG_ENEMY.get_width()//2) h=random.randrange(hl, hr) v=0 # And place it enemy.rect.center = (h, v) # Check for events for event in pygame.event.get(): # Did the player quit? if event.type == QUIT: # Quit pygame pygame.quit() sys.exit() # Update screen pygame.display.update()
Challenge 21.2
We used the left and right keys to move the player sprite. You don’t have to use those keys; you can pick your own. Many games use A and S, for example. Update the code to use those keys. You’ll want to check for K_a
and K_s
.
Or allow both sets of keys, letting players use left arrow or A to go left and right arrow or S to go right. Here’s a hint: You can do this easily with an or in your if statement. If you do this, be careful to group your conditions correctly with parentheses because you’ll have an and
and an or
.
# Imports import sys, os, random import pygame from pygame.locals import * # Game colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) # Build game paths GAME_ROOT_FOLDER=os.path.dirname(__file__) IMAGE_FOLDER=os.path.join(GAME_ROOT_FOLDER, "Images") # Game variables moveSpeed = 5 # Main game starts here # Initialize PyGame pygame.init() # Initialize frame manager clock = pygame.time.Clock() # Set frame rate clock.tick(60) # Set caption bar pygame.display.set_caption("Crazy Driver") # Load images IMG_ROAD = pygame.image.load(os.path.join(IMAGE_FOLDER, "Road.png")) IMG_PLAYER = pygame.image.load(os.path.join(IMAGE_FOLDER, "Player.png")) IMG_ENEMY = pygame.image.load(os.path.join(IMAGE_FOLDER, "Enemy.png")) # Initialize game screen screen = pygame.display.set_mode(IMG_ROAD.get_size()) # Create game objects # Calculate initial player position h=IMG_ROAD.get_width()//2 v=IMG_ROAD.get_height() - (IMG_PLAYER.get_height()//2) # Create player sprite player = pygame.sprite.Sprite() player.image = IMG_PLAYER player.surf = pygame.Surface(IMG_PLAYER.get_size()) player.rect = player.surf.get_rect(center = (h, v)) # Enemy # Calculate initial enemy position hl=IMG_ENEMY.get_width()//2 hr=IMG_ROAD.get_width()-(IMG_ENEMY.get_width()//2) h=random.randrange(hl, hr) v=0 # Create enemy sprite enemy = pygame.sprite.Sprite() enemy.image = IMG_ENEMY enemy.surf = pygame.Surface(IMG_ENEMY.get_size()) enemy.rect = enemy.surf.get_rect(center = (h, v)) # Main game loop while True: # Place background screen.blit(IMG_ROAD, (0,0)) # Place player on screen screen.blit(player.image, player.rect) # Get keys pressed keys = pygame.key.get_pressed() # Check for LEFT key if (keys[K_LEFT] or keys[K_a]) and player.rect.left > 0: # Move left player.rect.move_ip(-moveSpeed, 0) # Make sure we didn't go too far left if player.rect.left < 0: # To far, fix it player.rect.left = 0 # Check for RIGHT key if (keys[K_RIGHT] or keys[K_d]) and player.rect.right < IMG_ROAD.get_width(): # Move right player.rect.move_ip(moveSpeed, 0) # Make sure we didn't go too far right if player.rect.right > IMG_ROAD.get_width(): # To far, fix it player.rect.right = IMG_ROAD.get_width() # Place enemy on screen screen.blit(enemy.image, enemy.rect) # Move enemy downwards enemy.rect.move_ip(0, moveSpeed) # Check didn't go off edge of screen if (enemy.rect.bottom > IMG_ROAD.get_height()): # Calculate new random location hl=IMG_ENEMY.get_width()//2 hr=IMG_ROAD.get_width()-(IMG_ENEMY.get_width()//2) h=random.randrange(hl, hr) v=0 # And place it enemy.rect.center = (h, v) # Check for events for event in pygame.event.get(): # Did the player quit? if event.type == QUIT: # Quit pygame pygame.quit() sys.exit() # Update screen pygame.display.update()
Challenge 22.1
The game gets faster (and thus harder) the longer you play. But the scoring stays the same: 1 point per car avoided. Can you change that so that once the game doubles in speed, players get 2 points per car avoided?
# Imports import sys, os, random import pygame from pygame.locals import * # Game colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) # Build game paths GAME_ROOT_FOLDER=os.path.dirname(__file__) IMAGE_FOLDER=os.path.join(GAME_ROOT_FOLDER, "Images") # Game variables moveSpeed = 5 maxSpeed = 10 score = 0 # Game over function def GameOver(): # Quit Pygame pygame.quit() sys.exit() # Main game starts here # Initialize PyGame pygame.init() # Initialize frame manager clock = pygame.time.Clock() # Set frame rate clock.tick(60) # Set caption bar pygame.display.set_caption("Crazy Driver") # Load images IMG_ROAD = pygame.image.load(os.path.join(IMAGE_FOLDER, "Road.png")) IMG_PLAYER = pygame.image.load(os.path.join(IMAGE_FOLDER, "Player.png")) IMG_ENEMY = pygame.image.load(os.path.join(IMAGE_FOLDER, "Enemy.png")) # Initialize game screen screen = pygame.display.set_mode(IMG_ROAD.get_size()) # Create game objects # Calculate initial player position h=IMG_ROAD.get_width()//2 v=IMG_ROAD.get_height() - (IMG_PLAYER.get_height()//2) # Create player sprite player = pygame.sprite.Sprite() player.image = IMG_PLAYER player.surf = pygame.Surface(IMG_PLAYER.get_size()) player.rect = player.surf.get_rect(center = (h, v)) # Enemy # Calculate initial enemy position hl=IMG_ENEMY.get_width()//2 hr=IMG_ROAD.get_width()-(IMG_ENEMY.get_width()//2) h=random.randrange(hl, hr) v=0 # Create enemy sprite enemy = pygame.sprite.Sprite() enemy.image = IMG_ENEMY enemy.surf = pygame.Surface(IMG_ENEMY.get_size()) enemy.rect = enemy.surf.get_rect(center = (h, v)) # Main game loop while True: # Update caption with score pygame.display.set_caption("Crazy Driver - Score " + str(score)) # Place background screen.blit(IMG_ROAD, (0,0)) # Place player on screen screen.blit(player.image, player.rect) # Get keys pressed keys = pygame.key.get_pressed() # Check for LEFT key if keys[K_LEFT] and player.rect.left > 0: # Move left player.rect.move_ip(-moveSpeed, 0) # Make sure we didn't go too far left if player.rect.left < 0: # To far, fix it player.rect.left = 0 # Check for RIGHT key if keys[K_RIGHT] and player.rect.right < IMG_ROAD.get_width(): # Move right player.rect.move_ip(moveSpeed, 0) # Make sure we didn't go too far right if player.rect.right > IMG_ROAD.get_width(): # To far, fix it player.rect.right = IMG_ROAD.get_width() # Place enemy on screen screen.blit(enemy.image, enemy.rect) # Move enemy downwards enemy.rect.move_ip(0, moveSpeed) # Check didn't go off edge of screen if (enemy.rect.bottom > IMG_ROAD.get_height()): # Calculate new random location hl=IMG_ENEMY.get_width()//2 hr=IMG_ROAD.get_width()-(IMG_ENEMY.get_width()//2) h=random.randrange(hl, hr) v=0 # And place it enemy.rect.center = (h, v) # Update the score # 2 points if doubled in speed # Note: if maxSpeed wasn't double moveSpeed, you may need to make a startSpeed variable to test for when speed has doubled if moveSpeed == maxSpeed: score += 2 # 1 point otherwise else: score += 1 # Increase the speed if moveSpeed < maxSpeed: moveSpeed += 1 # Check for events for event in pygame.event.get(): # Did the player quit? if event.type == QUIT: # Quit pygame pygame.quit() sys.exit() # Check for collisions if pygame.sprite.collide_rect(player, enemy): # Crash! Game over GameOver() # Update screen pygame.display.update()
Challenge 23.1
We created a paused
variable to track whether or not the game is paused: True
if it is, False
if not. Was this necessary? Actually, no, it wasn’t. There is another way to know if we’re paused or not: just look at moveSpeed
, which will be 0
only if the game is paused. Modify the code to remove the paused
variable, and use the existing moveSpeed
variable to pause (and unpause) game play.
# Imports import sys, os, random, time import pygame from pygame.locals import * # Game colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) # Build game paths GAME_ROOT_FOLDER=os.path.dirname(__file__) IMAGE_FOLDER=os.path.join(GAME_ROOT_FOLDER, "Images") # Game variables moveSpeed = 5 maxSpeed = 10 score = 0 textFonts = ['comicsansms','arial'] textSize = 48 # GameOver function # Displays message and cleans things up def GameOver(): # Game Over text creation fontGameOver = pygame.font.SysFont(textFonts, textSize) textGameOver = fontGameOver.render("Game Over!", True, RED) rectGameOver = textGameOver.get_rect() rectGameOver.center = (IMG_ROAD.get_width()//2, IMG_ROAD.get_height()//2) # Black screen with game over text screen.fill(BLACK) screen.blit(textGameOver, rectGameOver) # Update the display pygame.display.update() # Destroy objects player.kill() enemy.kill() # Pause time.sleep(5) # Quit pygame pygame.quit() sys.exit() # Main game starts here # Initialize PyGame pygame.init() # Initialize frame manager clock = pygame.time.Clock() # Set frame rate clock.tick(60) # Set caption bar pygame.display.set_caption("Crazy Driver") # Load images IMG_ROAD = pygame.image.load(os.path.join(IMAGE_FOLDER, "Road.png")) IMG_PLAYER = pygame.image.load(os.path.join(IMAGE_FOLDER, "Player.png")) IMG_ENEMY = pygame.image.load(os.path.join(IMAGE_FOLDER, "Enemy.png")) # Initialize game screen screen = pygame.display.set_mode(IMG_ROAD.get_size()) # Create game objects # Calculate initial player position h=IMG_ROAD.get_width()//2 v=IMG_ROAD.get_height() - (IMG_PLAYER.get_height()//2) # Create player sprite player = pygame.sprite.Sprite() player.image = IMG_PLAYER player.surf = pygame.Surface(IMG_PLAYER.get_size()) player.rect = player.surf.get_rect(center = (h, v)) # Enemy # Calculate initial enemy position hl=IMG_ENEMY.get_width()//2 hr=IMG_ROAD.get_width()-(IMG_ENEMY.get_width()//2) h=random.randrange(hl, hr) v=0 # Create enemy sprite enemy = pygame.sprite.Sprite() enemy.image = IMG_ENEMY enemy.surf = pygame.Surface(IMG_ENEMY.get_size()) enemy.rect = enemy.surf.get_rect(center = (h, v)) # Main game loop while True: # Update caption with score pygame.display.set_caption("Crazy Driver - Score " + str(score)) # Place background screen.blit(IMG_ROAD, (0,0)) # Place player on screen screen.blit(player.image, player.rect) # Get keys pressed keys = pygame.key.get_pressed() # Are we paused? if moveSpeed == 0: # Check for SPACE if not keys[K_SPACE]: # Turn off pause # Set speed back to what it was moveSpeed=tempSpeed else: # Check for LEFT key if keys[K_LEFT] and player.rect.left > 0: # Move left player.rect.move_ip(-moveSpeed, 0) # Make sure we didn't go too far left if player.rect.left < 0: # To far, fix it player.rect.left = 0 # Check for RIGHT key if keys[K_RIGHT] and player.rect.right < IMG_ROAD.get_width(): # Move right player.rect.move_ip(moveSpeed, 0) # Make sure we didn't go too far right if player.rect.right > IMG_ROAD.get_width(): # To far, fix it player.rect.right = IMG_ROAD.get_width() # Check for SPACE key if keys[K_SPACE]: # Turn on pause # Save speed tempSpeed=moveSpeed # Set speed to 0 moveSpeed=0 # Place enemy on screen screen.blit(enemy.image, enemy.rect) # Move enemy downwards enemy.rect.move_ip(0, moveSpeed) # Check didn't go off edge of screen if (enemy.rect.bottom > IMG_ROAD.get_height()): # Calculate new random location hl=IMG_ENEMY.get_width()//2 hr=IMG_ROAD.get_width()-(IMG_ENEMY.get_width()//2) h=random.randrange(hl, hr) v=0 # And place it enemy.rect.center = (h, v) # Update the score score += 1 # Increase the speed if moveSpeed < maxSpeed: moveSpeed += 1 # Check for events for event in pygame.event.get(): # Did the player quit? if event.type == QUIT: # Quit pygame pygame.quit() sys.exit() # Check for collisions if pygame.sprite.collide_rect(player, enemy): # Crash! Game over GameOver() # Update screen pygame.display.update()
Challenge 23.2
Can you add additional vehicles to the game? You can create your own PNG files or try to find some online. Save the images to the Images folder and then add them to IMG_ENEMIES
.
# Imports import sys, os, random, time import pygame from pygame.locals import * # Game colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) # Build game paths GAME_ROOT_FOLDER=os.path.dirname(__file__) IMAGE_FOLDER=os.path.join(GAME_ROOT_FOLDER, "Images") # Game variables moveSpeed = 5 maxSpeed = 10 score = 0 textFonts = ['comicsansms','arial'] textSize = 48 paused = False eNum = -1 # GameOver function # Displays message and cleans things up def GameOver(): # Game Over text creation fontGameOver = pygame.font.SysFont(textFonts, textSize) textGameOver = fontGameOver.render("Game Over!", True, RED) rectGameOver = textGameOver.get_rect() rectGameOver.center = (IMG_ROAD.get_width()//2, IMG_ROAD.get_height()//2) # Black screen with game over text screen.fill(BLACK) screen.blit(textGameOver, rectGameOver) # Update the display pygame.display.update() # Destroy objects player.kill() enemy.kill() # Pause time.sleep(5) # Quit pygame pygame.quit() sys.exit() # Main game starts here # Initialize PyGame pygame.init() # Initialize frame manager clock = pygame.time.Clock() # Set frame rate clock.tick(60) # Set caption bar pygame.display.set_caption("Crazy Driver") # Load images IMG_ROAD = pygame.image.load(os.path.join(IMAGE_FOLDER, "Road.png")) IMG_PLAYER = pygame.image.load(os.path.join(IMAGE_FOLDER, "Player.png")) IMG_ENEMIES = [] IMG_ENEMIES.append(pygame.image.load(os.path.join(IMAGE_FOLDER, "Enemy.png"))) IMG_ENEMIES.append(pygame.image.load(os.path.join(IMAGE_FOLDER, "Enemy2.png"))) IMG_ENEMIES.append(pygame.image.load(os.path.join(IMAGE_FOLDER, "Enemy3.png"))) IMG_ENEMIES.append(pygame.image.load(os.path.join(IMAGE_FOLDER, "Enemy4.png"))) # Initialize game screen screen = pygame.display.set_mode(IMG_ROAD.get_size()) # Create game objects # Calculate initial player position h=IMG_ROAD.get_width()//2 v=IMG_ROAD.get_height() - (IMG_PLAYER.get_height()//2) # Create player sprite player = pygame.sprite.Sprite() player.image = IMG_PLAYER player.surf = pygame.Surface(IMG_PLAYER.get_size()) player.rect = player.surf.get_rect(center = (h, v)) # Main game loop while True: # Update caption with score pygame.display.set_caption("Crazy Driver - Score " + str(score)) # Place background screen.blit(IMG_ROAD, (0,0)) # Make sure we have an enemy if eNum == -1: # Get a random enemy eNum = random.randrange(0, len(IMG_ENEMIES)) # Calculate initial enemy position hl=IMG_ENEMIES[eNum].get_width()//2 hr=IMG_ROAD.get_width()-(IMG_ENEMIES[eNum].get_width()//2) h=random.randrange(hl, hr) v=0 # Create enemy sprite enemy = pygame.sprite.Sprite() enemy.image = IMG_ENEMIES[eNum] enemy.surf = pygame.Surface(IMG_ENEMIES[eNum].get_size()) enemy.rect = enemy.surf.get_rect(center = (h, v)) # Place player on screen screen.blit(player.image, player.rect) # Get keys pressed keys = pygame.key.get_pressed() # Are we paused? if paused: # Check for SPACE if not keys[K_SPACE]: # Turn off pause # Set speed back to what it was moveSpeed=tempSpeed # Turn off flag paused=False else: # Check for LEFT key if keys[K_LEFT] and player.rect.left > 0: # Move left player.rect.move_ip(-moveSpeed, 0) # Make sure we didn't go too far left if player.rect.left < 0: # To far, fix it player.rect.left = 0 # Check for RIGHT key if keys[K_RIGHT] and player.rect.right < IMG_ROAD.get_width(): # Move right player.rect.move_ip(moveSpeed, 0) # Make sure we didn't go too far right if player.rect.right > IMG_ROAD.get_width(): # To far, fix it player.rect.right = IMG_ROAD.get_width() # Check for SPACE key if keys[K_SPACE]: # Turn on pause # Save speed tempSpeed=moveSpeed # Set speed to 0 moveSpeed=0 # Turn on flag paused=True # Place enemy on screen screen.blit(enemy.image, enemy.rect) # Move enemy downwards enemy.rect.move_ip(0, moveSpeed) # Check didn't go off edge of screen if (enemy.rect.bottom > IMG_ROAD.get_height()): # Kill enemy object enemy.kill() # No enemy eNum = -1 # Increment the score score += 1 # Increase the speed moveSpeed += 1 # Increase the speed if moveSpeed < maxSpeed: moveSpeed += 1 # Check for events for event in pygame.event.get(): # Did the player quit? if event.type == QUIT: # Quit pygame pygame.quit() sys.exit() # Check for collisions if pygame.sprite.collide_rect(player, enemy): # Crash! Game over GameOver() # Update screen pygame.display.update()
Leave a Reply