CS21 Lab 4: functions and while loops

Due Saturday, Oct 5, before midnight

Goals

  • Develop your understanding of the stack and how function calls and returns work

  • Write programs with multiple functions.

  • Solve problems using indefinite while loops.

  • Learn to use the random library to make pseudo-random choices.

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 have a top-level comment! Please see our function example page if you are confused about writing function comments.

1. Written Assignment: Stack Diagram

The first part of this lab is a written assignment to trace through some Python code, show the program output and draw the stack. Download the following .pdf file, print it out: lab4stack.pdf

You should write your solution on the print out, and submit it at the start of the class on Friday.

1.1. Requirements

Given the pdf handbout above:

  1. Draw the correct stack diagram.

  2. Show the output of the program.

2. Big picture for solving this lab

In this lab, we will be writing a program that has the user play a high-low guessing game. You may find this short video helpful in understanding the premise of the game.

Normally this game is played with standard playing cards with values from 1 to 13. In our version, we will use integers from 1 to 13 to represent the cards. And instead of using a deck of cards, we will have the computer generate a random number between 1 and 13 each time we want a new card.

Before each turn, the computer will generate a random integer between 1 and 13. The user will then guess if the next number will be higher or lower. If the user guesses correctly, they continue playing. If they guess incorrectly, the user has made one mistake. The game ends when the user has made three mistakes. When the game ends, the user’s final score is displayed. The score is the number of correct guesses the user made during the entire game.

Building a game like this requires planning and design. You shouldn’t just sit down in front of your computer and start writing code. In this lab, we’re going to guide you through writing each step of the process, building the program incrementally. In future labs, we’ll give you more opportunities to design and implement programs on your own.

Each section will have you write one function which will be part of the larger program. After you write each new function, we’ll have you test that function by calling it in main. Until you get almost to the very end, you’ll be modifying the main function in order to test each function. There is no need to save your main function from a last step as you work on each next step. At the very end, we’ll tell you when it’s time to put all the functions together into a working version of the game.

You’ll put all of your code for the rest of this lab in the file hilo.py.

Before we dive in to the details, let’s take a look at what the final running program looks like. This example uses the same numbers from the video linked above.

Notice that after the first guess, the user’s next guess is based on the last number. In the example, the first number was 3. The user guessed "high" and the next number was 10, so the user was correct. When the user makes their next guess, they need to decide if the next number will be higher or lower than the last number, which was 3.

3. Get a prediction from the user

In the file named hilo.py, write a function named get_prediction that asks a user to enter either "H" or "L" (short for Higher or Lower). The function returns the string value entered by the user. If the user doesn’t enter one of these two string values, your function should print an error message, and prompt the user to try again. The function should only return when the user enters a valid string value.

In addition to "H" or "L", the user may also enter "h" or "l" (in lowercase). Regardless of whether the user types in uppercase or lowercase, your function should only return either "H" or "L" in uppercase.

The get_prediction function does not take any input values (it has no parameters), but it should return a string value. Its function definition will look like:

def get_prediction():

3.1. Test get_prediction in main

To test get_prediction, you should have your main function call the get_prediction function and print out the value returned. Your main should something look like this:

def main():
    prediction = get_prediction()
    print("get_prediction returned %s" % (prediction))

You can now test to make sure the get_prediction function is working properly. Here are some examples of what your program should do at this point:

$ python3 hilo.py
(H)igher or (L)ower?: H
get_prediction returned H
$ python3 hilo.py
(H)igher or (L)ower?: L
get_prediction returned L
$ python3 hilo.py
(H)igher or (L)ower?: apple
Oops! You can only guess H or L. Try again.
(H)igher or (L)ower?: higher
Oops! You can only guess H or L. Try again.
(H)igher or (L)ower?: high
Oops! You can only guess H or L. Try again.
(H)igher or (L)ower?: H
get_prediction returned H
$ python3 hilo.py
(H)igher or (L)ower?: h
get_prediction returned H
$ python3 hilo.py
(H)igher or (L)ower?: l
get_prediction returned L
Remember that python strings can be compared with relational operators like == or < just like integers and floats:
$ python3
>>> "rock" == "paper"   # evaluates to False
>>> "rock" != "paper"   # evaluates to True

4. Choose a random number

Write a function called get_next_number that generates and returns a random integer between 1 and 13. (You should use the randrange function, described in the subsection below, to help you do this.) Every integer from 1 to 13, including both 1 and 13, are values that get_next_number could generate. Although this may be a short function, it’s a good idea to make this a separate function because you will end up calling it multiple times in your program. If for some reason you wanted to change the game in some way (e.g. maybe you guess from 1 to 15), you only need to change this function and not the rest of your program.

The get_next_number function does not take any input values (it has no parameters), but it should return an integer value. Its function definition will look like:

def get_next_number():

4.1. Selecting a random integer from a range of integers

This subsection will remind you how the randrange function works. To use randrange, we import it from the random library.

The randrange function will randomly select one integer from all of the values that an equivalent range function would generate. For example, range(100) will generate a sequence of integers from 0 to 99. Similarly, the randrange(100) will randomly select one integer between 0 and 99. Calling range(2, 10, 3) will generate the sequence 2, 5, 8. Similarly, calling randrange(2, 10, 3) will randomly select one integer from the sequence 2, 5, 8.

Here’s a simple program that demonstrates how to use the randrange with the examples above:

from random import randrange

# When you run this program multiple times, you should get different,
# random results each time you run it.

def main():
    value = randrange(100) # randomly pick an integer from 0-99
    print(value)

    number = randrange(2, 10, 3) # randomly pick an integer from 2, 5, 8
    print(number)

main()

In the get_next_number function described above, you will use the randrange function to randomly select an integer between 1 and 13.

4.2. Test get_next_number in main

Remember, we are solving this incrementally so there’s no need to preserve your main function as you develop the solution to this next step.

To test get_next_number, you should have your main function call the get_next_number function and print out the value returned. Your main should something look like this:

def main():
    number = get_next_number()
    print("get_next_number returned %d" % (number))

You can now test to make sure the get_next_number function is working properly. Here are some examples of what your program should do at this point:

$ python3 hilo.py
get_next_number returned 7
$ python3 hilo.py
get_next_number returned 13
$ python3 hilo.py
get_next_number returned 1

5. Make one guess in the high-low game

Let’s extend our program to add a function that allows the user to make one guess in the high-low game.

Add a new function called determine_success. This function takes as input two integers, the last number and the next number, and a string representing the user’s prediction (either "H" or "L"). The function should determine whether the user’s prediction was correct. If the user’s prediction was correct, the function should return True. If the user’s prediction was incorrect, the function should return False.

For example, if the last number was 13, the next number was 2, and the user guessed "L", the function should return True since the user correct guessed that the next number (2) would be lower ("L") than the last number (13). If the last number was 8, the next number was 11, and the user guessed "L", the function should return False since the user incorrectly guessed that the next number (11) would be lower ("L") than the last number (8).

Note: There will be times when the last number and the next number are the same. In this case, the user’s prediction is considered correct and your function should return True.

def determine_success(last_number, next_number, prediction):

5.1. Test determine_success in main

To test the determine_success function, you need to simulate having two numbers picked and asking the user to make a prediction.

In main, call the get_next_number function to get the last number number. Then call the get_prediction function to get the user’s prediction. Then call get_next_number again to get the next number. Finally, call the determine_success function to determine if the user’s prediction was correct. Be sure to print the return value from determine_success to verify that the function is working properly. You should be able to use some of the code samples from main above to help you with this.

The prediction is automatically successful if the last_number and the next_number are the same.

Here are some example runs. Your main does not need to run exactly like this, but it should test determine_success in the same way as shown below.

$ python3 hilo.py
last number: 13
(H)igher or (L)ower?: L
Next number: 2
Result: True

$ python3 hilo.py
last number: 8
(H)igher or (L)ower?: L
Next number: 11
Result: False

$ python3 hilo.py
last number: 1
(H)igher or (L)ower?: H
Next number: 8
Result: True

$ python3 hilo.py
last number: 7
(H)igher or (L)ower?: H
Next number: 1
Result: False

$ python3 hilo.py
last number: 9
(H)igher or (L)ower?: L
Next number: 9
Result: True

6. Play the high-low game

For the final part, you are going to add code so that you can play the full game of high-low.

After each guess in the game, you should print out the current scores using a function called print_score.

There’s no need to preserve your main function from the last steps, but much of what you wrote in the last step can be reused in this step if you find that helpful.

6.1. The welcome function

In the sample output, you will notice that there is a welcome message displayed run the program is first run. Your message doesn’t have to be the same, but you should write a function called welcome that takes no parameters and returns nothing. The function should print out a welcome message to the user. The function definition will look like:

def welcome():

6.2. The print_score function

The print_score function should take the following parameters as input:

  • the integer number of points the user has earned so far

  • the integer number of mistakes the user has made so far

Note that this function does not return a value. It is called for the side-effect of printing some information. The function definition will look like:

def print_score(points, mistakes):

The function should print out the current score of the two players between two dashed lines. For example, with the argument values 3 and 1, it would print out something like this:

------------------------
Score: 3     Mistakes: 1
------------------------

6.2.1. Test print_score in main

Now that you’ve had some practice testing your other functions, write some code in main that will let you test the print_score function. When you’re satisfied that print_score works as expected, you can move on to the next section.

6.3. The main function

The main function of your program should do the following:

  • Call the welcome function to display a welcome message.

  • Choose a first number to start the game.

  • Enable the user to play as many rounds of high-low as needed until the player makes 3 mistakes.

  • Be sure to make use of the get_prediction function to get the player’s choice each round, use the get_next_number function to select the next random number, and use the determine_success function to compute whether or not the player won.

  • Display the result (e.g. report that the player was correct or incorrect) after each round. Then, use the print_score function to show the current scores.

  • Update the player’s score and mistakes as needed.

  • When the player makes 3 mistakes, print the total number of points that the player earned and end the game.

7. 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 meet the following requirements:

  1. Your program should implement the get_prediction function with the same number of parameters (in this case, none), the same return type (in this case, str), and the same behavior as described above (in this case, asking the user to type in "H" or "L", validating the input, and returning the uppercase version of the input).

  2. Your program should implement the get_next_number function with the same number of parameters, the same return type, and the same behavior described above.

  3. Your program should implement the determine_success function with the same number of parameters, the same return type, and the same behavior described above.

  4. Your program should implement both the print_score and the welcome function with the same number of parameters, the same return type, and the same behavior described above.

  5. In main, your program should repeatedly ask the user to make a prediction based on the last number. After making a prediction, the program should choose a new number and then display whether the user’s prediction was correct or incorrect.

  6. Your program should keep track of the number of points and mistakes the player makes as they play the game, displaying these values after each prediction that the user makes.

  7. The game should continue until the user has made 3 mistakes. At that point, the program should print out the total number of points the user earned.

Your solution should be contained within a main function that you call at the end of your program.

8. Sample output

Your program’s output does not need to be identical to the sample output shown earlier, but you should make sure to have all the required functionality and that your program displays the information in a readable way.

Here are two other example outputs that you may find helpful:

9. OPTIONAL fun things to try

If you’d like to make your game more interesting, here are some fun things to try. These are optional, but if you’re interested in trying them, copy your hilo.py file to a new file named hilo_fun.py and add these fun things to your hilo_fun.py file.

  1. Display a different message if the last number and next number are the same.

  2. Only give the user a point if the last number and next number are different. If they are the same, don’t count that as a mistake but also don’t give them a point.

  3. Instead of playing the game until they make 3 mistakes, let the user play until they earn a specified number of points (e.g. 52 points). When they reach the target number of points, print out how many mistakes they made.

  4. Let the user to quit the game at any time by typing "Q" instead of "H" or "L". If the user quits, print out how many points they earned and how many mistakes they made and end the game immediately.

  5. Allows the user to play the game multiple times in a row. After the user plays one game, ask them if they want to play again. If they do, start a new game. If they don’t, end the program.

  6. Allows the user to specify the range of numbers that the computer can choose from. For example, the user could specify that the computer should choose numbers between 1 and 20.

Do you have other ideas? Try them out! Make the game more fun for you. Just be sure you put your ideas in hilo_fun.py. Your hilo.py program should be the one that we will use to determine if you have met the requirements for the lab.

Answer the Questionnaire

After each lab, please complete the short Google Forms questionnaire. Please select the right lab number (Lab 04) 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!