#!/usr/bin/python import random import string from collections import defaultdict from enum import Enum import constraints import dictionary class LetterResult(Enum): """Is an individual letter in a word in the result?""" CORRECT = "C" MOVE = "M" INCORRECT = "I" UNKNOWN = "U" class InvalidWord(Exception): """Thrown when an input word is not a valid Wordle.""" pass def select_word(): return random.choice(list(dictionary.words())) def grade_word(target, guess): # We don't check word length, since all valid words are 5 letters. if guess not in dictionary.words(): raise InvalidWord(f"{guess} is not a valid Wordle.") result = [LetterResult.UNKNOWN] * dictionary.WORDLE_LENGTH # We copy target, and remove letters if we find them. This handles duplicate letters correctly. # For example, if the target is "pulls" and we guess "apple", only the first "p" should be a # MOVE. The second should be INCORRECT. remaining = list(target) # Find all correct results first. for i in range(dictionary.WORDLE_LENGTH): if target[i] == guess[i]: result[i] = LetterResult.CORRECT remaining.remove(guess[i]) for i in range(dictionary.WORDLE_LENGTH): if result[i] == LetterResult.UNKNOWN: if guess[i] in remaining: # Letter is in the wrong place. result[i] = LetterResult.MOVE remaining.remove(guess[i]) else: # Letter is not in target. result[i] = LetterResult.INCORRECT return result def display_result(result, text): opening = { LetterResult.CORRECT: "\033[92m", LetterResult.MOVE: "\033[33m", LetterResult.INCORRECT: "\033[36m", LetterResult.UNKNOWN: "", } closing = "\033[0m" return f"{opening[result]}{text}{closing}" def success_result(result): for r in result: if r != LetterResult.CORRECT: return False return True def analyze_dictionary(wordle_dictionary): # Word Position, Letter: Count position_matches = [ defaultdict(int), defaultdict(int), defaultdict(int), defaultdict(int), defaultdict(int), ] # Letter: Count word_matches = defaultdict(int) for word in wordle_dictionary: unique_letters = set(word) for letter in unique_letters: word_matches[letter] += 1 for index in range(len(word)): letter = word[index] assert letter in string.ascii_lowercase position_matches[index][letter] += 1 return position_matches, word_matches def score_word(position_matches, word_matches, word): result = 0 # Green Score for index in range(len(word)): letter = word[index] green_score = position_matches[index][letter] result += 10 * green_score # Yellow Score for letter in set(word): yellow_score = word_matches[letter] result += yellow_score return result def scored_words(wordle_dictionary=dictionary.words()): # word: score scored_words = dict() position_matches, word_matches = analyze_dictionary(wordle_dictionary) for word in wordle_dictionary: scored_words[word] = score_word(position_matches, word_matches, word) result = list(wordle_dictionary) result.sort(key=lambda word: scored_words[word], reverse=True) return [(word, scored_words[word]) for word in result] if __name__ == "__main__": consts = constraints.Constraints() consts.update_graded( "cares", [ LetterResult.INCORRECT, LetterResult.INCORRECT, LetterResult.INCORRECT, LetterResult.INCORRECT, LetterResult.INCORRECT, ], ) valid_words = consts.filter_words(dictionary.words()) print("Top: ", scored_words(valid_words)[:5])