Introduction

In January 2022 a simple, yet elegant game took the world by storm, WORDLE! This daily word game generates a unique 5-letter English word every 24 hours and allows you to guess the word in just 6 attempts. The game provides contextual colored clues to show how close your previous guess was to the word.

To take this addictive game to the next level, we present a WORDLE variation in which the word to guess is randomly selected from a corpus of 236,000 English words. Making this even more user-friendly, we are not limited to 5-letter words. The user can input the length of the word they want to attempt. Feeling brave? Choose a 12 letter word and see if you can guess it in just 6 attempts.

This Jupyter-labs implementation introduces the computer-competition algorithm. That is, you aren't just playing by yourself anymore. It's a competition against your computer to see who can guess the word in the least amount of tries!

Technical Implementation

Using natural language processing libraries, we use a corpus of all English words. This comprises 236,000 entries. The sample space is further limited by extracting just the words that are N (user-input) characters long.

All English words that are 5 letters in length only make up 4.4% of the entire corpus!

Once a user provides their desired word length, a random key is generated from the corpus. For the computer side, the logic is straightforward yet complex. The implementation for the logic is defined in the Guesser class. As we are working with such a large corpus, we aim to reduce the sample space per iteration. That is, based on the information from the previous attempt, the corpus will be reduced down to only words that fit the new criteria, and a new selection will be randomly chosen. This process is repeated six times, to generate six unique guesses.

Specifically, we can highlight the following operation of reducing the sample space:

sampleSpace = [ele for ele in sampleSpace if all(ch not in ele for ch in itemsDelete)]

sampleSpace = [ele for ele in sampleSpace if all(ch in ele for ch in itemsWithin)]

sampleSpace = [word for word in sampleSpace if all(firstGuess[i] == word[i] for i in indicesGREEN)]

The initial corpus is first reduced based on the length of the key. Following this, the sample space is reduced three times after each attempt. The first is reduced down based on the GRAY items. That is, remove all words that include any letters that are GRAY. Secondly, we keep all words that have a letter that is YELLOW. Finally, we iterate through that list and only keep words that have the same value and index as a GREEN letter in the guess.

Once our sample space is reduced drastically, a new guess is chosen. The probability of choosing the correct guess increases as sample space size decreases.

BeepBoop

Class CellVerdict:

This class is an enum that is used to mark a cell in a Wordle game. The cell will be given a background color based on this value. The class contains these values:

  • NOT_IN_WORD = 1 denotes that the character in the cell is not in the solution word.

  • IN_WORD = 2 denotes that the character in the cell is in the solution word, however is not in the correct position.

  • EXACT = 3 denotes that the character in the cell is in the solution word and is placed at the exact position (i.e., this character appears in the same position in the solution word).

  • NONE = 0 denotes any other cases, e.g., the cell is empty waiting for user input or the cell has a character but has not been given a verdict.

Class WordleGame:

This class holds data necessary for a classic Wordle game. The following is all the class members implemented.

Instance variables

  • word_list: the list of words from which a word will be chosen randomly

  • solution_word: a word chosen randomly from word_list

  • lines: 2-dimensional array that holds the text content of the game i.e., characters inputted by the user, line by line

  • cell_verdicts: 2-dimensional array that contains cell verdicts for all cells, row by row, column by column

  • max_attempts: an integer for maximum attempts/guesses the player could make in a game. If this variable holds a non-positive value, the player is allowed unlimited attempts.

  • game_over: a boolean value for whether the game has ended or not

  • winning: a boolean value for whether the player has won the game or not

Method start()

Another string for solution_word is randomly chosen from word_list. All lines and cell_verdicts are cleared. Reset game_over to False and winning to None.

Method abort()

Force stop the game by setting game_over to True.

Method get_letter_count()

The length of solution_word.

Method get_content_for_cell(row_idx, col_idx)

Get the character contained in the cell at row row_idx and column col_idx.

Method get_cell_verdict_for_cell(row_idx, col_idx)

Get the cell verdict the cell at row row_idx and column col_idx.

Method get_bg_text_colors_for_cell(row_idx, col_idx)

Get the background color and text color for the cell at row row_idx and column col_idx. This is decided based on the cell verdict for this cell.

Method give_verdicts_for(word)

Compare the word against the solution_word and mark each position in the word with a cell verdict, then return the array of cell verdicts for that word.

Method check_lose() and check_win()

Check whether the player has lost or has won the game.

Method process_input_key(key)

Process input key entered by the user. For this implementation, this method has logic for when an English-letter key is inputted, when the Backspace is inputted, and when the Enter key is inputted. When the game is over, i.e., the game_over variable is True, all inputs are rejected.

Class WordleRenderer:

This class helps render a WordleGame instance interactively using libraries ipycanvas.

Instance variables

  • game: a WordleGame instance

  • canvas: a Canvas instance (from ipycanvas)

  • do_render_text: a boolean for whether the text should be rendered

  • playfield_width: an integer for the width of the playfield (in pixels)

Method get_cell_size()

This method returns the cell size (in pixels) based on the word length in the game. Bigger word length means smaller cell size, to fit in the playfield_width.

Method get_cell_gap()

This method returns the gap (in pixels) between cells.

Method get_font_size()

This method returns the font size - being equal to half of the cell size.

Method generate_canvas()

This method draws on the canvas all the cells with their respective colors and content.

Method render()

This method lets ipython display the canvas.

Class WordleGameStatus:

This class is primarily for the text describing the game status.

Instance variables

  • game: a WordleGame instance
  • status: a Label widget from ipywidgets

Method update()

Let the text in the status be the current attempt number when the game is not over, and win/lose message when the game is over.

Method render()

  • This method lets ipython display the status.
  • Final code to start the game
  • First, let the user choose the word length to play.
  • Get the word list based on that word length.
  • Construct user_game, user_game_renderer, user_game_status, computer_game, computer_game_renderer, computer_game_status, guesser,and guess_gen.
  • Let ipyevents capture keydown events, and via function handle_keydown, manage the game on the player side and the computer side.
  • Render all elements in a layout using HBox and VBox from ipywidgets.
  • The game begins. When the cursor is in the canvas on the player’s side (not on the computer’s side), all keydown events are captured and won’t be sent to JupyterLab to avoid player pressing keys that trigger some JupyterLab actions like adding a cell or deleting a cell when the game is ongoing.

Challenges

  • Understanding and implementing the logic
  • Iteratively reducing the sample space of the computer side
  • Implementing dynamic cell sizes based on the length of the keyword
  • Allow the game to render quickly and efficiently
  • Allow the game to collect user input and prevent it from being sent to Jupyterlab
  • Updating the computer side when a user enters their guesses

Built With

Share this project:

Updates