From 3381cb24bed4b91e15716e505d35de4411911cb1 Mon Sep 17 00:00:00 2001 From: Peter Norvig Date: Wed, 12 Jan 2022 11:19:46 -0800 Subject: [PATCH] Add files via upload --- ipynb/jotto.ipynb | 2349 ++++--- ipynb/wordle-big.txt | 12971 +++++++++++++++++++++++++++++++++++++++ ipynb/wordle-small.txt | 2315 +++++++ 3 files changed, 16834 insertions(+), 801 deletions(-) create mode 100644 ipynb/wordle-big.txt create mode 100644 ipynb/wordle-small.txt diff --git a/ipynb/jotto.ipynb b/ipynb/jotto.ipynb index eae0737..b795aef 100644 --- a/ipynb/jotto.ipynb +++ b/ipynb/jotto.ipynb @@ -10,7 +10,7 @@ "\n", "[Jotto](https://en.wikipedia.org/wiki/Jotto) is a word game in which a **guesser** tries to guess a secret **target** word, which is chosen from a **word list** of allowable words, in as few guesses as possible. Each guess must be one of the allowable words, and the **reply** to each guess is the number of letters in common between the guess word and the target word, regardless of the positions of the letters. \n", "\n", - "Here is an example Jotto game, where I show the guesses, the replies, the number of remaining targets that are **consistent** with all the replies seen so far, and finally the letters that matched (the matches are an aid to you, the reader; they are not known to the guesser). In this game, the guesser gets to the target word, \"wonky\", in 7 guesses. \n", + "Here is an example Jotto game, where I show the guesses, the replies, the number of remaining consistent target words, and finally the letters that matched (the matches are an aid to you, the reader; they are not known to the guesser). A **consistent target** is a word that would give the same reply to each guess as the replies actually observed. In this game, the guesser gets to the target word, \"wonky\", in 7 guesses. \n", "\n", " Guess 1: \"stoma\" Reply: 1; Consistent targets: 1118 (Matched: \"o\")\n", " Guess 2: \"bairn\" Reply: 1; Consistent targets: 441 (Matched: \"n\")\n", @@ -21,13 +21,13 @@ " Guess 7: \"wonky\" Reply: 5; Consistent targets: 1 (Matched: \"wonky\")\n", "\n", "\n", - "There are several variants of the game; here are five key questions and my answers:\n", + "There are several variants of Jotto; here are five key questions and my answers:\n", "\n", - "- Q: How many letters can each word be?
A: **Only five-letter words are allowed**.\n", - "- Q: Does a guess have to be a word in the word list?
A: **Yes.**\n", - "- Q: What is the reply for a word that has the same letter twice, like the \"s\" in \"stars\"?
A: **Only words with no repeated letters are allowed in the word list**.\n", - "- Q: What if the reply is \"5\", but the guess is not the target?
A: **No two words in the word list are allowed to have the same set of five letters**.
(For example, only one of the anagrams apers/pares/parse/pears/reaps/spare/spear is allowed.)\n", - "- Q: Who chooses the target word?
A: **Random chance**. Jotto is sometimes a two-person game where the chooser is an adversary, but not here.\n", + "- How many letters can each word have?
**Five**.\n", + "- Does a guess have to be a word in the word list?
**Yes.**\n", + "- Can a word have repeated letters, like the \"s\" in \"stars\"?
**No.** Every word must have 5 different letters.\n", + "- What if the reply is \"5\", but the guess is not the target?
**Not allowed**. No two words in the word list may have the same set of five letters.
(For example, only one of the anagrams apers/pares/parse/pears/reaps/spare/spear is allowed.)\n", + "- Who chooses the target word?
**Random chance**. Jotto is sometimes a two-person game where the chooser is an adversary, but not here.\n", "\n", "# Jotto Preliminaries\n", "\n", @@ -45,12 +45,14 @@ "from statistics import mean, median, stdev\n", "from collections import defaultdict\n", "from math import log2\n", - "import random\n", + "import random \n", "import matplotlib.pyplot as plt\n", "\n", "Word = str # A word is a lower-case string of five different letters\n", "Score = int # A score is the number of guesses it took to get the target word\n", - "Reply = int # A reply is the number of letters in common between guess and target words" + "Reply = int # A reply is the number of letters in common between guess and target words\n", + "\n", + "random.seed(42) # For reproducibility" ] }, { @@ -58,7 +60,7 @@ "metadata": {}, "source": [ "We can make a Jotto word list by:\n", - "- Starting with a list of words.\n", + "- Starting with a file containing a list of words.\n", "- Discarding the ones that don't have 5 distinct letters.\n", "- Putting the rest into a dict of anagrams keyed by the set of letters.\n", "- Keeping only one word for each anagram." @@ -70,6 +72,8 @@ "metadata": {}, "outputs": [], "source": [ + "def read_words(filename) -> List[Word]: return open(filename).read().split()\n", + "\n", "def allowable(words) -> List[Word]:\n", " \"\"\"Build a list of allowable Jotto words from an iterable of words.\"\"\"\n", " anagrams = {frozenset(w): w for w in words if len(w) == 5 == len(set(w))}\n", @@ -91,7 +95,7 @@ { "data": { "text/plain": [ - "(5756, 2845)" + "2845" ] }, "execution_count": 3, @@ -102,17 +106,16 @@ "source": [ "! [ -e sgb-words.txt ] || curl -O https://norvig.com/ngrams/sgb-words.txt\n", " \n", - "sgb_words = open('sgb-words.txt').read().split()\n", - "wordlist = allowable(sgb_words)\n", + "wordlist = allowable(read_words('sgb-words.txt'))\n", "\n", - "len(sgb_words), len(wordlist)" + "len(wordlist)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We see there are 2,845 allowable Jotto words out of the 5,756 words in [sgb-words.txt](sgb-words.txt)." + "We see there are 2,845 allowable Jotto words in [sgb-words.txt](sgb-words.txt)." ] }, { @@ -125,14 +128,14 @@ "- `guesser`: a `callable` (e.g., a function) that should return the guess to make. The guesser is passed two arguments: \n", " - The reply to the previous guess.\n", " - A list of the words that are consistent with all the guesses made so far. \n", - "
(If the guesser wants to keep track of all the guesses made so far, or all the words in the word list, it is welcome to do so.)\n", + "
(If the guesser wants to keep track of all the guesses made so far, or all the words in the word list, it can.)\n", "- `target`: The target word. If none is given, the target word is chosen at random from the wordlist.\n", "- `wordlist`: The list of allowable words.\n", "- `verbose`: Unless false, print a message for each guess.\n", "\n", "Two corner cases: \n", "1. If the guesser improperly guesses a non-word, the reply is `None`. \n", - "2. To prevent a poor guesser from creating an infinite loop, the worst score you can get is the number of words in the wordlist." + "2. To prevent an infinite loop, the worst score you can get is the number of words in the wordlist." ] }, { @@ -144,20 +147,20 @@ "Guesser = Callable[[Reply, List[Word]], Word]\n", "\n", "def play(guesser: Guesser, target=None, wordlist=wordlist, verbose=True) -> Score:\n", - " \"\"\"How many guesses does it take for `guesser` to guess the Jotto target word,\n", - " which is selected by `chooser` from the words in `wordlist`?\"\"\"\n", + " \"\"\"The number of guesses it take for `guesser` to guess the Jotto word,\n", + " which is given by `target` or selected from the words in `wordlist`?\"\"\"\n", " target = target or random.choice(wordlist) # Choose a random target if none was given\n", " targets = wordlist # The targets that are consistent with all replies\n", " reply = None # For the first guess, there is no previous reply\n", - " N = len(wordlist) # After N guesses stop the game and record a score of N\n", - " for i in range(1, N + 1):\n", + " N = len(wordlist)\n", + " for turn in range(1, N + 1):\n", " guess = guesser(reply, targets)\n", " reply = reply_for(guess, target) if guess in wordlist else None\n", " targets = [t for t in targets if reply_for(guess, t) == reply]\n", " if verbose: \n", - " print(f'Guess {i}: \"{guess}\" Reply: {reply}; Consistent targets: {len(targets)}')\n", - " if guess == target or i == N: \n", - " return i\n", + " print(f'Guess {turn}: \"{guess}\" Reply: {reply}; Consistent targets: {len(targets)}')\n", + " if guess == target or turn == N: \n", + " return turn\n", " \n", "def reply_for(guess, target) -> Reply: \n", " \"The number of letters in common between the target and guess\"\n", @@ -168,9 +171,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "To play a game, we will need a guesser. Here are two simple ones:\n", - "- `human_guesser` asks a human for `input`.\n", - "- `random_guesser` guesses one of the remaining consistent targets, picked at random. \n" + "To play a game, we will need a guesser. Here are two simple ones:\n" ] }, { @@ -179,9 +180,13 @@ "metadata": {}, "outputs": [], "source": [ - "def human_guesser(reply, targets) -> Word: return input(f'Reply was {reply}. Your guess? ')\n", + "def human_guesser(reply, targets) -> Word: \n", + " \"\"\"Ask a human to make a guess.\"\"\"\n", + " return input(f'Reply was {reply}. Your guess? ')\n", "\n", - "def random_guesser(reply, targets) -> Word: return random.choice(targets)" + "def random_guesser(reply, targets) -> Word: \n", + " \"\"\"Choose a guess at random from the consistent targets.\"\"\"\n", + " return random.choice(targets)" ] }, { @@ -202,19 +207,20 @@ "name": "stdout", "output_type": "stream", "text": [ - "Guess 1: \"tempo\" Reply: 0; Consistent targets: 451\n", - "Guess 2: \"flick\" Reply: 0; Consistent targets: 67\n", - "Guess 3: \"hydra\" Reply: 3; Consistent targets: 21\n", - "Guess 4: \"hands\" Reply: 2; Consistent targets: 7\n", - "Guess 5: \"bawdy\" Reply: 2; Consistent targets: 4\n", - "Guess 6: \"guard\" Reply: 3; Consistent targets: 3\n", - "Guess 7: \"grays\" Reply: 5; Consistent targets: 1\n" + "Guess 1: \"strop\" Reply: 3; Consistent targets: 322\n", + "Guess 2: \"party\" Reply: 1; Consistent targets: 112\n", + "Guess 3: \"obits\" Reply: 3; Consistent targets: 44\n", + "Guess 4: \"sloth\" Reply: 2; Consistent targets: 12\n", + "Guess 5: \"brows\" Reply: 2; Consistent targets: 5\n", + "Guess 6: \"pious\" Reply: 4; Consistent targets: 4\n", + "Guess 7: \"pions\" Reply: 4; Consistent targets: 3\n", + "Guess 8: \"dipso\" Reply: 5; Consistent targets: 1\n" ] }, { "data": { "text/plain": [ - "7" + "8" ] }, "execution_count": 6, @@ -235,20 +241,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "Guess 1: \"chard\" Reply: 2; Consistent targets: 833\n", - "Guess 2: \"frank\" Reply: 1; Consistent targets: 377\n", - "Guess 3: \"lased\" Reply: 3; Consistent targets: 91\n", - "Guess 4: \"decks\" Reply: 2; Consistent targets: 58\n", - "Guess 5: \"haves\" Reply: 2; Consistent targets: 44\n", - "Guess 6: \"mated\" Reply: 4; Consistent targets: 8\n", - "Guess 7: \"taxed\" Reply: 4; Consistent targets: 3\n", - "Guess 8: \"adept\" Reply: 5; Consistent targets: 1\n" + "Guess 1: \"carpy\" Reply: 0; Consistent targets: 602\n", + "Guess 2: \"litho\" Reply: 2; Consistent targets: 249\n", + "Guess 3: \"loved\" Reply: 0; Consistent targets: 21\n", + "Guess 4: \"fugit\" Reply: 2; Consistent targets: 7\n", + "Guess 5: \"skint\" Reply: 1; Consistent targets: 1\n", + "Guess 6: \"thumb\" Reply: 5; Consistent targets: 1\n" ] }, { "data": { "text/plain": [ - "8" + "6" ] }, "execution_count": 7, @@ -269,18 +273,19 @@ "name": "stdout", "output_type": "stream", "text": [ - "Guess 1: \"chefs\" Reply: 0; Consistent targets: 420\n", - "Guess 2: \"junta\" Reply: 0; Consistent targets: 42\n", - "Guess 3: \"wimpy\" Reply: 1; Consistent targets: 10\n", - "Guess 4: \"dorky\" Reply: 3; Consistent targets: 3\n", - "Guess 5: \"glory\" Reply: 3; Consistent targets: 1\n", - "Guess 6: \"world\" Reply: 5; Consistent targets: 1\n" + "Guess 1: \"thous\" Reply: 1; Consistent targets: 1141\n", + "Guess 2: \"vocal\" Reply: 2; Consistent targets: 340\n", + "Guess 3: \"snack\" Reply: 0; Consistent targets: 46\n", + "Guess 4: \"vigor\" Reply: 2; Consistent targets: 19\n", + "Guess 5: \"oxlip\" Reply: 2; Consistent targets: 10\n", + "Guess 6: \"roble\" Reply: 3; Consistent targets: 4\n", + "Guess 7: \"world\" Reply: 5; Consistent targets: 1\n" ] }, { "data": { "text/plain": [ - "6" + "7" ] }, "execution_count": 8, @@ -296,90 +301,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Evaluating Guessers\n", - "\n", - "To properly evaluate a guesser, a sample of 3 games is not enough. We will have to play at least a few hundred games to get a statistically reliable result. The function `evaluate` takes a list of scores from playing multiple games and reports the following statistics:\n", - "- The median, mean, standard deviation, and worst case number of guesses, and total number of scores.\n", - "- The cumulative percentages guessed correctly (e.g., `\"≤5:11%\"` means 11% of the targets took 5 or fewer guesses).\n", - "- A histogram of scores." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def report(scores: Iterable[Score]) -> None:\n", - " \"\"\"Report statistics and a histogram for these scores.\"\"\"\n", - " scores = list(scores)\n", - " ctr = Counter(scores)\n", - " bins = range(min(ctr), max(ctr) + 2)\n", - " scale = 100 / len(scores)\n", - " weights = [scale * ctr[score] for score in ctr]\n", - " plt.hist(list(ctr), weights=weights, align='left', rwidth=0.9, bins=bins)\n", - " plt.xticks(bins[:-1])\n", - " plt.xlabel('Number of guesses'); plt.ylabel('% of scores')\n", - " print(f'median: {median(scores):.0f} guesses, mean: {mean(scores):.2f}',\n", - " f'± {stdev(scores):.2f}, worst: {max(scores)}, scores: {len(scores):,d}')\n", - " def pct(g): \n", - " \"\"\"What percent of games requires no mare than g guesses?\"\"\"\n", - " percent = scale * sum(ctr[i] for i in range(1, g + 1))\n", - " return round(percent, (1 if 99 < percent < 100 else None))\n", - " print('cumulative:', ', '.join(f'≤{g}:{pct(g)}%' for g in range(3, 11)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I'll evaluate `random_guesser` on every word in the word list:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "median: 7 guesses, mean: 7.35 ± 1.68, worst: 16, scores: 2,845\n", - "cumulative: ≤3:1%, ≤4:3%, ≤5:11%, ≤6:31%, ≤7:57%, ≤8:79%, ≤9:91%, ≤10:96%\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "report(play(random_guesser, target, verbose=False) for target in wordlist)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The random consistent guesser strategy might have seemed hopelessly naive, but it is actually a pretty decent strategy. The median is 7 guesses and about 80% of the time it will take 8 guesses or fewer. Can a better strategy improve on that?\n", - "\n", - "# Guessers that Partition Target Words\n", + "# Partitioning Target Words\n", "\n", "A key idea in guessing is to reduce the number of consistent targets. When there is a single consistent target left, the game is over. We can think of a guess as **partitioning** the consistent targets into different **branches** of a tree, each branch corresponding to a different reply:" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -395,86 +324,48 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here are two different partitions of just the first 22 words in `wordlist`; one by the guess word `'girth'` and one by `'ethos'`:" + "To get a feel for how this works, a 2,845 word list is too much to deal with; let's consider just the first 22 words in `wordlist`. Here are two partitions of those 22 words, one by the guess `'girth'` and one by `'ethos'`:" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 10, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "defaultdict(list,\n", - " {4: ['their', 'might'],\n", - " 1: ['about', 'sword', 'resay', 'nuder', 'house'],\n", - " 0: ['would', 'cloud', 'place', 'sound', 'fondu'],\n", - " 3: ['throe', 'write', 'rifts', 'think', 'grate'],\n", - " 2: ['water', 'after', 'ethos', 'while'],\n", - " 5: ['girth']})" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "words22 = wordlist[:22]\n", "\n", - "partition('girth', words22)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "defaultdict(list,\n", - " {3: ['their'],\n", - " 2: ['about',\n", - " 'sword',\n", - " 'write',\n", - " 'rifts',\n", - " 'water',\n", - " 'after',\n", - " 'girth',\n", - " 'think',\n", - " 'resay',\n", - " 'sound',\n", - " 'grate',\n", - " 'might',\n", - " 'while'],\n", - " 1: ['would', 'cloud', 'place', 'fondu', 'nuder'],\n", - " 4: ['throe', 'house'],\n", - " 5: ['ethos']})" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "partition('ethos', words22)" + "assert (partition('girth', words22) ==\n", + " {0: ['would', 'cloud', 'place', 'sound', 'fondu'],\n", + " 1: ['about', 'sword', 'resay', 'nuder', 'house'],\n", + " 2: ['water', 'after', 'ethos', 'while'],\n", + " 3: ['throe', 'write', 'rifts', 'think', 'grate'],\n", + " 4: ['their', 'might'],\n", + " 5: ['girth']})\n", + "\n", + "assert (partition('ethos', words22) ==\n", + " {1: ['would', 'cloud', 'place', 'fondu', 'nuder'],\n", + " 2: ['about', 'sword', 'write', 'rifts', 'water', 'after', 'girth', 'think', 'resay', \n", + " 'sound', 'grate', 'might', 'while'],\n", + " 3: ['their'],\n", + " 4: ['throe', 'house'],\n", + " 5: ['ethos']})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We see that after guesssing `'girth'`, no matter what the reply is, we will be left with no more than 5 targets. However, if we guessed `'ethos'` then a majority of the time the reply would be 2, and there would be 13 possible targets remaining. That suggests that `'girth'` is a better guess and that a good strategy is: **guess a word that partitions the possible targets into branches with small numbers of words.**\n", + "No matter what the reply to `'girth'` is, we will be left with no more than 5 targets. \n", + "\n", + "But if we guess `'ethos'` then 13 of the 22 targets would get a reply of 2, and there would be 13 possible targets remaining to deal with. That suggests that `'girth'` is a better guess and that a good strategy is: **guess a word that partitions the possible targets into small branches.**\n", "\n", "Since we only need to know the *size* of each branch, not the list of words therein, we can use `partition_counts`:" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -486,7 +377,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -495,7 +386,7 @@ "[2, 5, 5, 5, 4, 1]" ] }, - "execution_count": 15, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -506,7 +397,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -515,7 +406,7 @@ "[1, 13, 5, 2, 1]" ] }, - "execution_count": 16, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -528,23 +419,22 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Comparing Partitions\n", + "# Metrics for Minimizing Partitions\n", "\n", - "What exactly is the best metric for deciding which partition is best? Ideally, we want the partition that minimizes the average number of additional guesses it will take to finish the game, but since we don't know that, we can instead minimize one of the following proxy metrics to rank partitions, based on the sizes of the branches:\n", + "We want partitions with **small branches**, but what exactly does that mean? Ideally, we want the partition that minimizes the average number of additional guesses it will take to finish the game. But since we don't know that, we can instead minimize one of the following proxy metrics:\n", "\n", "- **Maximum**: choose the partition that minimizes the size of the largest branch.\n", "\n", + "- **Expectation**: In probability theory, the expectation (also known as expected value) is the weighted average of a random variable. Here it means the sum, over all branches, of the probability of ending up in the branch multiplied by the size of the branch. We are assuming that every target is equally likely, so the probability of a branch is proportional to the number of targets in it.\n", "\n", - "- **Expectation**: In probability theory the expectation or **expected value** is the weighted average of a random variable. Here it means the sum, over all branches, of the size of the branch multiplied by the probability of ending up in the branch; that's what we want to minimize. We are assuming that every target is equally likely, so the probability of a branch is proportional to the number of targets in it.\n", - "\n", - "- **Negative Entropy**: Entropy is an information-theoretic measure that is similar to expectation, except that it weights each branch size by its base 2 logarithm (whereas expectation weights it by its actual size). We want to maximkize entropy, or minimize *negative* entropy.\n", + "- **Negative Entropy**: In information theory, entropy is the weighted average amount of \"information\" measured in bits. The calculation is the same as expectation except that we count the base 2 logarithm of the branch sizes, not the branches sizes themselves. You can think of the base 2 logarithm as the number of times that you need to cut a branch in half to get it down to one word. We want to maximize entropy, or minimize *negative* entropy.\n", "\n", "The maximum is just the builtin `max` function; here are the other two metrics:" ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -565,155 +455,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Best and Worst First Guesses\n", - "\n", - "Here's a function to print a table of the best and worst words to partition the word list–that is, the best and worst first guesses in a game–according to each of the three metrics:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "def partition_scores(n, metrics=(max, expectation, neg_entropy), wordlist=wordlist): \n", - " \"\"\"The top and bottom `n` words to guess, according to each partition metric.\"\"\"\n", - " rankings = {metric: sorted((metric(partition_counts(g, wordlist)), g) for g in wordlist)\n", - " for metric in metrics}\n", - " def fmt(score): \n", - " return f'{round(score):5d}' if score >= 100 or isinstance(score, int) else f'{score:5.2f}'\n", - " bar = ' + '.join(['-' * 11] * len(metrics))\n", - " print(' | '.join(f'{metric.__name__:11}' for metric in metrics))\n", - " print(bar)\n", - " for i in [*range(n), *range(-n, 0)]:\n", - " fmts = [f'{word} {fmt(score)}' for metric in metrics \n", - " for score, word in [rankings[metric][i]]]\n", - " if i == -n: print(bar)\n", - " print(' | '.join(fmts))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First I'll try it with the 22-word list:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "max | expectation | neg_entropy\n", - "----------- + ----------- + -----------\n", - "girth 5 | girth 4.36 | girth -2.42\n", - "after 6 | grate 4.45 | grate -2.41\n", - "grate 6 | after 4.55 | after -2.39\n", - "write 6 | write 4.64 | write -2.38\n", - "might 8 | their 4.91 | their -2.36\n", - "place 8 | water 4.91 | water -2.36\n", - "rifts 8 | would 5.27 | would -2.26\n", - "their 8 | might 5.55 | might -2.21\n", - "think 8 | think 5.64 | fondu -2.20\n", - "throe 8 | rifts 5.73 | sound -2.18\n", - "water 8 | fondu 5.73 | cloud -2.11\n", - "----------- + ----------- + -----------\n", - "would 8 | sound 5.82 | think -2.10\n", - "fondu 9 | throe 5.91 | rifts -2.08\n", - "house 9 | resay 6.00 | resay -2.06\n", - "resay 9 | cloud 6.36 | throe -2.04\n", - "sound 9 | while 6.55 | while -1.96\n", - "while 9 | sword 6.82 | sword -1.94\n", - "about 10 | place 6.82 | house -1.77\n", - "cloud 10 | house 7.64 | place -1.77\n", - "sword 10 | nuder 8.00 | nuder -1.75\n", - "nuder 11 | ethos 9.09 | ethos -1.65\n", - "ethos 13 | about 9.18 | about -1.44\n" - ] - } - ], - "source": [ - "partition_scores(11, wordlist=words22)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The three metrics are similar; \"girth\" is good and \"ethos\" is bad on all three metrics.\n", - "\n", - "Now with the full wordlist (this will take about 12 seconds to run):" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "max | expectation | neg_entropy\n", - "----------- + ----------- + -----------\n", - "wader 1012 | raved 813 | debar -1.95\n", - "cadre 1026 | debar 818 | alder -1.95\n", - "armed 1028 | roved 827 | raved -1.94\n", - "diner 1029 | orbed 827 | dater -1.94\n", - "coder 1030 | wader 827 | cadre -1.94\n", - "padre 1035 | armed 827 | armed -1.94\n", - "raved 1038 | fader 828 | garde -1.94\n", - "rayed 1038 | dater 829 | wader -1.94\n", - "delta 1039 | alder 830 | lased -1.93\n", - "drone 1041 | cadre 830 | padre -1.93\n", - "eland 1043 | garde 830 | fader -1.93\n", - "garde 1043 | padre 832 | dears -1.93\n", - "heard 1044 | deign 832 | drone -1.93\n", - "tired 1044 | gored 834 | diner -1.93\n", - "debar 1046 | laved 834 | rayed -1.93\n", - "fader 1048 | rayed 837 | tired -1.93\n", - "----------- + ----------- + -----------\n", - "vacuo 1465 | jumpy 1067 | gauzy -1.61\n", - "miaow 1474 | gauze 1067 | quake -1.61\n", - "pique 1480 | juicy 1070 | humpf -1.61\n", - "okapi 1485 | quail 1073 | coqui -1.61\n", - "imago 1491 | imago 1081 | whump -1.60\n", - "haiku 1493 | miaow 1084 | diazo -1.60\n", - "audio 1494 | quake 1086 | zombi -1.59\n", - "gauze 1497 | coqui 1091 | okapi -1.59\n", - "quake 1500 | quota 1098 | azoic -1.59\n", - "quail 1507 | haiku 1099 | haiku -1.58\n", - "diazo 1513 | diazo 1100 | mujik -1.58\n", - "coqui 1535 | okapi 1105 | jumpy -1.57\n", - "quota 1548 | azoic 1120 | juicy -1.57\n", - "azoic 1555 | axiom 1157 | axiom -1.56\n", - "axiom 1615 | audio 1184 | audio -1.49\n", - "ouija 1848 | ouija 1413 | ouija -1.29\n" - ] - } - ], - "source": [ - "partition_scores(16)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The top guesses favor the letters \"a\", \"d\", \"e\", and \"r\". \n", - "\n", - "The word \"ouija\" is a uniquely terrible guess; mostly what it does is confirm that a majority of the words have exactly one of the vowels \"aiou\"." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Guess Trees: Caching Best Guesses\n", + "# Caching Best Guesses: Guess Trees\n", "\n", "Going through every word in the wordlist to decide which one makes the best partition takes some time. I would prefer to do that computation just once and cache it, rather than have to repeat the computation in every new game. I will cache the best guesses in a structure called a **guess tree**: a tree that has branches for every possible path the game might take, with the best guess for each situation already computed. A guess tree is either:\n", "- An **interior node**, which has a guess and a dict of branches, `Node(guess, {reply: subtree, ...})`, where each subtree covers all the target words that are consistent with the corresponding reply.\n", @@ -722,7 +464,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -731,6 +473,8 @@ " \"\"\"A node in a guess tree. It stores the best guess, and a branch for every possible reply.\"\"\"\n", " guess: Word\n", " branches: Dict[Reply, 'Tree']\n", + " \n", + " def __repr__(self) -> str: return f'Node(\"{self.guess}\", {self.branches})'\n", "\n", "Tree = Union[Node, Word] # A Tree is either an interior Node or a leaf Word" ] @@ -744,7 +488,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -763,33 +507,43 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here is a tree that covers a small list of five words:" + "Here is a minimizing tree that covers the 22 words. The tree says that the first guess is `\"girth\"`, and if the reply is `0` the next guess is `\"would\"`. If, say, the reply to `\"would\"` is 4, then there is only one other word left, the leaf word `'cloud'`. If the reply is 5, that means `\"would\"` was correct. I won't go through the other branches of the tree." ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ - "words5 = ['purge', 'bites', 'sulky', 'patsy', 'hayed']\n", - "tree5 = minimizing_tree(max, words5)\n", + "tree22 = minimizing_tree(max, words22)\n", "\n", - "assert tree5 == Node(guess='bites', \n", - " branches={1: Node(guess='purge', \n", - " branches={1: Node(guess='sulky', \n", - " branches={1: 'hayed', \n", - " 5: 'sulky'}), \n", - " 5: 'purge'}), \n", - " 2: 'patsy', \n", - " 5: 'bites'})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The tree says that the first guess is `'bites'`, and if the reply is `1` there is a complex subtree to consider (starting with the guess `'purge'`), but if the reply is `2` the target can only be `'patsy'` and of course if the reply is `5` then `'bites'` was the target and the game is over." + "assert (tree22 ==\n", + " Node(\"girth\", \n", + " {0: Node(\"would\", \n", + " {1: 'place', \n", + " 3: Node(\"sound\", {4: 'fondu', 5: 'sound'}), \n", + " 4: 'cloud', \n", + " 5: 'would'}), \n", + " 1: Node(\"about\", \n", + " {1: Node(\"sword\", \n", + " {2: Node(\"resay\", {2: 'nuder', 5: 'resay'}), \n", + " 5: 'sword'}), \n", + " 2: 'house', \n", + " 5: 'about'}), \n", + " 2: Node(\"after\", \n", + " {1: 'while', \n", + " 2: 'ethos', \n", + " 4: 'water', \n", + " 5: 'after'}), \n", + " 3: Node(\"throe\", \n", + " {2: Node(\"rifts\", {2: 'think', 5: 'rifts'}), \n", + " 3: Node(\"write\", {3: 'grate', 5: 'write'}), \n", + " 5: 'throe'}), \n", + " 4: Node(\"their\", \n", + " {3: 'might', \n", + " 5: 'their'}), \n", + " 5: 'girth'}))" ] }, { @@ -800,14 +554,15 @@ "\n", "A tree is not a guesser, but we can easily make a guesser from a tree. A `TreeGuesser` works as follows:\n", "- When *initialized*, it takes a tree as input, and stores the tree under the `.root` attribute.\n", - "- When *called* for the first turn in a game (when the reply is `None`), it resets `.tree` to `.root`.\n", - "- When *called* on subsequent turns within a game, it updates `.tree` to be the branch corresponding to the reply.\n", - "- It then returns the guess for that branch: either the `.guess` attribute or the leaf word. " + "- When *called*, it sets the `.tree` attribute:\n", + " - For the first turn in a game (when the reply is `None`), it resets `.tree` to `.root`.\n", + " - On subsequent turns, it updates `.tree` to be the branch corresponding to the reply.\n", + "- It then returns the guess for the current tree: either the `.guess` attribute or the leaf word. " ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -815,11 +570,11 @@ " \"\"\"Given a guess tree, use it to create a callable Guesser that can play Jotto.\"\"\"\n", " def __init__(self, tree): self.root = tree\n", " \n", - " def __call__(self, reply, targets) -> str:\n", + " def __call__(self, reply, targets) -> Word:\n", " \"\"\"If reply is None, start a new game; otherwise follow the branch for the reply.\n", " Then return the current leaf or interior node guess.\"\"\"\n", - " self.tree = self.root if reply is None else self.tree.branches[reply]\n", - " return self.tree.guess if isinstance(self.tree, Node) else self.tree" + " tree = self.tree = self.root if reply is None else self.tree.branches[reply]\n", + " return tree.guess if isinstance(tree, Node) else tree" ] }, { @@ -831,7 +586,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -843,44 +598,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Sample Jotto Games with the Minimizing Guesser\n" + "# Sample Games with the Minimizing Guesser\n" ] }, { "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Guess 1: \"wader\" Reply: 2; Consistent targets: 1004\n", - "Guess 2: \"lawns\" Reply: 2; Consistent targets: 354\n", - "Guess 3: \"sutra\" Reply: 2; Consistent targets: 125\n", - "Guess 4: \"heaps\" Reply: 1; Consistent targets: 40\n", - "Guess 5: \"carny\" Reply: 3; Consistent targets: 12\n", - "Guess 6: \"bairn\" Reply: 5; Consistent targets: 1\n" - ] - }, - { - "data": { - "text/plain": [ - "6" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "play(guesser)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -889,18 +612,19 @@ "text": [ "Guess 1: \"wader\" Reply: 0; Consistent targets: 466\n", "Guess 2: \"tings\" Reply: 2; Consistent targets: 160\n", - "Guess 3: \"gunky\" Reply: 2; Consistent targets: 44\n", - "Guess 4: \"plink\" Reply: 2; Consistent targets: 13\n", - "Guess 5: \"bonks\" Reply: 5; Consistent targets: 1\n" + "Guess 3: \"gunky\" Reply: 1; Consistent targets: 54\n", + "Guess 4: \"bouts\" Reply: 2; Consistent targets: 16\n", + "Guess 5: \"clogs\" Reply: 0; Consistent targets: 1\n", + "Guess 6: \"mufti\" Reply: 5; Consistent targets: 1\n" ] }, { "data": { "text/plain": [ - "5" + "6" ] }, - "execution_count": 27, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -911,30 +635,29 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Guess 1: \"wader\" Reply: 2; Consistent targets: 1004\n", - "Guess 2: \"lawns\" Reply: 2; Consistent targets: 354\n", - "Guess 3: \"sutra\" Reply: 2; Consistent targets: 125\n", - "Guess 4: \"heaps\" Reply: 2; Consistent targets: 43\n", - "Guess 5: \"eclat\" Reply: 2; Consistent targets: 13\n", - "Guess 6: \"hilar\" Reply: 0; Consistent targets: 3\n", - "Guess 7: \"wefts\" Reply: 4; Consistent targets: 1\n", - "Guess 8: \"owest\" Reply: 5; Consistent targets: 1\n" + "Guess 1: \"wader\" Reply: 1; Consistent targets: 1012\n", + "Guess 2: \"actin\" Reply: 2; Consistent targets: 355\n", + "Guess 3: \"flats\" Reply: 2; Consistent targets: 113\n", + "Guess 4: \"cloak\" Reply: 3; Consistent targets: 17\n", + "Guess 5: \"backs\" Reply: 2; Consistent targets: 5\n", + "Guess 6: \"clamp\" Reply: 3; Consistent targets: 2\n", + "Guess 7: \"plank\" Reply: 5; Consistent targets: 1\n" ] }, { "data": { "text/plain": [ - "8" + "7" ] }, - "execution_count": 28, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -943,33 +666,41 @@ "play(guesser)" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Evaluating Guess Trees\n", - "\n", - "We could evaluate a Guess Tree with repeated calls to `play`, but there is a faster way: walk the tree and keep track of how many guesses it takes to get to each leaf word. The function `tree_scores` does this. For each subtree branch in the tree there are three cases:\n", - "- If the subtree is a leaf word that is the same as the node's guess, we're done; it took one guess.\n", - "- If the subtree is a leaf word that is not the guess, it took two guesses: one for the incorrect guess and one for the leaf word.\n", - "- If the subtree is a Node, add one to each of the scores from the subtree and yield those scores." - ] - }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 22, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Guess 1: \"wader\" Reply: 3; Consistent targets: 319\n", + "Guess 2: \"sword\" Reply: 1; Consistent targets: 131\n", + "Guess 3: \"paled\" Reply: 4; Consistent targets: 21\n", + "Guess 4: \"abled\" Reply: 3; Consistent targets: 8\n", + "Guess 5: \"paved\" Reply: 4; Consistent targets: 6\n", + "Guess 6: \"caped\" Reply: 4; Consistent targets: 5\n", + "Guess 7: \"paged\" Reply: 4; Consistent targets: 4\n", + "Guess 8: \"adept\" Reply: 4; Consistent targets: 3\n", + "Guess 9: \"paned\" Reply: 4; Consistent targets: 2\n", + "Guess 10: \"payed\" Reply: 4; Consistent targets: 1\n", + "Guess 11: \"amped\" Reply: 5; Consistent targets: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "11" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "def tree_scores(node: Node) -> Iterable[Score]:\n", - " \"\"\"All the scores for playing all the target words in the tree under `node`.\"\"\"\n", - " for subtree in node.branches.values():\n", - " if isinstance(subtree, Word):\n", - " yield 1 if subtree == node.guess else 2\n", - " else:\n", - " yield from (score + 1 for score in tree_scores(subtree))\n", - " \n", - "assert sorted(tree_scores(tree5)) == [1, 2, 2, 3, 4]" + "play(guesser)" ] }, { @@ -980,12 +711,58 @@ "\n", "So far, we have always guessed one of the consistent targets. That seems reasonable; why waste a guess on a word that could not possibly be the target? But it turns out that in some cases it ***is*** a good strategy to guess such a word.\n", "\n", - "I will redefine `minimizing_tree` so that it is passed both the list of remaining consistent target words and the complete word list. It also takes a flag, `inconsistent`. When this flag is true, any word in the wordlist can be considered as a guess; when false, only consistent targets are considered, as before. (Note: when there are 3 or fewer target words left there is no use considering inconsistent guesses, since they cannot improve the average score over a consistent guess.)" + "Consider the branch of `tree22` where the reply to the first guess is 1. There are 5 consistent words remaining, and the max-minimizing tree (which starts by guessing `\"about\"`), can take up to 4 more guesses to find the target:" ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "assert (tree22.branches[1] == \n", + " Node(\"about\", \n", + " {1: Node(\"sword\", \n", + " {2: Node(\"resay\", \n", + " {2: 'nuder', \n", + " 5: 'resay'}), \n", + " 5: 'sword'}), \n", + " 2: 'house', \n", + " 5: 'about'}))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But now consider this tree, which covers the same five words:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "tree5 = Node(\"nerdy\", {0: 'about', \n", + " 1: 'house', \n", + " 2: 'sword', \n", + " 3: 'resay', \n", + " 4: 'nuder'})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first guess, `\"nerdy\"` is inconsistent–it is not one of the five words. This tree sacrifices the 1-in-5 chance of being right on the first guess in order to be assured that the second guess will be correct. So the min, max, mean, and median is 2 guesses. On the whole, this is better than guessing `\"about\"`, which gives a mean of 2.4 and a worst case of 4. \n", + "\n", + "I will redefine `minimizing_tree` so that it is passed both the list of remaining consistent target words and the complete word list. It also takes a flag, `inconsistent`. When this flag is true, any word in the wordlist can be considered as a guess; when false, only consistent targets are considered, as before. (Note: when there are 3 or fewer target words left there is no use considering inconsistent guesses, since they cannot improve the average score over a consistent guess. Also, when there are many targets, the odds are one of them will be as good as any inconsistent guess. The value of *many* was empirically chosen as `inconsistent_max = 400`.)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -994,112 +771,123 @@ " if len(targets) == 1:\n", " return targets[0]\n", " else:\n", - " guesses = wordlist if (inconsistent and len(targets) > 3) else targets\n", + " guesses = wordlist if (inconsistent and 3 < len(targets) <= inconsistent_max) else targets\n", " guess = min(guesses, key=lambda guess: metric(partition_counts(guess, targets))) \n", " branches = partition(guess, targets)\n", " return Node(guess, {reply: minimizing_tree(metric, branches[reply], wordlist, inconsistent) \n", - " for reply in sorted(branches)})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we see that by default, the redefined `minimizing_tree` behaves just as it did before:" + " for reply in sorted(branches)})\n", + " \n", + "inconsistent_max = 400" ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ - "assert minimizing_tree(max, words5) == tree5" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But with `inconsistent=True`, we get a better tree:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Node(guess='dashy', branches={0: 'purge', 1: 'bites', 2: 'sulky', 3: 'patsy', 4: 'hayed'})" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tree5i = minimizing_tree(max, words5, inconsistent=True)\n", - "tree5i" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The first guess by `tree5i` is an inconsistent word, `'dashy'`. There is no chance that this is the target, but it sets us up so that after we get the reply we will always be able to guess correctly on the second guess. So the minimum, mean, median, and maximum number of guesses is 2. \n", + "words5 = ['about', 'house', 'sword', 'resay', 'nuder']\n", "\n", - "In contrast, `tree5` makes only consistent guesses and has a mean of 2.4 guesses and a maximum of 4:" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "median: 2 guesses, mean: 2.40 ± 1.14, worst: 4, scores: 5\n", - "cumulative: ≤3:80%, ≤4:100%, ≤5:100%, ≤6:100%, ≤7:100%, ≤8:100%, ≤9:100%, ≤10:100%\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "report(play(TreeGuesser(tree5), w, words5, False) for w in words5)" + "assert minimizing_tree(max, words5, inconsistent=True) == tree5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "As a more extreme example, consider the following list of 11 words, which differ only in the first letter. A consistent guesser could do no better than to just go through the list one at a time (in any order), and would be equally likely to guess right on any of guesses 1 through 11, for an average of 6:" + "# Evaluating and Reporting on Guessers\n", + "\n", + "To properly evaluate a guesser, a sample of 3 games is not enough to be statistically reliable. I'll introduce three functions that together will evaluate a guesser and produce a report:\n", + "\n", + "- `report_minimizing_tree` builds a minimizing tree, gets its tree scores, and calls `report`.\n", + "- `report` takes a list of scores and reports the following statistics:\n", + " - The median, mean, standard deviation, and worst case number of guesses, and total number of scores.\n", + " - The cumulative percentages guessed correctly (e.g., `\"≤5:11%\"` means 11% of the targets took 5 or fewer guesses).\n", + " - A histogram of scores.\n", + "- `tree_scores` takes a guesser and returns a list of *all* the scores it would make for *all* its targets. For each subtree branch in the tree there are three cases:\n", + " - If the subtree is a leaf word that is the same as the node's guess, we're done; it took one guess.\n", + " - If the subtree is a leaf word that is not the guess, it took two guesses: one for the incorrect guess and one for the leaf word.\n", + " - If the subtree is a Node, add one to each of the scores from the subtree and yield those scores." ] }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "def report_minimizing_tree(metric, targets=wordlist, wordlist=wordlist, inconsistent=False) -> Tree:\n", + " \"\"\"Build a minimizing tree and report on its scores.\"\"\"\n", + " print(f\"minimizing the {metric.__name__} of partition sizes over\",\n", + " f\"{len(targets):,d} targets in a {len(wordlist):,d} word list,\")\n", + " print(f\"{'' if inconsistent else 'not '}including inconsistent words.\")\n", + " tree = minimizing_tree(metric, targets, wordlist, inconsistent)\n", + " print(f'first guess: \"{tree.guess}\"')\n", + " report(tree_scores(tree))\n", + " return tree\n", + "\n", + "def report(scores: Iterable[Score], label='') -> None:\n", + " \"\"\"Report statistics and a histogram for these scores.\"\"\"\n", + " scores = list(scores)\n", + " ctr = Counter(scores)\n", + " bins = range(min(ctr), max(ctr) + 2)\n", + " scale = 100 / len(scores)\n", + " weights = [scale * ctr[score] for score in ctr]\n", + " plt.hist(list(ctr), weights=weights, align='left', rwidth=0.9, bins=bins)\n", + " plt.xticks(bins[:-1])\n", + " plt.xlabel('Number of guesses'); plt.ylabel('% of scores')\n", + " def cumulative_pct(g) -> str: \n", + " \"\"\"What percent of games requires no more than g guesses?\"\"\"\n", + " percent = scale * sum(ctr[i] for i in range(1, g + 1))\n", + " return f'≤{g}:{percent:.{1 if 99 < percent < 100 else 0}f}%'\n", + " print(f'median: {median(scores):.0f} guesses, mean: {mean(scores):.2f}',\n", + " f'± {stdev(scores):.2f}, worst: {max(scores)}, scores: {len(scores):,d}\\n'\n", + " 'cumulative:', ', '.join(map(cumulative_pct, range(3, 11))))\n", + " \n", + "def tree_scores(node: Node) -> Iterable[Score]:\n", + " \"\"\"All the scores for playing all the target words in the tree under `node`.\"\"\"\n", + " for subtree in node.branches.values():\n", + " if isinstance(subtree, Word):\n", + " yield 1 if subtree == node.guess else 2\n", + " else:\n", + " yield from (score + 1 for score in tree_scores(subtree))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To test these functions, and to give another example of inconsistent guessing, consider this list of 11 words:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "ails = 'bails fails hails jails mails nails pails rails tails vails wails'.split()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A consistent guesser could guess them in any order, but wouldn't gain much information from the replies–every reply is either a 5 (which ends the game) or a 4, which leaves you with all the remaining words. Here is the report:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "minimizing the max of partition sizes over 11 targets in a 2,845 word list,\n", + "not including inconsistent words.\n", + "first guess: \"bails\"\n", "median: 6 guesses, mean: 6.00 ± 3.32, worst: 11, scores: 11\n", "cumulative: ≤3:27%, ≤4:36%, ≤5:45%, ≤6:55%, ≤7:64%, ≤8:73%, ≤9:82%, ≤10:91%\n" ] @@ -1118,27 +906,28 @@ } ], "source": [ - "ails = 'bails fails hails jails mails nails pails rails tails vails wails'.split()\n", - "\n", - "report(tree_scores(minimizing_tree(max, ails)))" + "t = report_minimizing_tree(max, ails)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "But an inconsistent guesser can make a first guess that partitions the remaining words, eventually leading to an average of only 4 guesses: " + "But an inconsistent guesser can make a guesses that more evenly partition the remaining words, giving an average of only 4 guesses, and a worst case of 5 guesses: " ] }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "minimizing the max of partition sizes over 11 targets in a 2,845 word list,\n", + "including inconsistent words.\n", + "first guess: \"front\"\n", "median: 4 guesses, mean: 4.00 ± 0.77, worst: 5, scores: 11\n", "cumulative: ≤3:27%, ≤4:73%, ≤5:100%, ≤6:100%, ≤7:100%, ≤8:100%, ≤9:100%, ≤10:100%\n" ] @@ -1157,61 +946,34 @@ } ], "source": [ - "report(tree_scores(minimizing_tree(max, ails, inconsistent=True)))" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [], - "source": [ - "assert (minimizing_tree(max, ails, inconsistent=True) == \n", - " Node(guess='front', \n", - " branches={0: Node(guess='thumb', \n", - " branches={0: Node(guess='power', \n", - " branches={0: Node(guess='jails', \n", - " branches={4: 'vails', \n", - " 5: 'jails'}), \n", - " 1: Node(guess='pails', \n", - " branches={4: 'wails', \n", - " 5: 'pails'})}), \n", - " 1: Node(guess='bails', \n", - " branches={4: Node(guess='hails', \n", - " branches={4: 'mails', \n", - " 5: 'hails'}), \n", - " 5: 'bails'})}), \n", - " 1: Node(guess='their', \n", - " branches={1: Node(guess='fails', \n", - " branches={4: 'nails', \n", - " 5: 'fails'}), \n", - " 2: Node(guess='rails', \n", - " branches={4: 'tails', \n", - " 5: 'rails'})})}))" + "t = report_minimizing_tree(max, ails, inconsistent=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Evaluating Consistent Guessers\n", + "# Reports on Consistent Guessers\n", "\n", - "Here are the evaluations of trees made from minimizing the three metrics, with only consistent guesses allowed:" + "Here are reports on trees made from minimizing the three metrics, with only consistent guesses allowed: " ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "minimizing the max of partition sizes over 2,845 targets in a 2,845 word list,\n", + "not including inconsistent words.\n", + "first guess: \"wader\"\n", "median: 7 guesses, mean: 7.15 ± 1.81, worst: 18, scores: 2,845\n", "cumulative: ≤3:1%, ≤4:4%, ≤5:13%, ≤6:35%, ≤7:67%, ≤8:86%, ≤9:92%, ≤10:95%\n", - "CPU times: user 6.71 s, sys: 17.1 ms, total: 6.73 s\n", - "Wall time: 6.76 s\n" + "CPU times: user 6.73 s, sys: 11.3 ms, total: 6.74 s\n", + "Wall time: 6.75 s\n" ] }, { @@ -1228,22 +990,25 @@ } ], "source": [ - "%time report(tree_scores(minimizing_tree(max, wordlist, inconsistent=False)))" + "%time t = report_minimizing_tree(max, inconsistent=False)" ] }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "minimizing the expectation of partition sizes over 2,845 targets in a 2,845 word list,\n", + "not including inconsistent words.\n", + "first guess: \"raved\"\n", "median: 7 guesses, mean: 7.14 ± 1.82, worst: 17, scores: 2,845\n", "cumulative: ≤3:1%, ≤4:4%, ≤5:13%, ≤6:36%, ≤7:68%, ≤8:85%, ≤9:91%, ≤10:95%\n", - "CPU times: user 6.75 s, sys: 25.6 ms, total: 6.78 s\n", - "Wall time: 6.89 s\n" + "CPU times: user 6.43 s, sys: 5.51 ms, total: 6.44 s\n", + "Wall time: 6.44 s\n" ] }, { @@ -1260,22 +1025,25 @@ } ], "source": [ - "%time report(tree_scores(minimizing_tree(expectation, wordlist, inconsistent=False)))" + "%time t = report_minimizing_tree(expectation, inconsistent=False)" ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "minimizing the neg_entropy of partition sizes over 2,845 targets in a 2,845 word list,\n", + "not including inconsistent words.\n", + "first guess: \"debar\"\n", "median: 7 guesses, mean: 7.09 ± 1.78, worst: 19, scores: 2,845\n", "cumulative: ≤3:1%, ≤4:4%, ≤5:13%, ≤6:36%, ≤7:69%, ≤8:86%, ≤9:92%, ≤10:96%\n", - "CPU times: user 6.68 s, sys: 18.8 ms, total: 6.7 s\n", - "Wall time: 6.73 s\n" + "CPU times: user 6.45 s, sys: 6.68 ms, total: 6.46 s\n", + "Wall time: 6.46 s\n" ] }, { @@ -1292,34 +1060,34 @@ } ], "source": [ - "%time report(tree_scores(minimizing_tree(neg_entropy, wordlist, inconsistent=False)))" + "%time t = report_minimizing_tree(neg_entropy, inconsistent=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We might as well also get an evaluation of the random guesser on every target in the wordlist:" + "The random guesser can also be classified as a consistent guesser. Here is a report on it:|" ] }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "median: 7 guesses, mean: 7.34 ± 1.73, worst: 18, scores: 2,845\n", - "cumulative: ≤3:1%, ≤4:4%, ≤5:12%, ≤6:29%, ≤7:57%, ≤8:80%, ≤9:91%, ≤10:96%\n", - "CPU times: user 6.9 s, sys: 26.7 ms, total: 6.92 s\n", - "Wall time: 6.95 s\n" + "median: 7 guesses, mean: 7.42 ± 1.71, worst: 17, scores: 2,845\n", + "cumulative: ≤3:1%, ≤4:3%, ≤5:11%, ≤6:28%, ≤7:55%, ≤8:77%, ≤9:91%, ≤10:96%\n", + "CPU times: user 6.57 s, sys: 9.57 ms, total: 6.58 s\n", + "Wall time: 6.58 s\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1331,31 +1099,40 @@ } ], "source": [ - "%time report(play(random_guesser, target, verbose=False) for target in wordlist)" + "%time report(play(random_guesser, target, wordlist, verbose=False) for target in wordlist)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Evaluating Inconsistent Guessers\n", - "\n", - "Now we'll build and evaluate trees with inconsistent guesses allowed. This will take longer; about 30 seconds per tree." + "The random consistent guesser strategy might have seemed hopelessly naive, but it is actually a pretty decent strategy, with mean number of guesses only 5% worse than the best minimizing tree. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Reports on Inconsistent Guessers\n", + "Now we'll report on trees with inconsistent guesses allowed. This will take longer; about 30 seconds per tree." ] }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "minimizing the max of partition sizes over 2,845 targets in a 2,845 word list,\n", + "including inconsistent words.\n", + "first guess: \"wader\"\n", "median: 7 guesses, mean: 7.05 ± 0.98, worst: 10, scores: 2,845\n", "cumulative: ≤3:0%, ≤4:1%, ≤5:6%, ≤6:24%, ≤7:69%, ≤8:95%, ≤9:99.9%, ≤10:100%\n", - "CPU times: user 30.5 s, sys: 116 ms, total: 30.7 s\n", - "Wall time: 30.8 s\n" + "CPU times: user 25.9 s, sys: 17.6 ms, total: 25.9 s\n", + "Wall time: 25.9 s\n" ] }, { @@ -1372,22 +1149,25 @@ } ], "source": [ - "%time report(tree_scores(minimizing_tree(max, wordlist, inconsistent=True)))" + "%time t = report_minimizing_tree(max, inconsistent=True)" ] }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "minimizing the expectation of partition sizes over 2,845 targets in a 2,845 word list,\n", + "including inconsistent words.\n", + "first guess: \"raved\"\n", "median: 7 guesses, mean: 6.84 ± 0.95, worst: 10, scores: 2,845\n", "cumulative: ≤3:0%, ≤4:1%, ≤5:7%, ≤6:32%, ≤7:78%, ≤8:97%, ≤9:100.0%, ≤10:100%\n", - "CPU times: user 29.7 s, sys: 39.8 ms, total: 29.7 s\n", - "Wall time: 29.7 s\n" + "CPU times: user 26.3 s, sys: 19.1 ms, total: 26.3 s\n", + "Wall time: 26.3 s\n" ] }, { @@ -1404,22 +1184,25 @@ } ], "source": [ - "%time report(tree_scores(minimizing_tree(expectation, wordlist, inconsistent=True)))" + "%time t = report_minimizing_tree(expectation, inconsistent=True)" ] }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "minimizing the neg_entropy of partition sizes over 2,845 targets in a 2,845 word list,\n", + "including inconsistent words.\n", + "first guess: \"debar\"\n", "median: 7 guesses, mean: 6.82 ± 1.00, worst: 10, scores: 2,845\n", "cumulative: ≤3:0%, ≤4:1%, ≤5:8%, ≤6:35%, ≤7:78%, ≤8:97%, ≤9:99.6%, ≤10:100%\n", - "CPU times: user 30 s, sys: 15.8 ms, total: 30 s\n", - "Wall time: 30 s\n" + "CPU times: user 27.1 s, sys: 24.7 ms, total: 27.2 s\n", + "Wall time: 27.2 s\n" ] }, { @@ -1436,25 +1219,23 @@ } ], "source": [ - "%time report(tree_scores(minimizing_tree(neg_entropy, wordlist, inconsistent=True)))" + "%time t = report_minimizing_tree(neg_entropy, inconsistent=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Jotto Evaluation Summary\n", + "# Report Summary\n", "\n", - "Here's a table of evaluation results for the mean and maximum number of guesses for the various approaches:\n", + "Here's a table of the mean and maximum number of guesses for the seven approaches:\n", "\n", - "|

Algorithm|Consistent
Only
Mean (Max)|Inconsistent
Allowed
Mean (Max)|\n", + "|
2,845 words
Algorithm|Consistent
Only
Mean (Max)|Inconsistent
Allowed
Mean (Max)|\n", "|--|--|--|\n", - "|random guesser|7.34 (18)| |\n", + "|random guesser|7.42 (17)| |\n", "|minimize max|7.15 (18)|7.05 (10)|\n", "|minimize expectation|7.14 (17)|6.84 (10)|\n", - "|minimize neg_entropy|7.09 (19)|6.82 (10)|\n", - "\n", - "(There can be slight variation from run to run.)\n" + "|minimize neg_entropy|7.09 (19)|6.82 (10)|\n" ] }, { @@ -1463,31 +1244,34 @@ "source": [ "# Wordle\n", "\n", - "[Wordle](https://www.powerlanguage.co.uk/wordle/) is a [suddenly-popular](https://www.nytimes.com/2022/01/03/technology/wordle-word-game-creator.html) variant of Jotto (with some Mastermind thrown in) with these differences:\n", - "- Words with repeated letters are allowed, as are anagrams.\n", + "[Wordle](https://www.powerlanguage.co.uk/wordle/) is a [suddenly-popular](https://www.nytimes.com/2022/01/03/technology/wordle-word-game-creator.html) variant of Jotto (with a little Mastermind thrown in) with these differences:\n", + "- Words with repeated letters are allowed (e.g. `'aback'`).\n", + "- Anagrams are allowed (e.g. `'arise'` and `'raise'`).\n", "- The reply to a guess consists of 5 pieces ([trits](https://en.wiktionary.org/wiki/trit#English)) of information, one for each position in the guess:\n", " - *Green* if the guess letter is in the correct spot.\n", " - *Yellow* if the guess letter is in the word but in the wrong spot.\n", " - *Miss* if the letter is not in the word in any spot.\n", + "- Wordle uses a larger list of 12,971 allowable guess words, but only 2,315 of them can be target words. (I think the idea is to follow [Postel's law](https://en.wikipedia.org/wiki/Robustness_principle) to avoid annoying a player: be conservative in the targets (so that a player is very likely to be familiar with the target word) and be liberal in accepting guess words.)\n", " \n", - "Since repeated letters and anagrams are allowed, I can use all of `sgb_words` as my list of allowable Wordle words.\n", - "\n", - "There seems to be an ambiguity in the rules. Assume the guess is *etude* and the target is *poems*. I think the correct reply should be that one letter *e* is *yellow* and the other is a *miss*, although a strict reading of the rules would say they both should be *yellow*, because both instances of *e* are \"in the word but in the wrong spot.\" I decided that in cases like this I would report the first one as yellow and the second as a miss." + "There is an ambiguity in the rules. Assume the guess is *etude* and the target is *poems*. A strict reading of the rules would say they both *e* positions should be *yellow*, because both instances of *e* are \"in the word but in the wrong spot.\" But it seems Wordle actually reports the first *e* as yellow and the second *e* as a miss." ] }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ - "Green, Yellow, Miss = 'GY.' # A reply is 5 characters, each one of 'GY.'\n", + "wordle_small = read_words('wordle-small.txt') # 2,315 target words\n", + "wordle_big = read_words('wordle-big.txt') # 12,971 guess words\n", + "\n", + "Green, Yellow, Miss = 'GY.' # A Wordle reply is 5 characters, each one of 'GY.'\n", "\n", "def wordle_reply_for(guess, target) -> str: \n", " \"The five-character reply for this guess on this target in Wordle.\"\n", " # We'll start by having each reply be either Green or Miss ...\n", " reply = [Green if guess[i] == target[i] else Miss for i in range(5)]\n", - " # ... then we'll put in the replies that should be yellow\n", + " # ... then we'll change the replies that should be yellow\n", " counts = Counter(target[i] for i in range(5) if guess[i] != target[i])\n", " for i in range(5):\n", " if reply[i] == Miss and counts[guess[i]] > 0:\n", @@ -1500,28 +1284,58 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The right thing to do now would be to refactor the code to allow for the injection of a different `reply_for` function. However, I'm not going to do that; instead I'm going to \"cheat\" and just redefine `reply_for` to be `wordle_reply_for`. So if you want to go back in this notebook and re-run some Jotto cells, you'll have to re-run the \"`def reply_for`\" cell first." + "The right thing to do now would be to refactor the code to allow for the injection of a different `reply_for` function, so that either game could be played at any time.\n", + "\n", + "However, I'm going to take a shortcut: I'm going to require the programmer to call `setup` with the name of the game they want, `'jotto'` or `'wordle'`. It will set global variables accordingly. I'll also require the programmer to use the right word list and target words." ] }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ - "reply_for = wordle_reply_for" + "jotto_reply_for = reply_for" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [], + "source": [ + "def setup(game: str) -> None:\n", + " \"Set global variables to play either 'jotto' or 'wordle'.\"\n", + " global reply_for, inconsistent_max\n", + " if game == 'jotto':\n", + " reply_for = jotto_reply_for\n", + " inconsistent_max = 400\n", + " elif game == 'wordle':\n", + " reply_for = wordle_reply_for\n", + " inconsistent_max = 125\n", + " else:\n", + " raise ValueError(f'unknown game: {game}')" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [], + "source": [ + "setup('wordle')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Note that in Jotto, `reply_for` was symmetric; `reply_for(g, t) == reply_for(t, g)`. But that is not true for Wordle. (I had a bug somewhere in my code that showed up when playing Wordle but not Jotto, and I thought it might be because I had inadvertently reversed arguments to `reply_for` somewhere (but it turned out to be that I had left out a `wordlist=sgb_words`).) Anyway, here are some tests for `wordle_reply_for`:" + "Note that in Jotto, `reply_for` was symmetric; `reply_for(g, t) == reply_for(t, g)`. But that is not true for Wordle. I made some tests for `wordle_reply_for` to give me some confidence I got it right:" ] }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 42, "metadata": {}, "outputs": [], "source": [ @@ -1543,7 +1357,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 43, "metadata": {}, "outputs": [ { @@ -1569,7 +1383,7 @@ " 'G.G.G': ['while']})" ] }, - "execution_count": 47, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -1586,7 +1400,138 @@ "\n", "# Sample Wordle Games\n", "\n", - "Let's see what some Wordle games with a random guesser looks like:" + "Let's see what some Wordle games look like, using a random guesser:" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Guess 1: \"pluck\" Reply: .Y...; Consistent targets: 269\n", + "Guess 2: \"false\" Reply: ..G.Y; Consistent targets: 9\n", + "Guess 3: \"below\" Reply: GGGGG; Consistent targets: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "play(random_guesser, wordlist=wordle_small)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Guess 1: \"caddy\" Reply: .....; Consistent targets: 729\n", + "Guess 2: \"rupee\" Reply: Y....; Consistent targets: 62\n", + "Guess 3: \"snort\" Reply: G.GGY; Consistent targets: 2\n", + "Guess 4: \"stork\" Reply: GGGG.; Consistent targets: 1\n", + "Guess 5: \"storm\" Reply: GGGGG; Consistent targets: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "play(random_guesser, wordlist=wordle_small)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Guess 1: \"wiser\" Reply: .G...; Consistent targets: 73\n", + "Guess 2: \"ninth\" Reply: .GG..; Consistent targets: 9\n", + "Guess 3: \"final\" Reply: .GG.G; Consistent targets: 1\n", + "Guess 4: \"vinyl\" Reply: GGGGG; Consistent targets: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "play(random_guesser, wordlist=wordle_small)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Reports on Wordle Guessers\n", + "\n", + "Wordle has about the same number of target words as Jotto, but many more guess words, and the `wordle_reply_for` computation is more complex, so computations take longer (more so for the inconsistent guessers). Here are reports on the seven strategies:" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "median: 4 guesses, mean: 4.10 ± 1.04, worst: 8, scores: 2,315\n", + "cumulative: ≤3:28%, ≤4:69%, ≤5:92%, ≤6:98%, ≤7:99.4%, ≤8:100%, ≤9:100%, ≤10:100%\n", + "CPU times: user 30.1 s, sys: 29.3 ms, total: 30.1 s\n", + "Wall time: 30.2 s\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "%time report(play(random_guesser, target, wordle_small, verbose=False) for target in wordle_small)" ] }, { @@ -1598,26 +1543,30 @@ "name": "stdout", "output_type": "stream", "text": [ - "Guess 1: \"guess\" Reply: .....; Consistent targets: 954\n", - "Guess 2: \"batch\" Reply: .Y...; Consistent targets: 114\n", - "Guess 3: \"villa\" Reply: ....Y; Consistent targets: 26\n", - "Guess 4: \"drank\" Reply: .YYY.; Consistent targets: 3\n", - "Guess 5: \"apron\" Reply: GGGGG; Consistent targets: 1\n" + "minimizing the max of partition sizes over 2,315 targets in a 12,971 word list,\n", + "not including inconsistent words.\n", + "first guess: \"arise\"\n", + "median: 4 guesses, mean: 3.68 ± 0.86, worst: 8, scores: 2,315\n", + "cumulative: ≤3:43%, ≤4:87%, ≤5:97%, ≤6:99.4%, ≤7:99.9%, ≤8:100%, ≤9:100%, ≤10:100%\n", + "CPU times: user 29 s, sys: 36.4 ms, total: 29 s\n", + "Wall time: 29 s\n" ] }, { "data": { + "image/png": "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\n", "text/plain": [ - "5" + "
" ] }, - "execution_count": 48, - "metadata": {}, - "output_type": "execute_result" + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" } ], "source": [ - "play(random_guesser, wordlist=sgb_words)" + "%time t = report_minimizing_tree(max, wordle_small, wordle_big, inconsistent=False)" ] }, { @@ -1629,25 +1578,30 @@ "name": "stdout", "output_type": "stream", "text": [ - "Guess 1: \"nurse\" Reply: .G.G.; Consistent targets: 8\n", - "Guess 2: \"gutsy\" Reply: .G.GG; Consistent targets: 5\n", - "Guess 3: \"pussy\" Reply: .GGGG; Consistent targets: 3\n", - "Guess 4: \"hussy\" Reply: GGGGG; Consistent targets: 1\n" + "minimizing the expectation of partition sizes over 2,315 targets in a 12,971 word list,\n", + "not including inconsistent words.\n", + "first guess: \"raise\"\n", + "median: 4 guesses, mean: 3.62 ± 0.86, worst: 8, scores: 2,315\n", + "cumulative: ≤3:47%, ≤4:88%, ≤5:98%, ≤6:99.4%, ≤7:99.9%, ≤8:100%, ≤9:100%, ≤10:100%\n", + "CPU times: user 29.2 s, sys: 25.8 ms, total: 29.2 s\n", + "Wall time: 29.2 s\n" ] }, { "data": { + "image/png": "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\n", "text/plain": [ - "4" + "
" ] }, - "execution_count": 49, - "metadata": {}, - "output_type": "execute_result" + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" } ], "source": [ - "play(random_guesser, wordlist=sgb_words)" + "%time t = report_minimizing_tree(expectation, wordle_small, wordle_big, inconsistent=False)" ] }, { @@ -1659,35 +1613,37 @@ "name": "stdout", "output_type": "stream", "text": [ - "Guess 1: \"dicey\" Reply: .Y.Y.; Consistent targets: 147\n", - "Guess 2: \"sepia\" Reply: .G.G.; Consistent targets: 14\n", - "Guess 3: \"kefir\" Reply: .G.GY; Consistent targets: 4\n", - "Guess 4: \"remit\" Reply: GGGG.; Consistent targets: 1\n", - "Guess 5: \"remix\" Reply: GGGGG; Consistent targets: 1\n" + "minimizing the neg_entropy of partition sizes over 2,315 targets in a 12,971 word list,\n", + "not including inconsistent words.\n", + "first guess: \"raise\"\n", + "median: 4 guesses, mean: 3.60 ± 0.85, worst: 8, scores: 2,315\n", + "cumulative: ≤3:49%, ≤4:89%, ≤5:97%, ≤6:99.5%, ≤7:99.9%, ≤8:100%, ≤9:100%, ≤10:100%\n", + "CPU times: user 29.1 s, sys: 25.4 ms, total: 29.2 s\n", + "Wall time: 29.2 s\n" ] }, { "data": { + "image/png": "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\n", "text/plain": [ - "5" + "
" ] }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" } ], "source": [ - "play(random_guesser, wordlist=sgb_words)" + "%time t = report_minimizing_tree(neg_entropy, wordle_small, wordle_big, inconsistent=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Wordle Strategies\n", - "\n", - "With the bigger `sgb_words` word list, computations take longer, so I'm only going to compare the random guesser with the two negative entropy minimizing guessers. This will take 15 minutes or so:" + "The **inconsistent guessers** will each take about 4 minutes to compute:" ] }, { @@ -1699,15 +1655,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "median: 4 guesses, mean: 4.63 ± 1.34, worst: 12, scores: 5,756\n", - "cumulative: ≤3:18%, ≤4:51%, ≤5:79%, ≤6:92%, ≤7:97%, ≤8:99%, ≤9:99.6%, ≤10:99.8%\n", - "CPU times: user 3min 6s, sys: 218 ms, total: 3min 7s\n", - "Wall time: 3min 7s\n" + "minimizing the max of partition sizes over 2,315 targets in a 12,971 word list,\n", + "including inconsistent words.\n", + "first guess: \"arise\"\n", + "median: 4 guesses, mean: 3.62 ± 0.64, worst: 6, scores: 2,315\n", + "cumulative: ≤3:42%, ≤4:94%, ≤5:99.7%, ≤6:100%, ≤7:100%, ≤8:100%, ≤9:100%, ≤10:100%\n", + "CPU times: user 4min 3s, sys: 193 ms, total: 4min 3s\n", + "Wall time: 4min 7s\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1719,7 +1678,7 @@ } ], "source": [ - "%time report(play(random_guesser, target, sgb_words, verbose=False) for target in sgb_words)" + "%time t = report_minimizing_tree(max, wordle_small, wordle_big, inconsistent=True)" ] }, { @@ -1731,15 +1690,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 3min 3s, sys: 119 ms, total: 3min 3s\n", - "Wall time: 3min 6s\n", - "median: 4 guesses, mean: 4.09 ± 1.20, worst: 12, scores: 5,756\n", - "cumulative: ≤3:32%, ≤4:73%, ≤5:89%, ≤6:95%, ≤7:98%, ≤8:99.4%, ≤9:99.8%, ≤10:99.9%\n" + "minimizing the expectation of partition sizes over 2,315 targets in a 12,971 word list,\n", + "including inconsistent words.\n", + "first guess: \"raise\"\n", + "median: 4 guesses, mean: 3.52 ± 0.62, worst: 6, scores: 2,315\n", + "cumulative: ≤3:50%, ≤4:96%, ≤5:99.8%, ≤6:100%, ≤7:100%, ≤8:100%, ≤9:100%, ≤10:100%\n", + "CPU times: user 4min 47s, sys: 144 ms, total: 4min 47s\n", + "Wall time: 4min 58s\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1751,8 +1713,7 @@ } ], "source": [ - "%time wtree = minimizing_tree(neg_entropy, sgb_words, sgb_words, inconsistent=False)\n", - "report(tree_scores(wtree))" + "%time t = report_minimizing_tree(expectation, wordle_small, wordle_big, inconsistent=True)" ] }, { @@ -1764,15 +1725,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 8min 17s, sys: 420 ms, total: 8min 18s\n", - "Wall time: 8min 21s\n", - "median: 4 guesses, mean: 3.82 ± 0.71, worst: 7, scores: 5,756\n", - "cumulative: ≤3:32%, ≤4:87%, ≤5:98%, ≤6:99.9%, ≤7:100%, ≤8:100%, ≤9:100%, ≤10:100%\n" + "minimizing the neg_entropy of partition sizes over 2,315 targets in a 12,971 word list,\n", + "including inconsistent words.\n", + "first guess: \"raise\"\n", + "median: 3 guesses, mean: 3.50 ± 0.62, worst: 6, scores: 2,315\n", + "cumulative: ≤3:52%, ≤4:96%, ≤5:99.7%, ≤6:100%, ≤7:100%, ≤8:100%, ≤9:100%, ≤10:100%\n", + "CPU times: user 4min 26s, sys: 147 ms, total: 4min 26s\n", + "Wall time: 4min 34s\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1784,35 +1748,25 @@ } ], "source": [ - "%time wtree = minimizing_tree(neg_entropy, sgb_words, sgb_words, inconsistent=True)\n", - "report(tree_scores(wtree))" + "%time t = report_minimizing_tree(neg_entropy, wordle_small, wordle_big, inconsistent=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Pretty good! The Wordle web site challenges you to solve each puzzle in six guesses; we can now do that 99.9% of the time when inconsistent guesses are allowed, a big jump from the 95% without inconsistent guesses and the 92% with random consistent guesses. \n", - "\n", - "This is all on the `sgb-words.txt` file. I poked around in the Wordle javascript, trying to find the official Wordle word list. If I correctly interpreted what I found, my algorithm gets these results:\n", - "\n", - " median: 3 guesses, mean: 3.49 ± 0.60, worst: 6, scores: 2,315\n", - " cumulative: ≤3:53%, ≤4:96%, ≤5:99.8%, ≤6:100%, ≤7:100%, ≤8:100%, ≤9:100%, ≤10:100%\n", - " \n", - "I won't post the word list here, because I don't have the author's permission.\n", + "**Mission accomplished!** All three metrics solve every target word in 6 guesses or less. \n", "\n", "# Jotto and Wordle Evaluation Summary\n", "\n", - "Here is a summary (the first four columns on `sgb-words.txt`, the last on the Wordle word list):\n", + "Here is a summary of the reports on both games:\n", "\n", - "|


Algorithm|JOTTO
Consistent
Only
Mean (Max)|JOTTO
Inconsistent
Allowed
Mean (Max)|WORDLE
Consistent
Only
Mean (Max)|WORDLE
Inconsistent
Allowed
Mean (Max)|WORDLE
Official
Wordlist
Mean (Max)|\n", + "|


Algorithm|JOTTO
Consistent
Only
Mean (Max)|JOTTO
Inconsistent
Allowed
Mean (Max)|WORDLE
Consistent
Only
Mean (Max)|WORDLE
Inconsistent
Allowed
Mean (Max)||\n", "|--|--|--|--|--|--|\n", - "|random guesser|7.34 (18)| ––––––– |4.63 (12) | ––––––– | 4.08 (8) |\n", - "|minimize max|7.15 (18)|7.05 (10)| ––––––– | ––––––– | ––––––– | ––––––– |\n", - "|minimize expectation|7.14 (17)|6.84 (10)| ––––––– | ––––––– | ––––––– | ––––––– |\n", - "|minimize neg_entropy|7.09 (19)|6.82 (10)| 4.09 (12) | 3.82 (7) | 3.49 (6) |\n", - "\n", - "\n", + "|random guesser|7.34 (18)| |4.10 (8) | |\n", + "|minimize max|7.15 (18)|7.05 (10)| 3.68 (8) | 3.62 (6) | \n", + "|minimize expectation|7.14 (17)|6.84 (10)| 3.62 (8) | 3.52 (6) | \n", + "|minimize neg_entropy|7.09 (19)|6.82 (10)| 3.60 (8) | 3.50 (6) |\n", "\n", "\n", "# Sample Wordle Games with Minimizing Guesser\n", @@ -1829,56 +1783,73 @@ "name": "stdout", "output_type": "stream", "text": [ - "Guess 1: \"tares\" Reply: YY..G; Consistent targets: 31\n", - "Guess 2: \"sloth\" Reply: G..G.; Consistent targets: 4\n", - "Guess 3: \"which\" Reply: .....; Consistent targets: 2\n", - "Guess 4: \"spats\" Reply: G.GGG; Consistent targets: 1\n", - "Guess 5: \"stats\" Reply: GGGGG; Consistent targets: 1\n" + "Guess 1: \"raise\" Reply: ...YY; Consistent targets: 797\n", + "Guess 2: \"spelt\" Reply: G.GG.; Consistent targets: 11\n", + "Guess 3: \"fehme\" Reply: .Y...; Consistent targets: 3\n", + "Guess 4: \"swell\" Reply: GGGGG; Consistent targets: 1\n", + "\n", + "Guess 1: \"raise\" Reply: YY...; Consistent targets: 297\n", + "Guess 2: \"tronc\" Reply: .G..Y; Consistent targets: 12\n", + "Guess 3: \"calmy\" Reply: GY..G; Consistent targets: 2\n", + "Guess 4: \"crazy\" Reply: GGGGG; Consistent targets: 1\n", + "\n", + "Guess 1: \"raise\" Reply: YY...; Consistent targets: 297\n", + "Guess 2: \"tronc\" Reply: .GY..; Consistent targets: 8\n", + "Guess 3: \"abram\" Reply: G.G..; Consistent targets: 2\n", + "Guess 4: \"arrow\" Reply: GGGGG; Consistent targets: 1\n", + "\n", + "Guess 1: \"raise\" Reply: Y.G..; Consistent targets: 75\n", + "Guess 2: \"crypt\" Reply: .G.Y.; Consistent targets: 7\n", + "Guess 3: \"primo\" Reply: GGGGG; Consistent targets: 1\n", + "\n", + "Guess 1: \"raise\" Reply: ..YY.; Consistent targets: 343\n", + "Guess 2: \"shout\" Reply: Y.Y..; Consistent targets: 46\n", + "Guess 3: \"bison\" Reply: .GGY.; Consistent targets: 6\n", + "Guess 4: \"disco\" Reply: GGGGG; Consistent targets: 1\n", + "\n" ] - }, - { - "data": { - "text/plain": [ - "5" - ] - }, - "execution_count": 54, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ - "play(TreeGuesser(wtree), wordlist=sgb_words)" + "for target in random.sample(wordle_small, 5):\n", + " play(TreeGuesser(t), target, wordle_big)\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Best and Worst First Guess Words\n", + "\n", + "Here's a function to create a table of the best and worst first guesses in a game (that is, the highest and lowest scoring words on the partition counts), according to each of the three metrics:" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Guess 1: \"tares\" Reply: ..GY.; Consistent targets: 39\n", - "Guess 2: \"peony\" Reply: .YY..; Consistent targets: 5\n", - "Guess 3: \"fight\" Reply: G.Y..; Consistent targets: 1\n", - "Guess 4: \"forge\" Reply: GGGGG; Consistent targets: 1\n" - ] - }, - { - "data": { - "text/plain": [ - "4" - ] - }, - "execution_count": 55, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "play(TreeGuesser(wtree), wordlist=sgb_words)" + "import pandas as pd\n", + "\n", + "def first_guesses(targets, wordlist) -> pd.DataFrame: \n", + " \"\"\"A data frame of words and scores on the 3 metrics, sorted best to worst.\"\"\"\n", + " metrics = (max, expectation, neg_entropy)\n", + " data = [sorted((metric(partition_counts(g, targets)), g) for g in wordlist)\n", + " for metric in metrics]\n", + " def reformat(row):\n", + " (val1, word1), (val2, word2), (val3, word3) = row\n", + " return [word1, val1, word2, round(val2, 2), word3, round(val3, 3)]\n", + " return pd.DataFrame(map(reformat, zip(*data)), \n", + " columns='max_word max exp_word exp ent_word ent'.split())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The best and worst first guesses for **Jotto**:" ] }, { @@ -1890,17 +1861,159 @@ "name": "stdout", "output_type": "stream", "text": [ - "Guess 1: \"tares\" Reply: YY.YY; Consistent targets: 20\n", - "Guess 2: \"stalk\" Reply: YYG..; Consistent targets: 3\n", - "Guess 3: \"feast\" Reply: .GGGG; Consistent targets: 2\n", - "Guess 4: \"beast\" Reply: .GGGG; Consistent targets: 1\n", - "Guess 5: \"yeast\" Reply: GGGGG; Consistent targets: 1\n" + "CPU times: user 13.9 s, sys: 7.46 ms, total: 13.9 s\n", + "Wall time: 13.9 s\n" ] }, { "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
max_wordmaxexp_wordexpent_wordent
0wader1012raved812.84debar-1.949
1cadre1026debar818.32alder-1.946
2armed1028roved826.51raved-1.944
3diner1029orbed826.71dater-1.943
4coder1030wader827.04cadre-1.943
.....................
2840coqui1535okapi1105.28jumpy-1.575
2841quota1548azoic1120.19juicy-1.566
2842azoic1555axiom1157.49axiom-1.563
2843axiom1615audio1184.28audio-1.494
2844ouija1848ouija1413.00ouija-1.292
\n", + "

2845 rows × 6 columns

\n", + "
" + ], "text/plain": [ - "5" + " max_word max exp_word exp ent_word ent\n", + "0 wader 1012 raved 812.84 debar -1.949\n", + "1 cadre 1026 debar 818.32 alder -1.946\n", + "2 armed 1028 roved 826.51 raved -1.944\n", + "3 diner 1029 orbed 826.71 dater -1.943\n", + "4 coder 1030 wader 827.04 cadre -1.943\n", + "... ... ... ... ... ... ...\n", + "2840 coqui 1535 okapi 1105.28 jumpy -1.575\n", + "2841 quota 1548 azoic 1120.19 juicy -1.566\n", + "2842 azoic 1555 axiom 1157.49 axiom -1.563\n", + "2843 axiom 1615 audio 1184.28 audio -1.494\n", + "2844 ouija 1848 ouija 1413.00 ouija -1.292\n", + "\n", + "[2845 rows x 6 columns]" ] }, "execution_count": 56, @@ -1909,16 +2022,18 @@ } ], "source": [ - "play(TreeGuesser(wtree), wordlist=sgb_words)" + "setup('jotto')\n", + "%time df = first_guesses(wordlist, wordlist)\n", + "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Best and Worst First Guesses for Wordle\n", + "The top guesses favor the letters \"a\", \"d\", \"e\", and \"r\". \n", "\n", - "This will take about 10 minutes:" + "The word \"ouija\" is a uniquely terrible guess; mostly what it does is confirm that a majority of the words have exactly one of the vowels \"aiou\". The output above only shows the top 5 guesses; here are the top 20:" ] }, { @@ -1927,69 +2042,697 @@ "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "max | expectation | neg_entropy\n", - "----------- + ----------- + -----------\n", - "aloes 298 | tares 133 | tares -6.21\n", - "arose 361 | rates 136 | rates -6.12\n", - "aides 383 | aloes 137 | tales -6.10\n", - "stoae 383 | tales 140 | tears -6.06\n", - "earls 393 | nares 140 | nares -6.02\n", - "reals 393 | saner 142 | tires -6.02\n", - "earns 395 | lores 144 | reals -6.00\n", - "nares 395 | reals 144 | dares -5.99\n", - "nears 395 | roles 145 | lores -5.99\n", - "saner 395 | lanes 148 | tries -5.99\n", - "snare 395 | tears 152 | teals -5.98\n", - "ideas 398 | riles 153 | saner -5.98\n", - "lanes 403 | earls 154 | pares -5.97\n", - "antes 409 | cares 155 | cares -5.97\n", - "nates 409 | nates 156 | roles -5.97\n", - "sepia 409 | laser 156 | aloes -5.96\n", - "aisle 410 | dares 157 | lanes -5.96\n", - "rates 415 | raise 157 | taels -5.95\n", - "stare 415 | races 159 | slate -5.92\n", - "tares 415 | teals 159 | riles -5.91\n", - "----------- + ----------- + -----------\n", - "jiffy 3158 | jiffy 1923 | oxbow -2.52\n", - "oxbow 3173 | whooo 1941 | quiff -2.52\n", - "duddy 3179 | oxbow 1954 | fizzy -2.50\n", - "whizz 3202 | boffo 1959 | jiffy -2.49\n", - "pffft 3208 | whizz 1978 | mummy -2.47\n", - "fluff 3216 | vivid 1986 | civic -2.47\n", - "whooo 3230 | fluff 1997 | mamma -2.46\n", - "boffo 3233 | pffft 1999 | yummy -2.45\n", - "vivid 3262 | kudzu 2016 | whizz -2.44\n", - "kudzu 3264 | puppy 2074 | fluff -2.43\n", - "civic 3348 | civic 2080 | kudzu -2.42\n", - "puppy 3361 | ahhhh 2114 | buzzy -2.40\n", - "mummy 3428 | mummy 2152 | jazzy -2.35\n", - "yummy 3428 | yummy 2154 | pffft -2.35\n", - "buzzy 3448 | buzzy 2181 | fuzzy -2.28\n", - "ohhhh 3507 | fuzzy 2296 | yukky -2.25\n", - "fuzzy 3542 | yukky 2328 | gyppy -2.25\n", - "immix 3543 | immix 2398 | immix -2.08\n", - "yukky 3569 | gyppy 2401 | ahhhh -2.05\n", - "gyppy 3636 | ohhhh 2476 | ohhhh -1.85\n", - "CPU times: user 8min 36s, sys: 396 ms, total: 8min 36s\n", - "Wall time: 8min 36s\n" - ] + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
max_wordmaxexp_wordexpent_wordent
0wader1012raved812.84debar-1.949
1cadre1026debar818.32alder-1.946
2armed1028roved826.51raved-1.944
3diner1029orbed826.71dater-1.943
4coder1030wader827.04cadre-1.943
5padre1035armed827.28armed-1.939
6raved1038fader828.28garde-1.938
7rayed1038dater829.41wader-1.938
8delta1039alder829.96lased-1.935
9drone1041cadre829.98padre-1.934
10eland1043garde830.47fader-1.934
11garde1043padre831.76dears-1.933
12heard1044deign832.29drone-1.931
13tired1044gored833.68diner-1.931
14debar1046laved834.20rayed-1.931
15fader1048rayed836.51tired-1.929
16ailed1050drone836.74orbed-1.927
17orbed1052oared836.91roved-1.925
18blear1054dower837.57coder-1.924
19raths1054coder838.35abled-1.924
\n", + "
" + ], + "text/plain": [ + " max_word max exp_word exp ent_word ent\n", + "0 wader 1012 raved 812.84 debar -1.949\n", + "1 cadre 1026 debar 818.32 alder -1.946\n", + "2 armed 1028 roved 826.51 raved -1.944\n", + "3 diner 1029 orbed 826.71 dater -1.943\n", + "4 coder 1030 wader 827.04 cadre -1.943\n", + "5 padre 1035 armed 827.28 armed -1.939\n", + "6 raved 1038 fader 828.28 garde -1.938\n", + "7 rayed 1038 dater 829.41 wader -1.938\n", + "8 delta 1039 alder 829.96 lased -1.935\n", + "9 drone 1041 cadre 829.98 padre -1.934\n", + "10 eland 1043 garde 830.47 fader -1.934\n", + "11 garde 1043 padre 831.76 dears -1.933\n", + "12 heard 1044 deign 832.29 drone -1.931\n", + "13 tired 1044 gored 833.68 diner -1.931\n", + "14 debar 1046 laved 834.20 rayed -1.931\n", + "15 fader 1048 rayed 836.51 tired -1.929\n", + "16 ailed 1050 drone 836.74 orbed -1.927\n", + "17 orbed 1052 oared 836.91 roved -1.925\n", + "18 blear 1054 dower 837.57 coder -1.924\n", + "19 raths 1054 coder 838.35 abled -1.924" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "%time partition_scores(20, wordlist=sgb_words)" + "df[:20]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The best and worst first guesses for **Wordle**:" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 9min 17s, sys: 291 ms, total: 9min 17s\n", + "Wall time: 9min 36s\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
max_wordmaxexp_wordexpent_wordent
0aesir168roate60.42soare-5.886
1arise168raise61.00roate-5.883
2raise168raile61.33raise-5.878
3reais168soare62.30raile-5.866
4serai168arise63.73reast-5.865
.....................
12966susus1369gyppy885.90yukky-2.205
12967fuffy1374jugum900.61xylyl-2.192
12968gyppy1380jujus903.31immix-2.053
12969jugum1410qajaq925.11jujus-2.038
12970immix1429immix967.40qajaq-1.892
\n", + "

12971 rows × 6 columns

\n", + "
" + ], + "text/plain": [ + " max_word max exp_word exp ent_word ent\n", + "0 aesir 168 roate 60.42 soare -5.886\n", + "1 arise 168 raise 61.00 roate -5.883\n", + "2 raise 168 raile 61.33 raise -5.878\n", + "3 reais 168 soare 62.30 raile -5.866\n", + "4 serai 168 arise 63.73 reast -5.865\n", + "... ... ... ... ... ... ...\n", + "12966 susus 1369 gyppy 885.90 yukky -2.205\n", + "12967 fuffy 1374 jugum 900.61 xylyl -2.192\n", + "12968 gyppy 1380 jujus 903.31 immix -2.053\n", + "12969 jugum 1410 qajaq 925.11 jujus -2.038\n", + "12970 immix 1429 immix 967.40 qajaq -1.892\n", + "\n", + "[12971 rows x 6 columns]" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "setup('wordle')\n", + "%time df2 = first_guesses(wordle_small, wordle_big)\n", + "df2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Again, the best words use popular letters. One difference is that in Jotto, the best guesses have two vowels, in Wordle three. The worst words have repeated unpopular letters, like \"qajaq\" and \"yukky\". Here are the top 20 guesses:" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
max_wordmaxexp_wordexpent_wordent
0aesir168roate60.42soare-5.886
1arise168raise61.00roate-5.883
2raise168raile61.33raise-5.878
3reais168soare62.30raile-5.866
4serai168arise63.73reast-5.865
5aiery171irate63.78slate-5.856
6ayrie171orate63.89crate-5.835
7ariel173ariel65.29salet-5.835
8raile173arose66.02irate-5.831
9aloes174raine67.06trace-5.831
10realo176artel67.50arise-5.821
11stoae177taler67.74orate-5.817
12alone182ratel69.84stare-5.807
13anole182aesir69.88carte-5.795
14aeros183arles69.89raine-5.787
15arose183realo69.95caret-5.777
16soare183alter69.99ariel-5.775
17oiler186saner70.13taler-5.771
18oriel186later70.22carle-5.770
19reoil186snare71.10slane-5.770
\n", + "
" + ], + "text/plain": [ + " max_word max exp_word exp ent_word ent\n", + "0 aesir 168 roate 60.42 soare -5.886\n", + "1 arise 168 raise 61.00 roate -5.883\n", + "2 raise 168 raile 61.33 raise -5.878\n", + "3 reais 168 soare 62.30 raile -5.866\n", + "4 serai 168 arise 63.73 reast -5.865\n", + "5 aiery 171 irate 63.78 slate -5.856\n", + "6 ayrie 171 orate 63.89 crate -5.835\n", + "7 ariel 173 ariel 65.29 salet -5.835\n", + "8 raile 173 arose 66.02 irate -5.831\n", + "9 aloes 174 raine 67.06 trace -5.831\n", + "10 realo 176 artel 67.50 arise -5.821\n", + "11 stoae 177 taler 67.74 orate -5.817\n", + "12 alone 182 ratel 69.84 stare -5.807\n", + "13 anole 182 aesir 69.88 carte -5.795\n", + "14 aeros 183 arles 69.89 raine -5.787\n", + "15 arose 183 realo 69.95 caret -5.777\n", + "16 soare 183 alter 69.99 ariel -5.775\n", + "17 oiler 186 saner 70.13 taler -5.771\n", + "18 oriel 186 later 70.22 carle -5.770\n", + "19 reoil 186 snare 71.10 slane -5.770" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df2[:20]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The best words use popular letters, especially \"e\", \"s\", \"a\", \"r\", \"l\", \"t\".\n", - "\n", - "The worst words have repeated unpopular letters, like \"zz\" and \"yukky\".\n", - "\n", "# Next Steps\n", "\n", "There are many directions you could take this if you are interested:\n", @@ -2001,10 +2744,14 @@ "- **Better strategy**:\n", " - Our `minimizing_tree` function is **greedy** in that it guesses the word that minimizes some metric of the current situation without looking ahead to future branches in the tree. Can you get better performance by doing some **look-ahead**? Perhaps with a beam search?\n", " - As an alternative to look-ahead, can you improve a tree by editing it? Given a tree, look for interior nodes that end up with a worse-than-expected average score, and see if the node can be replaced with something better (covering the same target words). Correcting a few bad nodes might be faster than carefully searching for good nodes in the first place.\n", - " - The three metrics (max, expectation, and negative entropy) are all designed as proxies to what we really want to minimize: the average number of guesses. Can we estimate that directly? For example, we know a branch of size 1 will always take 1 guess; of size 2 an average of 3/2 guesses; and of size 3 an average of 5/3 guesses if one of the words partitions the other two, otherwise an average of 2. Can we learn a function that takes a set of words as input and estimates the average number of guesses for the set?\n", + " - The three metrics (max, expectation, and negative entropy) are all designed as proxies to what we really want to minimize: the average number of guesses. Can we estimate that directly? For example, we know a branch of size 1 will always take 1 guess; of size 2 an average of 3/2 guesses; and of size 3 an average of 5/3 guesses if one of the words partitions the other two, otherwise an average of 2. Can we learn a function that takes a set of words as input and estimates the average number of guesses for the set? Would a deep neural net be a good way to learn this function?\n", " - Is it feasible to do a complete search and find the guaranteed optimal strategy? What optimizations to the code would be necessary? How long would the search take?\n", "- **Chooser strategy**:\n", - " - Analyze the game where the chooser is not random, but rather is an adversary to the guesser–the chooser tries to choose a word that will maximize the guesser's score. What's a good strategy for the chooser? Is there a strategy equilibrium? (One thing I thought of is to choose a word like one of the `ails` words that has many other words that are off by one letter.) \n" + " - Analyze the game where the chooser is not random, but rather is an adversary to the guesser–the chooser tries to choose a word that will maximize the guesser's score. What's a good strategy for the chooser? Is there a strategy equilibrium?\n", + " - Refactor `play` to accomodate three roles:\n", + " - A chooser, who decides what the target word is.\n", + " - A guesser, who guesses the target word.\n", + " - A replier, who says what the reply to the guess is." ] } ], diff --git a/ipynb/wordle-big.txt b/ipynb/wordle-big.txt new file mode 100644 index 0000000..dbdff5f --- /dev/null +++ b/ipynb/wordle-big.txt @@ -0,0 +1,12971 @@ +aalii +aargh +aarti +abaca +abaci +aback +abacs +abaft +abaka +abamp +aband +abase +abash +abask +abate +abaya +abbas +abbed +abbes +abbey +abbot +abcee +abeam +abear +abele +abers +abets +abhor +abide +abies +abled +abler +ables +ablet +ablow +abmho +abode +abohm +aboil +aboma +aboon +abord +abore +abort +about +above +abram +abray +abrim +abrin +abris +absey +absit +abuna +abune +abuse +abuts +abuzz +abyes +abysm +abyss +acais +acari +accas +accoy +acerb +acers +aceta +achar +ached +aches +achoo +acids +acidy +acing +acini +ackee +acker +acmes +acmic +acned +acnes +acock +acold +acorn +acred +acres +acrid +acros +acted +actin +acton +actor +acute +acyls +adage +adapt +adaws +adays +adbot +addax +added +adder +addio +addle +adeem +adept +adhan +adieu +adios +adits +adman +admen +admin +admit +admix +adobe +adobo +adopt +adore +adorn +adown +adoze +adrad +adred +adsum +aduki +adult +adunc +adust +advew +adyta +adzed +adzes +aecia +aedes +aegis +aeons +aerie +aeros +aesir +afald +afara +afars +afear +affix +afire +aflaj +afoot +afore +afoul +afrit +afros +after +again +agama +agami +agape +agars +agast +agate +agave +agaze +agene +agent +agers +agger +aggie +aggri +aggro +aggry +aghas +agila +agile +aging +agios +agism +agist +agita +aglee +aglet +agley +agloo +aglow +aglus +agmas +agoge +agone +agons +agony +agood +agora +agree +agria +agrin +agros +agued +agues +aguna +aguti +ahead +aheap +ahent +ahigh +ahind +ahing +ahint +ahold +ahull +ahuru +aidas +aided +aider +aides +aidoi +aidos +aiery +aigas +aight +ailed +aimed +aimer +ainee +ainga +aioli +aired +airer +airns +airth +airts +aisle +aitch +aitus +aiver +aiyee +aizle +ajies +ajiva +ajuga +ajwan +akees +akela +akene +aking +akita +akkas +alaap +alack +alamo +aland +alane +alang +alans +alant +alapa +alaps +alarm +alary +alate +alays +albas +albee +album +alcid +alcos +aldea +alder +aldol +aleck +alecs +alefs +aleft +aleph +alert +alews +aleye +alfas +algae +algal +algas +algid +algin +algor +algum +alias +alibi +alien +alifs +align +alike +aline +alist +alive +aliya +alkie +alkos +alkyd +alkyl +allay +allee +allel +alley +allis +allod +allot +allow +alloy +allyl +almah +almas +almeh +almes +almud +almug +alods +aloed +aloes +aloft +aloha +aloin +alone +along +aloof +aloos +aloud +alowe +alpha +altar +alter +altho +altos +alula +alums +alure +alvar +alway +amahs +amain +amass +amate +amaut +amaze +amban +amber +ambit +amble +ambos +ambry +ameba +ameer +amend +amene +amens +ament +amias +amice +amici +amide +amido +amids +amies +amiga +amigo +amine +amino +amins +amirs +amiss +amity +amlas +amman +ammon +ammos +amnia +amnic +amnio +amoks +amole +among +amort +amour +amove +amowt +amped +ample +amply +ampul +amrit +amuck +amuse +amyls +anana +anata +ancho +ancle +ancon +andro +anear +anele +anent +angas +angel +anger +angle +anglo +angry +angst +anigh +anile +anils +anima +anime +animi +anion +anise +anker +ankhs +ankle +ankus +anlas +annal +annas +annat +annex +annoy +annul +anoas +anode +anole +anomy +ansae +antae +antar +antas +anted +antes +antic +antis +antra +antre +antsy +anura +anvil +anyon +aorta +apace +apage +apaid +apart +apayd +apays +apeak +apeek +apers +apert +apery +apgar +aphid +aphis +apian +aping +apiol +apish +apism +apnea +apode +apods +apoop +aport +appal +appay +appel +apple +apply +appro +appui +appuy +apres +apron +apses +apsis +apsos +apted +apter +aptly +aquae +aquas +araba +araks +arame +arars +arbas +arbor +arced +archi +arcos +arcus +ardeb +ardor +ardri +aread +areae +areal +arear +areas +areca +aredd +arede +arefy +areic +arena +arene +arepa +arere +arete +arets +arett +argal +argan +argil +argle +argol +argon +argot +argue +argus +arhat +arias +ariel +ariki +arils +ariot +arise +arish +arked +arled +arles +armed +armer +armet +armil +armor +arnas +arnut +aroba +aroha +aroid +aroma +arose +arpas +arpen +arrah +arras +array +arret +arris +arrow +arroz +arsed +arses +arsey +arsis +arson +artal +artel +artic +artis +artsy +aruhe +arums +arval +arvee +arvos +aryls +asana +ascon +ascot +ascus +asdic +ashed +ashen +ashes +ashet +aside +asked +asker +askew +askoi +askos +aspen +asper +aspic +aspie +aspis +aspro +assai +assam +assay +asses +asset +assez +assot +aster +astir +astun +asura +asway +aswim +asyla +ataps +ataxy +atigi +atilt +atimy +atlas +atman +atmas +atmos +atocs +atoke +atoks +atoll +atoms +atomy +atone +atony +atopy +atria +atrip +attap +attar +attic +atuas +audad +audio +audit +auger +aught +augur +aulas +aulic +auloi +aulos +aumil +aunes +aunts +aunty +aurae +aural +aurar +auras +aurei +aures +auric +auris +aurum +autos +auxin +avail +avale +avant +avast +avels +avens +avers +avert +avgas +avian +avine +avion +avise +aviso +avize +avoid +avows +avyze +await +awake +award +aware +awarn +awash +awato +awave +aways +awdls +aweel +aweto +awful +awing +awmry +awned +awner +awoke +awols +awork +axels +axial +axile +axils +axing +axiom +axion +axite +axled +axles +axman +axmen +axoid +axone +axons +ayahs +ayaya +ayelp +aygre +ayins +ayont +ayres +ayrie +azans +azide +azido +azine +azlon +azoic +azole +azons +azote +azoth +azuki +azure +azurn +azury +azygy +azyme +azyms +baaed +baals +babas +babel +babes +babka +baboo +babul +babus +bacca +bacco +baccy +bacha +bachs +backs +bacon +baddy +badge +badly +baels +baffs +baffy +bafts +bagel +baggy +baghs +bagie +bahts +bahus +bahut +bails +bairn +baisa +baith +baits +baiza +baize +bajan +bajra +bajri +bajus +baked +baken +baker +bakes +bakra +balas +balds +baldy +baled +baler +bales +balks +balky +balls +bally +balms +balmy +baloo +balsa +balti +balun +balus +bambi +banak +banal +banco +bancs +banda +bandh +bands +bandy +baned +banes +bangs +bania +banjo +banks +banns +bants +bantu +banty +banya +bapus +barbe +barbs +barby +barca +barde +bardo +bards +bardy +bared +barer +bares +barfi +barfs +barge +baric +barks +barky +barms +barmy +barns +barny +baron +barps +barra +barre +barro +barry +barye +basal +basan +based +basen +baser +bases +basho +basic +basij +basil +basin +basis +basks +bason +basse +bassi +basso +bassy +basta +baste +basti +basto +basts +batch +bated +bates +bathe +baths +batik +baton +batta +batts +battu +batty +bauds +bauks +baulk +baurs +bavin +bawds +bawdy +bawks +bawls +bawns +bawrs +bawty +bayed +bayer +bayes +bayle +bayou +bayts +bazar +bazoo +beach +beads +beady +beaks +beaky +beals +beams +beamy +beano +beans +beany +beard +beare +bears +beast +beath +beats +beaty +beaus +beaut +beaux +bebop +becap +becke +becks +bedad +bedel +bedes +bedew +bedim +bedye +beech +beedi +beefs +beefy +beeps +beers +beery +beets +befit +befog +begad +began +begar +begat +begem +beget +begin +begot +begum +begun +beige +beigy +being +beins +bekah +belah +belar +belay +belch +belee +belga +belie +belle +bells +belly +belon +below +belts +bemad +bemas +bemix +bemud +bench +bends +bendy +benes +benet +benga +benis +benne +benni +benny +bento +bents +benty +bepat +beray +beres +beret +bergs +berko +berks +berme +berms +berob +berry +berth +beryl +besat +besaw +besee +beses +beset +besit +besom +besot +besti +bests +betas +beted +betel +betes +beths +betid +beton +betta +betty +bevel +bever +bevor +bevue +bevvy +bewet +bewig +bezel +bezes +bezil +bezzy +bhais +bhaji +bhang +bhats +bhels +bhoot +bhuna +bhuts +biach +biali +bialy +bibbs +bibes +bible +biccy +bicep +bices +biddy +bided +bider +bides +bidet +bidis +bidon +bield +biers +biffo +biffs +biffy +bifid +bigae +biggs +biggy +bigha +bight +bigly +bigos +bigot +bijou +biked +biker +bikes +bikie +bilbo +bilby +biled +biles +bilge +bilgy +bilks +bills +billy +bimah +bimas +bimbo +binal +bindi +binds +biner +bines +binge +bingo +bings +bingy +binit +binks +bints +biogs +biome +biont +biota +biped +bipod +birch +birds +birks +birle +birls +biros +birrs +birse +birsy +birth +bises +bisks +bisom +bison +bitch +biter +bites +bitos +bitou +bitsy +bitte +bitts +bitty +bivia +bivvy +bizes +bizzo +bizzy +blabs +black +blade +blads +blady +blaer +blaes +blaff +blags +blahs +blain +blame +blams +bland +blank +blare +blart +blase +blash +blast +blate +blats +blatt +blaud +blawn +blaws +blays +blaze +bleak +blear +bleat +blebs +blech +bleed +bleep +blees +blend +blent +blert +bless +blest +blets +bleys +blimp +blimy +blind +bling +blini +blink +blins +bliny +blips +bliss +blist +blite +blits +blitz +blive +bloat +blobs +block +blocs +blogs +bloke +blond +blood +blook +bloom +bloop +blore +blots +blown +blows +blowy +blubs +blude +bluds +bludy +blued +bluer +blues +bluet +bluey +bluff +bluid +blume +blunk +blunt +blurb +blurs +blurt +blush +blype +boabs +boaks +board +boars +boart +boast +boats +bobac +bobak +bobas +bobby +bobol +bobos +bocca +bocce +bocci +boche +bocks +boded +bodes +bodge +bodhi +bodle +boeps +boets +boeuf +boffo +boffs +bogan +bogey +boggy +bogie +bogle +bogue +bogus +bohea +bohos +boils +boing +boink +boite +boked +bokeh +bokes +bokos +bolar +bolas +bolds +boles +bolix +bolls +bolos +bolts +bolus +bomas +bombe +bombo +bombs +bonce +bonds +boned +boner +bones +boney +bongo +bongs +bonie +bonks +bonne +bonny +bonus +bonza +bonze +booai +booay +boobs +booby +boody +booed +boofy +boogy +boohs +books +booky +bools +booms +boomy +boong +boons +boord +boors +boose +boost +booth +boots +booty +booze +boozy +boppy +borak +boral +boras +borax +borde +bords +bored +boree +borel +borer +bores +borgo +boric +borks +borms +borna +borne +boron +borts +borty +bortz +bosie +bosks +bosky +bosom +boson +bossy +bosun +botas +botch +botel +botes +bothy +botte +botts +botty +bouge +bough +bouks +boule +boult +bound +bouns +bourd +bourg +bourn +bouse +bousy +bouts +bovid +bowat +bowed +bowel +bower +bowes +bowet +bowie +bowls +bowne +bowrs +bowse +boxed +boxen +boxer +boxes +boxla +boxty +boyar +boyau +boyed +boyfs +boygs +boyla +boyos +boysy +bozos +braai +brace +brach +brack +bract +brads +braes +brags +braid +brail +brain +brake +braks +braky +brame +brand +brane +brank +brans +brant +brash +brass +brast +brats +brava +brave +bravi +bravo +brawl +brawn +braws +braxy +brays +braza +braze +bread +break +bream +brede +breds +breed +breem +breer +brees +breid +breis +breme +brens +brent +brere +brers +breve +brews +breys +briar +bribe +brick +bride +brief +brier +bries +brigs +briki +briks +brill +brims +brine +bring +brink +brins +briny +brios +brise +brisk +briss +brith +brits +britt +brize +broad +broch +brock +brods +brogh +brogs +broil +broke +brome +bromo +bronc +brond +brood +brook +brool +broom +broos +brose +brosy +broth +brown +brows +brugh +bruin +bruit +brule +brume +brung +brunt +brush +brusk +brust +brute +bruts +buats +buaze +bubal +bubas +bubba +bubbe +bubby +bubus +buchu +bucko +bucks +bucku +budas +buddy +budge +budis +budos +buffa +buffe +buffi +buffo +buffs +buffy +bufos +bufty +buggy +bugle +buhls +buhrs +buiks +build +built +buist +bukes +bulbs +bulge +bulgy +bulks +bulky +bulla +bulls +bully +bulse +bumbo +bumfs +bumph +bumps +bumpy +bunas +bunce +bunch +bunco +bunde +bundh +bunds +bundt +bundu +bundy +bungs +bungy +bunia +bunje +bunjy +bunko +bunks +bunns +bunny +bunts +bunty +bunya +buoys +buppy +buran +buras +burbs +burds +buret +burfi +burgh +burgs +burin +burka +burke +burks +burls +burly +burns +burnt +buroo +burps +burqa +burro +burrs +burry +bursa +burse +burst +busby +bused +buses +bushy +busks +busky +bussu +busti +busts +busty +butch +buteo +butes +butle +butoh +butte +butts +butty +butut +butyl +buxom +buyer +buzzy +bwana +bwazi +byded +bydes +byked +bykes +bylaw +byres +byrls +byssi +bytes +byway +caaed +cabal +cabas +cabby +caber +cabin +cable +cabob +caboc +cabre +cacao +cacas +cache +cacks +cacky +cacti +caddy +cadee +cades +cadet +cadge +cadgy +cadie +cadis +cadre +caeca +caese +cafes +caffs +caged +cager +cages +cagey +cagot +cahow +caids +cains +caird +cairn +cajon +cajun +caked +cakes +cakey +calfs +calid +calif +calix +calks +calla +calls +calms +calmy +calos +calpa +calps +calve +calyx +caman +camas +camel +cameo +cames +camis +camos +campi +campo +camps +campy +camus +canal +candy +caned +caneh +caner +canes +cangs +canid +canna +canns +canny +canoe +canon +canso +canst +canto +cants +canty +capas +caped +caper +capes +capex +caphs +capiz +caple +capon +capos +capot +capri +capul +caput +carap +carat +carbo +carbs +carby +cardi +cards +cardy +cared +carer +cares +caret +carex +cargo +carks +carle +carls +carns +carny +carob +carol +carom +caron +carpi +carps +carrs +carry +carse +carta +carte +carts +carve +carvy +casas +casco +cased +cases +casks +casky +caste +casts +casus +catch +cater +cates +catty +cauda +cauks +cauld +caulk +cauls +caums +caups +cauri +causa +cause +cavas +caved +cavel +caver +caves +cavie +cavil +cawed +cawks +caxon +cease +ceaze +cebid +cecal +cecum +cedar +ceded +ceder +cedes +cedis +ceiba +ceili +ceils +celeb +cella +celli +cello +cells +celom +celts +cense +cento +cents +centu +ceorl +cepes +cerci +cered +ceres +cerge +ceria +ceric +cerne +ceroc +ceros +certs +certy +cesse +cesta +cesti +cetes +cetyl +cezve +chace +chack +chaco +chado +chads +chafe +chaff +chaft +chain +chair +chais +chalk +chals +champ +chams +chana +chang +chank +chant +chaos +chape +chaps +chapt +chara +chard +chare +chark +charm +charr +chars +chart +chary +chase +chasm +chats +chave +chavs +chawk +chaws +chaya +chays +cheap +cheat +check +cheek +cheep +cheer +chefs +cheka +chela +chelp +chemo +chems +chere +chert +chess +chest +cheth +chevy +chews +chewy +chiao +chias +chibs +chica +chich +chick +chico +chics +chide +chief +chiel +chiks +child +chile +chili +chill +chimb +chime +chimo +chimp +china +chine +ching +chink +chino +chins +chips +chirk +chirl +chirm +chiro +chirp +chirr +chirt +chiru +chits +chive +chivs +chivy +chizz +chock +choco +chocs +chode +chogs +choil +choir +choke +choko +choky +chola +choli +cholo +chomp +chons +choof +chook +choom +choon +chops +chord +chore +chose +chota +chott +chout +choux +chowk +chows +chubs +chuck +chufa +chuff +chugs +chump +chums +chunk +churl +churn +churr +chuse +chute +chuts +chyle +chyme +chynd +cibol +cided +cider +cides +ciels +cigar +ciggy +cilia +cills +cimar +cimex +cinch +cinct +cines +cinqs +cions +cippi +circa +circs +cires +cirls +cirri +cisco +cissy +cists +cital +cited +citer +cites +cives +civet +civic +civie +civil +civvy +clach +clack +clade +clads +claes +clags +claim +clame +clamp +clams +clang +clank +clans +claps +clapt +claro +clart +clary +clash +clasp +class +clast +clats +claut +clave +clavi +claws +clays +clean +clear +cleat +cleck +cleek +cleep +clefs +cleft +clegs +cleik +clems +clepe +clept +clerk +cleve +clews +click +clied +clies +cliff +clift +climb +clime +cline +cling +clink +clint +clipe +clips +clipt +clits +cloak +cloam +clock +clods +cloff +clogs +cloke +clomb +clomp +clone +clonk +clons +cloop +cloot +clops +close +clote +cloth +clots +cloud +clour +clous +clout +clove +clown +clows +cloye +cloys +cloze +clubs +cluck +clued +clues +cluey +clump +clung +clunk +clype +cnida +coach +coact +coady +coala +coals +coaly +coapt +coarb +coast +coate +coati +coats +cobbs +cobby +cobia +coble +cobra +cobza +cocas +cocci +cocco +cocks +cocky +cocoa +cocos +codas +codec +coded +coden +coder +codes +codex +codon +coeds +coffs +cogie +cogon +cogue +cohab +cohen +cohoe +cohog +cohos +coifs +coign +coils +coins +coirs +coits +coked +cokes +colas +colby +colds +coled +coles +coley +colic +colin +colls +colly +colog +colon +color +colts +colza +comae +comal +comas +combe +combi +combo +combs +comby +comer +comes +comet +comfy +comic +comix +comma +commo +comms +commy +compo +comps +compt +comte +comus +conch +condo +coned +cones +coney +confs +conga +conge +congo +conia +conic +conin +conks +conky +conne +conns +conte +conto +conus +convo +cooch +cooed +cooee +cooer +cooey +coofs +cooks +cooky +cools +cooly +coomb +cooms +coomy +coons +coops +coopt +coost +coots +cooze +copal +copay +coped +copen +coper +copes +coppy +copra +copse +copsy +coqui +coral +coram +corbe +corby +cords +cored +corer +cores +corey +corgi +coria +corks +corky +corms +corni +corno +corns +cornu +corny +corps +corse +corso +cosec +cosed +coses +coset +cosey +cosie +costa +coste +costs +cotan +coted +cotes +coths +cotta +cotts +couch +coude +cough +could +count +coupe +coups +courb +courd +coure +cours +court +couta +couth +coved +coven +cover +coves +covet +covey +covin +cowal +cowan +cowed +cower +cowks +cowls +cowps +cowry +coxae +coxal +coxed +coxes +coxib +coyau +coyed +coyer +coyly +coypu +cozed +cozen +cozes +cozey +cozie +craal +crabs +crack +craft +crags +craic +craig +crake +crame +cramp +crams +crane +crank +crans +crape +craps +crapy +crare +crash +crass +crate +crave +crawl +craws +crays +craze +crazy +creak +cream +credo +creds +creed +creek +creel +creep +crees +creme +crems +crena +crepe +creps +crept +crepy +cress +crest +crewe +crews +crias +cribs +crick +cried +crier +cries +crime +crimp +crims +crine +crios +cripe +crips +crise +crisp +crith +crits +croak +croci +crock +crocs +croft +crogs +cromb +crome +crone +cronk +crons +crony +crook +crool +croon +crops +crore +cross +crost +croup +crout +crowd +crown +crows +croze +cruck +crude +crudo +cruds +crudy +cruel +crues +cruet +cruft +crumb +crump +crunk +cruor +crura +cruse +crush +crust +crusy +cruve +crwth +cryer +crypt +ctene +cubby +cubeb +cubed +cuber +cubes +cubic +cubit +cuddy +cuffo +cuffs +cuifs +cuing +cuish +cuits +cukes +culch +culet +culex +culls +cully +culms +culpa +culti +cults +culty +cumec +cumin +cundy +cunei +cunit +cunts +cupel +cupid +cuppa +cuppy +curat +curbs +curch +curds +curdy +cured +curer +cures +curet +curfs +curia +curie +curio +curli +curls +curly +curns +curny +currs +curry +curse +cursi +curst +curve +curvy +cusec +cushy +cusks +cusps +cuspy +cusso +cusum +cutch +cuter +cutes +cutey +cutie +cutin +cutis +cutto +cutty +cutup +cuvee +cuzes +cwtch +cyano +cyans +cyber +cycad +cycas +cycle +cyclo +cyder +cylix +cymae +cymar +cymas +cymes +cymol +cynic +cysts +cytes +cyton +czars +daals +dabba +daces +dacha +dacks +dadah +dadas +daddy +dados +daffs +daffy +dagga +daggy +dagos +dahls +daiko +daily +daine +daint +dairy +daisy +daker +daled +dales +dalis +dalle +dally +dalts +daman +damar +dames +damme +damns +damps +dampy +dance +dancy +dandy +dangs +danio +danks +danny +dants +daraf +darbs +darcy +dared +darer +dares +darga +dargs +daric +daris +darks +darky +darns +darre +darts +darzi +dashi +dashy +datal +dated +dater +dates +datos +datto +datum +daube +daubs +dauby +dauds +dault +daunt +daurs +dauts +daven +davit +dawah +dawds +dawed +dawen +dawks +dawns +dawts +dayan +daych +daynt +dazed +dazer +dazes +deads +deair +deals +dealt +deans +deare +dearn +dears +deary +deash +death +deave +deaws +deawy +debag +debar +debby +debel +debes +debit +debts +debud +debug +debur +debus +debut +debye +decad +decaf +decal +decan +decay +decko +decks +decor +decos +decoy +decry +dedal +deeds +deedy +deely +deems +deens +deeps +deere +deers +deets +deeve +deevs +defat +defer +deffo +defis +defog +degas +degum +degus +deice +deids +deify +deign +deils +deism +deist +deity +deked +dekes +dekko +delay +deled +deles +delfs +delft +delis +dells +delly +delos +delph +delta +delts +delve +deman +demes +demic +demit +demob +demoi +demon +demos +dempt +demur +denar +denay +dench +denes +denet +denim +denis +dense +dents +deoxy +depot +depth +derat +deray +derby +dered +deres +derig +derma +derms +derns +derny +deros +derro +derry +derth +dervs +desex +deshi +desis +desks +desse +deter +detox +deuce +devas +devel +devil +devis +devon +devos +devot +dewan +dewar +dewax +dewed +dexes +dexie +dhaba +dhaks +dhals +dhikr +dhobi +dhole +dholl +dhols +dhoti +dhows +dhuti +diact +dials +diane +diary +diazo +dibbs +diced +dicer +dices +dicey +dicht +dicks +dicky +dicot +dicta +dicts +dicty +diddy +didie +didos +didst +diebs +diels +diene +diets +diffs +dight +digit +dikas +diked +diker +dikes +dikey +dildo +dilli +dills +dilly +dimbo +dimer +dimes +dimly +dimps +dinar +dined +diner +dines +dinge +dingo +dings +dingy +dinic +dinks +dinky +dinna +dinos +dints +diode +diols +diota +dippy +dipso +diram +direr +dirge +dirke +dirks +dirls +dirts +dirty +disas +disci +disco +discs +dishy +disks +disme +dital +ditas +ditch +dited +dites +ditsy +ditto +ditts +ditty +ditzy +divan +divas +dived +diver +dives +divis +divna +divos +divot +divvy +diwan +dixie +dixit +diyas +dizen +dizzy +djinn +djins +doabs +doats +dobby +dobes +dobie +dobla +dobra +dobro +docht +docks +docos +docus +doddy +dodge +dodgy +dodos +doeks +doers +doest +doeth +doffs +dogan +doges +dogey +doggo +doggy +dogie +dogma +dohyo +doilt +doily +doing +doits +dojos +dolce +dolci +doled +doles +dolia +dolls +dolly +dolma +dolor +dolos +dolts +domal +domed +domes +domic +donah +donas +donee +doner +donga +dongs +donko +donna +donne +donny +donor +donsy +donut +doobs +dooce +doody +dooks +doole +dools +dooly +dooms +doomy +doona +doorn +doors +doozy +dopas +doped +doper +dopes +dopey +dorad +dorba +dorbs +doree +dores +doric +doris +dorks +dorky +dorms +dormy +dorps +dorrs +dorsa +dorse +dorts +dorty +dosai +dosas +dosed +doseh +doser +doses +dosha +dotal +doted +doter +dotes +dotty +douar +doubt +douce +doucs +dough +douks +doula +douma +doums +doups +doura +douse +douts +doved +doven +dover +doves +dovie +dowar +dowds +dowdy +dowed +dowel +dower +dowie +dowle +dowls +dowly +downa +downs +downy +dowps +dowry +dowse +dowts +doxed +doxes +doxie +doyen +doyly +dozed +dozen +dozer +dozes +drabs +drack +draco +draff +draft +drags +drail +drain +drake +drama +drams +drank +drant +drape +draps +drats +drave +drawl +drawn +draws +drays +dread +dream +drear +dreck +dreed +dreer +drees +dregs +dreks +drent +drere +dress +drest +dreys +dribs +drice +dried +drier +dries +drift +drill +drily +drink +drips +dript +drive +droid +droil +droit +droke +drole +droll +drome +drone +drony +droob +droog +drook +drool +droop +drops +dropt +dross +drouk +drove +drown +drows +drubs +drugs +druid +drums +drunk +drupe +druse +drusy +druxy +dryad +dryas +dryer +dryly +dsobo +dsomo +duads +duals +duans +duars +dubbo +ducal +ducat +duces +duchy +ducks +ducky +ducts +duddy +duded +dudes +duels +duets +duett +duffs +dufus +duing +duits +dukas +duked +dukes +dukka +dulce +dules +dulia +dulls +dully +dulse +dumas +dumbo +dumbs +dumka +dumky +dummy +dumps +dumpy +dunam +dunce +dunch +dunes +dungs +dungy +dunks +dunno +dunny +dunsh +dunts +duomi +duomo +duped +duper +dupes +duple +duply +duppy +dural +duras +dured +dures +durgy +durns +duroc +duros +duroy +durra +durrs +durry +durst +durum +durzi +dusks +dusky +dusts +dusty +dutch +duvet +duxes +dwaal +dwale +dwalm +dwams +dwang +dwarf +dwaum +dweeb +dwell +dwelt +dwile +dwine +dyads +dyers +dying +dyked +dykes +dykey +dykon +dynel +dynes +dzhos +eager +eagle +eagre +ealed +eales +eaned +eards +eared +earls +early +earns +earnt +earst +earth +eased +easel +easer +eases +easle +easts +eaten +eater +eathe +eaved +eaves +ebbed +ebbet +ebons +ebony +ebook +ecads +eched +eches +echos +eclat +ecrus +edema +edged +edger +edges +edict +edify +edile +edits +educe +educt +eejit +eensy +eerie +eeven +eevns +effed +egads +egers +egest +eggar +egged +egger +egmas +egret +ehing +eider +eidos +eight +eigne +eiked +eikon +eilds +eisel +eject +ejido +eking +ekkas +elain +eland +elans +elate +elbow +elchi +elder +eldin +elect +elegy +elemi +elfed +elfin +eliad +elide +elint +elite +elmen +eloge +elogy +eloin +elope +elops +elpee +elsin +elude +elute +elvan +elven +elver +elves +emacs +email +embar +embay +embed +ember +embog +embow +embox +embus +emcee +emeer +emend +emerg +emery +emeus +emics +emirs +emits +emmas +emmer +emmet +emmew +emmys +emoji +emong +emote +emove +empts +empty +emule +emure +emyde +emyds +enact +enarm +enate +ended +ender +endew +endow +endue +enema +enemy +enews +enfix +eniac +enjoy +enlit +enmew +ennog +ennui +enoki +enols +enorm +enows +enrol +ensew +ensky +ensue +enter +entia +entry +enure +enurn +envoi +envoy +enzym +eorls +eosin +epact +epees +ephah +ephas +ephod +ephor +epics +epoch +epode +epopt +epoxy +epris +equal +eques +equid +equip +erase +erbia +erect +erevs +ergon +ergos +ergot +erhus +erica +erick +erics +ering +erned +ernes +erode +erose +erred +error +erses +eruct +erugo +erupt +eruvs +erven +ervil +escar +escot +esile +eskar +esker +esnes +essay +esses +ester +estoc +estop +estro +etage +etape +etats +etens +ethal +ether +ethic +ethne +ethos +ethyl +etics +etnas +ettin +ettle +etude +etuis +etwee +etyma +eughs +euked +eupad +euros +eusol +evade +evens +event +evert +every +evets +evhoe +evict +evils +evite +evohe +evoke +ewers +ewest +ewhow +ewked +exact +exalt +exams +excel +exeat +execs +exeem +exeme +exert +exfil +exies +exile +exine +exing +exist +exits +exode +exome +exons +expat +expel +expos +extol +extra +exude +exuls +exult +exurb +eyass +eyers +eying +eyots +eyras +eyres +eyrie +eyrir +ezine +fabby +fable +faced +facer +faces +facet +facia +facta +facts +faddy +faded +fader +fades +fadge +fados +faena +faery +faffs +faffy +faggy +fagin +fagot +faiks +fails +faine +fains +faint +fairs +fairy +faith +faked +faker +fakes +fakey +fakie +fakir +falaj +falls +false +famed +fames +fanal +fancy +fands +fanes +fanga +fango +fangs +fanks +fanny +fanon +fanos +fanum +faqir +farad +farce +farci +farcy +fards +fared +farer +fares +farle +farls +farms +faros +farro +farse +farts +fasci +fasti +fasts +fatal +fated +fates +fatly +fatso +fatty +fatwa +faugh +fauld +fault +fauna +fauns +faurd +fauts +fauve +favas +favel +faver +faves +favor +favus +fawns +fawny +faxed +faxes +fayed +fayer +fayne +fayre +fazed +fazes +feals +feare +fears +feart +fease +feast +feats +feaze +fecal +feces +fecht +fecit +fecks +fedex +feebs +feeds +feels +feens +feers +feese +feeze +fehme +feign +feint +feist +felch +felid +fella +fells +felly +felon +felts +felty +femal +femes +femme +femmy +femur +fence +fends +fendy +fenis +fenks +fenny +fents +feods +feoff +feral +ferer +feres +feria +ferly +fermi +ferms +ferns +ferny +ferry +fesse +festa +fests +festy +fetal +fetas +fetch +feted +fetes +fetid +fetor +fetta +fetts +fetus +fetwa +feuar +feuds +feued +fever +fewer +feyed +feyer +feyly +fezes +fezzy +fiars +fiats +fiber +fibre +fibro +fices +fiche +fichu +ficin +ficos +ficus +fides +fidge +fidos +fiefs +field +fiend +fient +fiere +fiers +fiery +fiest +fifed +fifer +fifes +fifis +fifth +fifty +figgy +fight +figos +fiked +fikes +filar +filch +filed +filer +files +filet +filii +filks +fille +fillo +fills +filly +filmi +films +filmy +filos +filth +filum +final +finca +finch +finds +fined +finer +fines +finis +finks +finny +finos +fiord +fiqhs +fique +fired +firer +fires +firie +firks +firms +firns +firry +first +firth +fiscs +fishy +fisks +fists +fisty +fitch +fitly +fitna +fitte +fitts +fiver +fives +fixed +fixer +fixes +fixit +fizzy +fjeld +fjord +flabs +flack +flaff +flags +flail +flair +flake +flaks +flaky +flame +flamm +flams +flamy +flane +flank +flans +flaps +flare +flary +flash +flask +flats +flava +flawn +flaws +flawy +flaxy +flays +fleam +fleas +fleck +fleek +fleer +flees +fleet +flegs +fleme +flesh +fleur +flews +flexi +flexo +fleys +flick +flics +flied +flier +flies +flimp +flims +fling +flint +flips +flirs +flirt +flisk +flite +flits +flitt +float +flobs +flock +flocs +floes +flogs +flong +flood +floor +flops +flora +flors +flory +flosh +floss +flota +flote +flour +flout +flown +flows +flubs +flued +flues +fluey +fluff +fluid +fluke +fluky +flume +flump +flung +flunk +fluor +flurr +flush +flute +fluty +fluyt +flyby +flyer +flype +flyte +foals +foams +foamy +focal +focus +foehn +fogey +foggy +fogie +fogle +fogou +fohns +foids +foils +foins +foist +folds +foley +folia +folic +folie +folio +folks +folky +folly +fomes +fonda +fonds +fondu +fones +fonly +fonts +foods +foody +fools +foots +footy +foram +foray +forbs +forby +force +fordo +fords +forel +fores +forex +forge +forgo +forks +forky +forme +forms +forte +forth +forts +forty +forum +forza +forze +fossa +fosse +fouat +fouds +fouer +fouet +foule +fouls +found +fount +fours +fouth +fovea +fowls +fowth +foxed +foxes +foxie +foyer +foyle +foyne +frabs +frack +fract +frags +frail +fraim +frame +franc +frank +frape +fraps +frass +frate +frati +frats +fraud +fraus +frays +freak +freed +freer +frees +freet +freit +fremd +frena +freon +frere +fresh +frets +friar +fribs +fried +frier +fries +frigs +frill +frise +frisk +frist +frith +frits +fritt +fritz +frize +frizz +frock +froes +frogs +frond +frons +front +frore +frorn +frory +frosh +frost +froth +frown +frows +frowy +froze +frugs +fruit +frump +frush +frust +fryer +fubar +fubby +fubsy +fucks +fucus +fuddy +fudge +fudgy +fuels +fuero +fuffs +fuffy +fugal +fuggy +fugie +fugio +fugle +fugly +fugue +fugus +fujis +fulls +fully +fumed +fumer +fumes +fumet +fundi +funds +fundy +fungi +fungo +fungs +funks +funky +funny +fural +furan +furca +furls +furol +furor +furrs +furry +furth +furze +furzy +fused +fusee +fusel +fuses +fusil +fusks +fussy +fusts +fusty +futon +fuzed +fuzee +fuzes +fuzil +fuzzy +fyces +fyked +fykes +fyles +fyrds +fytte +gabba +gabby +gable +gaddi +gades +gadge +gadid +gadis +gadje +gadjo +gadso +gaffe +gaffs +gaged +gager +gages +gaids +gaily +gains +gairs +gaita +gaits +gaitt +gajos +galah +galas +galax +galea +galed +gales +galls +gally +galop +galut +galvo +gamas +gamay +gamba +gambe +gambo +gambs +gamed +gamer +games +gamey +gamic +gamin +gamma +gamme +gammy +gamps +gamut +ganch +gandy +ganef +ganev +gangs +ganja +ganof +gants +gaols +gaped +gaper +gapes +gapos +gappy +garbe +garbo +garbs +garda +gares +garis +garms +garni +garre +garth +garum +gases +gasps +gaspy +gassy +gasts +gatch +gated +gater +gates +gaths +gator +gauch +gaucy +gauds +gaudy +gauge +gauje +gault +gaums +gaumy +gaunt +gaups +gaurs +gauss +gauze +gauzy +gavel +gavot +gawcy +gawds +gawks +gawky +gawps +gawsy +gayal +gayer +gayly +gazal +gazar +gazed +gazer +gazes +gazon +gazoo +geals +geans +geare +gears +geats +gebur +gecko +gecks +geeks +geeky +geeps +geese +geest +geist +geits +gelds +gelee +gelid +gelly +gelts +gemel +gemma +gemmy +gemot +genal +genas +genes +genet +genic +genie +genii +genip +genny +genoa +genom +genre +genro +gents +genty +genua +genus +geode +geoid +gerah +gerbe +geres +gerle +germs +germy +gerne +gesse +gesso +geste +gests +getas +getup +geums +geyan +geyer +ghast +ghats +ghaut +ghazi +ghees +ghest +ghost +ghoul +ghyll +giant +gibed +gibel +giber +gibes +gibli +gibus +giddy +gifts +gigas +gighe +gigot +gigue +gilas +gilds +gilet +gills +gilly +gilpy +gilts +gimel +gimme +gimps +gimpy +ginch +ginge +gings +ginks +ginny +ginzo +gipon +gippo +gippy +gipsy +girds +girls +girly +girns +giron +giros +girrs +girsh +girth +girts +gismo +gisms +gists +gitch +gites +giust +gived +given +giver +gives +gizmo +glace +glade +glads +glady +glaik +glair +glams +gland +glans +glare +glary +glass +glaum +glaur +glaze +glazy +gleam +glean +gleba +glebe +gleby +glede +gleds +gleed +gleek +glees +gleet +gleis +glens +glent +gleys +glial +glias +glibs +glide +gliff +glift +glike +glime +glims +glint +glisk +glits +glitz +gloam +gloat +globe +globi +globs +globy +glode +glogg +gloms +gloom +gloop +glops +glory +gloss +glost +glout +glove +glows +gloze +glued +gluer +glues +gluey +glugs +glume +glums +gluon +glute +gluts +glyph +gnarl +gnarr +gnars +gnash +gnats +gnawn +gnaws +gnome +gnows +goads +goafs +goals +goary +goats +goaty +goban +gobar +gobbi +gobbo +gobby +gobis +gobos +godet +godly +godso +goels +goers +goest +goeth +goety +gofer +goffs +gogga +gogos +goier +going +gojis +golds +goldy +golem +goles +golfs +golly +golpe +golps +gombo +gomer +gompa +gonad +gonch +gonef +goner +gongs +gonia +gonif +gonks +gonna +gonof +gonys +gonzo +gooby +goods +goody +gooey +goofs +goofy +googs +gooks +gooky +goold +gools +gooly +goons +goony +goops +goopy +goors +goory +goose +goosy +gopak +gopik +goral +goras +gored +gores +gorge +goris +gorms +gormy +gorps +gorse +gorsy +gosht +gosse +gotch +goths +gothy +gotta +gouch +gouge +gouks +goura +gourd +gouts +gouty +gowan +gowds +gowfs +gowks +gowls +gowns +goxes +goyim +goyle +graal +grabs +grace +grade +grads +graff +graft +grail +grain +graip +grama +grame +gramp +grams +grana +grand +grans +grant +grape +graph +grapy +grasp +grass +grate +grave +gravs +gravy +grays +graze +great +grebe +grebo +grece +greed +greek +green +grees +greet +grege +grego +grein +grens +grese +greve +grews +greys +grice +gride +grids +grief +griff +grift +grigs +grike +grill +grime +grimy +grind +grins +griot +gripe +grips +gript +gripy +grise +grist +grisy +grith +grits +grize +groan +groat +grody +grogs +groin +groks +groma +grone +groof +groom +grope +gross +grosz +grots +grouf +group +grout +grove +grovy +growl +grown +grows +grrls +grrrl +grubs +grued +gruel +grues +grufe +gruff +grume +grump +grund +grunt +gryce +gryde +gryke +grype +grypt +guaco +guana +guano +guans +guard +guars +guava +gucks +gucky +gudes +guess +guest +guffs +gugas +guide +guids +guild +guile +guilt +guimp +guiro +guise +gulag +gular +gulas +gulch +gules +gulet +gulfs +gulfy +gulls +gully +gulph +gulps +gulpy +gumbo +gumma +gummi +gummy +gumps +gundy +gunge +gungy +gunks +gunky +gunny +guppy +guqin +gurdy +gurge +gurls +gurly +gurns +gurry +gursh +gurus +gushy +gusla +gusle +gusli +gussy +gusto +gusts +gusty +gutsy +gutta +gutty +guyed +guyle +guyot +guyse +gwine +gyals +gyans +gybed +gybes +gyeld +gymps +gynae +gynie +gynny +gynos +gyoza +gypos +gyppo +gyppy +gypsy +gyral +gyred +gyres +gyron +gyros +gyrus +gytes +gyved +gyves +haafs +haars +habit +hable +habus +hacek +hacks +hadal +haded +hades +hadji +hadst +haems +haets +haffs +hafiz +hafts +haggs +hahas +haick +haika +haiks +haiku +hails +haily +hains +haint +hairs +hairy +haith +hajes +hajis +hajji +hakam +hakas +hakea +hakes +hakim +hakus +halal +haled +haler +hales +halfa +halfs +halid +hallo +halls +halma +halms +halon +halos +halse +halts +halva +halve +halwa +hamal +hamba +hamed +hames +hammy +hamza +hanap +hance +hanch +hands +handy +hangi +hangs +hanks +hanky +hansa +hanse +hants +haole +haoma +hapax +haply +happi +happy +hapus +haram +hards +hardy +hared +harem +hares +harim +harks +harls +harms +harns +haros +harps +harpy +harry +harsh +harts +hashy +hasks +hasps +hasta +haste +hasty +hatch +hated +hater +hates +hatha +hauds +haufs +haugh +hauld +haulm +hauls +hault +hauns +haunt +hause +haute +haven +haver +haves +havoc +hawed +hawks +hawms +hawse +hayed +hayer +hayey +hayle +hazan +hazed +hazel +hazer +hazes +heads +heady +heald +heals +heame +heaps +heapy +heard +heare +hears +heart +heast +heath +heats +heave +heavy +heben +hebes +hecht +hecks +heder +hedge +hedgy +heeds +heedy +heels +heeze +hefte +hefts +hefty +heids +heigh +heils +heirs +heist +hejab +hejra +heled +heles +helio +helix +hello +hells +helms +helos +helot +helps +helve +hemal +hemes +hemic +hemin +hemps +hempy +hence +hench +hends +henge +henna +henny +henry +hents +hepar +herbs +herby +herds +heres +herls +herma +herms +herns +heron +heros +herry +herse +hertz +herye +hesps +hests +hetes +heths +heuch +heugh +hevea +hewed +hewer +hewgh +hexad +hexed +hexer +hexes +hexyl +heyed +hiant +hicks +hided +hider +hides +hiems +highs +hight +hijab +hijra +hiked +hiker +hikes +hikoi +hilar +hilch +hillo +hills +hilly +hilts +hilum +hilus +himbo +hinau +hinds +hinge +hings +hinky +hinny +hints +hiois +hiply +hippo +hippy +hired +hiree +hirer +hires +hissy +hists +hitch +hithe +hived +hiver +hives +hizen +hoaed +hoagy +hoard +hoars +hoary +hoast +hobby +hobos +hocks +hocus +hodad +hodja +hoers +hogan +hogen +hoggs +hoghs +hohed +hoick +hoied +hoiks +hoing +hoise +hoist +hokas +hoked +hokes +hokey +hokis +hokku +hokum +holds +holed +holes +holey +holks +holla +hollo +holly +holme +holms +holon +holos +holts +homas +homed +homer +homes +homey +homie +homme +homos +honan +honda +honds +honed +honer +hones +honey +hongi +hongs +honks +honky +honor +hooch +hoods +hoody +hooey +hoofs +hooka +hooks +hooky +hooly +hoons +hoops +hoord +hoors +hoosh +hoots +hooty +hoove +hopak +hoped +hoper +hopes +hoppy +horah +horal +horas +horde +horis +horks +horme +horns +horny +horse +horst +horsy +hosed +hosel +hosen +hoser +hoses +hosey +hosta +hosts +hotch +hotel +hoten +hotly +hotty +houff +houfs +hough +hound +houri +hours +house +houts +hovea +hoved +hovel +hoven +hover +hoves +howbe +howdy +howes +howff +howfs +howks +howls +howre +howso +hoxed +hoxes +hoyas +hoyed +hoyle +hubby +hucks +hudna +hudud +huers +huffs +huffy +huger +huggy +huhus +huias +hulas +hules +hulks +hulky +hullo +hulls +hully +human +humas +humfs +humic +humid +humor +humph +humps +humpy +humus +hunch +hunks +hunky +hunts +hurds +hurls +hurly +hurra +hurry +hurst +hurts +hushy +husks +husky +husos +hussy +hutch +hutia +huzza +huzzy +hwyls +hydra +hydro +hyena +hyens +hygge +hying +hykes +hylas +hyleg +hyles +hylic +hymen +hymns +hynde +hyoid +hyped +hyper +hypes +hypha +hyphy +hypos +hyrax +hyson +hythe +iambi +iambs +ibrik +icers +iched +iches +ichor +icier +icily +icing +icker +ickle +icons +ictal +ictic +ictus +idant +ideal +ideas +idees +ident +idiom +idiot +idled +idler +idles +idola +idols +idyll +idyls +iftar +igapo +igged +igloo +iglus +ihram +ikans +ikats +ikons +ileac +ileal +ileum +ileus +iliac +iliad +ilial +ilium +iller +illth +image +imago +imams +imari +imaum +imbar +imbed +imbue +imide +imido +imids +imine +imino +immew +immit +immix +imped +impel +impis +imply +impot +impro +imshi +imshy +inane +inapt +inarm +inbox +inbye +incel +incle +incog +incur +incus +incut +indew +index +india +indie +indol +indow +indri +indue +inept +inerm +inert +infer +infix +infos +infra +ingan +ingle +ingot +inion +inked +inker +inkle +inlay +inlet +inned +inner +innit +inorb +input +inrun +inset +inspo +intel +inter +intil +intis +intra +intro +inula +inure +inurn +inust +invar +inwit +iodic +iodid +iodin +ionic +iotas +ippon +irade +irate +irids +iring +irked +iroko +irone +irons +irony +isbas +ishes +isled +isles +islet +isnae +issei +issue +istle +itchy +items +ither +ivied +ivies +ivory +ixias +ixnay +ixora +ixtle +izard +izars +izzat +jaaps +jabot +jacal +jacks +jacky +jaded +jades +jafas +jaffa +jagas +jager +jaggs +jaggy +jagir +jagra +jails +jaker +jakes +jakey +jalap +jalop +jambe +jambo +jambs +jambu +james +jammy +jamon +janes +janns +janny +janty +japan +japed +japer +japes +jarks +jarls +jarps +jarta +jarul +jasey +jaspe +jasps +jatos +jauks +jaunt +jaups +javas +javel +jawan +jawed +jaxie +jazzy +jeans +jeats +jebel +jedis +jeels +jeely +jeeps +jeers +jeeze +jefes +jeffs +jehad +jehus +jelab +jello +jells +jelly +jembe +jemmy +jenny +jeons +jerid +jerks +jerky +jerry +jesse +jests +jesus +jetes +jeton +jetty +jeune +jewed +jewel +jewie +jhala +jiaos +jibba +jibbs +jibed +jiber +jibes +jiffs +jiffy +jiggy +jigot +jihad +jills +jilts +jimmy +jimpy +jingo +jinks +jinne +jinni +jinns +jirds +jirga +jirre +jisms +jived +jiver +jives +jivey +jnana +jobed +jobes +jocko +jocks +jocky +jocos +jodel +joeys +johns +joins +joint +joist +joked +joker +jokes +jokey +jokol +joled +joles +jolls +jolly +jolts +jolty +jomon +jomos +jones +jongs +jonty +jooks +joram +jorum +jotas +jotty +jotun +joual +jougs +jouks +joule +jours +joust +jowar +jowed +jowls +jowly +joyed +jubas +jubes +jucos +judas +judge +judgy +judos +jugal +jugum +juice +juicy +jujus +juked +jukes +jukus +julep +jumar +jumbo +jumby +jumps +jumpy +junco +junks +junky +junta +junto +jupes +jupon +jural +jurat +jurel +jures +juror +justs +jutes +jutty +juves +juvie +kaama +kabab +kabar +kabob +kacha +kacks +kadai +kades +kadis +kafir +kagos +kagus +kahal +kaiak +kaids +kaies +kaifs +kaika +kaiks +kails +kaims +kaing +kains +kakas +kakis +kalam +kales +kalif +kalis +kalpa +kamas +kames +kamik +kamis +kamme +kanae +kanas +kandy +kaneh +kanes +kanga +kangs +kanji +kants +kanzu +kaons +kapas +kaphs +kapok +kapow +kappa +kapus +kaput +karas +karat +karks +karma +karns +karoo +karos +karri +karst +karsy +karts +karzy +kasha +kasme +katal +katas +katis +katti +kaugh +kauri +kauru +kaury +kaval +kavas +kawas +kawau +kawed +kayak +kayle +kayos +kazis +kazoo +kbars +kebab +kebar +kebob +kecks +kedge +kedgy +keech +keefs +keeks +keels +keema +keeno +keens +keeps +keets +keeve +kefir +kehua +keirs +kelep +kelim +kells +kelly +kelps +kelpy +kelts +kelty +kembo +kembs +kemps +kempt +kempy +kenaf +kench +kendo +kenos +kente +kents +kepis +kerbs +kerel +kerfs +kerky +kerma +kerne +kerns +keros +kerry +kerve +kesar +kests +ketas +ketch +ketes +ketol +kevel +kevil +kexes +keyed +keyer +khadi +khafs +khaki +khans +khaph +khats +khaya +khazi +kheda +kheth +khets +khoja +khors +khoum +khuds +kiaat +kiack +kiang +kibbe +kibbi +kibei +kibes +kibla +kicks +kicky +kiddo +kiddy +kidel +kidge +kiefs +kiers +kieve +kievs +kight +kikes +kikoi +kiley +kilim +kills +kilns +kilos +kilps +kilts +kilty +kimbo +kinas +kinda +kinds +kindy +kines +kings +kinin +kinks +kinky +kinos +kiore +kiosk +kipes +kippa +kipps +kirby +kirks +kirns +kirri +kisan +kissy +kists +kited +kiter +kites +kithe +kiths +kitty +kitul +kivas +kiwis +klang +klaps +klett +klick +klieg +kliks +klong +kloof +kluge +klutz +knack +knags +knaps +knarl +knars +knaur +knave +knawe +knead +kneed +kneel +knees +knell +knelt +knife +knish +knits +knive +knobs +knock +knoll +knops +knosp +knots +knout +knowe +known +knows +knubs +knurl +knurr +knurs +knuts +koala +koans +koaps +koban +kobos +koels +koffs +kofta +kogal +kohas +kohen +kohls +koine +kojis +kokam +kokas +koker +kokra +kokum +kolas +kolos +kombu +konbu +kondo +konks +kooks +kooky +koori +kopek +kophs +kopje +koppa +korai +koras +korat +kores +korma +koros +korun +korus +koses +kotch +kotos +kotow +koura +kraal +krabs +kraft +krais +krait +krang +krans +kranz +kraut +krays +kreep +kreng +krewe +krill +krona +krone +kroon +krubi +krunk +ksars +kubie +kudos +kudus +kudzu +kufis +kugel +kuias +kukri +kukus +kulak +kulan +kulas +kulfi +kumis +kumys +kuris +kurre +kurta +kurus +kusso +kutas +kutch +kutis +kutus +kuzus +kvass +kvell +kwela +kyack +kyaks +kyang +kyars +kyats +kybos +kydst +kyles +kylie +kylin +kylix +kyloe +kynde +kynds +kypes +kyrie +kytes +kythe +laari +labda +label +labia +labis +labor +labra +laced +lacer +laces +lacet +lacey +lacks +laddy +laded +laden +lader +lades +ladle +laers +laevo +lagan +lager +lahal +lahar +laich +laics +laids +laigh +laika +laiks +laird +lairs +lairy +laith +laity +laked +laker +lakes +lakhs +lakin +laksa +laldy +lalls +lamas +lambs +lamby +lamed +lamer +lames +lamia +lammy +lamps +lanai +lanas +lance +lanch +lande +lands +lanes +lanks +lanky +lants +lapel +lapin +lapis +lapje +lapse +larch +lards +lardy +laree +lares +large +largo +laris +larks +larky +larns +larnt +larum +larva +lased +laser +lases +lassi +lasso +lassu +lassy +lasts +latah +latch +lated +laten +later +latex +lathe +lathi +laths +lathy +latke +latte +latus +lauan +lauch +lauds +laufs +laugh +laund +laura +laval +lavas +laved +laver +laves +lavra +lavvy +lawed +lawer +lawin +lawks +lawns +lawny +laxed +laxer +laxes +laxly +layed +layer +layin +layup +lazar +lazed +lazes +lazos +lazzi +lazzo +leach +leads +leady +leafs +leafy +leaks +leaky +leams +leans +leant +leany +leaps +leapt +leare +learn +lears +leary +lease +leash +least +leats +leave +leavy +leaze +leben +leccy +ledes +ledge +ledgy +ledum +leear +leech +leeks +leeps +leers +leery +leese +leets +leeze +lefte +lefts +lefty +legal +leger +leges +legge +leggo +leggy +legit +lehrs +lehua +leirs +leish +leman +lemed +lemel +lemes +lemma +lemme +lemon +lemur +lends +lenes +lengs +lenis +lenos +lense +lenti +lento +leone +leper +lepid +lepra +lepta +lered +leres +lerps +lesbo +leses +lests +letch +lethe +letup +leuch +leuco +leuds +leugh +levas +levee +level +lever +leves +levin +levis +lewis +lexes +lexis +lezes +lezza +lezzy +liana +liane +liang +liard +liars +liart +libel +liber +libra +libri +lichi +licht +licit +licks +lidar +lidos +liefs +liege +liens +liers +lieus +lieve +lifer +lifes +lifts +ligan +liger +ligge +light +ligne +liked +liken +liker +likes +likin +lilac +lills +lilos +lilts +liman +limas +limax +limba +limbi +limbo +limbs +limby +limed +limen +limes +limey +limit +limma +limns +limos +limpa +limps +linac +linch +linds +lindy +lined +linen +liner +lines +liney +linga +lingo +lings +lingy +linin +links +linky +linns +linny +linos +lints +linty +linum +linux +lions +lipas +lipes +lipid +lipin +lipos +lippy +liras +lirks +lirot +lisks +lisle +lisps +lists +litai +litas +lited +liter +lites +lithe +litho +liths +litre +lived +liven +liver +lives +livid +livor +livre +llama +llano +loach +loads +loafs +loams +loamy +loans +loast +loath +loave +lobar +lobby +lobed +lobes +lobos +lobus +local +loche +lochs +locie +locis +locks +locos +locum +locus +loden +lodes +lodge +loess +lofts +lofty +logan +loges +loggy +logia +logic +logie +login +logoi +logon +logos +lohan +loids +loins +loipe +loirs +lokes +lolls +lolly +lolog +lomas +lomed +lomes +loner +longa +longe +longs +looby +looed +looey +loofa +loofs +looie +looks +looky +looms +loons +loony +loops +loopy +loord +loose +loots +loped +loper +lopes +loppy +loral +loran +lords +lordy +lorel +lores +loric +loris +lorry +losed +losel +losen +loser +loses +lossy +lotah +lotas +lotes +lotic +lotos +lotsa +lotta +lotte +lotto +lotus +loued +lough +louie +louis +louma +lound +louns +loupe +loups +loure +lours +loury +louse +lousy +louts +lovat +loved +lover +loves +lovey +lovie +lowan +lowed +lower +lowes +lowly +lownd +lowne +lowns +lowps +lowry +lowse +lowts +loxed +loxes +loyal +lozen +luach +luaus +lubed +lubes +lubra +luces +lucid +lucks +lucky +lucre +ludes +ludic +ludos +luffa +luffs +luged +luger +luges +lulls +lulus +lumas +lumbi +lumen +lumme +lummy +lumps +lumpy +lunar +lunas +lunch +lunes +lunet +lunge +lungi +lungs +lunks +lunts +lupin +lupus +lurch +lured +lurer +lures +lurex +lurgi +lurgy +lurid +lurks +lurry +lurve +luser +lushy +lusks +lusts +lusty +lusus +lutea +luted +luter +lutes +luvvy +luxed +luxer +luxes +lweis +lyams +lyard +lyart +lyase +lycea +lycee +lycra +lying +lymes +lymph +lynch +lynes +lyres +lyric +lysed +lyses +lysin +lysis +lysol +lyssa +lyted +lytes +lythe +lytic +lytta +maaed +maare +maars +mabes +macas +macaw +maced +macer +maces +mache +machi +macho +machs +macks +macle +macon +macro +madam +madge +madid +madly +madre +maerl +mafia +mafic +mages +maggs +magic +magma +magot +magus +mahoe +mahua +mahwa +maids +maiko +maiks +maile +maill +mails +maims +mains +maire +mairs +maise +maist +maize +major +makar +maker +makes +makis +makos +malam +malar +malas +malax +males +malic +malik +malis +malls +malms +malmy +malts +malty +malus +malva +malwa +mamas +mamba +mambo +mamee +mamey +mamie +mamma +mammy +manas +manat +mandi +maneb +maned +maneh +manes +manet +manga +mange +mango +mangs +mangy +mania +manic +manis +manky +manly +manna +manor +manos +manse +manta +manto +manty +manul +manus +mapau +maple +maqui +marae +marah +maras +march +marcs +mardy +mares +marge +margs +maria +marid +marka +marks +marle +marls +marly +marms +maron +maror +marra +marri +marry +marse +marsh +marts +marvy +masas +mased +maser +mases +mashy +masks +mason +massa +masse +massy +masts +masty +masus +matai +match +mated +mater +mates +matey +maths +matin +matlo +matte +matts +matza +matzo +mauby +mauds +mauls +maund +mauri +mausy +mauts +mauve +mauzy +maven +mavie +mavin +mavis +mawed +mawks +mawky +mawns +mawrs +maxed +maxes +maxim +maxis +mayan +mayas +maybe +mayed +mayor +mayos +mayst +mazed +mazer +mazes +mazey +mazut +mbira +meads +meals +mealy +meane +means +meant +meany +meare +mease +meath +meats +meaty +mebos +mecca +mechs +mecks +medal +media +medic +medii +medle +meeds +meers +meets +meffs +meins +meint +meiny +meith +mekka +melas +melba +melds +melee +melic +melik +mells +melon +melts +melty +memes +memos +menad +mends +mened +menes +menge +mengs +mensa +mense +mensh +menta +mento +menus +meous +meows +merch +mercs +mercy +merde +mered +merel +merer +meres +merge +meril +meris +merit +merks +merle +merls +merry +merse +mesal +mesas +mesel +meses +meshy +mesic +mesne +meson +messy +mesto +metal +meted +meter +metes +metho +meths +metic +metif +metis +metol +metre +metro +meuse +meved +meves +mewed +mewls +meynt +mezes +mezze +mezzo +mhorr +miaou +miaow +miasm +miaul +micas +miche +micht +micks +micky +micos +micra +micro +middy +midge +midgy +midis +midst +miens +mieve +miffs +miffy +mifty +miggs +might +mihas +mihis +miked +mikes +mikra +mikva +milch +milds +miler +miles +milfs +milia +milko +milks +milky +mille +mills +milor +milos +milpa +milts +milty +miltz +mimed +mimeo +mimer +mimes +mimic +mimsy +minae +minar +minas +mince +mincy +minds +mined +miner +mines +minge +mings +mingy +minim +minis +minke +minks +minny +minor +minos +mints +minty +minus +mired +mires +mirex +mirid +mirin +mirks +mirky +mirly +miros +mirth +mirvs +mirza +misch +misdo +miser +mises +misgo +misos +missa +missy +mists +misty +mitch +miter +mites +mitis +mitre +mitts +mixed +mixen +mixer +mixes +mixte +mixup +mizen +mizzy +mneme +moans +moats +mobby +mobes +mobey +mobie +moble +mocha +mochi +mochs +mochy +mocks +modal +model +modem +moder +modes +modge +modii +modus +moers +mofos +moggy +mogul +mohel +mohos +mohrs +mohua +mohur +moile +moils +moira +moire +moist +moits +mojos +mokes +mokis +mokos +molal +molar +molas +molds +moldy +moled +moles +molla +molls +molly +molto +molts +molys +momes +momma +mommy +momus +monad +monal +monas +monde +mondo +moner +money +mongo +mongs +monic +monie +monks +monos +monte +month +monty +moobs +mooch +moods +moody +mooed +mooks +moola +mooli +mools +mooly +moong +moons +moony +moops +moors +moory +moose +moots +moove +moped +moper +mopes +mopey +moppy +mopsy +mopus +morae +moral +moras +morat +moray +morel +mores +moria +morne +morns +moron +morph +morra +morro +morse +morts +mosed +moses +mosey +mosks +mosso +mossy +moste +mosts +moted +motel +moten +motes +motet +motey +moths +mothy +motif +motis +motor +motte +motto +motts +motty +motus +motza +mouch +moues +mould +mouls +moult +mound +mount +moups +mourn +mouse +moust +mousy +mouth +moved +mover +moves +movie +mowas +mowed +mower +mowra +moxas +moxie +moyas +moyle +moyls +mozed +mozes +mozos +mpret +mucho +mucic +mucid +mucin +mucks +mucky +mucor +mucro +mucus +muddy +mudge +mudir +mudra +muffs +mufti +mugga +muggs +muggy +muhly +muids +muils +muirs +muist +mujik +mulch +mulct +muled +mules +muley +mulga +mulie +mulla +mulls +mulse +mulsh +mumms +mummy +mumps +mumsy +mumus +munch +munga +munge +mungo +mungs +munis +munts +muntu +muons +mural +muras +mured +mures +murex +murid +murks +murky +murls +murly +murra +murre +murri +murrs +murry +murti +murva +musar +musca +mused +muser +muses +muset +musha +mushy +music +musit +musks +musky +musos +musse +mussy +musth +musts +musty +mutch +muted +muter +mutes +mutha +mutis +muton +mutts +muxed +muxes +muzak +muzzy +mvule +myall +mylar +mynah +mynas +myoid +myoma +myope +myops +myopy +myrrh +mysid +mythi +myths +mythy +myxos +mzees +naams +naans +nabes +nabis +nabks +nabla +nabob +nache +nacho +nacre +nadas +nadir +naeve +naevi +naffs +nagas +naggy +nagor +nahal +naiad +naifs +naiks +nails +naira +nairu +naive +naked +naker +nakfa +nalas +naled +nalla +named +namer +names +namma +namus +nanas +nance +nancy +nandu +nanna +nanny +nanos +nanua +napas +naped +napes +napoo +nappa +nappe +nappy +naras +narco +narcs +nards +nares +naric +naris +narks +narky +narre +nasal +nashi +nasty +natal +natch +nates +natis +natty +nauch +naunt +naval +navar +navel +naves +navew +navvy +nawab +nazes +nazir +nazis +nduja +neafe +neals +neaps +nears +neath +neats +nebek +nebel +necks +neddy +needs +needy +neeld +neele +neemb +neems +neeps +neese +neeze +negro +negus +neifs +neigh +neist +neive +nelis +nelly +nemas +nemns +nempt +nenes +neons +neper +nepit +neral +nerds +nerdy +nerka +nerks +nerol +nerts +nertz +nerve +nervy +nests +netes +netop +netts +netty +neuks +neume +neums +nevel +never +neves +nevus +newbs +newed +newel +newer +newie +newly +newsy +newts +nexts +nexus +ngaio +ngana +ngati +ngoma +ngwee +nicad +nicer +niche +nicht +nicks +nicol +nidal +nided +nides +nidor +nidus +niece +niefs +nieve +nifes +niffs +niffy +nifty +niger +nighs +night +nihil +nikab +nikah +nikau +nills +nimbi +nimbs +nimps +niner +nines +ninja +ninny +ninon +ninth +nipas +nippy +niqab +nirls +nirly +nisei +nisse +nisus +niter +nites +nitid +niton +nitre +nitro +nitry +nitty +nival +nixed +nixer +nixes +nixie +nizam +nkosi +noahs +nobby +noble +nobly +nocks +nodal +noddy +nodes +nodus +noels +noggs +nohow +noils +noily +noint +noirs +noise +noisy +noles +nolls +nolos +nomad +nomas +nomen +nomes +nomic +nomoi +nomos +nonas +nonce +nones +nonet +nongs +nonis +nonny +nonyl +noobs +nooit +nooks +nooky +noons +noops +noose +nopal +noria +noris +norks +norma +norms +north +nosed +noser +noses +nosey +notal +notch +noted +noter +notes +notum +nould +noule +nouls +nouns +nouny +noups +novae +novas +novel +novum +noway +nowed +nowls +nowts +nowty +noxal +noxes +noyau +noyed +noyes +nubby +nubia +nucha +nuddy +nuder +nudes +nudge +nudie +nudzh +nuffs +nugae +nuked +nukes +nulla +nulls +numbs +numen +nummy +nunny +nurds +nurdy +nurls +nurrs +nurse +nutso +nutsy +nutty +nyaff +nyala +nying +nylon +nymph +nyssa +oaked +oaken +oaker +oakum +oared +oases +oasis +oasts +oaten +oater +oaths +oaves +obang +obeah +obeli +obese +obeys +obias +obied +obiit +obits +objet +oboes +obole +oboli +obols +occam +occur +ocean +ocher +oches +ochre +ochry +ocker +ocrea +octad +octal +octan +octas +octet +octyl +oculi +odahs +odals +odder +oddly +odeon +odeum +odism +odist +odium +odors +odour +odyle +odyls +ofays +offal +offed +offer +offie +oflag +often +ofter +ogams +ogeed +ogees +oggin +ogham +ogive +ogled +ogler +ogles +ogmic +ogres +ohias +ohing +ohmic +ohone +oidia +oiled +oiler +oinks +oints +ojime +okapi +okays +okehs +okras +oktas +olden +older +oldie +oleic +olein +olent +oleos +oleum +olios +olive +ollas +ollav +oller +ollie +ology +olpae +olpes +omasa +omber +ombre +ombus +omega +omens +omers +omits +omlah +omovs +omrah +oncer +onces +oncet +oncus +onely +oners +onery +onion +onium +onkus +onlay +onned +onset +ontic +oobit +oohed +oomph +oonts +ooped +oorie +ooses +ootid +oozed +oozes +opahs +opals +opens +opepe +opera +opine +oping +opium +oppos +opsin +opted +opter +optic +orach +oracy +orals +orang +orant +orate +orbed +orbit +orcas +orcin +order +ordos +oread +orfes +organ +orgia +orgic +orgue +oribi +oriel +orixa +orles +orlon +orlop +ormer +ornis +orpin +orris +ortho +orval +orzos +oscar +oshac +osier +osmic +osmol +ossia +ostia +otaku +otary +other +ottar +otter +ottos +oubit +oucht +ouens +ought +ouija +oulks +oumas +ounce +oundy +oupas +ouped +ouphe +ouphs +ourie +ousel +ousts +outby +outdo +outed +outer +outgo +outre +outro +outta +ouzel +ouzos +ovals +ovary +ovate +ovels +ovens +overs +overt +ovine +ovist +ovoid +ovoli +ovolo +ovule +owche +owies +owing +owled +owler +owlet +owned +owner +owres +owrie +owsen +oxbow +oxers +oxeye +oxide +oxids +oxies +oxime +oxims +oxlip +oxter +oyers +ozeki +ozone +ozzie +paals +paans +pacas +paced +pacer +paces +pacey +pacha +packs +pacos +pacta +pacts +paddy +padis +padle +padma +padre +padri +paean +paedo +paeon +pagan +paged +pager +pages +pagle +pagod +pagri +paiks +pails +pains +paint +paire +pairs +paisa +paise +pakka +palas +palay +palea +paled +paler +pales +palet +palis +palki +palla +palls +pally +palms +palmy +palpi +palps +palsa +palsy +pampa +panax +pance +panda +pands +pandy +paned +panel +panes +panga +pangs +panic +panim +panko +panne +panni +pansy +panto +pants +panty +paoli +paolo +papal +papas +papaw +paper +papes +pappi +pappy +parae +paras +parch +pardi +pards +pardy +pared +paren +pareo +parer +pares +pareu +parev +parge +pargo +paris +parka +parki +parks +parky +parle +parly +parma +parol +parps +parra +parrs +parry +parse +parti +parts +party +parve +parvo +paseo +pases +pasha +pashm +paska +paspy +passe +pasta +paste +pasts +pasty +patch +pated +paten +pater +pates +paths +patin +patio +patka +patly +patsy +patte +patty +patus +pauas +pauls +pause +pavan +paved +paven +paver +paves +pavid +pavin +pavis +pawas +pawaw +pawed +pawer +pawks +pawky +pawls +pawns +paxes +payed +payee +payer +payor +paysd +peace +peach +peage +peags +peaks +peaky +peals +peans +peare +pearl +pears +peart +pease +peats +peaty +peavy +peaze +pebas +pecan +pechs +pecke +pecks +pecky +pedal +pedes +pedis +pedro +peece +peeks +peels +peens +peeoy +peepe +peeps +peers +peery +peeve +peggy +peghs +peins +peise +peize +pekan +pekes +pekin +pekoe +pelas +pelau +peles +pelfs +pells +pelma +pelon +pelta +pelts +penal +pence +pends +pendu +pened +penes +pengo +penie +penis +penks +penna +penne +penni +penny +pents +peons +peony +pepla +pepos +peppy +pepsi +perai +perce +perch +percs +perdu +perdy +perea +peres +peril +peris +perks +perky +perms +perns +perog +perps +perry +perse +perst +perts +perve +pervo +pervs +pervy +pesky +pesos +pesto +pests +pesty +petal +petar +peter +petit +petre +petri +petti +petto +petty +pewee +pewit +peyse +phage +phang +phare +pharm +phase +pheer +phene +pheon +phese +phial +phish +phizz +phlox +phoca +phone +phono +phons +phony +photo +phots +phpht +phuts +phyla +phyle +piani +piano +pians +pibal +pical +picas +piccy +picks +picky +picot +picra +picul +piece +piend +piers +piert +pieta +piets +piety +piezo +piggy +pight +pigmy +piing +pikas +pikau +piked +piker +pikes +pikey +pikis +pikul +pilae +pilaf +pilao +pilar +pilau +pilaw +pilch +pilea +piled +pilei +piler +piles +pilis +pills +pilot +pilow +pilum +pilus +pimas +pimps +pinas +pinch +pined +pines +piney +pingo +pings +pinko +pinks +pinky +pinna +pinny +pinon +pinot +pinta +pinto +pints +pinup +pions +piony +pious +pioye +pioys +pipal +pipas +piped +piper +pipes +pipet +pipis +pipit +pippy +pipul +pique +pirai +pirls +pirns +pirog +pisco +pises +pisky +pisos +pissy +piste +pitas +pitch +piths +pithy +piton +pitot +pitta +piums +pivot +pixel +pixes +pixie +pized +pizes +pizza +plaas +place +plack +plage +plaid +plain +plait +plane +plank +plans +plant +plaps +plash +plasm +plast +plate +plats +platt +platy +playa +plays +plaza +plead +pleas +pleat +plebe +plebs +plena +pleon +plesh +plews +plica +plied +plier +plies +plims +pling +plink +ploat +plods +plong +plonk +plook +plops +plots +plotz +plouk +plows +ploye +ploys +pluck +plues +pluff +plugs +plumb +plume +plump +plums +plumy +plunk +pluot +plush +pluto +plyer +poach +poaka +poake +poboy +pocks +pocky +podal +poddy +podex +podge +podgy +podia +poems +poeps +poesy +poets +pogey +pogge +pogos +pohed +poilu +poind +point +poise +pokal +poked +poker +pokes +pokey +pokie +polar +poled +poler +poles +poley +polio +polis +polje +polka +polks +polls +polly +polos +polts +polyp +polys +pombe +pomes +pommy +pomos +pomps +ponce +poncy +ponds +pones +poney +ponga +pongo +pongs +pongy +ponks +ponts +ponty +ponzu +pooch +poods +pooed +poofs +poofy +poohs +pooja +pooka +pooks +pools +poons +poops +poopy +poori +poort +poots +poove +poovy +popes +poppa +poppy +popsy +porae +poral +porch +pored +porer +pores +porge +porgy +porin +porks +porky +porno +porns +porny +porta +ports +porty +posed +poser +poses +posey +posho +posit +posse +posts +potae +potch +poted +potes +potin +potoo +potsy +potto +potts +potty +pouch +pouff +poufs +pouke +pouks +poule +poulp +poult +pound +poupe +poupt +pours +pouts +pouty +powan +power +powin +pownd +powns +powny +powre +poxed +poxes +poynt +poyou +poyse +pozzy +praam +prads +prahu +prams +prana +prang +prank +praos +prase +prate +prats +pratt +praty +praus +prawn +prays +predy +preed +preen +prees +preif +prems +premy +prent +preon +preop +preps +presa +prese +press +prest +preve +prexy +preys +prial +price +prick +pricy +pride +pried +prief +prier +pries +prigs +prill +prima +prime +primi +primo +primp +prims +primy +prink +print +prion +prior +prise +prism +priss +privy +prize +proas +probe +probs +prods +proem +profs +progs +proin +proke +prole +proll +promo +proms +prone +prong +pronk +proof +props +prore +prose +proso +pross +prost +prosy +proto +proud +proul +prove +prowl +prows +proxy +proyn +prude +prune +prunt +pruta +pryer +pryse +psalm +pseud +pshaw +psion +psoae +psoai +psoas +psora +psych +psyop +pubco +pubes +pubic +pubis +pucan +pucer +puces +pucka +pucks +puddy +pudge +pudgy +pudic +pudor +pudsy +pudus +puers +puffa +puffs +puffy +puggy +pugil +puhas +pujah +pujas +pukas +puked +puker +pukes +pukey +pukka +pukus +pulao +pulas +puled +puler +pules +pulik +pulis +pulka +pulks +pulli +pulls +pully +pulmo +pulps +pulpy +pulse +pulus +pumas +pumie +pumps +punas +punce +punch +punga +pungs +punji +punka +punks +punky +punny +punto +punts +punty +pupae +pupal +pupas +pupil +puppy +pupus +purda +pured +puree +purer +pures +purge +purin +puris +purls +purpy +purrs +purse +pursy +purty +puses +pushy +pusle +pussy +putid +puton +putti +putto +putts +putty +puzel +pwned +pyats +pyets +pygal +pygmy +pyins +pylon +pyned +pynes +pyoid +pyots +pyral +pyran +pyres +pyrex +pyric +pyros +pyxed +pyxes +pyxie +pyxis +pzazz +qadis +qaids +qajaq +qanat +qapik +qibla +qophs +qorma +quack +quads +quaff +quags +quail +quair +quais +quake +quaky +quale +qualm +quant +quare +quark +quart +quash +quasi +quass +quate +quats +quayd +quays +qubit +quean +queen +queer +quell +queme +quena +quern +query +quest +queue +queyn +queys +quich +quick +quids +quiet +quiff +quill +quilt +quims +quina +quine +quino +quins +quint +quipo +quips +quipu +quire +quirk +quirt +quist +quite +quits +quoad +quods +quoif +quoin +quoit +quoll +quonk +quops +quota +quote +quoth +qursh +quyte +rabat +rabbi +rabic +rabid +rabis +raced +racer +races +rache +racks +racon +radar +radge +radii +radio +radix +radon +raffs +rafts +ragas +ragde +raged +ragee +rager +rages +ragga +raggs +raggy +ragis +ragus +rahed +rahui +raias +raids +raiks +raile +rails +raine +rains +rainy +raird +raise +raita +raits +rajah +rajas +rajes +raked +rakee +raker +rakes +rakia +rakis +rakus +rales +rally +ralph +ramal +ramee +ramen +ramet +ramie +ramin +ramis +rammy +ramps +ramus +ranas +rance +ranch +rands +randy +ranee +ranga +range +rangi +rangs +rangy +ranid +ranis +ranke +ranks +rants +raped +raper +rapes +raphe +rapid +rappe +rared +raree +rarer +rares +rarks +rased +raser +rases +rasps +raspy +rasse +rasta +ratal +ratan +ratas +ratch +rated +ratel +rater +rates +ratha +rathe +raths +ratio +ratoo +ratos +ratty +ratus +rauns +raupo +raved +ravel +raven +raver +raves +ravey +ravin +rawer +rawin +rawly +rawns +raxed +raxes +rayah +rayas +rayed +rayle +rayne +rayon +razed +razee +razer +razes +razoo +razor +reach +react +readd +reads +ready +reais +reaks +realm +realo +reals +reame +reams +reamy +reans +reaps +rearm +rears +reast +reata +reate +reave +rebar +rebbe +rebec +rebel +rebid +rebit +rebop +rebus +rebut +rebuy +recal +recap +recce +recco +reccy +recit +recks +recon +recta +recti +recto +recur +recut +redan +redds +reddy +reded +redes +redia +redid +redip +redly +redon +redos +redox +redry +redub +redux +redye +reech +reede +reeds +reedy +reefs +reefy +reeks +reeky +reels +reens +reest +reeve +refed +refel +refer +reffo +refis +refit +refix +refly +refry +regal +regar +reges +reggo +regie +regma +regna +regos +regur +rehab +rehem +reifs +reify +reign +reiki +reiks +reink +reins +reird +reist +reive +rejig +rejon +reked +rekes +rekey +relax +relay +relet +relic +relie +relit +rello +reman +remap +remen +remet +remex +remit +remix +renal +renay +rends +renew +reney +renga +renig +renin +renne +renos +rente +rents +reoil +reorg +repay +repeg +repel +repin +repla +reply +repos +repot +repps +repro +reran +rerig +rerun +resat +resaw +resay +resee +reses +reset +resew +resid +resin +resit +resod +resow +resto +rests +resty +resus +retag +retax +retch +retem +retia +retie +retox +retro +retry +reuse +revel +revet +revie +revue +rewan +rewax +rewed +rewet +rewin +rewon +rewth +rexes +rezes +rheas +rheme +rheum +rhies +rhime +rhine +rhino +rhody +rhomb +rhone +rhumb +rhyme +rhyne +rhyta +riads +rials +riant +riata +ribas +ribby +ribes +riced +ricer +rices +ricey +richt +ricin +ricks +rider +rides +ridge +ridgy +ridic +riels +riems +rieve +rifer +riffs +rifle +rifte +rifts +rifty +riggs +right +rigid +rigol +rigor +riled +riles +riley +rille +rills +rimae +rimed +rimer +rimes +rimus +rinds +rindy +rines +rings +rinks +rinse +rioja +riots +riped +ripen +riper +ripes +ripps +risen +riser +rises +rishi +risks +risky +risps +risus +rites +ritts +ritzy +rival +rivas +rived +rivel +riven +river +rives +rivet +riyal +rizas +roach +roads +roams +roans +roars +roary +roast +roate +robed +robes +robin +roble +robot +rocks +rocky +roded +rodeo +rodes +roger +rogue +roguy +rohes +roids +roils +roily +roins +roist +rojak +rojis +roked +roker +rokes +rolag +roles +rolfs +rolls +romal +roman +romeo +romps +ronde +rondo +roneo +rones +ronin +ronne +ronte +ronts +roods +roofs +roofy +rooks +rooky +rooms +roomy +roons +roops +roopy +roosa +roose +roost +roots +rooty +roped +roper +ropes +ropey +roque +roral +rores +roric +rorid +rorie +rorts +rorty +rosed +roses +roset +roshi +rosin +rosit +rosti +rosts +rotal +rotan +rotas +rotch +roted +rotes +rotis +rotls +roton +rotor +rotos +rotte +rouen +roues +rouge +rough +roule +rouls +roums +round +roups +roupy +rouse +roust +route +routh +routs +roved +roven +rover +roves +rowan +rowdy +rowed +rowel +rowen +rower +rowie +rowme +rownd +rowth +rowts +royal +royne +royst +rozet +rozit +ruana +rubai +rubby +rubel +rubes +rubin +ruble +rubli +rubus +ruche +rucks +rudas +rudds +ruddy +ruder +rudes +rudie +rudis +rueda +ruers +ruffe +ruffs +rugae +rugal +rugby +ruggy +ruing +ruins +rukhs +ruled +ruler +rules +rumal +rumba +rumbo +rumen +rumes +rumly +rummy +rumor +rumpo +rumps +rumpy +runch +runds +runed +runes +rungs +runic +runny +runts +runty +rupee +rupia +rural +rurps +rurus +rusas +ruses +rushy +rusks +rusma +russe +rusts +rusty +ruths +rutin +rutty +ryals +rybat +ryked +rykes +rymme +rynds +ryots +ryper +saags +sabal +sabed +saber +sabes +sabha +sabin +sabir +sable +sabot +sabra +sabre +sacks +sacra +saddo +sades +sadhe +sadhu +sadis +sadly +sados +sadza +safed +safer +safes +sagas +sager +sages +saggy +sagos +sagum +saheb +sahib +saice +saick +saics +saids +saiga +sails +saims +saine +sains +saint +sairs +saist +saith +sajou +sakai +saker +sakes +sakia +sakis +sakti +salad +salal +salat +salep +sales +salet +salic +salix +salle +sally +salmi +salol +salon +salop +salpa +salps +salsa +salse +salto +salts +salty +salue +salut +salve +salvo +saman +samas +samba +sambo +samek +samel +samen +sames +samey +samfu +sammy +sampi +samps +sands +sandy +saned +saner +sanes +sanga +sangh +sango +sangs +sanko +sansa +santo +sants +saola +sapan +sapid +sapor +sappy +saran +sards +sared +saree +sarge +sargo +sarin +saris +sarks +sarky +sarod +saros +sarus +saser +sasin +sasse +sassy +satai +satay +sated +satem +sates +satin +satis +satyr +sauba +sauce +sauch +saucy +saugh +sauls +sault +sauna +saunt +saury +saute +sauts +saved +saver +saves +savey +savin +savor +savoy +savvy +sawah +sawed +sawer +saxes +sayed +sayer +sayid +sayne +sayon +sayst +sazes +scabs +scads +scaff +scags +scail +scala +scald +scale +scall +scalp +scaly +scamp +scams +scand +scans +scant +scapa +scape +scapi +scare +scarf +scarp +scars +scart +scary +scath +scats +scatt +scaud +scaup +scaur +scaws +sceat +scena +scend +scene +scent +schav +schmo +schul +schwa +scion +sclim +scody +scoff +scogs +scold +scone +scoog +scoop +scoot +scopa +scope +scops +score +scorn +scots +scoug +scoup +scour +scout +scowl +scowp +scows +scrab +scrae +scrag +scram +scran +scrap +scrat +scraw +scray +scree +screw +scrim +scrip +scrob +scrod +scrog +scrow +scrub +scrum +scuba +scudi +scudo +scuds +scuff +scuft +scugs +sculk +scull +sculp +sculs +scums +scups +scurf +scurs +scuse +scuta +scute +scuts +scuzz +scyes +sdayn +sdein +seals +seame +seams +seamy +seans +seare +sears +sease +seats +seaze +sebum +secco +sechs +sects +sedan +seder +sedes +sedge +sedgy +sedum +seeds +seedy +seeks +seeld +seels +seely +seems +seeps +seepy +seers +sefer +segar +segni +segno +segol +segos +segue +sehri +seifs +seils +seine +seirs +seise +seism +seity +seiza +seize +sekos +sekts +selah +seles +selfs +sella +selle +sells +selva +semee +semen +semes +semie +semis +senas +sends +senes +sengi +senna +senor +sensa +sense +sensi +sente +senti +sents +senvy +senza +sepad +sepal +sepia +sepic +sepoy +septa +septs +serac +serai +seral +sered +serer +seres +serfs +serge +seric +serif +serin +serks +seron +serow +serra +serre +serrs +serry +serum +serve +servo +sesey +sessa +setae +setal +seton +setts +setup +seven +sever +sewan +sewar +sewed +sewel +sewen +sewer +sewin +sexed +sexer +sexes +sexto +sexts +seyen +shack +shade +shads +shady +shaft +shags +shahs +shake +shako +shakt +shaky +shale +shall +shalm +shalt +shaly +shama +shame +shams +shand +shank +shans +shape +shaps +shard +share +shark +sharn +sharp +shash +shaul +shaveaahed +shawl +shawm +shawn +shaws +shaya +shays +shchi +sheaf +sheal +shear +sheas +sheds +sheel +sheen +sheep +sheer +sheet +sheik +shelf +shell +shend +shent +sheol +sherd +shere +shero +shets +sheva +shewn +shews +shiai +shied +shiel +shier +shies +shift +shill +shily +shims +shine +shins +shiny +ships +shire +shirk +shirr +shirs +shirt +shish +shiso +shist +shite +shits +shiur +shiva +shive +shivs +shlep +shlub +shmek +shmoe +shoal +shoat +shock +shoed +shoer +shoes +shogi +shogs +shoji +shojo +shola +shone +shook +shool +shoon +shoos +shoot +shope +shops +shore +shorl +shorn +short +shote +shots +shott +shout +shove +showd +shown +shows +showy +shoyu +shred +shrew +shris +shrow +shrub +shrug +shtik +shtum +shtup +shuck +shule +shuln +shuls +shuns +shunt +shura +shush +shute +shuts +shwas +shyer +shyly +sials +sibbs +sibyl +sices +sicht +sicko +sicks +sicky +sidas +sided +sider +sides +sidha +sidhe +sidle +siege +sield +siens +sient +sieth +sieur +sieve +sifts +sighs +sight +sigil +sigla +sigma +signa +signs +sijos +sikas +siker +sikes +silds +siled +silen +siler +siles +silex +silks +silky +sills +silly +silos +silts +silty +silva +simar +simas +simba +simis +simps +simul +since +sinds +sined +sines +sinew +singe +sings +sinhs +sinks +sinky +sinus +siped +sipes +sippy +sired +siree +siren +sires +sirih +siris +siroc +sirra +sirup +sisal +sises +sissy +sista +sists +sitar +sited +sites +sithe +sitka +situp +situs +siver +sixer +sixes +sixmo +sixte +sixth +sixty +sizar +sized +sizel +sizer +sizes +skags +skail +skald +skank +skart +skate +skats +skatt +skaws +skean +skear +skeds +skeed +skeef +skeen +skeer +skees +skeet +skegg +skegs +skein +skelf +skell +skelm +skelp +skene +skens +skeos +skeps +skers +skets +skews +skids +skied +skier +skies +skiey +skiff +skill +skimo +skimp +skims +skink +skins +skint +skios +skips +skirl +skirr +skirt +skite +skits +skive +skivy +sklim +skoal +skody +skoff +skogs +skols +skool +skort +skosh +skran +skrik +skuas +skugs +skulk +skull +skunk +skyed +skyer +skyey +skyfs +skyre +skyrs +skyte +slabs +slack +slade +slaes +slags +slaid +slain +slake +slams +slane +slang +slank +slant +slaps +slart +slash +slate +slats +slaty +slave +slaws +slays +slebs +sleds +sleek +sleep +sleer +sleet +slept +slews +sleys +slice +slick +slide +slier +slily +slime +slims +slimy +sling +slink +slipe +slips +slipt +slish +slits +slive +sloan +slobs +sloes +slogs +sloid +slojd +slomo +sloom +sloop +sloot +slope +slops +slopy +slorm +slosh +sloth +slots +slove +slows +sloyd +slubb +slubs +slued +slues +sluff +slugs +sluit +slump +slums +slung +slunk +slurb +slurp +slurs +sluse +slush +sluts +slyer +slyly +slype +smaak +smack +smaik +small +smalm +smalt +smarm +smart +smash +smaze +smear +smeek +smees +smeik +smeke +smell +smelt +smerk +smews +smile +smirk +smirr +smirs +smite +smith +smits +smock +smogs +smoke +smoko +smoky +smolt +smoor +smoot +smore +smorg +smote +smout +smowt +smugs +smurs +smush +smuts +snabs +snack +snafu +snags +snail +snake +snaky +snaps +snare +snarf +snark +snarl +snars +snary +snash +snath +snaws +snead +sneak +sneap +snebs +sneck +sneds +sneed +sneer +snees +snell +snibs +snick +snide +snies +sniff +snift +snigs +snipe +snips +snipy +snirt +snits +snobs +snods +snoek +snoep +snogs +snoke +snood +snook +snool +snoop +snoot +snore +snort +snots +snout +snowk +snows +snowy +snubs +snuck +snuff +snugs +snush +snyes +soaks +soaps +soapy +soare +soars +soave +sobas +sober +socas +soces +socko +socks +socle +sodas +soddy +sodic +sodom +sofar +sofas +softa +softs +softy +soger +soggy +sohur +soils +soily +sojas +sojus +sokah +soken +sokes +sokol +solah +solan +solar +solas +solde +soldi +soldo +solds +soled +solei +soler +soles +solid +solon +solos +solum +solus +solve +soman +somas +sonar +sonce +sonde +sones +songs +sonic +sonly +sonne +sonny +sonse +sonsy +sooey +sooks +sooky +soole +sools +sooms +soops +soote +sooth +soots +sooty +sophs +sophy +sopor +soppy +sopra +soral +soras +sorbo +sorbs +sorda +sordo +sords +sored +soree +sorel +sorer +sores +sorex +sorgo +sorns +sorra +sorry +sorta +sorts +sorus +soths +sotol +souce +souct +sough +souks +souls +soums +sound +soups +soupy +sours +souse +south +souts +sowar +sowce +sowed +sower +sowff +sowfs +sowle +sowls +sowms +sownd +sowne +sowps +sowse +sowth +soyas +soyle +soyuz +sozin +space +spacy +spade +spado +spaed +spaer +spaes +spags +spahi +spail +spain +spait +spake +spald +spale +spall +spalt +spams +spane +spang +spank +spans +spard +spare +spark +spars +spart +spasm +spate +spats +spaul +spawl +spawn +spaws +spayd +spays +spaza +spazz +speak +speal +spean +spear +speat +speck +specs +spect +speed +speel +speer +speil +speir +speks +speld +spelk +spell +spelt +spend +spent +speos +sperm +spets +speug +spews +spewy +spial +spica +spice +spick +spics +spicy +spide +spied +spiel +spier +spies +spiff +spifs +spike +spiks +spiky +spile +spill +spilt +spims +spina +spine +spink +spins +spiny +spire +spirt +spiry +spite +spits +spitz +spivs +splat +splay +split +splog +spode +spods +spoil +spoke +spoof +spook +spool +spoom +spoon +spoor +spoot +spore +spork +sport +sposh +spots +spout +sprad +sprag +sprat +spray +spred +spree +sprew +sprig +sprit +sprod +sprog +sprue +sprug +spuds +spued +spuer +spues +spugs +spule +spume +spumy +spunk +spurn +spurs +spurt +sputa +spyal +spyre +squab +squad +squat +squaw +squeg +squib +squid +squit +squiz +stabs +stack +stade +staff +stage +stags +stagy +staid +staig +stain +stair +stake +stale +stalk +stall +stamp +stand +stane +stang +stank +staph +staps +stare +stark +starn +starr +stars +start +stash +state +stats +staun +stave +staws +stays +stead +steak +steal +steam +stean +stear +stedd +stede +steds +steed +steek +steel +steem +steen +steep +steer +steil +stein +stela +stele +stell +steme +stems +stend +steno +stens +stent +steps +stept +stere +stern +stets +stews +stewy +steys +stich +stick +stied +sties +stiff +stilb +stile +still +stilt +stime +stims +stimy +sting +stink +stint +stipa +stipe +stire +stirk +stirp +stirs +stive +stivy +stoae +stoai +stoas +stoat +stobs +stock +stoep +stogy +stoic +stoit +stoke +stole +stoln +stoma +stomp +stond +stone +stong +stonk +stonn +stony +stood +stook +stool +stoop +stoor +stope +stops +stopt +store +stork +storm +story +stoss +stots +stott +stoun +stoup +stour +stout +stove +stown +stowp +stows +strad +strae +strag +strak +strap +straw +stray +strep +strew +stria +strig +strim +strip +strop +strow +stroy +strum +strut +stubs +stuck +stude +studs +study +stuff +stull +stulm +stumm +stump +stums +stung +stunk +stuns +stunt +stupa +stupe +sture +sturt +styed +styes +style +styli +stylo +styme +stymy +styre +styte +suave +subah +subas +subby +suber +subha +succi +sucks +sucky +sucre +sudds +sudor +sudsy +suede +suent +suers +suete +suets +suety +sugan +sugar +sughs +sugos +suhur +suids +suing +suint +suite +suits +sujee +sukhs +sukuk +sulci +sulfa +sulfo +sulks +sulky +sully +sulph +sulus +sumac +sumis +summa +sumos +sumph +sumps +sunis +sunks +sunna +sunns +sunny +sunup +super +supes +supra +surah +sural +suras +surat +surds +sured +surer +sures +surfs +surfy +surge +surgy +surly +surra +sused +suses +sushi +susus +sutor +sutra +sutta +swabs +swack +swads +swage +swags +swail +swain +swale +swaly +swami +swamp +swamy +swang +swank +swans +swaps +swapt +sward +sware +swarf +swarm +swart +swash +swath +swats +swayl +sways +sweal +swear +sweat +swede +sweed +sweel +sweep +sweer +swees +sweet +sweir +swell +swelt +swept +swerf +sweys +swies +swift +swigs +swile +swill +swims +swine +swing +swink +swipe +swire +swirl +swish +swiss +swith +swits +swive +swizz +swobs +swole +swoln +swoon +swoop +swops +swopt +sword +swore +sworn +swots +swoun +swung +sybbe +sybil +syboe +sybow +sycee +syces +sycon +syens +syker +sykes +sylis +sylph +sylva +symar +synch +syncs +synds +syned +synes +synod +synth +syped +sypes +syphs +syrah +syren +syrup +sysop +sythe +syver +taals +taata +tabby +taber +tabes +tabid +tabis +tabla +table +taboo +tabor +tabun +tabus +tacan +taces +tacet +tache +tacho +tachs +tacit +tacks +tacky +tacos +tacts +taels +taffy +tafia +taggy +tagma +tahas +tahrs +taiga +taigs +taiko +tails +tains +taint +taira +taish +taits +tajes +takas +taken +taker +takes +takhi +takin +takis +takky +talak +talaq +talar +talas +talcs +talcy +talea +taler +tales +talks +talky +talls +tally +talma +talon +talpa +taluk +talus +tamal +tamed +tamer +tames +tamin +tamis +tammy +tamps +tanas +tanga +tangi +tango +tangs +tangy +tanhs +tanka +tanks +tanky +tanna +tansy +tanti +tanto +tanty +tapas +taped +tapen +taper +tapes +tapet +tapir +tapis +tappa +tapus +taras +tardo +tardy +tared +tares +targa +targe +tarns +taroc +tarok +taros +tarot +tarps +tarre +tarry +tarsi +tarts +tarty +tasar +tased +taser +tases +tasks +tassa +tasse +tasso +taste +tasty +tatar +tater +tates +taths +tatie +tatou +tatts +tatty +tatus +taube +tauld +taunt +tauon +taupe +tauts +tavah +tavas +taver +tawai +tawas +tawed +tawer +tawie +tawny +tawse +tawts +taxed +taxer +taxes +taxis +taxol +taxon +taxor +taxus +tayra +tazza +tazze +teach +teade +teads +teaed +teaks +teals +teams +tears +teary +tease +teats +teaze +techs +techy +tecta +teddy +teels +teems +teend +teene +teens +teeny +teers +teeth +teffs +teggs +tegua +tegus +tehrs +teiid +teils +teind +teins +telae +telco +teles +telex +telia +telic +tells +telly +teloi +telos +temed +temes +tempi +tempo +temps +tempt +temse +tench +tends +tendu +tenes +tenet +tenge +tenia +tenne +tenno +tenny +tenon +tenor +tense +tenth +tents +tenty +tenue +tepal +tepas +tepee +tepid +tepoy +terai +teras +terce +terek +teres +terfe +terfs +terga +terms +terne +terns +terra +terry +terse +terts +tesla +testa +teste +tests +testy +tetes +teths +tetra +tetri +teuch +teugh +tewed +tewel +tewit +texas +texes +texts +thack +thagi +thaim +thale +thali +thana +thane +thang +thank +thans +thanx +tharm +thars +thaws +thawy +thebe +theca +theed +theek +thees +theft +thegn +theic +thein +their +thelf +thema +theme +thens +theow +there +therm +these +thesp +theta +thete +thews +thewy +thick +thief +thigh +thigs +thilk +thill +thine +thing +think +thins +thiol +third +thirl +thoft +thole +tholi +thong +thorn +thoro +thorp +those +thous +thowl +thrae +thraw +three +threw +thrid +thrip +throb +throe +throw +thrum +thuds +thugs +thuja +thumb +thump +thunk +thurl +thuya +thyme +thymi +thymy +tians +tiara +tiars +tibia +tical +ticca +ticed +tices +tichy +ticks +ticky +tidal +tiddy +tided +tides +tiers +tiffs +tifos +tifts +tiger +tiges +tight +tigon +tikas +tikes +tikis +tikka +tilak +tilde +tiled +tiler +tiles +tills +tilly +tilth +tilts +timbo +timed +timer +times +timid +timon +timps +tinas +tinct +tinds +tinea +tined +tines +tinge +tings +tinks +tinny +tints +tinty +tipis +tippy +tipsy +tired +tires +tirls +tiros +tirrs +titan +titch +titer +tithe +titis +title +titre +titty +titup +tiyin +tiyns +tizes +tizzy +toads +toady +toast +toaze +tocks +tocky +tocos +today +todde +toddy +toeas +toffs +toffy +tofts +tofus +togae +togas +toged +toges +togue +tohos +toile +toils +toing +toise +toits +tokay +toked +token +toker +tokes +tokos +tolan +tolar +tolas +toled +toles +tolls +tolly +tolts +tolus +tolyl +toman +tombs +tomes +tomia +tommy +tomos +tonal +tondi +tondo +toned +toner +tones +toney +tonga +tongs +tonic +tonka +tonks +tonne +tonus +tools +tooms +toons +tooth +toots +topaz +toped +topee +topek +toper +topes +tophe +tophi +tophs +topic +topis +topoi +topos +toppy +toque +torah +toran +toras +torch +torcs +tores +toric +torii +toros +torot +torrs +torse +torsi +torsk +torso +torta +torte +torts +torus +tosas +tosed +toses +toshy +tossy +total +toted +totem +toter +totes +totty +touch +tough +touks +touns +tours +touse +tousy +touts +touze +touzy +towed +towel +tower +towie +towns +towny +towse +towsy +towts +towze +towzy +toxic +toxin +toyed +toyer +toyon +toyos +tozed +tozes +tozie +trabs +trace +track +tract +trade +trads +tragi +traik +trail +train +trait +tramp +trams +trank +tranq +trans +trant +trape +traps +trapt +trash +trass +trats +tratt +trave +trawl +trayf +trays +tread +treat +treck +treed +treen +trees +trefa +treif +treks +trema +trems +trend +tress +trest +trets +trews +treyf +treys +triac +triad +trial +tribe +trice +trick +tride +tried +trier +tries +triff +trigo +trigs +trike +trild +trill +trims +trine +trins +triol +trior +trios +tripe +trips +tripy +trist +trite +troad +troak +troat +trock +trode +trods +trogs +trois +troke +troll +tromp +trona +tronc +trone +tronk +trons +troop +trooz +trope +troth +trots +trout +trove +trows +troys +truce +truck +trued +truer +trues +trugo +trugs +trull +truly +trump +trunk +truss +trust +truth +tryer +tryke +tryma +tryps +tryst +tsade +tsadi +tsars +tsked +tsuba +tsubo +tuans +tuart +tuath +tubae +tubal +tubar +tubas +tubby +tubed +tuber +tubes +tucks +tufas +tuffe +tuffs +tufts +tufty +tugra +tuile +tuina +tuism +tuktu +tules +tulip +tulle +tulpa +tulsi +tumid +tummy +tumor +tumps +tumpy +tunas +tunds +tuned +tuner +tunes +tungs +tunic +tunny +tupek +tupik +tuple +tuque +turbo +turds +turfs +turfy +turks +turme +turms +turns +turnt +turps +turrs +tushy +tusks +tusky +tutee +tutor +tutti +tutty +tutus +tuxes +tuyer +twaes +twain +twals +twang +twank +twats +tways +tweak +tweed +tweel +tween +tweep +tweer +tweet +twerk +twerp +twice +twier +twigs +twill +twilt +twine +twink +twins +twiny +twire +twirl +twirp +twist +twite +twits +twixt +twoer +twyer +tyees +tyers +tying +tyiyn +tykes +tyler +tymps +tynde +tyned +tynes +typal +typed +types +typey +typic +typos +typps +typto +tyran +tyred +tyres +tyros +tythe +tzars +udals +udder +udons +ugali +ugged +uhlan +uhuru +ukase +ulama +ulans +ulcer +ulema +ulmin +ulnad +ulnae +ulnar +ulnas +ulpan +ultra +ulvas +ulyie +ulzie +umami +umbel +umber +umble +umbos +umbra +umbre +umiac +umiak +umiaq +ummah +ummas +ummed +umped +umphs +umpie +umpty +umrah +umras +unais +unapt +unarm +unary +unaus +unbag +unban +unbar +unbed +unbid +unbox +uncap +unces +uncia +uncle +uncos +uncoy +uncus +uncut +undam +undee +under +undid +undos +undue +undug +uneth +unfed +unfit +unfix +ungag +unget +ungod +ungot +ungum +unhat +unhip +unica +unify +union +unite +units +unity +unjam +unked +unket +unkid +unlaw +unlay +unled +unlet +unlid +unlit +unman +unmet +unmew +unmix +unpay +unpeg +unpen +unpin +unred +unrid +unrig +unrip +unsaw +unsay +unsee +unset +unsew +unsex +unsod +untax +untie +until +untin +unwed +unwet +unwit +unwon +unzip +upbow +upbye +updos +updry +upend +upjet +uplay +upled +uplit +upped +upper +upran +uprun +upsee +upset +upsey +uptak +upter +uptie +uraei +urali +uraos +urare +urari +urase +urate +urban +urbex +urbia +urdee +ureal +ureas +uredo +ureic +urena +urent +urged +urger +urges +urial +urine +urite +urman +urnal +urned +urped +ursae +ursid +urson +urubu +urvas +usage +users +usher +using +usnea +usque +usual +usure +usurp +usury +uteri +utile +utter +uveal +uveas +uvula +vacua +vaded +vades +vagal +vague +vagus +vails +vaire +vairs +vairy +vakas +vakil +vales +valet +valid +valis +valor +valse +value +valve +vamps +vampy +vanda +vaned +vanes +vangs +vants +vaped +vaper +vapes +vapid +vapor +varan +varas +vardy +varec +vares +varia +varix +varna +varus +varve +vasal +vases +vasts +vasty +vatic +vatus +vauch +vault +vaunt +vaute +vauts +vawte +vaxes +veale +veals +vealy +veena +veeps +veers +veery +vegan +vegas +veges +vegie +vegos +vehme +veils +veily +veins +veiny +velar +velds +veldt +veles +vells +velum +venae +venal +vends +vendu +veney +venge +venin +venom +vents +venue +venus +verbs +verge +verra +verry +verse +verso +verst +verts +vertu +verve +vespa +vesta +vests +vetch +vexed +vexer +vexes +vexil +vezir +vials +viand +vibes +vibex +vibey +vicar +viced +vices +vichy +video +viers +views +viewy +vifda +viffs +vigas +vigia +vigil +vigor +vilde +viler +villa +villi +vills +vimen +vinal +vinas +vinca +vined +viner +vines +vinew +vinic +vinos +vints +vinyl +viola +viold +viols +viper +viral +vired +vireo +vires +virga +virge +virid +virls +virtu +virus +visas +vised +vises +visie +visit +visne +vison +visor +vista +visto +vitae +vital +vitas +vitex +vitro +vitta +vivas +vivat +vivda +viver +vives +vivid +vixen +vizir +vizor +vleis +vlies +vlogs +voars +vocab +vocal +voces +voddy +vodka +vodou +vodun +voema +vogie +vogue +voice +voids +voila +voile +voips +volae +volar +voled +voles +volet +volks +volta +volte +volti +volts +volva +volve +vomer +vomit +voted +voter +votes +vouch +vouge +voulu +vowed +vowel +vower +voxel +vozhd +vraic +vrils +vroom +vrous +vrouw +vrows +vuggs +vuggy +vughs +vughy +vulgo +vulns +vulva +vutty +vying +waacs +wacke +wacko +wacks +wacky +wadds +waddy +waded +wader +wades +wadge +wadis +wadts +wafer +waffs +wafts +waged +wager +wages +wagga +wagon +wagyu +wahoo +waide +waifs +waift +wails +wains +wairs +waist +waite +waits +waive +wakas +waked +waken +waker +wakes +wakfs +waldo +walds +waled +waler +wales +walie +walis +walks +walla +walls +wally +walty +waltz +wamed +wames +wamus +wands +waned +wanes +waney +wangs +wanks +wanky +wanle +wanly +wanna +wants +wanty +wanze +waqfs +warbs +warby +wards +wared +wares +warez +warks +warms +warns +warps +warre +warst +warts +warty +wases +washy +wasms +wasps +waspy +waste +wasts +watap +watch +water +watts +wauff +waugh +wauks +waulk +wauls +waurs +waved +waver +waves +wavey +wawas +wawes +wawls +waxed +waxen +waxer +waxes +wayed +wazir +wazoo +weald +weals +weamb +weans +wears +weary +weave +webby +weber +wecht +wedel +wedge +wedgy +weeds +weedy +weeke +weeks +weels +weems +weens +weeny +weeps +weepy +weest +weete +weets +wefte +wefts +weids +weigh +weils +weird +weirs +weise +weize +wekas +welch +welds +welke +welks +welkt +wells +welly +welsh +welts +wembs +wench +wends +wenge +wenny +wents +weros +wersh +wests +wetas +wetly +wexed +wexes +whack +whale +whamo +whams +whang +whaps +whare +wharf +whata +whats +whaup +whaur +wheal +whear +wheat +wheel +wheen +wheep +wheft +whelk +whelm +whelp +whens +where +whets +whews +wheys +which +whids +whiff +whift +whigs +while +whilk +whims +whine +whins +whiny +whios +whips +whipt +whirl +whirr +whirs +whish +whisk +whiss +whist +white +whits +whity +whizz +whole +whomp +whoof +whoop +whoot +whops +whore +whorl +whort +whose +whoso +whows +whump +whups +whyda +wicca +wicks +wicky +widdy +widen +wider +wides +widow +width +wield +wiels +wifed +wifes +wifey +wifie +wifty +wigan +wigga +wiggy +wight +wikis +wilco +wilds +wiled +wiles +wilga +wilis +wilja +wills +willy +wilts +wimps +wimpy +wince +winch +winds +windy +wined +wines +winey +winge +wings +wingy +winks +winna +winns +winos +winze +wiped +wiper +wipes +wired +wirer +wires +wirra +wised +wiser +wises +wisha +wisht +wisps +wispy +wists +witan +witch +wited +wites +withe +withs +withy +witty +wived +wiver +wives +wizen +wizes +woads +woald +wocks +wodge +woful +wojus +woken +woker +wokka +wolds +wolfs +wolly +wolve +woman +wombs +womby +women +womyn +wonga +wongi +wonks +wonky +wonts +woods +woody +wooed +wooer +woofs +woofy +woold +wools +wooly +woons +woops +woopy +woose +woosh +wootz +woozy +words +wordy +works +world +worms +wormy +worry +worse +worst +worth +worts +would +wound +woven +wowed +wowee +woxen +wrack +wrang +wraps +wrapt +wrast +wrate +wrath +wrawl +wreak +wreck +wrens +wrest +wrick +wried +wrier +wries +wring +wrist +write +writs +wroke +wrong +wroot +wrote +wroth +wrung +wryer +wryly +wuddy +wudus +wulls +wurst +wuses +wushu +wussy +wuxia +wyled +wyles +wynds +wynns +wyted +wytes +xebec +xenia +xenic +xenon +xeric +xerox +xerus +xoana +xrays +xylan +xylem +xylic +xylol +xylyl +xysti +xysts +yaars +yabas +yabba +yabby +yacca +yacht +yacka +yacks +yaffs +yager +yages +yagis +yahoo +yaird +yakka +yakow +yales +yamen +yampy +yamun +yangs +yanks +yapok +yapon +yapps +yappy +yarak +yarco +yards +yarer +yarfa +yarks +yarns +yarrs +yarta +yarto +yates +yauds +yauld +yaups +yawed +yawey +yawls +yawns +yawny +yawps +ybore +yclad +ycled +ycond +ydrad +ydred +yeads +yeahs +yealm +yeans +yeard +yearn +years +yeast +yecch +yechs +yechy +yedes +yeeds +yeesh +yeggs +yelks +yells +yelms +yelps +yelts +yenta +yente +yerba +yerds +yerks +yeses +yesks +yests +yesty +yetis +yetts +yeuks +yeuky +yeven +yeves +yewen +yexed +yexes +yfere +yield +yiked +yikes +yills +yince +yipes +yippy +yirds +yirks +yirrs +yirth +yites +yitie +ylems +ylike +ylkes +ymolt +ympes +yobbo +yobby +yocks +yodel +yodhs +yodle +yogas +yogee +yoghs +yogic +yogin +yogis +yoick +yojan +yoked +yokel +yoker +yokes +yokul +yolks +yolky +yomim +yomps +yonic +yonis +yonks +yoofs +yoops +yores +yorks +yorps +youks +young +yourn +yours +yourt +youse +youth +yowed +yowes +yowie +yowls +yowza +yrapt +yrent +yrivd +yrneh +ysame +ytost +yuans +yucas +yucca +yucch +yucko +yucks +yucky +yufts +yugas +yuked +yukes +yukky +yukos +yulan +yules +yummo +yummy +yumps +yupon +yuppy +yurta +yurts +yuzus +zabra +zacks +zaida +zaidy +zaire +zakat +zaman +zambo +zamia +zanja +zante +zanza +zanze +zappy +zarfs +zaris +zatis +zaxes +zayin +zazen +zeals +zebec +zebra +zebub +zebus +zedas +zeins +zendo +zerda +zerks +zeros +zests +zesty +zetas +zexes +zezes +zhomo +zibet +ziffs +zigan +zilas +zilch +zilla +zills +zimbi +zimbs +zinco +zincs +zincy +zineb +zines +zings +zingy +zinke +zinky +zippo +zippy +ziram +zitis +zizel +zizit +zlote +zloty +zoaea +zobos +zobus +zocco +zoeae +zoeal +zoeas +zoism +zoist +zombi +zonae +zonal +zonda +zoned +zoner +zones +zonks +zooea +zooey +zooid +zooks +zooms +zoons +zooty +zoppa +zoppo +zoril +zoris +zorro +zouks +zowee +zowie +zulus +zupan +zupas +zuppa +zurfs +zuzim +zygal +zygon +zymes +zymic diff --git a/ipynb/wordle-small.txt b/ipynb/wordle-small.txt new file mode 100644 index 0000000..4dd830e --- /dev/null +++ b/ipynb/wordle-small.txt @@ -0,0 +1,2315 @@ +aback +abase +abate +abbey +abbot +abhor +abide +abled +abode +abort +about +above +abuse +abyss +acorn +acrid +actor +acute +adage +adapt +adept +admin +admit +adobe +adopt +adore +adorn +adult +affix +afire +afoot +afoul +after +again +agape +agate +agent +agile +aging +aglow +agony +agora +agree +ahead +aider +aisle +alarm +album +alert +algae +alibi +alien +align +alike +alive +allay +alley +allot +allow +alloy +aloft +alone +along +aloof +aloud +alpha +altar +alter +amass +amaze +amber +amble +amend +amiss +amity +among +ample +amply +amuse +angel +anger +angle +angry +angst +anime +ankle +annex +annoy +annul +anode +antic +anvil +aorta +apart +aphid +aping +apnea +apple +apply +apron +aptly +arbor +ardor +arena +argue +arise +armor +aroma +arose +array +arrow +arson +artsy +ascot +ashen +aside +askew +assay +asset +atoll +atone +attic +audio +audit +augur +aunty +avail +avert +avian +avoid +await +awake +award +aware +awash +awful +awoke +axial +axiom +axion +azure +bacon +badge +badly +bagel +baggy +baker +baler +balmy +banal +banjo +barge +baron +basal +basic +basil +basin +basis +baste +batch +bathe +baton +batty +bawdy +bayou +beach +beady +beard +beast +beech +beefy +befit +began +begat +beget +begin +begun +being +belch +belie +belle +belly +below +bench +beret +berry +berth +beset +betel +bevel +bezel +bible +bicep +biddy +bigot +bilge +billy +binge +bingo +biome +birch +birth +bison +bitty +black +blade +blame +bland +blank +blare +blast +blaze +bleak +bleat +bleed +bleep +blend +bless +blimp +blind +blink +bliss +blitz +bloat +block +bloke +blond +blood +bloom +blown +bluer +bluff +blunt +blurb +blurt +blush +board +boast +bobby +boney +bongo +bonus +booby +boost +booth +booty +booze +boozy +borax +borne +bosom +bossy +botch +bough +boule +bound +bowel +boxer +brace +braid +brain +brake +brand +brash +brass +brave +bravo +brawl +brawn +bread +break +breed +briar +bribe +brick +bride +brief +brine +bring +brink +briny +brisk +broad +broil +broke +brood +brook +broom +broth +brown +brunt +brush +brute +buddy +budge +buggy +bugle +build +built +bulge +bulky +bully +bunch +bunny +burly +burnt +burst +bused +bushy +butch +butte +buxom +buyer +bylaw +cabal +cabby +cabin +cable +cacao +cache +cacti +caddy +cadet +cagey +cairn +camel +cameo +canal +candy +canny +canoe +canon +caper +caput +carat +cargo +carol +carry +carve +caste +catch +cater +catty +caulk +cause +cavil +cease +cedar +cello +chafe +chaff +chain +chair +chalk +champ +chant +chaos +chard +charm +chart +chase +chasm +cheap +cheat +check +cheek +cheer +chess +chest +chick +chide +chief +child +chili +chill +chime +china +chirp +chock +choir +choke +chord +chore +chose +chuck +chump +chunk +churn +chute +cider +cigar +cinch +circa +civic +civil +clack +claim +clamp +clang +clank +clash +clasp +class +clean +clear +cleat +cleft +clerk +click +cliff +climb +cling +clink +cloak +clock +clone +close +cloth +cloud +clout +clove +clown +cluck +clued +clump +clung +coach +coast +cobra +cocoa +colon +color +comet +comfy +comic +comma +conch +condo +conic +copse +coral +corer +corny +couch +cough +could +count +coupe +court +coven +cover +covet +covey +cower +coyly +crack +craft +cramp +crane +crank +crash +crass +crate +crave +crawl +craze +crazy +creak +cream +credo +creed +creek +creep +creme +crepe +crept +cress +crest +crick +cried +crier +crime +crimp +crisp +croak +crock +crone +crony +crook +cross +croup +crowd +crown +crude +cruel +crumb +crump +crush +crust +crypt +cubic +cumin +curio +curly +curry +curse +curve +curvy +cutie +cyber +cycle +cynic +daddy +daily +dairy +daisy +dally +dance +dandy +datum +daunt +dealt +death +debar +debit +debug +debut +decal +decay +decor +decoy +decry +defer +deign +deity +delay +delta +delve +demon +demur +denim +dense +depot +depth +derby +deter +detox +deuce +devil +diary +dicey +digit +dilly +dimly +diner +dingo +dingy +diode +dirge +dirty +disco +ditch +ditto +ditty +diver +dizzy +dodge +dodgy +dogma +doing +dolly +donor +donut +dopey +doubt +dough +dowdy +dowel +downy +dowry +dozen +draft +drain +drake +drama +drank +drape +drawl +drawn +dread +dream +dress +dried +drier +drift +drill +drink +drive +droit +droll +drone +drool +droop +dross +drove +drown +druid +drunk +dryer +dryly +duchy +dully +dummy +dumpy +dunce +dusky +dusty +dutch +duvet +dwarf +dwell +dwelt +dying +eager +eagle +early +earth +easel +eaten +eater +ebony +eclat +edict +edify +eerie +egret +eight +eject +eking +elate +elbow +elder +elect +elegy +elfin +elide +elite +elope +elude +email +embed +ember +emcee +empty +enact +endow +enema +enemy +enjoy +ennui +ensue +enter +entry +envoy +epoch +epoxy +equal +equip +erase +erect +erode +error +erupt +essay +ester +ether +ethic +ethos +etude +evade +event +every +evict +evoke +exact +exalt +excel +exert +exile +exist +expel +extol +extra +exult +eying +fable +facet +faint +fairy +faith +false +fancy +fanny +farce +fatal +fatty +fault +fauna +favor +feast +fecal +feign +fella +felon +femme +femur +fence +feral +ferry +fetal +fetch +fetid +fetus +fever +fewer +fiber +fibre +ficus +field +fiend +fiery +fifth +fifty +fight +filer +filet +filly +filmy +filth +final +finch +finer +first +fishy +fixer +fizzy +fjord +flack +flail +flair +flake +flaky +flame +flank +flare +flash +flask +fleck +fleet +flesh +flick +flier +fling +flint +flirt +float +flock +flood +floor +flora +floss +flour +flout +flown +fluff +fluid +fluke +flume +flung +flunk +flush +flute +flyer +foamy +focal +focus +foggy +foist +folio +folly +foray +force +forge +forgo +forte +forth +forty +forum +found +foyer +frail +frame +frank +fraud +freak +freed +freer +fresh +friar +fried +frill +frisk +fritz +frock +frond +front +frost +froth +frown +froze +fruit +fudge +fugue +fully +fungi +funky +funny +furor +furry +fussy +fuzzy +gaffe +gaily +gamer +gamma +gamut +gassy +gaudy +gauge +gaunt +gauze +gavel +gawky +gayer +gayly +gazer +gecko +geeky +geese +genie +genre +ghost +ghoul +giant +giddy +gipsy +girly +girth +given +giver +glade +gland +glare +glass +glaze +gleam +glean +glide +glint +gloat +globe +gloom +glory +gloss +glove +glyph +gnash +gnome +godly +going +golem +golly +gonad +goner +goody +gooey +goofy +goose +gorge +gouge +gourd +grace +grade +graft +grail +grain +grand +grant +grape +graph +grasp +grass +grate +grave +gravy +graze +great +greed +green +greet +grief +grill +grime +grimy +grind +gripe +groan +groin +groom +grope +gross +group +grout +grove +growl +grown +gruel +gruff +grunt +guard +guava +guess +guest +guide +guild +guile +guilt +guise +gulch +gully +gumbo +gummy +guppy +gusto +gusty +gypsy +habit +hairy +halve +handy +happy +hardy +harem +harpy +harry +harsh +haste +hasty +hatch +hater +haunt +haute +haven +havoc +hazel +heady +heard +heart +heath +heave +heavy +hedge +hefty +heist +helix +hello +hence +heron +hilly +hinge +hippo +hippy +hitch +hoard +hobby +hoist +holly +homer +honey +honor +horde +horny +horse +hotel +hotly +hound +house +hovel +hover +howdy +human +humid +humor +humph +humus +hunch +hunky +hurry +husky +hussy +hutch +hydro +hyena +hymen +hyper +icily +icing +ideal +idiom +idiot +idler +idyll +igloo +iliac +image +imbue +impel +imply +inane +inbox +incur +index +inept +inert +infer +ingot +inlay +inlet +inner +input +inter +intro +ionic +irate +irony +islet +issue +itchy +ivory +jaunt +jazzy +jelly +jerky +jetty +jewel +jiffy +joint +joist +joker +jolly +joust +judge +juice +juicy +jumbo +jumpy +junta +junto +juror +kappa +karma +kayak +kebab +khaki +kinky +kiosk +kitty +knack +knave +knead +kneed +kneel +knelt +knife +knock +knoll +known +koala +krill +label +labor +laden +ladle +lager +lance +lanky +lapel +lapse +large +larva +lasso +latch +later +lathe +latte +laugh +layer +leach +leafy +leaky +leant +leapt +learn +lease +leash +least +leave +ledge +leech +leery +lefty +legal +leggy +lemon +lemur +leper +level +lever +libel +liege +light +liken +lilac +limbo +limit +linen +liner +lingo +lipid +lithe +liver +livid +llama +loamy +loath +lobby +local +locus +lodge +lofty +logic +login +loopy +loose +lorry +loser +louse +lousy +lover +lower +lowly +loyal +lucid +lucky +lumen +lumpy +lunar +lunch +lunge +lupus +lurch +lurid +lusty +lying +lymph +lynch +lyric +macaw +macho +macro +madam +madly +mafia +magic +magma +maize +major +maker +mambo +mamma +mammy +manga +mange +mango +mangy +mania +manic +manly +manor +maple +march +marry +marsh +mason +masse +match +matey +mauve +maxim +maybe +mayor +mealy +meant +meaty +mecca +medal +media +medic +melee +melon +mercy +merge +merit +merry +metal +meter +metro +micro +midge +midst +might +milky +mimic +mince +miner +minim +minor +minty +minus +mirth +miser +missy +mocha +modal +model +modem +mogul +moist +molar +moldy +money +month +moody +moose +moral +moron +morph +mossy +motel +motif +motor +motto +moult +mound +mount +mourn +mouse +mouth +mover +movie +mower +mucky +mucus +muddy +mulch +mummy +munch +mural +murky +mushy +music +musky +musty +myrrh +nadir +naive +nanny +nasal +nasty +natal +naval +navel +needy +neigh +nerdy +nerve +never +newer +newly +nicer +niche +niece +night +ninja +ninny +ninth +noble +nobly +noise +noisy +nomad +noose +north +nosey +notch +novel +nudge +nurse +nutty +nylon +nymph +oaken +obese +occur +ocean +octal +octet +odder +oddly +offal +offer +often +olden +older +olive +ombre +omega +onion +onset +opera +opine +opium +optic +orbit +order +organ +other +otter +ought +ounce +outdo +outer +outgo +ovary +ovate +overt +ovine +ovoid +owing +owner +oxide +ozone +paddy +pagan +paint +paler +palsy +panel +panic +pansy +papal +paper +parer +parka +parry +parse +party +pasta +paste +pasty +patch +patio +patsy +patty +pause +payee +payer +peace +peach +pearl +pecan +pedal +penal +pence +penne +penny +perch +peril +perky +pesky +pesto +petal +petty +phase +phone +phony +photo +piano +picky +piece +piety +piggy +pilot +pinch +piney +pinky +pinto +piper +pique +pitch +pithy +pivot +pixel +pixie +pizza +place +plaid +plain +plait +plane +plank +plant +plate +plaza +plead +pleat +plied +plier +pluck +plumb +plume +plump +plunk +plush +poesy +point +poise +poker +polar +polka +polyp +pooch +poppy +porch +poser +posit +posse +pouch +pound +pouty +power +prank +prawn +preen +press +price +prick +pride +pried +prime +primo +print +prior +prism +privy +prize +probe +prone +prong +proof +prose +proud +prove +prowl +proxy +prude +prune +psalm +pubic +pudgy +puffy +pulpy +pulse +punch +pupal +pupil +puppy +puree +purer +purge +purse +pushy +putty +pygmy +quack +quail +quake +qualm +quark +quart +quash +quasi +queen +queer +quell +query +quest +queue +quick +quiet +quill +quilt +quirk +quite +quota +quote +quoth +rabbi +rabid +racer +radar +radii +radio +rainy +raise +rajah +rally +ralph +ramen +ranch +randy +range +rapid +rarer +raspy +ratio +ratty +raven +rayon +razor +reach +react +ready +realm +rearm +rebar +rebel +rebus +rebut +recap +recur +recut +reedy +refer +refit +regal +rehab +reign +relax +relay +relic +remit +renal +renew +repay +repel +reply +rerun +reset +resin +retch +retro +retry +reuse +revel +revue +rhino +rhyme +rider +ridge +rifle +right +rigid +rigor +rinse +ripen +riper +risen +riser +risky +rival +river +rivet +roach +roast +robin +robot +rocky +rodeo +roger +rogue +roomy +roost +rotor +rouge +rough +round +rouse +route +rover +rowdy +rower +royal +ruddy +ruder +rugby +ruler +rumba +rumor +rupee +rural +rusty +sadly +safer +saint +salad +sally +salon +salsa +salty +salve +salvo +sandy +saner +sappy +sassy +satin +satyr +sauce +saucy +sauna +saute +savor +savoy +savvy +scald +scale +scalp +scaly +scamp +scant +scare +scarf +scary +scene +scent +scion +scoff +scold +scone +scoop +scope +score +scorn +scour +scout +scowl +scram +scrap +scree +screw +scrub +scrum +scuba +sedan +seedy +segue +seize +semen +sense +sepia +serif +serum +serve +setup +seven +sever +sewer +shack +shade +shady +shaft +shake +shaky +shale +shall +shalt +shame +shank +shape +shard +share +shark +sharp +shave +shawl +shear +sheen +sheep +sheer +sheet +sheik +shelf +shell +shied +shift +shine +shiny +shire +shirk +shirt +shoal +shock +shone +shook +shoot +shore +shorn +short +shout +shove +shown +showy +shrew +shrub +shrug +shuck +shunt +shush +shyly +siege +sieve +sight +sigma +silky +silly +since +sinew +singe +siren +sissy +sixth +sixty +skate +skier +skiff +skill +skimp +skirt +skulk +skull +skunk +slack +slain +slang +slant +slash +slate +slave +sleek +sleep +sleet +slept +slice +slick +slide +slime +slimy +sling +slink +sloop +slope +slosh +sloth +slump +slung +slunk +slurp +slush +slyly +smack +small +smart +smash +smear +smell +smelt +smile +smirk +smite +smith +smock +smoke +smoky +smote +snack +snail +snake +snaky +snare +snarl +sneak +sneer +snide +sniff +snipe +snoop +snore +snort +snout +snowy +snuck +snuff +soapy +sober +soggy +solar +solid +solve +sonar +sonic +sooth +sooty +sorry +sound +south +sower +space +spade +spank +spare +spark +spasm +spawn +speak +spear +speck +speed +spell +spelt +spend +spent +sperm +spice +spicy +spied +spiel +spike +spiky +spill +spilt +spine +spiny +spire +spite +splat +split +spoil +spoke +spoof +spook +spool +spoon +spore +sport +spout +spray +spree +sprig +spunk +spurn +spurt +squad +squat +squib +stack +staff +stage +staid +stain +stair +stake +stale +stalk +stall +stamp +stand +stank +stare +stark +start +stash +state +stave +stead +steak +steal +steam +steed +steel +steep +steer +stein +stern +stick +stiff +still +stilt +sting +stink +stint +stock +stoic +stoke +stole +stomp +stone +stony +stood +stool +stoop +store +stork +storm +story +stout +stove +strap +straw +stray +strip +strut +stuck +study +stuff +stump +stung +stunk +stunt +style +suave +sugar +suing +suite +sulky +sully +sumac +sunny +super +surer +surge +surly +sushi +swami +swamp +swarm +swash +swath +swear +sweat +sweep +sweet +swell +swept +swift +swill +swine +swing +swirl +swish +swoon +swoop +sword +swore +sworn +swung +synod +syrup +tabby +table +taboo +tacit +tacky +taffy +taint +taken +taker +tally +talon +tamer +tango +tangy +taper +tapir +tardy +tarot +taste +tasty +tatty +taunt +tawny +teach +teary +tease +teddy +teeth +tempo +tenet +tenor +tense +tenth +tepee +tepid +terra +terse +testy +thank +theft +their +theme +there +these +theta +thick +thief +thigh +thing +think +third +thong +thorn +those +three +threw +throb +throw +thrum +thumb +thump +thyme +tiara +tibia +tidal +tiger +tight +tilde +timer +timid +tipsy +titan +tithe +title +toast +today +toddy +token +tonal +tonga +tonic +tooth +topaz +topic +torch +torso +torus +total +totem +touch +tough +towel +tower +toxic +toxin +trace +track +tract +trade +trail +train +trait +tramp +trash +trawl +tread +treat +trend +triad +trial +tribe +trice +trick +tried +tripe +trite +troll +troop +trope +trout +trove +truce +truck +truer +truly +trump +trunk +truss +trust +truth +tryst +tubal +tuber +tulip +tulle +tumor +tunic +turbo +tutor +twang +tweak +tweed +tweet +twice +twine +twirl +twist +twixt +tying +udder +ulcer +ultra +umbra +uncle +uncut +under +undid +undue +unfed +unfit +unify +union +unite +unity +unlit +unmet +unset +untie +until +unwed +unzip +upper +upset +urban +urine +usage +usher +using +usual +usurp +utile +utter +vague +valet +valid +valor +value +valve +vapid +vapor +vault +vaunt +vegan +venom +venue +verge +verse +verso +verve +vicar +video +vigil +vigor +villa +vinyl +viola +viper +viral +virus +visit +visor +vista +vital +vivid +vixen +vocal +vodka +vogue +voice +voila +vomit +voter +vouch +vowel +vying +wacky +wafer +wager +wagon +waist +waive +waltz +warty +waste +watch +water +waver +waxen +weary +weave +wedge +weedy +weigh +weird +welch +welsh +wench +whack +whale +wharf +wheat +wheel +whelp +where +which +whiff +while +whine +whiny +whirl +whisk +white +whole +whoop +whose +widen +wider +widow +width +wield +wight +willy +wimpy +wince +winch +windy +wiser +wispy +witch +witty +woken +woman +women +woody +wooer +wooly +woozy +wordy +world +worry +worse +worst +worth +would +wound +woven +wrack +wrath +wreak +wreck +wrest +wring +wrist +write +wrong +wrote +wrung +wryly +yacht +yearn +yeast +yield +young +youth +zebra +zesty +zonal