This game has come to the forefront in the last few weeks: Wordle. Unfortunately, I’m a boring guy; I don’t particularly appreciate playing it, so I made an algorithm that plays Wordle for me. This is how the algorithm works.

What the hell is Wordle? It’s a game; you have to guess a five-letter word – only one per day – in six attempts, getting help from attempts. If a letter turns green, it means that we have guessed its position in the word of the day; if it is yellow, that letter is included in the word but not in the correct place; if it is grey, it means that it does not appear in the word. That’s all.

Want to try it out? Here’s the code. Have fun.

Algorithm #1, the dumb one.

This algorithm is quite simple. The first thing we need is a driver for chrome. I have created a Website class that uses selenium to send words as input and receive output. There is a single function, Website.send_word_get_answer; this function takes a word as an argument and does nothing but send it as input to the web page and collects the output. The function returns a tuple (int, dict). The integer tells us the number of attempts, the dictionary is composed of 5 key-value pairs where the key is the letter, and the value is one of “present”, “correct”, or “absent”.

import sys
import time
import re
import warnings
warnings.filterwarnings("ignore")
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

class Website:
    def __init__(self):
        self.browser = webdriver.Chrome("/usr/lib/chromium-browser/chromedriver")
        self.browser.get('https://www.powerlanguage.co.uk/wordle/')
        time.sleep(1)
        self.background = self.browser.find_element(By.TAG_NAME, 'html')
        self.background.click()
        time.sleep(1)
        self.counter = 0

    def send_word_get_answer(self, word):
        self.background.send_keys(word)
        self.background.send_keys(Keys.ENTER)
        time.sleep(1)
        host = self.browser.find_element(By.TAG_NAME, "game-app")
        game = self.browser.execute_script("return arguments[0].shadowRoot.getElementById('game')",
                                       host)
        board = game.find_element(By.ID, "board")
        rows = self.browser.execute_script("return arguments[0].getElementsByTagName('game-row')",
                                       board)
        row = self.browser.execute_script("return arguments[0].shadowRoot.querySelector("
                                      "'.row').innerHTML",
                                     rows[self.counter])
        self.counter += 1

        bs_text = BeautifulSoup(row, 'html.parser')
        results = {}
        for word in bs_text.findAll('game-tile'):
            letter = word.get('letter')
            eval = word.get('evaluation')
            results[letter] = eval

        return (self.counter -1 , results)

The second thing we need is a list of all English words with 5 letters:

with open("words.txt", "r") as file:
    words = file.readlines()[0].split(" ")

The algorithm starts with a combination of words that contain almost every letter of the alphabet: ['quick', 'brown', 'shady', 'cleft', 'gimps']. Each iteration I enter a word from the list and evaluate the output. I instantiate two lists: present_list, an initially empty list containing all the letters in the word to be found and absence_list, a list of absent letters. I also instantiate a third vocab list, a vocabulary of all candidate words that is updated at each iteration to converge to the target word. If this vocabulary is left with a single word, I have found it. If we get to the fifth iteration, we have one last try, so we take the first word in the vocab and hope it is the last one left.

def simple_strategy():
    web = Website()

    # This is a strong combination found somewhere!
    guess = ['quick', 'brown', 'shady', 'cleft', 'gimps']

    vocab = words
    present_list = list()
    absence_list = list()
    for i in range(6):
        if i == 5:
            print(vocab[0])
            web.send_word_get_answer(vocab[0])
            time.sleep(10)
        elif len(vocab) == 1:
            web.send_word_get_answer(vocab[0])
            time.sleep(10)
        else:
            round_ = web.send_word_get_answer(guess[i])
            for key, value in round_[1].items():
                if value == "correct" or value == "present":
                    present_list.append(key)
                else:
                    absence_list.append(key)

            vocab = filter_by_presence(present_list, absence_list, vocab)

            print("Current vocabulary: ")
            print(vocab)

How does vocabulary filtering work? Again, this is very simple. First, I get all the words that have all the letters in the present list. That, I eliminate all the words with at least one letter in the absent list.

def filter_by_presence(present_list, absent_list, words):
    candidates = list()
    for w in words:
        if all(letter in w for letter in present_list):
            candidates.append(w)
        else:
            pass

    for w in candidates:
        if any(letter in w for letter in absent_list):
            candidates.remove(w)
        else:
            pass

    return candidates

The number of iterations required to close the game ranges from 3 to 5. Is it efficient? No, because it does not take into account the position. Does it work? Yes, it does.

Source(s):
Post image: Ariel Davis