Wordle solver python script. Duplicate letter problem

Question:

When there’s a word that has a letter more than once, if that letter is already confirmed in the right spot, but you tell it the next instance of that letter is wrong, it crashes. You can trick it into working by saying that it’s just in the wrong spot.

from english_words import get_english_words_set
web2lowerset = get_english_words_set(['web2'], lower=True)
import random
from os import system, name

def clear_screen():
        if name == 'nt':
            _ = system('cls')
        else:
            _ = system('clear')
            
board = [
[" ", " ", " ", " ", " "],
[" ", " ", " ", " ", " "],
[" ", " ", " ", " ", " "],
[" ", " ", " ", " ", " "],
[" ", " ", " ", " ", " "],
[" ", " ", " ", " ", " "],
]

startingWords = ["SOARE", "SAREE", "SEARE", "STARE", "ROATE"]

def print_board():
    clear_screen()
    for r in board:
        print (*r)
    
def validInput(w):
    if len(w) != 5:
        return False
    else:
        return True
 
def solver():
    fiveLetterWords = set()

    for word in web2lowerset:
        if len(word) == 5:
            fiveLetterWords.add(word.upper())
            
    initialInput = input("Would you like me to provide a starting word?")
    startingWord = 0
    if "y" in initialInput.lower():
        startingWord = random.choice(startingWords)
        print (startingWord)
    elif "n" in initialInput.lower():
        while True:
            userWord = input("What was your starting word?")
            if validInput(userWord) == True:
                startingWord = userWord.upper()
                break
            else:
                print ("Invalid word length")
        print (startingWord)
    else:
        print("Invalid answer")
        
    board[0][0] = startingWord[0]
    board[0][1] = startingWord[1]
    board[0][2] = startingWord[2]
    board[0][3] = startingWord[3]
    board[0][4] = startingWord[4]
    
    moveCount = 0   
    currentWord = [*startingWord]
    while True:
        print_board()
        print("Enter 'G' to generate a new word nUse Y to represent present letters in the right spot, nN to represent absent letters, nand W to represent present letters in the wrong spot")
        print("E.G. NNYWW")
        results = input()
        currentResult = [*results]
        
        resultIndex = 0
        tempSet = set()
        #chatGPT suggested loop
        tempWordSet = fiveLetterWords.copy()
        resultIndex = 0
        for result in currentResult:
            if result == "N":
                tempWordSet = {word for word in tempWordSet if currentWord[resultIndex] not in word}
            elif result == "Y":
                tempWordSet = {wordY for wordY in tempWordSet if currentWord[resultIndex] == wordY[resultIndex]}
            elif result == "W":
                tempWordSet = {wordW for wordW in tempWordSet if currentWord[resultIndex] != wordW[resultIndex] and currentWord[resultIndex] in wordW}
            elif result == "G":
                currentWord = [*fiveLetterWords.pop()]
                moveCount -= 1
            resultIndex += 1
            if resultIndex == 5:
                resultIndex = 0

        moveCount += 1
        # print (fiveLetterWords)
        # fiveLetterWords.clear()
        fiveLetterWords = tempWordSet
        currentWord = [*fiveLetterWords.pop()]
        board[moveCount][0] = currentWord[0]
        board[moveCount][1] = currentWord[1]
        board[moveCount][2] = currentWord[2]
        board[moveCount][3] = currentWord[3]
        board[moveCount][4] = currentWord[4]

    
while True:
    solver()

I tried making it to where the index of the letter within the string has to match with the index of the user input, but that comes with it’s own problem, namely it will keep trying that letter in different spots.

Asked By: Cason Berry

||

Answers:

This looks like a very fun project!

The issue in your code seems to be in the "for result in currentResult" loop. Whenever a letter is not in the word (N), the code removes every word that contains that letter from the tempWordSet. This causes issues since a letter should be marked N if the same letter was marked Y in another position of the word. To correct this, I separated the loops that dictate whether the word contains the letter or not. The loops should look more like this (if I understand the G input correctly):

    if "G" in currentResult:
        currentWord = [*fiveLetterWords.pop()]
        moveCount -= 1
    else:
        for i in range(0, 5):
            if currentResult[i] == "Y":
                tempWordSet = {wordY for wordY in tempWordSet if currentWord[i] == wordY[i]}
            elif currentResult[i] == "W":
                tempWordSet = {wordW for wordW in tempWordSet if currentWord[i] != wordW[i] and currentWord[i] in wordW}
        for i in range(0, 5):
            if currentResult[i] == "N":
                if (currentWord.count(currentWord[i]) > 1):
                    tempWordSet = {word for word in tempWordSet if word.count(currentWord[i]) == currentWord.count(currentWord[i])-1}
                else:
                    tempWordSet = {word for word in tempWordSet if currentWord[i] not in word}

Also for other general improvements:

  • I recommend using a better library for words. It would be more practical to use the word bank that wordle uses.

  • Your validInput function can be shortened to one line via

     return len(w) == 5
    
  • Rather than grabbing a random word from the bank that meets your required conditions, it may be more effective to use some mathematical strategies like in this article.

Happy Coding!

Answered By: cavalierDev

I got it all working the way I intended. I also have a method that doesn’t just give you a random word that meets the criteria. It’s a .csv that has the most frequently used English words, with an associated number. It also boots out any invalid words from the wordle_words.txt when you type "G" (even though the list I pulled is supposed to be straight from the game).

I’m sure there are better, more compact ways to do some of the logic, but it runs fast and solves the puzzle almost every single time.

import random
from os import system, name
import os
import csv

with open('commonFiveLetter.csv') as c:
    common=[tuple(line) for line in csv.reader(c)]
c.close()

original_file = "wordle_words.txt"
temp_file = "temp.txt"

def clear_screen():
        if name == 'nt':
            _ = system('cls')
        else:
            _ = system('clear')
            
board = [
[" ", " ", " ", " ", " "],
[" ", " ", " ", " ", " "],
[" ", " ", " ", " ", " "],
[" ", " ", " ", " ", " "],
[" ", " ", " ", " ", " "],
[" ", " ", " ", " ", " "],
]

startingWords = ["SOARE", "SAREE", "SEARE", "STARE", "ROATE"]

def print_board():
    clear_screen()
    for r in board:
        print (*r)
    
def validInput(w):
    return len(w) == 5
        
def convert(s):
    new = ""
    for x in s:
        new += x
    return new
 
def solver():
    fiveLetterWords = set(open('wordle_words.txt').read().upper().split())
    initialInput = input("Would you like me to provide a starting word?")
    startingWord = 0
    if "y" in initialInput.lower():
        startingWord = random.choice(startingWords)
    elif "n" in initialInput.lower():
        while True:
            userWord = input("What was your starting word?")
            if validInput(userWord) == True:
                startingWord = userWord.upper()
                break
            else:
                print ("Invalid word length")
    else:
        print("Invalid answer")
        
    board[0][0] = startingWord[0]
    board[0][1] = startingWord[1]
    board[0][2] = startingWord[2]
    board[0][3] = startingWord[3]
    board[0][4] = startingWord[4]
    
    moveCount = 0
    currentWord = [*startingWord]
    while True:
        print_board()
        print("Enter 'G' to generate a new word nUse Y to represent present letters in the right spot, nN to represent absent letters, nand W to represent present letters in the wrong spot")
        results = input().upper()
        print("E.G. NNYWW")
        currentResult = [*results]
        tempWordSet = fiveLetterWords.copy()
        contains = []
        containsIndex = []
        wrongSpot = []
        wrongSpotIndex = []
        absent = []
        sortaAbsent = []
        sortaAbsentIndex = []
        if "G" in currentResult:
            string_to_delete = [convert(currentWord)]
            with open(original_file, "r") as inputT:
                with open(temp_file, "w") as output:
                    for line in inputT:
                        for wordT in string_to_delete:
                            line = line.replace(wordT, "")
                        output.write(line)
            os.replace('temp.txt', 'wordle_words.txt')
            currentWord = [*fiveLetterWords.pop()]
            moveCount -= 1
        else:
            for j in range(0, 5):
                if currentResult[j] == "Y":
                        contains.append(currentWord[j])
                        containsIndex.append(j)
            for i in range(0, 5):
                if currentResult[i] == "W":
                    wrongSpot.append(currentWord[i])
                    wrongSpotIndex.append(i)
                elif currentResult[i] == "N" and (currentWord[i] in contains or currentWord[i] in wrongSpot):
                    sortaAbsent.append(currentWord[i])
                    sortaAbsentIndex.append(i)
                elif currentResult[i] == "N" and (currentWord[i] not in contains and currentWord[i] not in wrongSpot):
                    absent.append(currentWord[i])
            if contains:
                for lettersY in range(len(contains)):
                    tempWordSet = {wordY for wordY in tempWordSet if contains[lettersY] in wordY and contains[lettersY] == wordY[int(containsIndex[lettersY])]}
            if wrongSpot:
                for lettersW in range(len(wrongSpot)):
                    if wrongSpot[lettersW] in contains:
                        tempWordSet = {wordW for wordW in tempWordSet if wrongSpot[lettersW] in wordW and wrongSpot[lettersW] != wordW[int(wrongSpotIndex[lettersW])] and wordW.count(wrongSpot[lettersW]) > 1}
                    else:
                        tempWordSet = {wordW for wordW in tempWordSet if wrongSpot[lettersW] in wordW and wrongSpot[lettersW] != wordW[int(wrongSpotIndex[lettersW])]}
            if sortaAbsent:
                for lettersSN in range(len(sortaAbsent)):
                    tempWordSet = {wordSN for wordSN in tempWordSet if sortaAbsent[lettersSN] in wordSN and sortaAbsent[lettersSN] != wordSN[int(sortaAbsentIndex[lettersSN])]}           
            if absent:
                for lettersN in range(len(absent)):
                    tempWordSet = {wordN for wordN in tempWordSet if absent[lettersN] not in wordN}
        moveCount += 1
        fiveLetterWords = tempWordSet
        tempIntersect = []
        for pair in common:
            if pair[0].upper() in fiveLetterWords:
                tempIntersect.append(pair)
        if tempIntersect:
            highest = 0
            highestWord = 0
            for pairI in tempIntersect:
                if int(pairI[1]) > int(highest):
                    highest = pairI[1]
                    highestWord = pairI[0].upper()
            currentWord = [*highestWord]
        else:
            currentWord = [*fiveLetterWords.pop()]
        board[moveCount][0] = currentWord[0]
        board[moveCount][1] = currentWord[1]
        board[moveCount][2] = currentWord[2]
        board[moveCount][3] = currentWord[3]
        board[moveCount][4] = currentWord[4]

while True:
    solver()
Answered By: Cason Berry
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.