CS21 Lab 4: Functions and While loops
Due Sunday, Oct 4, 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
whileloops. -
Learn to use the
randomlibrary 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 online via gradescope.
1.1. Requirements
Given the pdf handout above:
-
Draw the correct stack diagram.
-
Show the output of the program.
2. Big picture for solving this lab
In this lab, we will be writing a single program that has the user play a dice game called Pips. The game is similar to the game Pig, though you should follow the rules outlined below for this lab.
In our version of Pips, a human player (You!) will compete against a computer agent that you will also design. The game is played in turns, with the human and computer players alternating. In each turn, the player will roll a pair of dice one or more times, trying to accumulate a number of points before a) rolling a one on either die, or b) choosing to stop.
The human player will go first, and the computer player will always have a turn after the human player finishes their turn.
If at any point in the turn, a player rolls a one on either die, they lose their turn and score nothing. Otherwise, the sum of the two dice is added to their turn score, and they may either stop or roll again.
-
If a player chooses to stop, their turn score is added to their game score, and the next player (the computer) takes a turn.
-
If a player chooses to continue, they roll again, potentially earning more points, but also risking losing all the points they’ve earned this turn if a one is rolled.
Human and computer players alternate taking turns until one player earns 100 points or more. The player with the higher score at the end of the game wins. If both players have the same score, the game is a tie.
Should you continue to roll, and maybe earn more points, or should you stop and lock in the points you’ve earned this turn? In playing the game Pips, we will assign a fixed strategy to the computer player, and you will be able to choose your own strategy.
Building a game like this requires planning and design. Writing multiple functions and testing them individually allows the game designer to make incremental progress toward the final game. 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 or two functions 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
Pips.py.
Before we dive in to the details, let’s take a look at what the final running program looks like.
3. Get a yes or no answer from the user
In the file named Pips.py, write a function named get_y_or_n that
has one string parameter called question. The string question should be a
question that can be answered with y (yes) or n (no). Your function should prompt the user with the question until the user answers with either y for yes or n for no. The function should return a valid string, either y or n. For any other input, the function should print a helpful error message and prompt the user to try again. The function should only return when the user enters y or n.
The function prototype should look like this:
def get_y_or_n(question):
3.1. Test get_y_or_n in main
To test get_y_or_n, you should have your main function call
the get_y_or_n function and print out the value returned. Your
main should look something like this:
def main():
choice = get_y_or_n("Do you like puppies? ")
print("User answered: %s" % (choice))
choice = get_y_or_n("Do you like orcas? ")
print("User answered %s" % (choice))
You can now test to make sure the get_y_or_n function is working
properly. Here are some examples of what your program should do at
this point:
$ python3 Pips.py
Do you like puppies? y
User answered: y
Do you like orcas? n
User answered n
$ python3 Pips.py
Do you like puppies? maybe
Please answer with 'y' or 'n'.
Do you like puppies? if they are well behaved
Please answer with 'y' or 'n'.
Do you like puppies? y
User answered: y
Do you like orcas? no
Please answer with 'y' or 'n'.
Do you like orcas? y
User answered: y
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 roll_die that generates and returns a
random integer between 1 and 6. (You should use the randint function,
described in the subsection below, to help you do this.)
Every integer from 1 to 6, including both 1 and 6,
are values that roll_die could generate. Although this may be a short
function, it’s a good idea to make this a separate function because
it puts the randomness in your game into a single function. If for some
reason you wanted to change the game in some way (e.g. maybe you roll a die with eight sides instead of six), you only need to change this function and not the rest
of your program.
The roll_die function does not take any input values (it has no
parameters), but it should return an integer value. Its function
prototype will look like:
def roll_die():
4.1. Selecting a random integer from a range of integers
This subsection will remind you how the randint function works.
To use randint, we import it from the random library.
|
The randint(a, b) function returns a random integer from a specific
range. For example, randint(1, 10) returns a random integer
from the range 1…10. Calling randint(2,5) will randomly select one
integer from the integers 2,3,4, or 5.
Here’s a simple program that demonstrates how to use the randint with the
examples above:
# place this import at the top of your program, before any function definitions
from random import randint
# When you run this program multiple times, you should get different,
# random results each time you run it.
def main():
value = randint(1, 100) # randomly pick an integer from 1-100
print(value)
year = randint(2027, 2030) # randomly pick an integer from 2027-2030
print(year)
main()
In the roll_die function described above, you will use the
randint function to randomly select an integer between 1 and 6 inclusive.
You should place the from random import randint statement at the top of your program, before any function definitions.
|
4.2. Test roll_die 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 roll_die, you should have your main function call
the roll_die function and print out the value returned. Your
main should look something like this:
def main():
number = roll_die()
print("roll_die returned %d" % (number))
You can now test to make sure the roll_die function is
working properly. Here are some examples of what your program should
do at this point:
$ python3 Pips.py
roll_die returned 4
$ python3 Pips.py
roll_die returned 1
$ python3 Pips.py
roll_die returned 2
5. Showing the current score
Our game will be played in a number of turns. To help you make decisions and to help delineate each turn, we will print out the current score of each player at the start of each turn. To do this, write a function called show_score that takes two integer parameters: the current score of the human player, and the current score of the computer player.
The function should print out the current score of each player in a readable format. The show_score function does not return a value, but does print output to the screen.
The function prototype for show_score is shown below:
def show_score(human_score, computer_score):
The variables human_score and computer_score will be the current scores (integers)of the human player and computer player, respectively.
5.1. Test show_score in main
To test show_score, you should have your main function call
the show_score function. An example main should look something like this:
def main():
show_score(14, 12)
The output of the program should look something like this:
$ python3 Pips.py
---------------SCORE---------------
You: 14 Computer: 12
-----------------------------------
Changing the arguments to show_score in main should change the output accordingly.
| You can use string formatting to help you format the output nicely. |
6. Implementing the computer’s strategy
Next, we will write a function to implement the computer’s strategy for taking a turn. For this lab, we will have the computer strategy to attempt to roll a pair of dice four times before stopping. If the computer rolls a one on either die, it will immediately lose its turn and score nothing. If the computer rolls four times without rolling a one, it will stop and return the total points earned in this turn. The function should be called computer_turn and it should take no parameters. It should return the total points earned in this turn (possibly zero). Additionally, the function should print out the result of each roll, and the total points earned in this turn.
The function prototype for computer_turn is shown below:
def computer_turn():
Recall if a function reaches a return statement, it immediately exits the function and returns the value specified. You can use this feature to exit the function early if, e.g., the computer rolls a one on its first roll and is unable to continue with additional rolls.
Your function should use the roll_die function you wrote earlier to generate the random die rolls.
6.1. Test computer_turn in main
Make a call to computer_turn in your main function to test it. Save the return value in a variable and print it out.
def main():
score = computer_turn()
print("Score: %d " % (score))
Sample output:
$ python3 Pips.py
Computer rolled 6 and 2
Computer rolled 5 and 2
Computer rolled 5 and 5
Computer rolled 4 and 4
Computer scored 33 this turn
Score: 33
$ python3 Pips.py
Computer rolled 2 and 6
Computer rolled 6 and 1
Computer scored 0 this turn
Score: 0
In the first example above, the computer completed all four rolls without rolling a one, and scored 33 points. In the second example, the computer rolled a one on its second roll, and scored zero points for the turn, without rolling a third or fourth time.
7. Taking one user turn in the game
On the user side, taking a turn is similar to the computer’s turn, but the user has the option to stop rolling at any time. Write a function called user_turn that takes no parameters and returns the total points earned in this turn for the user(possibly zero). The function should print out the result of each roll, and the total points earned in this turn. After each roll, if the user has not rolled a one, the function should ask the user if they want to roll again. If the user chooses to continue rolling, the function should roll again, possibly losing points accumulated in earlier rolls in the same turn. If the user rolls a one on either die, they lose their turn and score nothing. If the user answers 'n' to rolling again, the function should return the total points earned in this turn.
In this function, you must use the roll_die function you wrote earlier to generate the random die rolls, and the get_y_or_n function to ask the user if they want to roll again.
Like the computer_turn function print the result of each roll, and the total points earned in this turn.
def user_turn():
The structure of the user_turn function will be similar to the computer_turn function, but the user will continue rolling until the user either rolls a one or chooses to stop rolling. You will also need to use the get_y_or_n function in user_turn when you did not need this in computer_turn.
|
7.1. Testing user_turn in main
Replace the call to computer_turn in your main function with a call to user_turn. Save the return value in a variable and print it out.
def main():
score = user_turn()
print("Score: %d" % (score))
Some sample output is shown below. Note that the user may choose to roll again or stop rolling at any time, and the output will vary based on the random rolls and the user’s choices. Note that if at any point the user rolls a one on either die, they will immediately lose their turn and score nothing.
$ python3 Pips.py
You rolled 3 and 5
Your total so far is 8
Do you want to roll again? (y/n): y
You rolled 4 and 4
Your total so far is 16
Do you want to roll again? (y/n): y
You rolled 3 and 5
Your total so far is 24
Do you want to roll again? (y/n): n
You scored 24 this turn
Score: 24
$ python3 Pips.py
You rolled 2 and 2
Your total so far is 4
Do you want to roll again? (y/n): hmm
Please answer with 'y' or 'n'.
Do you want to roll again? (y/n): y
You rolled 2 and 2
Your total so far is 8
Do you want to roll again? (y/n): y
You rolled 1 and 6
You scored 0 this turn
Score: 0
8. Play the full game
Now we are ready to assemble all of our functions into a complete game. We will write the full game in main. 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.
8.1. The print_intro function
In the sample output, you will notice that there
is a welcome message displayed when the program first runs. Your message
doesn’t have to be the same, but you should write a function called print_intro
that takes no parameters and returns nothing. The function should print
out a welcome message to the user. The function prototype will look like:
def print_intro():
8.2. Implement the rest of main
After writing the print_intro function, the rest of the main function of your program should do the following to play a full game of Pips:
-
Initialize the current score of each player to zero
-
Repeat while the game is not over
-
print out the current scores
-
have the human player take a turn
-
have the computer player take a turn
-
-
Print out the final scores of each player.
-
Print out information about who won the game.
In each loop iteration, both the human player and the computer player will take a turn. The game is over when either the human player or the computer player has earned 100 points or more. If both players have earned 100 points or more, the player with the higher score wins. If both players have the same score, print that the game is a tie.
8.3. Testing main
Run your program and play a full game of Pips. Make sure that the game ends when one player earns 100 points or more, and that the correct winner is printed out at the end of the game.
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:
|
Your program should meet the following requirements:
-
Your program should implement the
get_y_or_nfunction with the same number of parameters (in this case, one), the same return type (in this case,str), and the same behavior as described above (in this case, asking the user to type in "yes" or "no", validating the input, and returning the input). -
Your program should implement the
roll_diefunction with the same number of parameters, the same return type, and the same behavior described above. -
Your program should implement the
show_scorefunction with the same number of parameters, the same return type, and the same behavior described above. -
Your program should implement the
computer_turnfunction with the same number of parameters, the same return type, and the same behavior described above. -
Your program should implement the
user_turnfunction with the same number of parameters, the same return type, and the same behavior described above. It must use both theroll_dieandget_y_or_nfunctions. -
Your program should implement the
print_introfunction as described above. -
Your program should implement a
mainfunction that plays one full game of Pips and prints the result (You win, Computer wins, or Tie) at the end of the game.
10. 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.
11. 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 Pips.py file to a new file named Pips_fun.py and
add these fun things to your Pips_fun.py file.
-
Have the game stop at 200 points instead of 100.
-
Have the ability to ask the user if they want to play again after a game is over and play multiple games.
-
When playing multiple games, keep track of how many games the user has won and how many games the computer has won. Print out the number of wins for each player at the end of each game.
-
-
If the computer or user rolls one on both dice, they lose all of their points for the entire game, not just the turn. Note that you can implement this without changing the
computer_turnoruser_turnfunctions much. Just return 2 from those functions if the player rolled two ones, and handle this special case in themainfunction. -
Allow for two human players
-
Prompt for the names of the human players
-
Implement a different strategy for the computer player. For example, have the computer stop rolling if it has earned at least 10 points in a turn, or if it has rolled three times in a turn.
Do you have other ideas? Try them out! Make the game more fun for
you. Just be sure you put your ideas in Pips_fun.py. Your Pips.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 (
) 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!