CS21 Lab 4: Functions and while loops
Due Saturday, February 18 by 11.59PM
Programming Tips
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.
Are your files in the correct place?
Make sure all programs are saved to your cs21/labs/04
directory! Files
outside that directory will not be graded.
$ update21 $ cd ~/cs21/labs/04 $ pwd /home/username/cs21/labs/04 $ ls Questions-04.txt (should see your program files here)
Goals
The goals for this lab assignment are:
-
Develop your understanding of the stack and how function calls and returns work.
-
Write programs with multiple functions.
-
Use indefinite
while
loops to solve problems for whichfor
loops are not suitable -
Use the
random
library to make pseudo-random choices
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. |
2. Programming Assignment: Dice Race!
For this week’s assignment, we’ll program a game called Dice Race! This is a two-player game, where the player is playing against the computer. Each player takes a turn at rolling a fair die, and advances a certain number of steps according to the rules described below. After a preset number of rounds, the player who has taken the most steps, wins.
Since this is a more complex program than the ones we’ve implemented so far, it will be even more important to practice good incremental development.
Follow the development plan given below and implement one function at a time. Each time, be sure to test your function thoroughly and make sure it works before moving on to the next one.
Your main()
function should be used to test whatever function you are currently working on; when you get to the final stage,
your main()
function will play the entire
game. For this program, we will keep the body of main()
concise, and work on writing modular portions of our program, in other functions that are called by main.
2.1. Welcome Message
Write a function called welcome
that prints out a message welcoming the
players to the game and explaining the rules. This function takes no arguments,
and returns no value; it should just contain print statements. The prototype
for the function should look like this:
def welcome():
Remember that all functions, including this one, should have a block comment at the top explaining what the function does, as well as any parameters or return values (though in this case you don’t have either of those). |
Here’s an example of what the output should look like for welcome()
. To test this function, call it in main()
.
$ python3 dice_race.py Welcome to Dice Race! ----------------------------------------- The objective of the game is to get to the finish line before your opponent, the computer. First, provide your name, choose the number of rounds of the game you would like to play. Then, at each round, roll the die, and predict the outcome of the die. If you predict a value less than the outcome of the die, then you only get to advance by one step. If you predict a value greater than the die outcome you get to advance by the value you predicted! Next, the computer gets to play by the same rules. To win the game, your task is to have more steps in total, across all rounds, than the computer. Good Luck! -----------------------------------------
2.2. Checking if a die roll prediction is valid
Write a function called get_prediction
that takes a
potential prediction of the die roll from the user,
and checks whether or not it is a valid choice.
"Valid" in this case means that the user chose
an integer in the range from 1
to a 6
(inclusive) — i.e., all possible values that a die can take.
If the choice is invalid, the function should print a helpful message saying what went wrong (see examples below). If the choice is valid, the function doesn’t need to print anything.
The prototype for the function should look like this:
def get_prediction():
"""
Purpose: Get the user's prediction of the die roll
Parameters: none
Returns: (int) - Positive integer between 1 and 6
"""
When testing a function like this, be sure to check the edge cases, which are the values right around the limits of the range of accepted values, where the behavior should change. In this case, you’ll want to test what happens for values exaclty equal to, one less than, and one greater than, the minimum and maximum values.
Test this function by calling it in main()
; for example, your
main
might look like this:
def main():
player_choice = get_prediction()
print("%s chose %d" %(player_name, player_choice))
Remember at this point, we are simply testing for valid input. We are not rolling a die yet! |
The output of a few runs of our input validation might look like this:
$ python3 dice_race.py Predict the die outcome: 1 Player chose 1. $ python3 dice_race.py Predict the die outcome: 3 Player chose 3. $ python3 dice_race.py Predict the die outcome: 6 Player chose 6. $ python3 dice_race.py Predict the die outcome: -1 Sorry, -1 is not a valid choice. Please enter a positive integer value between 1 and 6. Predict the die outcome: 4 Player chose 4. $ python3 dice_race.py Predict the die outcome: 10 Sorry, 10 is not a valid choice. Please enter a positive integer value between 1 and 6. Predict the die outcome: 2 Player chose 2.
2.2.1. OPTIONAL: Checking if a string is an integer
While checking the range of a value is a good first step, the
program will still crash if the user types in something that’s
not an integer. As an optional extension, you can modify your
get_prediction
function to safely detect and handle non-integer
inputs. Note that the way we call and use the function will be
the same, so it’s recommended to finish the rest of the lab
first and then come back to this part if you have time (since
it’s optional and won’t count against you if you don’t have time).
Detecting numbers
To check if a string can safely be converted to an int, you can ask whether all the characters in the string are digits:
Note that the syntax here is a bit different than we’ve seen before; you need to
use the name of the variable you want to check, then a period, then the name of
the function you want to use. In this example, Note also that |
If you choose to do the optional extra of detecting non-numeric inputs, the output of some testing runs might look like this:
$ python3 dice_race.py Predict the die outcome: hello Sorry, hello is not a valid choice. Please enter a positive integer value between 1 and 6. Predict the die outcome: 1 Player chose 1. $ python3 dice_race.py Predict the die outcome: six Sorry, six is not a valid choice. Please enter a positive integer value between 1 and 6. Predict the die outcome: 6 Player chose 6. $ python3 dice_race.py Predict the die outcome: 4.7 Sorry, 4.7 is not a valid choice. Please enter a positive integer value between 1 and 6. Predict the die outcome: 4 Player chose 4.
2.3. Rolling a die
Let’s now write a function roll_die
to roll the die for each player. To do
so, we will import the randint
function from the random
module.
randint(a, b)
will return a random integer between a and b (inclusive). For example:
from random import randint
# Randomly select an integer between 3 and 10 (inclusive)
random_value = randint(3, 10)
print(random_value)
# This will print something different every time from the set {3, 4, 5, 6, 7, 8, 9, 10}
The prototype for the function should look like this:
def roll_die():
"""
Your Function Comments Here
Purpose:
Parameters:
Returns:
"""
Test it by calling the function in main
; e.g.
def main():
die_outcome = roll_die()
print("Here's the outcome from one die roll: " + str(die_outcome))
The output might look like this:
$ python3 dice_race.py Here's the outcome from one die roll: 1 $ python3 dice_race.py Here's the outcome from one die roll: 4 $ python3 dice_race.py Here's the outcome from one die roll: 6 $ python3 dice_race.py Here's the outcome from one die roll: 3
2.4. Printing the player prediction and die outcome
Let’s now write a function called print_outcome()
, that prints both the
player’s prediction of the die roll and the actual outcome. The prototype for the function should look like this:
def print_outcome(player_name, player_choice, die_outcome):
"""
Your Function Comments Here
Purpose:
Parameters:
Returns:
"""
Test it by calling the function in main
; e.g.
def main():
player_name = "Lauri"
player_choice = 4
die_outcome = 2
print_outcome(player_name, player_choice, die_outcome)
The output might look like this for the example shown above:
$ python3 dice_race.py Lauri choose 4 and the die result was 2.
2.5. Advancing each player
Next, write a function called advance_player()
that adds the correct number
of steps to advance the player, and returns the updated total number of steps
for the player. In the body of the function, make sure to check if the player’s
choice is less than or greater than the die result and if the player’s choice
is equal to die result. For each branching condition, add the appropriate
number of steps according to the game’s rules.
The prototype for the function should look like this:
def advance_player(die_outcome, player_choice, player_steps):
"""
Purpose: Perform a roll of a fair die
Parameters: player_choice, die_result, player_steps
Return: player_steps: the updated accumulator value of the
number of steps the player has taken
"""
Test it by calling the function in main
; e.g.
def main():
player_name = "Vasanta"
die_outcome = 4
player_choice = 3
player_steps = 2
player_steps = advance_player(die_outcome, player_choice, player_steps)
print("Total steps for %s are %d" %(player_name, player_steps))
For the example values shown above, since the die outcome is greater than the
player’s prediction, the player only advances by 1 step. Therefore, the total
step count, after the call to advance_player
would now be 3. The output might
look like this:
$ python3 dice_race.py Total steps for Vasanta are: 3
2.6. Programming the computer’s prediction
Congratulations for making it this far! We’ve now got everything we need for one player to play Dice Race!
Let’s now write a function computer_prediction()
that programs our opponent -
the computer’s prediction. We will assume that the computer will simply pick a
random integer between 1 and 6. We’ve already seen how we can select a random integer using randint
. We will use the same logic here, to return the computer’s choice.
Here’s how you might call and test your function in main
:
def main():
computer_choice = computer_prediction()
print("The computer's prediction is: " + str(computer_choice))
The output might look like this:
$ python3 dice_race.py The computer's prediction is: 3 $ python3 dice_race.py The computer's prediction is: 5 $ python3 dice_race.py The computer's prediction is: 1
2.7. Printing scores for each round
For each round of the game we also would like to print scores for both the
players. Let’s write a function called print_scores()
that prints the scores for both the players. The prototype for the function should look like this:
def print_scores(player_name, player_steps, computer_steps):
"""
Your Function Comments Here
Purpose:
Parameters:
Returns:
"""
Note that since we are playing against the computer, we aren’t explicitly passing in the names of two players.
Here’s how you might call and test your function in main
:
def main():
player_name = "Crystal"
current_round = 2
player_steps = 3
computer_steps = 5
print_scores(player_name, player_steps, computer_steps)
The output might look like this:
$ python3 dice_race.py Crytal's Total Steps: 3 Computer's Total Steps: 5
2.8. End-of-game messages
Write a function game_over
that takes in the names of all the players, and the total steps that each player has accumulated.
The output of this function might look like this.
================================== We've reached the end of the game! The champion is: Lauri!
2.9. Putting it all together: Playing the game
Now that you have all the functions you need, let’s combine them to play multiple rounds of Dice Race against a computer opponent. Be sure that all of the functions you write for this lab are well commented — if you haven’t done so already, now would be a good time to write those comments, as they may help you to put all the pieces together.
2.10. The main function
The main function of your program should now use the functions that we have built so far to construct the complete game.
Structuring your code
The following tips are to help use the functions we have written to write clean, modular code.
|
Overall flow of main
:
-
Ask the player for their name and the number of rounds to play. You may assume the player enters a valid positive integer for the number of rounds.
-
Continue playing the game for the number of requested rounds, keeping track of the number of steps for the human player and the computer. Use string formatting when printing results.
-
In each round, make use of the
get_prediction
orcomputer_prediction
as appropriate for the human player and the computer. -
For each round, use the
roll_die
,computer_prediction
, andadvance_player
functions to calculate the player’s game progress. -
Use your functions to generate the die roll and the computer’s prediction.
-
Use the print functions:
print_outcome
, andprint_scores
for each round, and finally usegame_over
once all the rounds are complete to declare the winner.
2.10.1. Example Output
Here is an example of run of the complete game:
$ python3 dice_race.py Welcome to Dice Race! ----------------------------------------- The objective of the game is to get to the finish line before your opponent, the computer. First, provide your name, choose the number of rounds of the game you would like to play. Then, at each round, roll the die, and predict the outcome of the die. If you predict a value less than the outcome of the die, then you only get to advance by one step. If you predict a value greater than the die outcome you get to advance by the value you predicted! Next, the computer gets to play by the same rules. To win the game, your task is to have more steps in total, across all rounds, than the computer. Good Luck! ----------------------------------------- Please enter your name: Rain How many rounds would you like to play? 4 ------Starting the game-------- ------Round Number: 1----------- Predict the die outcome: 5 Rain choose 5 and the die result was 1 Computer choose 6 and the die result was 3 Rain's Total Steps: 5 Computer's Total Steps: 6 ------Round Number: 2----------- Predict the die outcome: 6 Rain choose 6 and the die result was 6 Computer choose 1 and the die result was 2 Rain's Total Steps: 5 Computer's Total Steps: 7 ------Round Number: 3----------- Predict the die outcome: 2 Rain choose 2 and the die result was 2 Computer choose 1 and the die result was 4 Rain's Total Steps: 5 Computer's Total Steps: 8 ------Round Number: 4----------- Predict the die outcome: 6 Rain choose 6 and the die result was 3 Computer choose 1 and the die result was 5 Rain's Total Steps: 11 Computer's Total Steps: 9 ================================== We've reached the end of the game! The champion is: Rain!
3. Answer the Questionnaire
Each lab will have a short questionnaire at the end. Please edit
the Questions-04.txt
file in your cs21/labs/04
directory
and answer the questions in that file.
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, like 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!