CS21 Lab 4: functions and while loops
Due Sunday, February 23, 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 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. 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:
-
Draw the correct stack diagram.
-
Show the output of the program.
2. Big picture for solving this lab
In this lab, you’ll build a two-player game in which the two players roll a six-sided die (simulated by having the program generate a random number) as they attempt to get as close as possible to a target value without going over it.
On each player’s turn, they decide whether or not to roll the die. If they roll, then the value is added to their score; if they exceed the target value, then the opponent wins. After the second player rolls, if both players have achieved the target score, then the game is a draw.
Here’s a sample game with a target value of 10 (changes to score are shown in bold):
Whose turn? |
Roll? |
Rolled value |
Player 1 score |
Player 2 score |
Player 1 |
yes |
4 |
4 |
0 |
Player 2 |
yes |
2 |
4 |
2 |
Player 1 |
yes |
3 |
7 |
2 |
Player 2 |
yes |
5 |
7 |
7 |
Player 1 |
yes |
4 |
11 |
8 |
At this point, Player 1 now has 11 points, which exceeds the target, so Player 2 wins.
Here’s another sample game, this time with a target value of 12:
Whose turn? |
Roll? |
Rolled value |
Player 1 score |
Player 2 score |
Player 1 |
yes |
5 |
5 |
0 |
Player 2 |
yes |
6 |
5 |
6 |
Player 1 |
yes |
4 |
9 |
6 |
Player 2 |
yes |
1 |
9 |
7 |
Player 1 |
no |
- |
9 |
7 |
Player 2 |
yes |
5 |
9 |
12 |
Player 1 |
yes |
3 |
12 |
12 |
Player 2 |
no |
- |
12 |
12 |
At this point, after Player 2’s turn, both players' scores match the target (12), so the game is a draw.
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, you’ll put all the functions together into a
working version of the game.
All of your code for the rest of this lab should be in the file
dice.py
.
Before we dive in to the details, take a look at what the final running program looks like. This example uses the same numbers as above.
3. Prompt the user to enter 'y' or 'n'
The first function that we write will be used for asking the player if they want to roll the die, and then if they want to play again at the end of the game. However, the function needs to make sure that the user only answers "y" or "n" to these questions.
The get_yes_or_no(prompt)
function that is provided in dice.py
passes the prompt
to the input
function so that it asks the user a question, and then returns whatever they answer.
Modify the function so that it shows "Please enter only y or n" if they answer anything other than "y" or "n", and keeps showing that until they give a valid answer, which the function should then return.
3.1. Test the get_yes_or_no
function in main
The main
function that is provided in dice.py
calls get_yes_or_no
with the argument "Would you like to roll (y/n)?"
Run your program to test your get_yes_or_no
function.
You should not need to modify main
for this, and should see similar outputs as below (user input in bold):
$ python3 dice.py
Would you like to roll (y/n)? y
You chose: y
$ python3 dice.py
Would you like to roll (y/n)? n
You chose: n
$ python3 dice.py
Would you like to roll (y/n)? yes
Please enter only y or n
Would you like to roll (y/n)? maybe
Please enter only y or n
Would you like to roll (y/n)? puppies!
Please enter only y or n
Would you like to roll (y/n)? n
You chose: n
Keep in mind that the code that prints "You chose:" should be in main
, not get_yes_or_no
, since get_yes_or_no
is returning the user’s choice to main
.
4. Prompt the user to enter a value between two numbers
Next, write a function that displays a message to the user and then asks them to enter an integer between two values low
and hi
.
This function will be used for asking the the target value of the game.
Implement the get_value_between(msg, low, hi)
function using the prototype below.
def get_value_between(msg, low, hi):
"""
Shows a message and then prompts the user to enter a number between
low and hi (inclusive) and returns the user's choice.
Re-prompts the user until a valid choice is made.
msg: (str) message to display to the user
low: (int) the smallest allowable value
hi: (int) the largest allowable value
returns: (int) the user's selected value
"""
This function should first display the message, i.e. the msg
parameter.
Then it should (separately) prompt the user to enter
a number between low
and hi
(using the values of low
and hi
that are passed as arguments to the function) and read the value that the user enters; see the examples below.
You may assume that the user will enter an integer,
but if the user enters an invalid choice, i.e. a number less than low
or greater than hi
,
print an error message and prompt the user repeatedly until they enter a valid choice.
4.1. Test the get_value_between
function in main
Check that your function works correctly by calling it from main
with "What is the target value?" as the message, 7 as low
and 21 as hi
.
You should comment out the call to get_yes_or_no
in main
for testing get_value_between
.
Run your program to test your function. You should see similar outputs as below (user inputs in bold):
$ python3 dice.py
What is the target value?
Enter a number between 7 and 21: 9
You chose: 9
$ python3 dice.py
What is the target value?
Enter a number between 7 and 21: 21
You chose: 21
$ python3 dice.py
What is the target value?
Enter a number between 7 and 21: 6
Enter a number between 7 and 21: 51
Enter a number between 7 and 21: 11
You chose: 11
As above, the code that prints "You chose:" should be in main
, not get_value_between
, which is returning the user’s choice to main
.
5. Simulate rolling the die and updating the score
Now we’re ready to start implementing parts of the game!
Write a function that does the following:
-
Show the user their score, their opponent’s score, and the target value
-
Ask if they want to roll the die
-
If so, generate a random number to simulate rolling the die and show the value
-
Return the value that they rolled, or 0 if they didn’t roll
Implement the roll(score, opp, target)
function using the prototype below.
def roll(score, opp, target):
"""
Shows the current scores and target, then asks the user if they want
to roll the die. If so, simulates rolling the die and displays the
result. Then returns the rolled value, or 0 if the player did not roll.
score: (int) the player's current score
opp: (int) the opponent's current score
target: (int) the target value
returns: (int) the rolled value, or 0 if the player did not roll
"""
When asking the user whether they want to roll the die,
your roll
function must call the get_yes_or_no
function that you implemented above.
To simulate rolling the die, your program should generate a random int between 1 and 6, inclusive.
Note that this function must both display and return the value the player rolled, or return 0 if they did not roll.
5.1. Test the roll
function in main
In your main
function, comment out any calls to get_yes_or_no
and get_value_between
,
and test the roll
function by calling it with score
= 5, opp
= 6, and target
= 10.
Run your program to test your function. You should see similar outputs as below, keeping in mind that there may be slight differences depending on what value is rolled:
$ python3 dice.py
Your score is 5; your opponent's score is 6; the target is 10
Would you like to roll (y/n)? y
You rolled 3
Function returned 3
$ python3 dice.py
Your score is 5; your opponent's score is 6; the target is 10
Would you like to roll (y/n)? banana
Please enter only y or n
Would you like to roll (y/n)? y
You rolled 5
Function returned 5
As above, the code that prints "Function returned…" should be in main
, not roll
.
In the example below, the player chooses not to roll, so the roll
function returns 0, and does not show "You rolled…":
$ python3 dice.py
Your score is 5; your opponent's score is 6; the target is 10
Would you like to roll (y/n)? n
Function returned 0
6. Playing the game
Now it’s time to implement the game!
Write a function that allows the two players to play the game by doing the following in a loop:
-
Call the the
roll
function for Player 1 -
Update Player 1’s score
-
Show Player 1’s current score
-
If they exceeded the target value, indicate that Player 2 won the game and return
-
Otherwise, call the
roll
function for Player 2 -
Update Player 2’s score
-
Show Player 2’s current score
-
If they exceeded the target value, indicate that Player 1 won the game and return
-
If both players' scores equal the target, indicate that the game is a draw and return
Implement the play_game(target)
function using the prototype below.
def play_game(target):
"""
Play the game until someone wins
target: (int) the target value
returns: none
"""
Aside from using the roll
function, you will need to initialize and update variables in this function to keep track of various aspects of the game.
6.1. Test the play_game
function in main
Edit the main
function to remove the call to roll
and instead call play_game
with target
= 10.
Run the program and your output should be similar to what is shown in the sample program run, though the values will be different depending on the random numbers that are generated.
Note that at this point your program does not yet ask the user if they want to play again, so you should only see one game being played.
You may notice while playing the game that it is possible to achieve a state of "deadlock" in which neither player ever rolls so the game never ends. This is not something you need to address in implementing this game.
7. Finishing the main
function
We’re almost done! The last step is to modify the main
function so that it does the following in a loop:
-
Use the
get_value_between
function to ask for the target value, which should be between 7 and 21 -
Use the
play_game
function to play the game with the selected target value -
After the game is over, use the
get_yes_or_no
function to see if the user wants to play again; if so, loop again
Now when you run your program, you should see something like what is shown in the sample program run, including asking if the user wants to play again.
Your output does not need to exactly match what is shown in the sample, but the logic of the gameplay should be the same. Ask for help if you’re unsure about how to handle any particular cases, though!
8. Requirements for dice.py
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:
|
Your program should meet the following requirements:
-
Your program should correctly implement the
get_yes_or_no
function as described above. -
Your program should correctly implement the
get_value_between
function as described above. -
Your program should correctly implement the
roll
function as described above. -
Your program should implement the
play_game
function such that it correctly determines the winner if one player’s score exceeds the target. -
Your program should implement the
play_game
function such that it correctly ends in a draw if both players' scores equal the target. -
Your
main
function should correctly useget_value_between
for getting the target value, correctly useplay_game
for playing a single game, correctly useget_yes_or_no
for asking if the user wants to play again, and correctly playing again (or not) depending on their response.
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 ( or
) 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 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!