CS21 Lab 5: Advanced functions

Due Sunday, March 2nd, before midnight

Goals

  • Work with functions and lists

  • Work with mutable and immutable function parameters

As you write programs, use good programming practices:

  • Use a comment at the top of the file to describe the purpose of the program (see example).

  • All programs should have a main() function (see example).

  • Use variable names that describe the contents of the variables.

  • Write your programs incrementally and test them as you go. This is really crucial to success: don’t write lots of code and then test it all at once! Write a little code, make sure it works, then add some more and test it again.

  • Don’t assume that if your program passes the sample tests we provide that it is completely correct. Come up with your own test cases and verify that the program is producing the right output on them.

  • Avoid writing any lines of code that exceed 80 columns.

    • Always work in a terminal window that is 80 characters wide (resize it to be this wide)

    • In vscode, at the bottom right in the window, there is an indication of both the line and the column of the cursor.

Function Comments

All functions should include a comment explaining their purpose, parameters, return value, and describe any side effects. Please see our function example page if you are confused about writing function comments.

1. Wheel of Fortune

200

Wheel of Fortune is an American game show where contestants compete to guess a mystery phrase and win cash.

In this assignment, you will implement a simplified, single player version of wheel of fortune. The goal of the game is to win the most cash with the least number of turns to solve the puzzle.

At the beginning of the game:

  • The player starts with $0 dollars.

  • A phrase is shown to the player with all of the alphabetic letters hidden, but with spaces and punctuation included.

Each turn, the player can choose one of the following options:

  1. Spin the wheel: the player spins the wheel of fortune

    • If the player lands on cash, they may guess a letter. If the letter is one that they guessed before their turn is over. If the letter is in the phrase, they win (cash amount) * (number of letter in phrase).

    • If the player goes bankrupt, their total cash is reset to $0 and the game continues.

  2. Guess the phrase: the player can guess the phrase. If they are correct, they win the game. Otherwise, the game continues.

  3. Quit the game: the player can quit the game at any time. The answer is shown to the player when they quit.

We will assume that once the player fills in all of the letters of the puzzle, that they will have to still officially "guess" the answer before the game will quit.

Below are sample playthroughs of the game:

We will build this program incrementally, one step at a time.

2. Help Functions for the Game

The file utilsWheelOfFortune.py provides some help functions for you to use as you write your game solution.

You should not modify any of these functions!

Here’s a summary of the functions that are available:

  • choose_phrase() chooses a random phrase from a file of many possible phrases and returns it as a list of characters

  • list_to_string(phrase) converts a list of characters into a string and returns the string

  • spin_wheel() simulates spinning the wheel and returns either a positive cash value or -1 to indicate that the player hit bankrupt

  • menu() prints the game menu and returns the player’s choice, which must be the integer 1, 2, or 3

Let’s begin by testing out a few of these utility functions. The file wheelOfFortune.py has a short main program (shown below) that chooses a phrase and then turns it into a string to print it. Try running it a number of times. You should get a different puzzle each time.

def main():
    answer = choose_phrase()
    print("Answer: %s" % (list_to_string(answer)))

3. Define init_puzzle

In the file wheelOfFortune.py, define a function called init_puzzle(answer) that takes a list of characters containing the answer and generates a puzzle (a new list) from the answer, but with all letters (a-z) replaced by "-".

Specification:

  • Parameters:

    • answer (list): characters representing the phrase to guess

  • Return (list): characters with alphabetic characters hidden

  • Side effects: None

For example, if the answer were:

["n", "o", " ", "w", "a", "y", "!"]

Then the puzzle would be:

["-", "-", " ", "-", "-", "-", "!"]

Python has a function called isalpha() that can be applied to a string. It returns True when the string contains only alphabetic characters, and returns False otherwise. For instance, you can try this in the Python interpreter:

$ python3
>>> s1 = "t"
>>> s2 = "!"
>>> s3 = "2"
>>> s1.isalpha()
True
>>> s2.isalpha()
False
>>> s3.isalpha()
False
>>> quit()

This isalpha() function may be helpful as you implement init_puzzle.

Once you have implemented this function, update the main program as shown below to test it:

def main():
    answer = choose_phrase()
    print("Answer: %s" % (list_to_string(answer)))
    # Add the following lines
    puzzle = init_puzzle(answer)
    print("Puzzle: %s" % (list_to_string(puzzle)))

Try running this a number of times. Your puzzle should have the same length and format as the answer, but with all of the letters replaced by dashes like this:

$ python3 wheeloffortune.py
Answer: that's the last straw
Puzzle: ----'- --- ---- -----
$ python3 wheeloffortune.py
Answer: digging up dirt
Puzzle: ------- -- ----

4. Define ask_for_letter

Specifications

  • Parameters: None

  • Return (str): an alphabetic character chosen by the user

  • Side effects: None

This function should repeatedly ask for a letter until it gets a single alphabetic character. The isalpha() function may be helpful in determining whether you have an alphabetic character, but think about how would you determine whether you have a single letter.

Update your main program to test out this function as shown below:

def main():
    answer = choose_phrase()
    print("Answer: %s" % (list_to_string(answer)))
    puzzle = init_puzzle(answer)
    print("Puzzle: %s" % (list_to_string(puzzle)))
    # Add the following lines
    letter = ask_for_letter()
    print("Letter: %s" % (letter))

Try running this a number of times to ensure that your function only returns when a valid alphabetic character is provided like this:

$ python3 wheeloffortune.py
Answer: it runs in the family
Puzzle: -- ---- -- --- ------
Enter a letter: dkjdh
dkjdh is not valid. Try again.
Enter a letter: 47
47 is not valid. Try again.
Enter a letter: +
+ is not valid. Try again.
Enter a letter: g
Letter: g

5. Define update_puzzle

Implement a function called update_puzzle(puzzle, letter, answer) which updates a puzzle to show the given letter. If letter is not in the answer, the puzzle is unchanged.

Specifications:

  • Parameters:

    • puzzle (list): the current state of the puzzle

    • letter (str): the character to update the puzzle with

    • answer (list): the answer to the puzzle

  • Return: (int): a count of the number of times the letter appeared in the answer

  • Side effects: puzzle will be changed to show letter, if letter exists in the answer

Update the main program as shown below to test out this function.

def main():
    answer = choose_phrase()
    print("Answer: %s" % (list_to_string(answer)))
    puzzle = init_puzzle(answer)
    print("Puzzle: %s" % (list_to_string(puzzle)))
    letter = ask_for_letter()
    print("Letter: %s" % (letter))
    # Add the following lines
    count = update_puzzle(puzzle, letter, answer)
    print("The letter %s appeared %d times" % (letter, count))
    print("Puzzle: %s" % (list_to_string(puzzle)))

If the letter provided is in the answer, then puzzle should reveal that letter.

$ python3 wheeloffortune.py
Answer: as american as apple pie
Puzzle: -- -------- -- ----- ---
Enter a letter: a
Letter: a
The letter a appeared 5 times
Puzzle: a- a-----a- a- a---- ---

If the letter provided is not in the answer, then the updated puzzle will be unchanged.

$ python3 wheeloffortune.py
Answer: mystery of life
Puzzle: ------- -- ----
Enter a letter: d
Letter: d
The letter d appeared 0 times
Puzzle: ------- -- ----

6. Define do_spin_wheel

Implement the function do_spin_wheel(money, puzzle, answer, guessed).

Specification:

  • Parameters:

    • money (int): current total won by the player

    • puzzle (list): current puzzle state

    • answer (list): the puzzle answer

    • guessed (list): all of the letters guessed so far

  • Return (int): the new total amount. $0 if the user goes bankrupt. Otherwise, the old total + cash won

  • Side effects:

    • prints result of spin

    • guessed list is updated with new guess

    • phrase may be updated if player correctly guesses a letter

Function features:

  • Use the function spin_wheel defined in utilsWheelOfFortune. This function returns either a dollar quantity or -1 if the player lands on bankrupt

  • If the wheel lands on bankrupt, the player’s cash is reset to $0 and that turn is over

  • If the wheel lands on a cash amount, use the function ask_for_letter to get the player’s choice

  • If the letter was already guessed, print an error message and that turn is over

  • If the letter is in the answer, but not already shown in the puzzle

    • update the puzzle and save the number of times letter appears as count

    • calculate the cash won (letter count * spin amount) and add to the player’s total

Update the main program as shown below to test out this function.

Remember to remove or comment out the code you added for testing the previous function.

def main():
    answer = choose_phrase()
    print("Answer: %s" % (list_to_string(answer)))
    puzzle = init_puzzle(answer)
    print("Puzzle: %s" % (list_to_string(puzzle)))
    # Add the following lines
    cash = 500   # set cash to some reasonable value
    guessed = ["z"] # initialize letters guessed to contain a letter
    cash = do_spin_wheel(cash, puzzle, answer, guessed)
    print()
    # Check that all side effects have been accomplished
    print("updated cash amount $%d" % (cash))
    print("updated puzzle %s" % (list_to_string(puzzle)))
    print("updated letters guessed %s" % (list_to_string(guessed)))

Here is what a sample test run might produce. Notice that there were two instances of the player’s guessed letter in the puzzle, and since the wheel randomly landed on $700, their cash increased by 2 * 700 = 1400. Also note that both the puzzle and the letters guessed were also updated as a result.

% python3 wheeloffortune.py
Answer: within your grasp
Puzzle: ------ ---- -----
The wheel spin lands on $700
Letters guessed so far: z

Enter a letter: i
There are 2 of i in the phrase. You win 1400!

updated cash amount $1900
updated puzzle -i--i- ---- ---
updated letters guessed zi

7. Define do_guess_puzzle

Implement the function do_guess_puzzle(answer, turn, cash)

Specification:

  • Parameters:

    • answer (list): the answer to the puzzle

    • turn (int): player’s current turn number

    • cash (int): player’s current money total

  • Return (bool): True if the player guesses correctly; False otherwise

  • Side effects: If the player is correct, prints a summary message about the number of turns taken and the total cash won, otherwise prints a message telling them their guess was incorrect

Features:

  • Function should ask user for the phrase and compare it to the answer

  • Return True if the phrase matches the answer

  • Return False if the phrase does not match the answer

Update the main program as shown below to test out this function.

Remember to remove or comment out the code you added for testing the previous function.

def main():
    answer = choose_phrase()
    print("Answer: %s" % (list_to_string(answer)))
    puzzle = init_puzzle(answer)
    print("Puzzle: %s" % (list_to_string(puzzle)))
    # Add the following lines
    turn = 2
    cash = 1000
    result = do_guess_puzzle(answer, turn, cash)

Here are some sample test runs. In the first test, an incorrect answer is provided.

% python3 wheeloffortune.py
Answer: let's get going!
Puzzle: ---'- --- -----!
Enter your guess: testing
Sorry, that is NOT the correct answer!

In the second test, the correct answer is provided.

% python3 wheeloffortune.py
Answer: back to the basics
Puzzle: ---- -- --- ------
Enter your guess: back to the basics
Correct!
----------------------------------------
You solved the puzzle after 2 turns and with $1000 cash!

8. Game Time!

Now we can start working on the structure of our game.

8.1. Testing the menu function

One of the utility functions provided for you is called menu() and it handles getting an appropriate choice from the player on each turn. Let’s test out how this function works. Update your main program as shown below:

Remember to remove or comment out the code you added for testing the previous function.

def main():
    answer = choose_phrase()
    print("Answer: %s" % (list_to_string(answer)))
    puzzle = init_puzzle(answer)
    print("Puzzle: %s" % (list_to_string(puzzle)))
    # Add the following lines
    option = menu()
    print("User chose %d" % (option))

Here’s what a test run of your updated program might produce:

% python3 wheeloffortune.py
Answer: a new lease on life
Puzzle: - --- ----- -- ----
1. Spin wheel
2. Guess phrase
3. Quit

Choice: hello
hello is invalid. Try again.
1. Spin wheel
2. Guess phrase
3. Quit

Choice: 4
4 is invalid. Try again.
1. Spin wheel
2. Guess phrase
3. Quit

Choice: 1
User chose 1

Notice that it will repeatedly prompt the user for a valid input, until one is provided.

8.2. Pseudocode for main

So far we’ve been using the main() program to test out functions as we write them. This is a great way to iteratively build a solution.

Now we will work on creating the actual structure of the game. Some of the lines you previously wrote can be reused. Here’s a summary of the steps needed to build the game:

Choose a random phrase and call it answer
Initialize the puzzle based on answer
Initialize the guessed letters
Initialize the number of turns
Initialize the the user's cash
Initialize done flag
Print welcome message

while not done:
    Print the current status of the game (turn, puzzle, cash)
    Use menu() to ask the player if they want to spin, guess, or quit
    If spin
        call function to do spin wheel, and update the cash
    Elif guess:
        call function to do guess answer
        if player guesses correctly, set done to True
    Else:
        they want to quit, set done to True
    Update number of turns

Print closing message that includes answer

Continue to build your program incrementally.

8.3. Debugging tips

It is very helpful to know the puzzle’s answer while you are trying to debug your program. So we suggest printing the puzzle’s answer at the beginning of your program.

Once you think your program is working correctly, remove the line that prints the answer, and try your program out on a friend. Letting someone else test your program is a great way to find potential problems.

9. Requirements

The code you submit for labs is expected to follow good style practices, and to meet one of the course standards, you’ll need to demonstrate good style on six or more of the lab assignments across the semester. To meet the good style expectations, you should:

  • Write a comment that describes the program as a whole using a comment at the top of the file.

  • All programs should have a main() function.

  • Use descriptive variable names.

  • Write code with readable line length, avoiding writing any lines of code that exceed 80 columns.

  • Add comments to explain complex code segments (as applicable).

  • Write a comment for each function (except main) that describes its purpose, parameters and return value.

Your program should correctly implement the following functions as described above:

  • init_puzzle

  • ask_for_letter

  • update_puzzle

  • do_spin_wheel

  • do_guess_puzzle

Your main program should use these functions, along with the utility functions provided, to correctly implement the wheel of fortune game. The game must include:

  • A loop which uses the menu function, and when the player chooses

    • 1, they can spin the wheel and guess a letter

    • 2, they can try to guess the puzzle

    • 3, they quit the game

10. Answer the Questionnaire

After each lab, please complete the short Google Forms questionnaire. Please select the right lab number (Lab 05) from the dropdown menu on the first question.

Once you’re done with that, you should run handin21 again.

Submitting lab assignments

Remember to run handin21 to turn in your lab files! You may run handin21 as many times as you want. Each time it will turn in any new work. We recommend running handin21 after you complete each program or after you complete significant work on any one program.

Logging out

When you’re done working in the lab, you should log out of the computer you’re using.

First quit any applications you are running, including your vscode editor, the browser and the terminal. Then click on the logout icon (logout icon or other logout icon) and choose "log out".

If you plan to leave the lab for just a few minutes, you do not need to log out. It is, however, a good idea to lock your machine while you are gone. You can lock your screen by clicking on the lock xlock icon. PLEASE do not leave a session locked for a long period of time. Power may go out, someone might reboot the machine, etc. You don’t want to lose any work!