#!python # Wordle in Python for Mac # # expects python3; uses ANSI colours, so expects a terminal that # supports them; gets words from /usr/dict/share/words unless you've # found a properly-formatted json dictionary and left it lying around. # Wordle is intellectual property in some way, shape or form of Josh # Wardle; this code is in no way intended to infringe that and should # be considered "something I hacked out on a Friday night". Play the # real thing here: https://www.powerlanguage.co.uk/wordle/ import argparse import json import random import re import os import sys def get_words(): """ Get the list of words. Returns a tuple: words that are answers, and words we'll accept as guesses. """ # heyyyy someone extracted the actual dictionary # https://raw.githubusercontent.com/printfn/wordle-dict/main/wordle-dict.json try: with open("wordle-dict.json") as w: dictionary = json.load(w) return dictionary["answers"], dictionary["answers"] + dictionary["allowed"] except Exception: # lazy pass print(color("Failed to load JSON dictionary, falling back to words", "red")) # fallback: /usr/share/dict/words with open("/usr/share/dict/words") as w: # len 6 because newline; islower to exclude proper nouns dict_words = [word.strip() for word in w if len(word) == 6 and word[0].islower()] return dict_words, dict_words def best_starter(): answers, guesses = get_words() try: with open("scoreboard.json") as w: scores = json.load(w) print("Loaded scoreboard") except Exception as e: scores = dict() for idx, word in enumerate(guesses, start=1): wordscore = 'yyyyy' words = get_possibles(guesses, wordscore, word, word) score = len(words) scores.setdefault(score, []).append(word) if idx % 100 == 0: print(f"... {idx} of {len(guesses)}") with open("scoreboard.json", "w") as w: json.dump(scores, w, indent=2) print("Wrote scoreboard") scores = {int(key): value for key, value in scores.items()} idx = max(scores.keys()) print(f"{idx}: {', '.join(scores[idx])}") def color(text, color): # display 'color' text on black background ansi = {"red": "41", "green": "42", "yellow": "43", "grey": "47"}.get(color, "0") return f"\033[30;{ansi}m{text}\033[0m" def get_possibles(allowed, score, guess, answer): matchword = ['.'] * len(score) notletters = [] haveletters = [] for idx, letter in enumerate(score): if letter == 'Y': matchword[idx] = guess[idx] haveletters.append(guess[idx]) elif letter == 'y': haveletters.append(guess[idx]) else: notletters.append(guess[idx]) matchword_re = "".join(matchword) maybe = [] for ans in allowed: if re.match(matchword_re, ans): if all([letter in ans for letter in haveletters]): if not any([letter in ans for letter in notletters]): maybe.append(ans) return maybe def replay_game(gamefile): answers, allowed = get_words() # try to replay a game from the scoreboard result # gamefile format: # guess score # guess is the word guessed # score is 5 chars: . for no match, y for wrong place, Y for correct with open(gamefile) as g: scores = [l.strip() for l in g.readlines()] answer = scores.pop().lower() print(f"Final answer was '{answer}'") scores.reverse() for scoreline in scores: guess, score = scoreline.split(' ') print(f" score: {score}") maybe = get_possibles(allowed, score, guess, answer) choices = ", ".join(maybe[:9]) if len(maybe) > 10: choices += f" and {len(maybe) - 9} more" print(f" {choices}") def game(args): words, guesswords = get_words() if args.word: selected = words[words.index(args.word)] else: selected = random.choice(words) print(color("Waidle!", "green")) print() letters = {chr(letter): chr(letter) for letter in range(97, 123)} game_round = 0 max_round = 6 while game_round < max_round: guess = input(f"enter guess #{game_round + 1}: ") if guess not in guesswords: print(color(f"'{guess}' is not in the dictionary, guess again", "red")) continue game_round += 1 result = "" scorestring = "" for idx, letter in enumerate(guess): if selected.count(letter) == 0: score = color(letter, "grey") scorestring += "." elif selected[idx] == guess[idx]: score = color(letter, "green") scorestring += "Y" elif letter in selected: score = color(letter, "yellow") scorestring += "y" letters[letter] = score result += score letter_score = " ".join(letters.values()) maybe = get_possibles(guesswords, scorestring, guess, selected) definitely_maybe = get_possibles(words, scorestring, guess, selected) print(f"{game_round}/{max_round}: {result} {letter_score} ({len(maybe)}, {len(definitely_maybe)})") if guess == selected: print("Well done!") # lazy: just drop out of the program sys.exit() print(color("Sorry, you ran out of guesses.", "red")) print("The word was '" + color(selected, "green") + "'.") def main(): parser = argparse.ArgumentParser() parser.add_argument("--reverse", type=str, action="store") parser.add_argument("--word", type=str, action="store") parser.add_argument("--best", action="store_true") args = parser.parse_args() if args.best: best_starter() elif not args.reverse: game(args) else: replay_game(args.reverse) if __name__ == "__main__": main()