Wordle

Prereqs

You'll make Wordle. First go to the official site, read the instructions, and play a few games. I expect that your Wordle will follow the standard Wordle rules.

You should be able to do this project if you've completed all of chapters 1 through 5. Also, do the Number Guess project before you do this one. They share a same basic structure - a game_loop function that calls one_game and then, when one_game is done, asks the user whether she wishes to play again. This is all discussed in detail in the instructions for Number Guess.

Part One: The Score Function

The first year I had students make Wordle, I discovered that one task was harder - much harder - than all others. So let's do that task first. You'll write me a function that carries out this task, and then when we're all done with that, we'lll move on.

What's the task? To score a guess. When you play Wordle, the score is given by letter color: a grey letter isn't in the answer, a green letter is in the answer and is the same position in the guess as in the answer, and a yellow letter is in the answer but it's position in guess isn't the same as it is in the answer.

This is actually a bit tricky to get right. Consider this example: guess = "melee" and answer = "belle". Let's consider just the "e"s. If we just worked left to right in "melee" and made color decisions as we went, we'd make the first "e" green (both guess and answer have an "e" at index 1), we'd make the second "e" yellow ("melee" has a second "e" at index 3, and "belle" has a second "e" at index 4), and finally we'd make the last "e" in "belle" grey since we'd already matched against all the "e"s in "belle".

However, this is a mistake. We have to take care of all greens before we move to yellow. So here's the right way to score "melee" (and again, the answer is "belle"):

  1. Let's first do those greys where the guess letter isn't anywhere in the answer. That's just the "m" in "melee". So our color sequence is now Grey _ _ _ _. (That's "Grey" and then four blanks to fill.)

  2. Now let's do the greens. The first "e" and the last "e" in "melee" match the first and last "e"s in "belle". Moreover, the "l" in "melee" matches the first "l" in "belle". So the color sequence is now Grey Green Green _ Green.

  3. Now let's do yellows. The one letter that remains to consider is the middle "e" in "melee". Note what I'm about to say carefully: the two "e"s is "belle" have already been matched against, and thus, though there is indeed an "e" in "belle", there is no "e" left in "belle" to match against. Thus the middle "e" is in "melee" shoul be grey! So our final color squence is Grey Green Green Grey Green

Details of the Score Function

I've created a replit project for you. Follow the link and fork.

If you take a look at the "main.py", you'll find I've begun a score_guess function. Here in Pt. 1, you'll work on this function and this function alone. Don't move on to the rest of the project until this function works. That means of course that you'll test it in the console.

The score_guess function takes a guess and the answer and returns a score. What will the score be? I suggest a list of 0's, 1's and 2's of length 5. A 0 at a position in that list means that the letter at that position in the guess is not in the answer. A 2 at a position in that list means that the letter at that position in the guess matches the letter at that position in the answer. Finally, a 1 at a position in that list means that the letter at that position in the guess is indeed in the answer but the positions of the two are not the same.

Examples:

Thus if the inputs to score_guess are "salty" and "swill", it should return [2, 0, 1, 0, 0]. Etc.

The code I've provided first populates a list with 5 -1's. You'll replace these with 0's, 1's and 2's as appropriate. Here's a simple way to replace an item at a given position in a list.

>>> L = [1, 2, 3]

>>> L[1] = 'a'

>>> L

[1, 'a', 3]

The second line says to replace the item at index 1, which is the 2, with an "a"; and as you see on the last line, that was indeed done.

Hard Cases for Score

I've discovered that certain combinations of guess and answer often reveal errors in students' code. The "melee", "belle" example that I walked you through above is one example. Here are some more. It will give an opportunity to make a code suggestion.

If the guess is "apple" and the answer is "plane, your score_guess function should return [1, 1, 0, 1, 2].

If the answer is "nasty" and the guess is "nanny", "nanny", your score_guess function should return {2, 2, 0, 0, 2]. Students often write code that will instead return [2, 2, 1, 1, 2] They matched the first "n" in "nanny" against the first "n" in "nasty" (that gives a 2), and then they matched the second and third "n"s in "nanny" against that same "n" is "nasty" to get the two 1's. The mistake is that their code did not "remember" that the first "n" in "nasty" had already been matched against and so was not available to match against the second and third "n"s in "nanny". So the advice here is to somehow have your code remember letters in the answer that have already been matched against. How? Well, now, that's for you to figure out.

Below I've compiled a number of hard cases, some old, some new. I'd test your code against them all:

  1. guess = "salty", answer = "swill" [2, 0, 1, 0, 0].

  2. guess = "swill", answer = "salty'" [2, 0, 0, 1, 0].

  3. guess = "nasty", answer = "nanny" [2, 2, 0, 0, 2].

  4. guess = "nanny", answer = "nasty" [2, 2, 0, 0, 2].

  5. guess = "melee", answer = "belle" [0, 2, 2, 0, 2].

  6. guess = "belle", answer = "melee" [0, 2, 2, 0, 2].

  7. guess = "level", answer = "revel" [1, 2, 2, 2, 1].

  8. guess = "revel", answer = "level" [1, 2, 2, 2, 1].

  9. guess = "apple", answer = "plane" [1, 1, 0, 1, 2].

  10. guess = "plane", answer = "apple" [1, 1, 1, 0, 0].

Part Two: Wordle

So you now have your score_guess function. Now it's time to make Wordle.

Look at the project I had you fork. It should contain two files, an "answers.txt" file and a "all_words.txt" file. You'll read the contents of these files into your code; I suggest that you store their contents in two lists. (A section below gives more details.)

At the start of the game, ask the user if she wants to input the answer. If she does, verify of course that it is indeed in "answers.txt". If she doesn't, generate an answer at random.

Once the answer has been chosen, clear the console.

After each guess, clear the console and then print all the guesses so far, correctly color-coded.

Let me say a bit more about color. As I sad, you'll need to color your text - a green letter means right letter in right place, red means the letter isn't in the answer, and yellow means the letter is in the answer but it's not in the right place. (Official Wordle uses grey, not yellow. But I find yellow shows up better in the console. Use yellow.) I used the termcolor module, but there are other ways. Google "termcolor Python" and follow the instructions you find. It's really very simple to use.

Once a game is finished, ask the user if she wants to play again.

Incorporate a cheat code: if the user ever types the word "answer" instead of a guess, show her the answer.

I expect that, when I hit the Run button, the game will begin. To do this, I made my last line of code game_loop().

Like This

Watch me play my Wordle. Yours needs to be highly similar.

Write Functions, Make Them Short

Almost all of your code should be wrapped up in functions. Keep those functions short and single-task.

I really mean this. Watch yourself as you code; if you realize that a function has gotten long or complex, ask yourself how it can be broken down into multiple functions each of which performs just one task.

In the project I had your fork, I've begun some of the functions that I think you shold have. They are game_loop, one_game, score_guess and display_guess, create_answer, get_guess and read_file.

The Display Function

The display_score will take the current guess and the score for that guess (that's the list of 0's, 1's and 2's returned by the score function), and print out the letters in the guess colored appropriately. I suggest you use the termcolor module. Google it and you'll find sample code that can be easily adapted.

Two Global Variables

Th e project I had you fork contains two files, an "answers.txt" file and an "all_words.txt" file. All Wordle game answers should be drawn from "answers.txt", but as the player makes guesses, any word from "all_words.txt" should be accepted. 'all_words.txt' has 14,855 entries; 'answers.txt' has 2,317. (The project also contains a little code. I'll talk about that in a moment.)

I have a final suggestion for you. (It's in the project I had you fork.) It's to create a pair of global variables named "answers" and "all_words" that hold, respectively, all the words in the "answers.txt" file and all the words in the "all_words.txt" file. Hold how? As lists of course!

Those two variables are defined at the bottom of the project I had you fork. The definition of each calls the read_file function; and that function should, as noted above, return a list of words.

What's it to mean to say those are global variables? It means they are defined outside every function; and since they are defined outside every function, they are accessible inside any function. Any function can "see" and then use their values. That's super-handy if you have a variable whose value needs to be accessed widely.