Homework 3
Objectives
- Choose an appropriate loop (
fororwhile) for repetition - Convert user input from strings to integers
- Use the
timemodule to implement a time-bounded game loop - Collect values into a list inside a loop, then loop over that list to find the smallest value
- Practice incremental development: build, run, and test each piece before combining them
Background
In this assignment you will write a timed game in which the player answers as many math questions as they can in a given amount of time.
The homework builds directly on Lab 3: Part 1 uses Lab 3’s random_equation, and Part 3 uses the timing pattern from Lab 3’s ask_name and time_entries. If you finish Lab 3 during your lab session, start here. Parts 3 and 4 use lists, from Lecture 8.
Read the whole writeup before you start. Then build the program one function at a time, and test each function in the Thonny shell before moving on.
Part 0: Setup
Click “Download Starter Code” above to download hw3.py, then move it to your cs145/homework folder and open it in Thonny. The starter file is nearly empty: you will write each function and its docstring yourself. When you write main, delete its placeholder comment and pass.
Part 1: Generating random equations – random_equation
random_equation(num_op) returns a random equation with num_op operators as a string, such as '8 - 5 * 2'. You write it in Lab 3. Once it passes Lab 3’s tests, copy its definition, along with any imports and constants it uses, into hw3.py.
Part 2: Getting an answer – query_equation
query_equation(equation) takes an equation as a string, asks the user for the answer until they get it right, and prints feedback after each answer. It doesn’t return anything.
- Prompt with the equation followed by
=, as in5 + 9 + 7 =. - If the answer is correct, print
Correct!and stop asking. - If the answer is wrong but within 2 of the correct answer, print
Close. Try again.For example, if the correct answer is 20, both 22 and 18 are close. - Otherwise, print
Keep trying.
>>> query_equation('5 + 9 + 7')
5 + 9 + 7 = 25
Keep trying.
5 + 9 + 7 = 22
Close. Try again.
5 + 9 + 7 = 20
Close. Try again.
5 + 9 + 7 = 21
Correct!To find the correct answer, use Python’s built-in eval, which evaluates an expression written as a string:
>>> eval('7 * 5 - 2 - 4 * 8')
1This function is a lot like the guessing game from Lecture 7 (slide 20). There, the loop keeps asking until the guess equals a random number. Here, it keeps asking until the answer equals eval(equation), and the feedback depends on how close the answer is rather than on whether it’s too high or too low.
Suggested steps:
- Without a loop, ask once and print
Correct!orKeep trying.inputreturns a string, so convert the answer to an integer before comparing. - Add a loop that asks again while the answer is wrong.
- Add the
Close. Try again.case.
Part 3: Playing the game – play_game
play_game(seconds, num_op) plays the game for seconds seconds, using equations with num_op operators. As long as time hasn’t run out, it asks a new question and records how long the player took to answer it, collecting those times in a list. When time runs out, it prints a summary line. It doesn’t return anything.
This function should be short, because the other functions do the work:
record the start time of the game
while time hasn't run out:
record the start time of this question
get a new equation ← random_equation
ask it until it's answered correctly ← query_equation
add this question's time to the list
print the summary line
print the fastest time ← best_time (Part 4)
Times work as in Lab 3: record a start time with time.time(), and later subtract it from the current time. Collect the question times into a list the same way time_entries(n) collected times from ask_name().
The summary line must match this format exactly (“correct” and “right” mean the same thing to us, but not to the autograder):
You got 2 correct in 12.731420040130615 seconds.
The number correct is the length of your list of times, so you don’t need a separate counter. The time is how long the game actually took, not seconds.
>>> play_game(10, 2)
10 - 9 * 7 = -53
Correct!
3 + 8 * 2 = 21
Close. Try again.
3 + 8 * 2 = 19
Correct!
You got 2 correct in 12.731420040130615 seconds.Because each question is timed from prompt to correct answer, time never runs out in the middle of a question, so the game can last longer than seconds. That’s expected.
Suggested steps:
- Get the timing loop working on its own: ask for any input with
inputand print it, over and over, until time runs out. - Replace that with a call to
random_equationand a call toquery_equation. - Time each question, append each time to a list, and print the summary line.
Part 4: Finding the fastest answer – best_time
best_time(times) takes a list of times and returns the smallest one. You can assume times has at least one value.
>>> best_time([4.5, 2.1, 6.7, 3.0])
2.1
>>> best_time([3.2])
3.2In Lab 3 you saw that the built-in min() does this. Here, don’t use min(): write the loop yourself. Lecture 8 showed how to write max ourselves by keeping the best value so far and replacing it when you find something better. best_time works the same way, with “better” meaning smaller.
Then finish play_game: after the summary line, call best_time on your list and print the result.
You got 2 correct in 12.731420040130615 seconds.
Fastest answer: 5.2318127155303955 seconds.
The wording of the fastest-answer line isn’t checked, as long as it comes after the summary line and includes the time.
Part 5: The finishing touches – main
main() asks the user if they want to play. If they answer yes, it asks how long to play (in seconds) and then how many operators to use, in that order, converts both answers to integers, and calls play_game. If they answer anything else, it prints a goodbye message and asks nothing more. main runs automatically when you run hw3.py.
Do you want to play a game [yes/no]? yes
How long do you want to play for [seconds]? 10
How many operators do you want to play with? 2
10 - 9 * 7 = -53
Correct!
3 + 8 * 2 = 21
Close. Try again.
3 + 8 * 2 = 19
Correct!
You got 2 correct in 12.731420040130615 seconds.
Fastest answer: 5.2318127155303955 seconds.
Do you want to play a game [yes/no]? no
OK. Goodbye.
Specification
At a minimum your submission should have:
- A function named
random_equationwith one parameter, the number of operators, that returns a random equation with that many operators as a string, as in Lab 3. - A function named
query_equationwith one parameter, an equation as a string, that prompts the user until they answer correctly, printingCorrect!,Close. Try again., orKeep trying.after each answer. It doesn’t return anything. - A function named
play_gamewith two parameters, the game duration in seconds and the number of operators, that presents new equations (viarandom_equationandquery_equation) until time runs out, collecting each answer time into a list. It prints the summary line exactly as shown above, then the fastest time (viabest_time). - A function named
best_timewith one parameter, a list of times, that returns the smallest time in the list, found with a loop rather thanmin(). - A function named
mainwith no parameters that asks the user if they want to play. If they answeryes, it asks for the duration and then the number of operators, and callsplay_game. Otherwise, it prints a goodbye message.
Style and Design
- You must add your code to the provided starter file.
- At the top of your file, include your name (
Author:) and the resources you used (included in the starter file). - Each function needs a docstring with its purpose, parameters, and return value. The Lab 3 starter code has good examples.
- Use constants instead of “magic numbers”: for example, the range of numbers and the operators used by
random_equation, and the “within 2” threshold inquery_equation. - Follow the Guidelines for Code Quality.
Common issues
Getting an “EOF when reading a line” error on Gradescope? Your program called input() more times than the test expected, usually because a prompt ran when it shouldn’t have. Check that:
query_equationstops asking as soon as the user answers correctly.mainasks for the duration and number of operators only if the user answersyes, and asks for them in that order.
Running your program yourself and watching when each prompt appears is the fastest way to catch this before you submit.
Submitting your work
When you are done, submit hw3.py to Gradescope.
Ensure that the upload succeeds and your submission passes all visible tests. Passing all of the visible tests does not guarantee that your submission correctly satisfies all of the requirements of the assignment. Some tests are hidden until after the grace period; they check cases such as an answer exactly 2 away in query_equation and the smallest time being last in the list for best_time. We encourage you to submit early and often so you have time to resolve any issues.