Skip to content

Instantly share code, notes, and snippets.

@Starwarsfan2099
Last active June 5, 2023 13:42
Show Gist options
  • Save Starwarsfan2099/600899c8ebbe75e5b3e0bf581c38c5fa to your computer and use it in GitHub Desktop.
Save Starwarsfan2099/600899c8ebbe75e5b3e0bf581c38c5fa to your computer and use it in GitHub Desktop.
#!/usr/bin/python3
# Quick and dirty wordle solver
# b is blank square, y is yellow square, g is green square.
from sys import argv
from time import time
# Wordle Solver class
class WordleSolve:
def __init__(self, file_name, start_word):
# Alphabet letters sorted by frequency
self.letter_freq = ["s", "e", "a", "r", "o", "i", "l", "t", "n", "u", "d", "c", "y", "p", "m", "h", "g", "b", "k", "f", "w", "v", "z", "x", "j", "q"]
self.letter_freq_value = [46.1, 44.8, 40.5, 30.9, 29.5, 28.1, 25, 24, 21.4, 18.6, 18.1, 15.7, 15.4, 14.6, 14.2, 13.3, 11.8, 11.5, 10.2, 7.9, 7.7, 5.2, 2.5, 2.4, 2.1, 0.9]
self.letter_pairs = ["th", "he", "an", "in", "er", "nd", "re", "ed", "es", "ou", "to", "ha", "en", "ea", "st", "nt", "on", "at", "hi", "as", "it", "ng", "is", "or", "et", "of", "ti", "ar", "te", "se", "me", "sa", "ne", "wa", "ve", "le", "no", "ta", "al", "de", "ot", "so", "dt", "ll", "tt", "el", "ro", "ad", "di", "ew", "ra", "ri", "sh"]
self.letter_pairs_freq = [33, 30.2, 18.1, 17.9, 16.9, 14.6, 13.3, 12.6, 11.5, 11.5, 11.5, 11.4, 11.1, 11, 10.9, 10.6, 10.6, 10.4, 9.7, 9.5, 9.3, 9.2, 8.6, 8.4, 8.3, 8, 7.6, 7.5, 7.5, 7.4, 6.8, 6.7, 6.6, 6.6, 6.5, 6.4, 6, 5.9, 5.7, 5.7, 5.7, 5.7, 5.6, 5.6, 5.6, 5.5, 5.5, 5.2, 5, 5, 5, 5, 5]
self.target_word = ["_", "_", "_", "_", "_"]
self.yellow_green_list = []
self.not_list = []
self.word_history = []
self.score_list = []
self.start_word = start_word
# Open a list of 5 letter words and save in memory
word_list_file = open(file_name, "r")
self.word_list = word_list_file.read().split()
word_list_file.close()
self.word_list_total = len(self.word_list)
# Print a list of words in a box like Wordle does
def box(self, words):
# Box top and bottom
box_top = " ___________________ "
box_spacer = " ------------------- "
output = box_top
for word in words:
output += "\n" + ' | ' + ' | '.join(list(word)) + ' | ' + "\n" + box_spacer
return output
# Get user input
def get_input(self):
text = input("Enter results: ")
input_list = []
for letter in text:
input_list.append(letter)
return input_list
# Print iterations progress
def printProgressBar (self, iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '#', printEnd = "\r"):
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
filledLength = int(length * iteration // total)
bar = fill * filledLength + '-' * (length - filledLength)
print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
# Print New Line on Complete
if iteration == total:
print()
# If we know a letter is or isn't in a word, adjust the word list apropriatly
def letter_modify(self, letter, remove):
i = 0
while i < len(self.word_list):
if remove:
if letter in self.word_list[i]:
if letter not in self.yellow_green_list:
del self.word_list[i]
del self.score_list[i]
continue
if not remove:
if letter not in self.word_list[i]:
del self.word_list[i]
del self.score_list[i]
continue
i += 1
# If we know what place a letter is or isn't, adjust the word list apropriatly
def letter_place(self, place, letter, remove):
i = 0
while i < len(self.word_list):
if remove:
if letter in self.word_list[i][place]:
del self.word_list[i]
del self.score_list[i]
continue
if not remove:
if letter not in self.word_list[i][place]:
del self.word_list[i]
del self.score_list[i]
continue
i += 1
# Add known letters in the word to a list
def add_known_letters(self, letter):
if letter not in self.yellow_green_list:
self.yellow_green_list.append(letter)
# Add known letter not in the word to a list
def add_known_not_letters(self, letter):
if letter not in self.not_list:
self.not_list.append(letter)
# Print a few stats
def stats(self):
print("\nWordle: %s" % ' '.join(self.target_word))
print("Known letters in word: %s" % ' '.join(self.yellow_green_list))
print("Known letters not in word: %s" % ' '.join(self.not_list))
print("{0} words remaining, down to {1:.2%} of words.\n".format(len(self.word_list), (len(self.word_list)/self.word_list_total)))
# Sort word list based on a letter frequency score
def sort_by_freq(self):
self.score_list = []
for word in self.word_list:
i = 1
score = 0
letters = []
for letter in word:
# Add to score for top signifigant letters by position
if i == 1 and letter == "s": score += 11.5
if i == 2 and letter == "a": score += 12.5
if i == 3 and letter == "r": score += 11.7
if i == 4 and letter == "e": score += 12.5
if i == 5 and letter == "s": score += 13.1
if letter not in letters:
# Add the frequency score of a letter to the score.
score += float(self.letter_freq_value[self.letter_freq.index(letter)])
else:
# If a letter is in a word more than once, reduce value of any other occurences. Magic number seems freq/5.
score += float(self.letter_freq_value[self.letter_freq.index(letter)])/5
letters.append(letter)
i += 1
# Now, we add a value based upon frequency of common letter pairs.
for pair in self.letter_pairs:
if pair in word:
score += float(self.letter_pairs_freq[self.letter_pairs.index(pair)])
self.score_list.append(score)
# Sort the list of scores.
zipped_list = sorted(zip(self.score_list, self.word_list), reverse=True)
self.word_list = [self.word_list for score_list, self.word_list in zipped_list]
self.score_list = sorted(self.score_list, reverse=True)
return zipped_list
# Process input and adjust the word list starting with known letters in the target word
def process_guess(self, input_list, iteration):
# Known letter and position [Green square]
i = 0
for letter in input_list:
if letter == "g":
self.add_known_letters(self.word_history[iteration][i])
self.letter_place(i, self.word_history[iteration][i], False)
self.target_word[i] = self.word_history[iteration][i]
i +=1
# Known letter but not position [Yellow square]
i = 0
for letter in input_list:
if letter == "y":
self.add_known_letters(self.word_history[iteration][i])
self.letter_place(i, self.word_history[iteration][i], True)
self.letter_modify(self.word_history[iteration][i], False)
i +=1
# Known not a letter in the target word [Blank square]
i = 0
for letter in input_list:
if letter == "b":
self.letter_modify(self.word_history[iteration][i], True)
self.add_known_not_letters(self.word_history[iteration][i])
i += 1
# Wordle logic for running tests
def wordle_logic(self, guess, wordle):
i = 0
output = []
for letter in guess:
if letter == wordle[i]:
output.append("g")
elif letter in wordle:
output.append("y")
elif letter not in wordle:
output.append("b")
else:
print("Something bad happened.")
i += 1
if len(output) != 5:
print("Something bad happened x2.")
return output
# Automatically play Wordle for testing code, start words, etc.
def play_bot(solver, target_list, start_word, verbose):
succsess = []
solver.word_list = []
word_list_sorted = []
score_list_sorted = []
fails = 0
for word in target_list:
solver.word_list.append(word)
# Sort by frequency and backup those values
scores = solver.sort_by_freq()
for word in solver.word_list:
word_list_sorted.append(word)
for score in solver.score_list:
score_list_sorted.append(score)
j = 0
for wordle in target_list:
if verbose: solver.printProgressBar(j, len(target_list) - 1, prefix = start_word, suffix = "Done", length = 50)
j += 1
solver.word_list = []
solver.word_history = []
solver.yellow_green_list = []
solver.score_list = []
# Copy the saved frequency values instead of calculating them again
for word in word_list_sorted:
solver.word_list.append(word)
for score in score_list_sorted:
solver.score_list.append(score)
i = 0
for i in range(0, 6):
if i == 0:
solver.word_history.append(start_word)
guess = solver.wordle_logic(start_word, wordle)
else:
solver.word_history.append(solver.word_list[0])
guess = solver.wordle_logic(solver.word_list[0], wordle)
solver.process_guess(guess, i)
if ''.join(guess) == "ggggg":
succsess.append(i + 1)
break
if len(solver.word_list) == 1:
succsess.append(i + 1)
break
if i == 5:
fails += 1
if solver.word_list[0] in solver.word_history:
solver.word_list.pop(0)
count = 0
for i in succsess:
count += i
return count, succsess, fails
# Using each word in the word list as a Wordle, try to solve it and see what the average number of guesses is
def test():
start_word = "least"
successes = []
fails = 0
target_list = []
solver = WordleSolve("5_letter_word_list.txt", start_word)
for word in solver.word_list:
target_list.append(word)
count, successes, fails = play_bot(solver, target_list, start_word, True)
print("Number of wordles: %s" % len(target_list))
print("Number of succsess: %s" % len(successes))
print("Average guesses for successes: %s" % (count/len(successes)))
print("Fails: %s" % fails)
# Using each word in the word list as a Wordle, try to solve it and see what the average number of guesses is
def test_start_words():
averages = []
target_list = []
start_word_list =[]
start_word = "least"
solver = WordleSolve("5_letter_word_list.txt", start_word)
for word in solver.word_list:
start_word_list.append(word)
for word in solver.word_list:
target_list.append(word)
for start_word in start_word_list:
successes = []
fails = 0
count, successes, fails = play_bot(solver, target_list, start_word, True)
averages.append((count/len(successes), start_word))
result = sorted(averages)
print("The best start word is %s with a average of %f Wordle guesses." % (result[0][1], result[0][0]))
# Solve a Wordle with a user
def play():
start_word = "least"
solver = WordleSolve("5_letter_word_list.txt", start_word)
solver.stats()
print("Begin with:")
scores = solver.sort_by_freq()
print(solver.box([start_word]))
solver.word_history.append(start_word)
i = 0
for i in range(0, 6):
user_input = solver.get_input()
solver.process_guess(user_input[:5], i)
solver.stats()
print("Top words:")
try:
for x in range(0, 4):
print("""%s (Freq score: %.2f)""" % (solver.word_list[x], round(solver.score_list[x], 2)))
except Exception as e:
pass
if len(solver.word_list) == 1:
if ''.join(user_input[:5]) == "ggggg":
print(solver.box(solver.word_history))
else:
print(solver.box(solver.word_history + [solver.word_list[0]]))
print("Done! Hooray!!")
exit()
if solver.word_list[0] in solver.word_history:
solver.word_list.pop(0)
print(solver.box(solver.word_history + [solver.word_list[0]]))
solver.word_history.append(solver.word_list[0])
print("Oof. Good luck picking final word!")
# Main
if __name__ == "__main__":
startTime = time()
if len(argv) == 1:
print("Try again.")
elif argv[1] == "test":
test()
elif argv[1] == "play":
play()
elif argv[1] == "test_start_words":
test_start_words()
else:
print("Try again.")
executionTime = (time() - startTime)
print('Time: ' + str(executionTime))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment