Wordle Solver
import pandas as pd
import numpy as np
from IPython.display import clear_output
word_bank_series = pd.read_csv('words-new.txt',names=['words']).squeeze()
Algorithm
Wordle Class
class Wordle:
def __init__(self,letters_series):
self.series = letters_series
def word(self,chosen_word):
self.chosen_word = chosen_word
self.series = self.series.loc[~self.series.str.contains(self.chosen_word,regex=False)].reset_index(drop=True)
def filter_green(self,green_letters=None):
self.existing = list(filter(None,green_letters))
self.green_letters = green_letters
if any(green_letters):
regex_filter = ['[a-z]' if letter is None else letter for letter in green_letters]
regex_filter = ''.join(regex_filter)
self.series = self.series.loc[self.series.str.contains(regex_filter,regex=True)].reset_index(drop=True)
def filter_yellow(self,yellow_letters=None):
if any(yellow_letters):
self.yellow = list(filter(None,yellow_letters))
self.existing.extend(self.yellow)
#First filter: keep words with 'yellow letters'
regex_filter = [f'(?=.*{letter})' for letter in yellow_letters if letter is not None]
regex_filter = ''.join(regex_filter)
self.series = self.series.loc[self.series.str.contains(regex_filter,regex=True)].reset_index(drop=True)
#Second filter: drop words with 'yellow letters' in their place.
regex_filter = ''.join(['[a-z]' if letter is None else f'[a-z](?<!{letter})' for letter in yellow_letters])
self.series = self.series.loc[self.series.str.contains(regex_filter,regex=True)].reset_index(drop=True)
#Third filter: if a yellow letter is a green letter, keep only words with both.
yellow_green_letters = [letter for letter in yellow_letters if letter in self.green_letters and letter is not None]
if any(yellow_green_letters):
regex_filter = ''.join([f'(?=.*[{letter}].*[{letter}])' for letter in yellow_green_letters])
self.series = self.series.loc[self.series.str.contains(regex_filter,regex=True)].reset_index(drop=True)
def filter_gray(self,gray_letters=None):
if any(gray_letters):
self.gray_letters = gray_letters
#Drop letters that are gray, but skip gray letters that are also green or yellow letters.
regex_filter = [letter for letter in gray_letters if letter is not None and letter not in self.existing]
if regex_filter:
regex_filter = '|'.join(regex_filter)
self.series = self.series.loc[~self.series.str.contains(regex_filter,regex=True)].reset_index(drop=True)
#Drop gray letters that are also green letters but are in a different position
gray_green_letters_list = set(filter(None,gray_letters)).intersection(list(filter(None,self.green_letters)))
if gray_green_letters_list:
pre_regex = '|'.join(gray_green_letters_list)
regex_filter = ''.join([f'[a-z](?<!{pre_regex})' if letter is None else f'[{letter}]' for letter in self.green_letters])
self.series = self.series.loc[self.series.str.contains(regex_filter,regex=True)].reset_index(drop=True)
def show_by_incidence(self):
if self.series.size > 5 and any(self.existing):
word_letters_df = self.series.str.split("",expand=True).drop(labels=[0,6],axis=1)
columns = [index+1 for index,letter in list(enumerate(self.gray_letters)) if letter is None]
words_incidence = word_letters_df.value_counts(subset = columns).iloc[0:1]
incidence_combination = words_incidence.index.map(list)
pre_regex_incidence = list(map(lambda letter: "(?=.*{})".format(letter),incidence_combination[0]))
regex_incidence = "|".join([''.join(row) for row in pre_regex_incidence])
best_options = self.series.loc[self.series.str.contains(regex_incidence,regex=True)].reset_index(drop=True)
print(best_options[0:5])
else:
print(self.series)
Answers Class
class Answers(Wordle):
def __init__(self,letter_series):
super().__init__(letter_series)
@property
def player_input(self):
print(f'Word: {self.word}')
print(f'Color places: {self.color_position}')
@player_input.setter
def player_input(self,word_and_color = None):
self.word, color_position = word_and_color
while len(self.word) != 5:
print('Sorry, the word must have five letters.')
self.word = input('Word: ')
self.color_position = color_position.split(',')
while len(self.color_position) != 5:
print('Sorry, you must specify five color positions.')
color_position = input('Color places: ')
self.color_position = color_position.split(',')
def __format_input(self,word,color_position):
self.word_letters = list(self.word)
self.color_letter = list(zip(self.color_position,self.word_letters))
self.green_letters = [letter if color == 'green' else None for color,letter in self.color_letter]
self.yellow_letters = [letter if color == 'yellow' else None for color,letter in self.color_letter]
self.gray_letters = [letter if color == 'gray' else None for color,letter in self.color_letter]
def analyze_letters(self):
self.__format_input(self.word,self.color_position)
super().word(self.word)
super().filter_green(self.green_letters)
super().filter_yellow(self.yellow_letters)
super().filter_gray(self.gray_letters)
if self.series.empty:
print('Sorry, it appears the word is not in the dataset.')
else:
super().show_by_incidence()
How to?
#Number of chances a player has to find the right word.
count = 0
#Reset the Series with the answers.
answers = Answers(word_bank_series)
chosen_word = input('Word: ')
#If you won, enter "win" as the input.
while count < 6 and chosen_word != 'win':
color_position = input('Color places:')
answers.player_input = (chosen_word,color_position)
answers.player_input
answers.analyze_letters()
chosen_word = input('Word: ')
count += 1