CS21 Lab 7: Top Down Design: Wordle

Design is due 2 November, before midnight

Full implementation is due 9 November, before midnight

Goals

The goals for this lab assignment are:

  • Practice using Top-Down Design

  • Write a complex program, with multiple functions, from scratch

Notes

Please read through the entire lab before starting!

This is a two-part lab, split over two weeks. For the first week you will work with a partner and focus on using top-down design to create the overall structure for the program. If you have a partner in mind, you can email tdd@cs.swarthmore.edu and CC your partner to let us know of your preferred pairing. It will work best if your partner is in the same lab section. If you do not have a partner when you start lab, we can help assign one to you.

Once your proposed structure has been reviewed and approved by your professor or lab instructor, you will individually use bottom-up implementation to complete the full program.

You have two weeks to complete the full program. The first week is for the design, and the second for the full program. Your initial top-down design is due this Saturday (2 November) and the full implementation the following week (9 November). It is highly recommended that you submit your top-down design as soon as is possible so that we have time to give you feedback before you begin your full implementation. If you submit your design on the last day at midnight, it might take us a few days to get to it and send you comments.

Please read the guidelines for lab partnerships for information on how to best collaborate with a partner on lab assignments.

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.

1. The game of Wordle

Wordle is a daily word puzzle in which a player has six chances to guess a five-letter word. After each guess, the player is given feedback on which letters are correct and in the right position, which letters are correct but in the wrong position, and which letters are not in the word at all. The player uses this feedback to make subsequent guesses until they guess the correct word or run out of guesses. Each user guess must be a valid five-letter word that is in the dictionary of English words.

While the New York Times version of Wordle only lets you play once per day, for lab 07, you design and implement a version that you can play as many times as you like!

To describe the basic rules of the game, let’s use an example where the computer is selecting a five-letter word. Normally, as a player of the game, you would not know what the computer had chosen. However, let’s make things easier and peek at the computer’s choice: guava.

It’s time for you to begin guessing the word. Your initial guess is: spray. Notice that there is only one letter that you guessed that’s also in the secret word (the letter a). After each user guess, the game will compare the user’s guess to the puzzle word. The game displays a status message that indicates which letters are correct and in the right position, which letters are correct but in the wrong position, and which letters are not in the word at all.

In our version, letters that are in the word and in the correct position will be displayed as UPPERCASE letters. Letters that are in the word but in the wrong position will be displayed as lowercase letters. Letters that are not in the word at all will be displayed as dashes.

In this case of spray, where the letter a is an inexact match, you’d receive the status message:

- - - a -

Let’s make another another guess: arrow. Again, the only correct letter is the a, and now it’s in a different wrong place. So, the status you would receive is:

a - - - -

Suppose our next guess is again. This time the guessed word has two a’s, as does the secret word. The first guessed a is in the wrong position and will appear as lower case, but the second guessed a is in the correct position (the third character) and should show up as an UPPERCASE letter in the status. There is also a correctly guessed g but it is in the wrong position and will appear as lower case. Since neither the i nor the n appear in the secret word, the status of this guess is the following:

a g A - -

Let’s guess games next. While it’s true that you know there are no s 's in the solution (from your guess of spray earlier), you’re trying to see if some of these other letters are present (the m and e) and you’re still trying to figure out where the other a goes. The status message now that you have the g in the right position and the a is still in the wrong position:

G a - - -

Here is this example as it may appear in a game (see the Sample Output section below for other examples of a working solution). User input is shown in bold.

Enter a 5 letter word: spray
- - - a -
Enter a 5 letter word: arrow
a - - - -
Enter a 5 letter word: again
a g A - -
Enter a 5 letter word: games
G a - - -
Enter a 5 letter word: curve
- U - V -
Enter a 5 letter word: guava
You won in 6 turns!

2. General Game Rules

  • The length of the words to be used for this game is 5.

  • The secret words will be drawn from the online dictionary stored in: /usr/local/doc/wordle_words.txt. This file contains a list of puzzle words from the actual Wordle game with one word per line. They are usually common English words, but they are not proper nouns or plurals ending in s. You should read into a python list, all the words from this file.

  • The list of valid words a user can guess can be found in /usr/share/dict/words. This file contains one word per line, but not all words in this list can be valid user guesses. The file contains some proper nouns, abbreviations, and words with punctuation. You should read this file and create a python list of words that could be valid user guesses. A valid word in this context:

    • appears in the list of words from /usr/share/dict/words

    • has only five letters

    • has no punctuation or digits

    • consists only of lowercase letters

  • The player may not guess arbitrary sequences of letters; the player’s guesses must be valid English words from list generated above. If the player enters a non-word or a word that is not the correct length, print out an error message and prompt them to enter a valid word.

  • The status information must show inexact matches (correct letter but in incorrect position in the word) in lower case letters and exact matches (correct letter in correct position in the word) in upper case letters.

  • Allow the user to give up and see the secret word by entering the word QUIT all in upper case letters.

  • If the user guesses the word correctly, print out a congratulatory message and the number of turns it took to guess the word.

  • If the user does not solve the puzzle after six guesses of valid English words, print out a message indicating that the user is out of guesses. You should also display the secret word.

3. Example Output

Below are links to example output of the three runs of the Wordle game

4. Top-Down Design Requirements

You should complete your top-down design (TDD), submit it (run handin21), and obtain feedback on it before beginning the full game implementation. Special procedures for this two-week lab:

  • create your design in design_wordle.py first

  • after you have a working design (see example below), run handin21 to turn it in! Then send a short email to tdd@cs.swarthmore.edu (and cc your partner), letting us know your design is done. We will take a look at each design and send you comments (usually within a day or two). If the design is incomplete or insufficient, you may be asked to submit a second design.

  • after you have the design done and have heard back from us, please copy the design file to wordle.py and implement the full game in wordle.py (i.e., leave design_wordle.py as is!)

    cp design_wordle.py wordle.py
  • please ensure your design meets the following requirements before submitting:

    • main() should be completely written, and should perform high-level steps without focusing on details

    • main() should call the functions you create in your design, in the appropriate order, and with arguments that match parameters. Return values should be saved in main(), even if you don’t do anything with them yet.

    • all functions should be stubbed out with parameters, a block comment, and a return statement. They don’t need to actually do anything yet, except possibly call other functions.

    • if your function is meant to return something, you should return a reasonable value of the appropriate type (e.g., return 0 for an int, or [1,2,3] for a list).

    • your design should have several functions. Each function should be function worthy (i.e., not a trivial task) and also demonstrate encapsulation (one clearly defined purpose).

    • the design should run without syntax errors (although it doesn’t play the full game yet)

Your goal for the design is to completely write main(), such that it won’t need to change much as you implement and test all of the other functions.

You should review the requirements below to get a general sense of how to design your program in the first week, but all the features do not need to be fully implemented. You just need the function stubs and main to be complete.

If you are confused about how the top-down design should look, please see the following examples of a design and full program for a simple guessing game program.

4.1. Sharing code with your partner

We recommend that you and your partner work together on a single copy of your design. One person can take the lead writing code for a bit while the other person makes suggestions and asks questions. Then you can switch roles.

Once you have completed your design, or if you are taking a break to work on it later, you can share your code with your partner via the scp command. This command copies the file from your computer to your partner’s computer. You and your partner need to be at the same computer together to do this, as your partner will need to type his/her/their password to copy the file to their computer.

Before running scp run handin21 to submit your design in case anything goes wrong with the scp command.

To copy a file from your computer to your partner’s computer, you can use the following command, replacing the username below with your partner’s username. For example, if Kevin and Andy are working together on Kevin’s account, and he wishes to share his design with Andy, whose username is astudent2, he would type in his cs21/labs/07 directory:

scp design_wordle.py astudent2@cslab:cs21/labs/07
(astudent2@cslab) Password:

This command will copy the file design_wordle.py from Kevin’s computer to Andy’s computer. Andy will need to type in his password to complete the copy. Please copy/paste the command above exactly, only changing the username (adanner) to your partner’s username. Note that this will overwrite any file of the same name on your partner’s computer, so be careful to go in the right direction! Copy from the complete version to the incomplete version.

5. Full Program Requirements

At the end of the two weeks, you should have a fully functioning Wordle game. The game should meet the following 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:

  • Write a comment that describes the program as a whole using a comment at the top of the file.

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

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

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

Your program should meet the following requirements:

  1. Display a welcome message describing the game and the rules.

  2. Puzzle words must be selected randomly from the file /usr/local/doc/wordle_words.txt.

  3. The user must be able to enter up to six valid English words as guesses. Valid English words can be found in the file /usr/share/dict/words, though you will need to exclude proper nouns, abbreviations, words with punctuation, and words that are not five letters long.

  4. Display an error message if a user guess is invalid, but do not count this as one of the six valid guesses.

  5. Stop the game if the user correctly guesses the word in fewer than six guesses.

  6. Display a congratulatory message if the user guesses the word correctly.

  7. Allow the user to stop the game at any time by entering the word QUIT in all uppercase letters.

  8. Display a message if the user runs out of guesses (or types QUIT) and show the secret word.

  9. Correctly display the status for exact matches

  10. Correctly display the status for inexact matches

  11. Use proper top-down design and have a main function with at least four other helper functions.

6. Autograder (or lack thereof)

There is no autograder for Lab 7! The autograder doesn’t work well with open ended design. You should be sure to test your functions as you go to make sure that they are working correctly and following the specifications provided.

7. Hints and Tips

  • You can use the methods isalpha(), islower(), and isupper() to check if a string consists entirely of alphabetic characters, lowercase letters (ignoring punctuation), or uppercase letters (ignoring punctuation), respectively.

    txt txt.isalpha() txt.islower() txt.isupper()

    "hello"

    True

    True

    False

    "Hello"

    True

    False

    False

    "HELLO"

    True

    False

    True

    "hello!"

    False

    True

    False

  • In python, you can test whether a particular string in e.g., the variable word is in a list of words, called word_list by doing:

    if word in word_list:
        print("The word is in the list")
    else:
        print("The word is not in the list")
  • You can convert a string, such as word = "apple" into a list ['a', 'p', 'p', 'l', 'e'], simply by doing: lword=list(word). You can then join the list of characters back into a word as shown in the interaction below:

    >>> word = "apple"
    >>> word
    'apple'
    >>> char_list = list(word)
    >>> char_list
    ['a', 'p', 'p', 'l', 'e']
    >>> rejoined = ' '.join(char_list) # join the list of characters with a space between them
    >>> rejoined
    'a p p l e'
  • Lists are mutable. You can change the content of a single element of a list. Strings are not mutable.

  • You can use the random module to select a random word from a list of words. You can use the choice() function to select a random word from a list of words. For example, to select a random word from a list of words stored in the variable word_list, you simply type choice(word_list). You can also use randrange to select a random index from a list of words. For example, to select a random word from a list of words stored in the variable word_list, you can type word_list[randrange(len(word_list))]. You will need to import these functions from the random library at the top of your program to use these functions.

    from random import choice, randrange
  • You can find the index of a particular element in a list by using the index() method. For example, to find the index of the letter a in the list l = ['a', 'p', 'p', 'l', 'e'], you can type l.index('a'). This will return the index of the first occurrence of the letter a in the list, in this case the integer 0. If the letter is not in the list, it will raise a ValueError and crash your program so you should be sure to check if the letter is in the list before trying to find its index. The 'in' operator can be used to check if an element is in a list, though it won’t tell you where it is. For example "a" in l will return True for the list l above, while "z" in l will return False.

  • Producing the appropriate status information is the trickiest aspect of implementing this program. You will need to re-compute the status after each guess from the user. You may want to first get the entire flow of control in the program correct, with incorrect status updates (perhaps ignoring double characters in guessed and secret word, and ignoring the correct position part) , and then go back and work on getting the status update part correct.

    We suggest that you store the secret word and the guessed word as strings. Then, to produce the status information, create list versions of these words that can be modified. You can use the list version of the guessed word to hold the status information.

    Our suggested algorithm is to handle the exact matches first, then worry about the inexact matches.

7.1. Checking Exact Matches

When an exact match is found, change the letter in the guessed word to upper case and hide the letter in the secret word by changing it to some other character, such as '.' (you don’t want to find it again when you search for inexact matches).

For example, given the secret word guava and the user guess gulag, we expect the status message: G U - a -. We’ll start by converting both the secret and the guess into lists:

secret: ['g','u','a','v','a']     guess: ['g','u','l','a','g']

Now, we’ll check for exact matches, beginning from left to right in the guess list. Does the first letter of 'gulag' match the first letter of 'guava'? It does, so we’ll capitalize the first letter of the guess list and we’ll change the first letter of the secret list to a period:

secret: ['.','u','a','v','a']     guess: ['G','u','l','a','g']

Next, does the second letter of 'gulag' match the second letter of 'guava'? It does again, so we’ll capitalize the second letter of the guess list and we’ll change the second letter of the secret list to a period:

secret: ['.','.','a','v','a']     guess: ['G','U','l','a','g']

We then continue with the third, fourth, and fifth letters. None of these letters in the guess are exact matches, so the rest of the two lists will remain unchanged.

7.2. Checking Inexact Matches

To find inexact matches do the following for each letter in the guess list:

  1. If the letter is in the secret list, we replace one of the matching secret letters (only replace one occurrence if there are one or more matching letters in the secret word) with a '.'

  2. Otherwise, if the letter is not uppercase (an exact match), we replace the guess letter with a '-'.

Note that we are using the lists as they appear after the exact matches have been found.

Following this algorithm, we look at the first letter is the guess list: G. Since it’s capitalized, we go on to the next letter: U. It too is capitalized, so we move on to the third letter, l. The l is not in the secret word, so we replace the letter in the guess list with a '-':

secret: ['.','.','a','v','a']     guess: ['G','U','-','a','g']

The fourth letter in the guess list, a, is in the secret list, so we replace the letter in the secret list with a .:

secret: ['.','.','.','v','a']     guess: ['G','U','-','a','g']

The final letter in the guess list, g, is not in the secret list, so replace the letter in the guess list with a '-':

secret: ['.','.','.','v','a']     guess: ['G','U','-','a','-']

The guess list is now nearly the status information which you need to provide. You just need to join the letters in the list together with spaces between to form the string 'G U - a -'.

Answer the Questionnaire

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