CS21 Lab 5: Lists and Functions

Due Monday, March 16, before 11:59pm

This lab is due the Monday after spring break.

We strongly recommend that you complete much of it before break.

There will be a Ninja Session Sunday March 15, from 7-9pm.

Goals

  • More practice writing programs with multiple functions, notably seeing how small functions can be assembled together into a larger program.

  • Practice with lists and operations on lists.

  • Practice with functions and lists.

  • More practice with loops and conditional statements.

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.

The Winter Olympics Game

You will implement a guessing game based on choosing which winter Olympic sports to watch over a four day period. A player gets 10 guesses to guess their viewing schedule. After each guess, if the player has not guessed correctly, the program will give them information about their guess to help them with their next guess.

Main Game Playing Control Flow

The main control flow of your game program is the following:

  1. Print out a welcome message with the game playing rules

  2. Compute the answer to the game—​randomly choose 4 unique sports from a set of 6 sports.

  3. Play the Game: give the player up to 10 guesses to guess the answer. If they answer correctly the game ends with the user winning. If they run out of guesses, the player loses. In each guessing round, your program should:

    1. get the user’s guesses of 4 sports, one for each of the 4 days.

    2. compute the number of exact matches that they guessed (the correct sport on the correct day).

    3. compute the number of correct sports that they guessed regardless of if they guessed the sport on the correct day.

    4. determine if the they won, lost, or answered incorrectly but still have guesses left, and print out an appropriate message for each case. In addition, if they answered incorrectly but still have guesses left, the program prints out some information to help the player with their next guess, namely: (1) the number of sports guessed correctly; and (2) the number guessed correctly on the correct day.

  4. print out the answer at the end of the game

Sample Output

Here is output from one run of a program where the player wins. Note how the program handles errors when the user enters invalid options for a sport. Also, note that a player’s guess can include duplicate sports even though the answer will not contain duplicates.

$ python olympics.py

Welcome! I will select a set of winter Olympic sports for you to watch
over the next 4 days, choosing a different one for each day from the set:
   A: Alpine skiing
   C: Curling
   H: Hockey
   L: Luge
   N: Nordic skiing
   S: Speed skating
To make this fun, you have to guess what I picked.  Each round you will enter
your guess for the sport on each day, and we will give you the following
information about your guess to help with subsequent guesses:
   1. the number of sports you guessed correctly
   2. the number sports you guessed correctly on their correct day

You have 10 chances to guess correctly.
Good luck!
--------------------------------------------------

Round 1 pick sports from:
( A, C, H, L, N, S )
enter sport for day 1: hello
hello is not a valid sport, try again from this set:
( A, C, H, L, N, S )
enter sport for day 1: b
b is not a valid sport, try again from this set:
( A, C, H, L, N, S )
enter sport for day 1: A
enter sport for day 2: A
enter sport for day 3: C
enter sport for day 4: C
Sorry, that isn't correct.
  Here is some information to help you with your next try:
  Number of correct sports: 2
  Number of correct sports on the correct day: 1
Try again...

Round 2 pick sports from:
( A, C, H, L, N, S )
enter sport for day 1: C
enter sport for day 2: A
enter sport for day 3: H
enter sport for day 4: L
Sorry, that isn't correct.
  Here is some information to help you with your next try:
  Number of correct sports: 3
  Number of correct sports on the correct day: 2
Try again...

Round 3 pick sports from:
( A, C, H, L, N, S )
enter sport for day 1: C
enter sport for day 2: A
enter sport for day 3: L
enter sport for day 4: N
Sorry, that isn't correct.
  Here is some information to help you with your next try:
  Number of correct sports: 3
  Number of correct sports on the correct day: 3
Try again...

Round 4 pick sports from:
( A, C, H, L, N, S )
enter sport for day 1: C
enter sport for day 2: A
enter sport for day 3: L
enter sport for day 4: S

Congratulations! you won in 4 tries!

The answer was:
( C, A, L, S )

Here is output from some other runs, one of which demonstrates the program behavior when a player loses:

Writing Helper Functions

To complete your game program, you must write the following functions, and your implementation must match the specifications given here. You are welcome to write additional functions if you’d like.

Please read all the way through this section, and each required function specification, before you start writing code; only start programming once you have an understanding of how main() uses each of these functions.

Every function you write should have a comment that includes: 1. a high-level description its purpose or what the function does/computes. 2. a description of what each parameter value is 3. a description of what its return value is

Additionally, if the function has side-effects (like modifying a list parameter or printing) those should be described in the comment.

welcome

This function takes an int value parameter for the total number of guesses a player gets and prints out a welcome message with the rules of the game. See our sample output for what this function’s output should look like.

Note that you will need to make several calls to print to avoid line wrapping and to print out the number of guesses parameter value in the message.

Using this method is not required, but you can print out a multi-line string using a single call to print statement like the following:

  print("""
This output is printed on three lines
with each line looking
just like it appears here.""")

Even if you use this approach, you will still need more than a single print statement to print out the message that includes the number of guesses.

print_sports

This function takes a list of strings, and prints out the strings on a single line as a comma separated listing inside parentheses.

For example, for these two lists of strings:

items = ["apple", "banana", "pear", "orange"]
letters = ["C", "A", "T"]

The following calls to your function:

print_sports(items)
print_sports(letters)

would print:

( apple, banana, pear, orange )
( C, A, T )

Note the parentheses, commas, and spaces in the string of sports that is printed for the given list. Your output should match this exactly with the exception of it being okay if you have a comma after the last item in the list, so either of these two is okay for a call to print_sports(letters):

( C, A, T )
( C, A, T, )

Your program will use this function to print out a list with the set of sports to choose from as well as to print out the solution list at the end of the game.

Add some test code to main to test out this function in isolation for different lists.

guess_a_sport

Once you have written and tested print_sports, write a function guess_a_sport(sports, day) that takes list of sports (a list of strings), and an int value for the day. The function asks the user to enter a selection from the list, and returns a string that is the user’s selection. The day parameter is used in the prompt to the user (see the example output).

This function should not return until the user enters a sport that is in the passed list. If they enter an invalid option (i.e. what they enter is not in the list of sports), the function should print out an error message and prompt the user to try again. See the sample output for some examples.

This function is an example of input validation; in this case, valid means a string in the list of sports.

Add some calls to main to test this program in isolation. For example, if you add some test code that looks like this:

def main():

  test_list = ["A", "B", "C"]  # just a list for testing purposes
  sport = guess_a_sport(test_list, 2)
  print("you picked: " + sport)

Some output from two runs might look like:

$ python olympics.py
enter sport for day 2: B
you picked: B

$ python olympics.py
enter sport for day 2: hello
hello is not a valid sport, try again from this set:
( A, B, C )
enter sport for day 2: F
F is not a valid sport, try again from this set:
( A, B, C )
enter sport for day 2: A
you picked: A

Remember that you wrote a function to print out a list of sports. It can be called by this function as part of the error handling message to the user when they enter an invalid choice.

get_guesses

Once you have written and tested guess_a_sport, write a function get_guesses(sports) that takes a list of sports (a list of strings) and returns a new list of 4 sports (a list of strings) guessed by the user from the passed list, one for each day. The returned list can include the same sport guessed more than once.

This function should make four calls to the guess_a_sport function, one call to get each of the sports in the returned list.

Add some calls to main to test this program in isolation. For example, if you add some test code that looks like this:

def main():

  sports = ["A", "C", "H", "L", "N", "S"]
  guess = get_guesses(sports)
  print_sports(guess)

Some output from two runs might look like:

$ python olympics.py
enter sport for day 1: A
enter sport for day 2: C
enter sport for day 3: H
enter sport for day 4: L
( A, C, H, L )

$ python olympics.py
enter sport for day 1: C
enter sport for day 2: C
enter sport for day 3: H
enter sport for day 4: L
( C, C, H, L )

create_solution

This function takes one parameter, options, which is a list of the 6 sport strings to choose from.

It returns a new list of 4 strings randomly chosen from the options list that is the solution to this play of the game, where the first element in the returned list is the solution for day 1, the 2nd element for day two, and so on.

The constraints on the sports in the list returned by this function (the solution to this play of the game) are:

  1. each must be randomly selected from the input options list

  2. each must be unique (no duplicate sports can appear in the solution list)

Constraints on the options list are:

  1. the function should NOT modify this list: the set of strings in this list and their positions in the list should be the same before and after a call to this function.

Tips:

  • There are many ways to implement this function, but all require using some functions from the random library.

    The choice function might be particularly useful. It takes a list and returns an element randomly chosen from the list. Here is an example of a call to the choice function to randomly select a value from a list of ints (choice(l) returns one of 11, 22 or 33):

    l = [11, 22, 33]
    x = choice(l)

    To use this function, you need to import it from the random library. For example:

    from random import choice
  • You can create an empty list and use the append method to add elements to the end of it. Here is an example:

    l = []
    l.append(4)      # adds 4 to the end of l
    l.append(3)      # adds 3 to the end of l
  • Before a randomly seleted sport form the options list can be added to the solution list, you need to check that it is not already a sport in the solution list. If it is, you need to keep selecting a sport from the options list until you get a unique one to add.

  • Think about how you can use the in operator to ensure that you do not add duplicates to a list

  • You may want to add a helper function that takes the two lists and returns a string from the options list that is not in the solution list.

Testing:

Add code to main to test your create_solution function, passing it the list of sports and an list of 4 strings, each element initialized to "":

def main():

    options  = ["A", "C", "H", "L", "N", "S"]
    solution = create_solution(options)
    print_sports(solution)
    print("chosen from:")
    print_sports(options)
NOTE. In a correct implementation you should see a different (randomized) solution list printed out each run, and the solution list should not contain duplicate sports.

Here are some example runs:

$ *python olympics.py*
( H, S, N, L )
chosen from:
( A, C, H, L, N, S )

$ *python olympics.py*
( N, L, C, H )
chosen from:
( A, C, H, L, N, S )

$ *python olympics.py*
( S, H, N, A )
chosen from:
( A, C, H, L, N, S )

num_exact_matches

Write a function num_exact_matches(solution, guess) that takes two lists. The first, solution, is a list of the 4 sport strings that is the solution to the game, and the second, guess, is the list of the 4 sports strings entered by the user. The function returns the number of exact matches in the user’s guess (i.e., the number of correctly guessed sports on the correct day).

For example, if these are the two lists:

soln =  ["A", "L", "H", "S"]
guess = ["A", "H", "C", "L"]

A call to:

n = num_exact_matches(soln, guess)

returns the value 1 because only the sport on day one ("A") is guessed correctly. Although "H" and "L" are also sports in the solution, the user didn’t guess them on their correct days.

Add some test code to main to test your num_exact_match function, passing it some different solution and guess lists to test different cases.

num_sport_matches

Write a function that takes two lists (num_sport_matches(solution, guess)). The first is a list of the 4 sport strings that is the solution to the game, and the second is the list of the 4 sports strings entered by the user. The function returns the number of correct sports in the user’s guess (i.e., the number of correctly guessed sports regardless of if they are guessed on the correct day).

For example, if these are the two lists:

soln =  ["A", "L", "H", "S"]
guess = ["A", "H", "C", "L"]

A call to:

n = num_sport_matches(soln, guess)

returns the value 3 because the sports "A", "C", and `"L`" are in the solution.

Note, that your function needs to correctly handle the case when the user guess may have duplicates (which is allowed in guesses). For example, if the user’s guess is:

guess = ["A", "A", "C", "L"]

The function should return 2 and not 3 ("A" and "L" are in the solution, and it should not count "A" twice just because the user included it twice in their guess).

To handle this case, your function may want to make a temporary list to keep track of sports that have been correctly guessed so far. Alternately, it could make a copy of the solution list to mark entries in some way as being guessed to avoid double counting when a guess includes duplicate sports. Remember that this function cannot modify the passed solution list, thus modifying solution list entries to keep track of duplicates is not allowed.

Here is one way to create a copy of a list:

soln_copy = list(soln)

Add some test code to main to test your num_sport_matches function, passing it some different solution and guess lists to test different cases.

Writing a main Function

In the beginning, you should use main to incrementally test your individual helper functions and then gradually build the complete program. The final program should give the user 10 turns, and it should count the number of matches the user achieves after all of their turns are done. At the end of the game, it should print the final match count and show the final state of the matched cards.

A finished program might have the primary steps in main shown below. Think about how to use the helper functions described above to implement each step. Note that some helper functions may need to be called by other helpers and may not appear in main at all!

  1. Initialize game playing variables, and print out a welcome message with the rules of the game.

  2. Create the the solution list of 4 randomly selected sports from the set of option sports. (tip: to help you with debugging and testing your program, you may want to print out the solution list after this step. Be sure to remove this debug printing of the solution in the version you submit for grading)

  3. While the game hasn’t yet ended…​

    1. Get the user’s 4 guesses (one for each day stored in a list of 4 strings)

    2. Compute the number of correctly guessed exact matches (correct sport on the correct day)

    3. Compute the number of correctly guessed sports in their guess

    4. Determine if the game is ended because they won (they have guessed correctly) and print out a message if so

    5. Determine if the game is ended because they lost (they have guessed incorrectly and have run out of tries) and print out a message indicating this

    6. Determine if they get another try (they guessed incorrectly and have tries left). In this case, print out a message and the following information to help them with their next guess:

      1. the number of sports that they guessed correctly

      2. the number of sports that they guessed correctly on the correct day

  4. Print out the solution at the end of the game.

Tips

As you develop and debug your program, we recommend that you add a debugging print statment in main function that prints out the game solution (after step 2.). This will help you to verify that your program is doing the right thing at each round of play, and it will help you to better help you test that your program handles certain cases correctly. Be sure to comment out or remove this debug printing of the solution in the version you submit for grading.

Think carefully about how each of your functions should be used. Each of the steps in the main function that we have outlined should only need a few lines of code — the helper functions handle most of the complexity. In general, the main function should reflect the main steps of the top down design of the program, and the functions that it calls implement the refined details of the steps.

Additionally, it is very important that you use incremental development. The outline of main is a good starting point — you should complete one step at a time, thoroughly test it to see if your program works, and only move on after getting it to work. The ninjas and instructors will ask you to backtrack and do incremental development if you do not follow this strategy. If you try to implement the whole program at once, you will waste a lot of time and energy trying to fix your program.

For testing certain functionality, it may be helpful to call functions with hard-coded lists that you define to test specific behavior.

Some specific Python language features:

  • You will need to use one or more functions from the random library to randomly select 4 sports from the set of 6 for the game answer. We suggest using the choice function, but you can

  • You will need to use lists and operations on lists in your solution, including looping over list items. Refer to the in-class code for example list code, including indexing, the in operator, and looping over elements a list.

  • Each sport should be represented as a single character string. Refer to in-class code examples for weeks when we manipulated string types.

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:

  • Complete the top-level comment with a high-level description of what this program is/does.

  • 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.

  • Write a comment for each function (except main) that describes its parameters, its return value, and its purpose (what it does).

  • (If applicable) Add comments to explain complex code segments.

Your program should meet the following requirements:

  1. Implement each of the functions described above. Your implementations of each must have the same parameters and return values as in their specifications above.

  2. The main function should put all the pieces together and implement a fully playable game that allows the user to make up to 10 guess to solve the puzzle. It should correctly handle both winning and losing plays of the game. See the examples in the sample output section above.

  3. Your program’s prompts and other output should basically match the output of our program (see sample output section above).

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

  5. Your code should be well commented as describe in the note above.

Answer the Questionnaire

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