From 6ed2674a49c0671f0954d0e2e082a02fd5c50499 Mon Sep 17 00:00:00 2001 From: Peter Norvig Date: Tue, 27 Apr 2021 15:03:54 -0700 Subject: [PATCH] Add files via upload --- ipynb/SpellingBee.ipynb | 1852 +++++++++++++++++++++++---------------- 1 file changed, 1115 insertions(+), 737 deletions(-) diff --git a/ipynb/SpellingBee.ipynb b/ipynb/SpellingBee.ipynb index 85751a8..3dae3b7 100644 --- a/ipynb/SpellingBee.ipynb +++ b/ipynb/SpellingBee.ipynb @@ -27,7 +27,7 @@ "\n", "\n", "\n", - "Since the referenced [word list](https://norvig.com/ngrams/enable1.txt) came from *my* web site, I felt somewhat compelled to solve this one. (Note I didn't make up the word list; it is a standard Scrabble word list that I happen to host a copy of.) I'll show you how I address the problem, step by step:" + "Since the referenced [word list](https://norvig.com/ngrams/enable1.txt) came from [*my* web site](https://norvig.com/ngrams/enable1.txt), I felt somewhat compelled to solve this one. (Note I didn't make up the word list; it is a standard Scrabble word list that I happen to host a copy of.) I'll show you how I address the problem, step by step:" ] }, { @@ -51,7 +51,8 @@ "outputs": [], "source": [ "from typing import List, Set, Tuple, Dict\n", - "from collections import Counter, defaultdict, namedtuple\n", + "from collections import Counter, defaultdict\n", + "from dataclasses import dataclass\n", "from itertools import combinations\n", "import matplotlib.pyplot as plt" ] @@ -171,7 +172,7 @@ " 1. The seven distinct letters in the honeycomb.\n", " 2. The one distinguished center letter.\n", " \n", - "Thus, we can represent a honeycomb as follows (I wanted to put in my own less verbose `__repr__` method):\n", + "Thus, we can represent a honeycomb as follows:\n", " " ] }, @@ -183,7 +184,7 @@ { "data": { "text/plain": [ - "Honeycomb('AEGLMPX', 'G')" + "Honeycomb(letters='AEGLMPX', center='G')" ] }, "execution_count": 6, @@ -192,8 +193,10 @@ } ], "source": [ - "class Honeycomb(namedtuple('_', 'letters, center')):\n", - " def __repr__(self): return f'Honeycomb({self.letters!r}, {self.center!r})'\n", + "@dataclass(frozen=True, order=True)\n", + "class Honeycomb:\n", + " letters: str # 7 letters\n", + " center: str # 1 letter\n", "\n", "hc = Honeycomb('AEGLMPX', 'G')\n", "hc" @@ -219,8 +222,7 @@ "\n", "def can_make(honeycomb, word) -> bool:\n", " \"\"\"Can the honeycomb make this word?\"\"\"\n", - " letters, center = honeycomb\n", - " return center in word and all(L in letters for L in word)" + " return honeycomb.center in word and all(L in honeycomb.letters for L in word)" ] }, { @@ -269,13 +271,10 @@ "source": [ "# Step 3: Best Honeycomb\n", "\n", - "\n", - "How many possible honeycombs are there? We can put any letter in the center, then any 6 letters around the outside (order doesn't matter); since the letter 'S' is not allowed, this gives a total of 25 × (24 choose 6) = 3,364,900 possible honeycombs. We could conceivably ask for the game score of every one of them and pick the best; that would probably take hours of computation (not seconds, and not days).\n", - "\n", - "However, a key constraint of the game is that **there must be at least one pangram** in the set of words that a valid honeycomb can make. That means that a valid honeycomb must ***be*** the set of seven letters in one of the pangram words in the word list, with any of the seven letters as the center. My approach to find the best (highest scoring) honeycomb is:\n", - "\n", - " * Go through all the words and find all the valid honeycombs: the 7-letter pangram letter sets, with any of the 7 letters as center.\n", - " * Compute the game score for each valid honeycomb and return a honeycomb with maximal game score." + "Now that we can compute the game score of a honeycomb, a strategy for finding the best honeycomb is:\n", + " - Compile a list of valid candidate honeycombs.\n", + " - For each one, compute the game score.\n", + " - Return the one with the highest game score." ] }, { @@ -287,8 +286,24 @@ "def best_honeycomb(words) -> Honeycomb: \n", " \"\"\"Return a honeycomb with highest game score on these words.\"\"\"\n", " return max(valid_honeycombs(words), \n", - " key=lambda h: game_score(h, words))\n", + " key=lambda h: game_score(h, words))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What are the possible candidate honeycombs? We can put any letter in the center, then any 6 letters around the outside (order doesn't matter); since the letter 'S' is not allowed, this gives a total of 25 × (24 choose 6) = 3,364,900 possible honeycombs. \n", "\n", + "However, a key constraint of the game is that a valid honeycomb **must make at least one pangram**. That means that a valid honeycomb must ***be*** the set of seven letters in a pangram (with any of the seven letters as the center). There should be fewer than 3,364,900 of these." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ "def valid_honeycombs(words) -> List[Honeycomb]:\n", " \"\"\"Valid Honeycombs are the pangram lettersets, with any center.\"\"\"\n", " pangram_lettersets = {letterset(w) for w in words if pangram_bonus(w)}\n", @@ -301,7 +316,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "I will represent a **set of letters** as a sorted string of distinct letters. Why not a Python `set` (or `frozenset` if we want it to be the key of a dict)? Because a string takes up less space in memory, and its printed representation is easier to read when debugging. Compare:\n", + "I will represent a **set of letters** as a sorted string of distinct letters. Why not a Python `set` (or `frozenset` to be hashable)? Because a string takes up less space in memory, and its printed representation is easier to read when debugging. Compare:\n", "- `frozenset({'A', 'E', 'G', 'L', 'M', 'P', 'X'})`\n", "- `'AEGLMPX'`\n", "\n", @@ -310,7 +325,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -323,7 +338,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -337,7 +352,7 @@ " 'EROTICA': 'ACEIORT'}" ] }, - "execution_count": 12, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -355,29 +370,29 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[Honeycomb('AEGLMPX', 'A'),\n", - " Honeycomb('AEGLMPX', 'E'),\n", - " Honeycomb('AEGLMPX', 'G'),\n", - " Honeycomb('AEGLMPX', 'L'),\n", - " Honeycomb('AEGLMPX', 'M'),\n", - " Honeycomb('AEGLMPX', 'P'),\n", - " Honeycomb('AEGLMPX', 'X'),\n", - " Honeycomb('ACEIORT', 'A'),\n", - " Honeycomb('ACEIORT', 'C'),\n", - " Honeycomb('ACEIORT', 'E'),\n", - " Honeycomb('ACEIORT', 'I'),\n", - " Honeycomb('ACEIORT', 'O'),\n", - " Honeycomb('ACEIORT', 'R'),\n", - " Honeycomb('ACEIORT', 'T')]" + "[Honeycomb(letters='ACEIORT', center='A'),\n", + " Honeycomb(letters='ACEIORT', center='C'),\n", + " Honeycomb(letters='ACEIORT', center='E'),\n", + " Honeycomb(letters='ACEIORT', center='I'),\n", + " Honeycomb(letters='ACEIORT', center='O'),\n", + " Honeycomb(letters='ACEIORT', center='R'),\n", + " Honeycomb(letters='ACEIORT', center='T'),\n", + " Honeycomb(letters='AEGLMPX', center='A'),\n", + " Honeycomb(letters='AEGLMPX', center='E'),\n", + " Honeycomb(letters='AEGLMPX', center='G'),\n", + " Honeycomb(letters='AEGLMPX', center='L'),\n", + " Honeycomb(letters='AEGLMPX', center='M'),\n", + " Honeycomb(letters='AEGLMPX', center='P'),\n", + " Honeycomb(letters='AEGLMPX', center='X')]" ] }, - "execution_count": 13, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -388,16 +403,16 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Honeycomb('ACEIORT', 'A')" + "Honeycomb(letters='ACEIORT', center='A')" ] }, - "execution_count": 14, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -419,7 +434,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -437,7 +452,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -446,7 +461,7 @@ "44585" ] }, - "execution_count": 16, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -458,7 +473,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -467,7 +482,7 @@ "14741" ] }, - "execution_count": 17, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -479,7 +494,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -488,7 +503,7 @@ "7986" ] }, - "execution_count": 18, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -499,7 +514,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -508,7 +523,7 @@ "55902" ] }, - "execution_count": 19, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -528,43 +543,9 @@ "- 14,741 pangram words \n", "- 7,986 distinct pangram lettersets\n", "- 55,902 (7 × 7,986) valid pangram-containing honeycombs\n", + "- 3,364,900 possible honeycombs (most of which can't make a panagram and thus are invalid)\n", "\n", - "How long will it take to run `best_honeycomb(enable1)`? Most of the computation time is in `game_score` (which has to look at all 44,585 valid words), so let's estimate the total time by first checking how long it takes to compute the game score of a single honeycomb:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 9.35 ms, sys: 82 µs, total: 9.43 ms\n", - "Wall time: 9.42 ms\n" - ] - }, - { - "data": { - "text/plain": [ - "153" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%time game_score(hc, enable1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Roughly 10 milliseconds on my computer (this may vary). How many minutes would it be to run `game_score` for all 55,902 valid honeycombs?" + "How long will it take to run `best_honeycomb(enable1)`? Most of the computation time is in `game_score` (each call has to look at all 44,585 valid words), so let's estimate the total time by first checking how long it takes to compute the game score of a single honeycomb:" ] }, { @@ -573,25 +554,49 @@ "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "9.317" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "8.7 ms ± 96.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + ] } ], "source": [ - "55902 * 10/1000 / 60" + "%timeit game_score(hc, enable1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "About 9 or 10 minutes. I could run `best_honeycomb(enable1)` right now and take a coffee break until it completes, but I think that a puzzle like this deserves a more elegant solution. I'd like to get the run time under a minute (as is suggested in [Project Euler](https://projecteuler.net/)), and I have an idea how to do it.\n", + "Roughly 9 milliseconds on my computer (this may vary). How many seconds would it be to run `game_score` for all 55,902 valid honeycombs?" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "503.118" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "55902 * 9/1000" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "About 500 seconds, or 8 minutes. I could run `best_honeycomb(enable1)`, go take a coffee break, and come back to see the solution and declare victory. But I think that a puzzle like this deserves a more elegant solution. I'd like to get the run time under a minute (as is suggested in [Project Euler](https://projecteuler.net/)), and I have an idea how to do it. (*Note:* I should point out that [William Shunn](https://www.shunn.net/about.html) has a list of [58,838 pangrams](https://www.shunn.net/bee/pangrams.html) that form 20,597 distinct pangram lettersets, so it would take more than twice as long with his dictionary. However, we don't know what dictionary the NY Times is using, so we don't know what words would be accepted.)\n", "\n", "# Step 5: Faster Algorithm: Points Table\n", "\n", @@ -599,12 +604,11 @@ "\n", "1. Keep the same strategy of trying every pangram letterset, but do some precomputation that will make `game_score` much faster.\n", "1. The precomputation is: compute the `letterset` and `word_score` for each word, and make a table of `{letterset: total_points}` giving the total number of word score points for all the words that correspond to each letterset. I call this a **points table**.\n", - "3. These calculations are independent of the honeycomb, so they need to be done only once, not 55,902 times. \n", - "4. `game_score2` (the name is changed because the interface has changed) takes a honeycomb and a points table as input. The idea is that every word that the honeycomb can make must have a letterset that is the same as a valid **letter subset** of the honeycomb. A valid letter subset must include the center letter, and it may or may not include each of the other 6 letters, so there are exactly $2^6 = 64$ valid letter subsets. (The function `letter_subsets(honeycomb)` computes these.)\n", + "3. These calculations are independent of the honeycomb, so they need be done only once, not 55,902 times. \n", + "4. `game_score2` takes a honeycomb and a points table as input. The idea is that every word that the honeycomb can make must have a letterset that is the same as a valid **letter subset** of the honeycomb. A valid letter subset must include the center letter, and it may or may not include each of the other 6 letters, so there are exactly $2^6 = 64$ valid letter subsets. The function `letter_subsets(honeycomb)` computes these.\n", "The result of `game_score2` is the sum of the honeycomb's 64 letter subset entries in the points table.\n", "\n", - "\n", - "That means that in `game_score2` we no longer need to iterate over 44,585 words and check if each word is a subset of the honeycomb. Instead we iterate over the 64 subsets of the honeycomb and for each one check—in one table lookup—whether it is a word (or more than word) and how many total points those word(s) score. Since 64 < 44,585, that's a nice optimization!\n", + "That means that in `game_score2` we no longer need to iterate over 44,585 words and check if each word is a subset of the honeycomb. Instead we iterate over the 64 subsets of the honeycomb and for each one check—in one table lookup—whether it is a word (or more than word) and how many total points those word(s) score. Since 64 is less than 44,585, that's a nice optimization!\n", "\n", "\n", "Here's the code:" @@ -612,11 +616,11 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ - "PointsTable = Dict[Letterset, int]\n", + "PointsTable = Dict[Letterset, int] # How many points does a letterset score?\n", "\n", "def best_honeycomb(words) -> Honeycomb: \n", " \"\"\"Return a honeycomb with highest game score on these words.\"\"\"\n", @@ -634,7 +638,7 @@ " return table\n", "\n", "def letter_subsets(honeycomb) -> List[Letterset]:\n", - " \"\"\"The 64 subsets of the letters in the honeycomb, always including the center letter.\"\"\"\n", + " \"\"\"The 64 subsets of the letters in the honeycomb, each including the center letter.\"\"\"\n", " return [letters \n", " for n in range(1, 8) \n", " for letters in map(''.join, combinations(honeycomb.letters, n))\n", @@ -651,27 +655,7 @@ "source": [ "Let's get a feel for how this works. \n", "\n", - "First `letter_subsets` (a 4-letter honeycomb makes $2^3 = 8$ subsets; 7-letter honeycombs make $2^6 = 64$):" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['G', 'GL', 'GA', 'GM', 'GLA', 'GLM', 'GAM', 'GLAM']" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "letter_subsets(Honeycomb('GLAM', 'G')) " + "First consider `letter_subsets`. A 4-letter honeycomb makes $2^3 = 8$ subsets; 7-letter honeycombs make $2^6 = 64$ subsets:" ] }, { @@ -682,7 +666,7 @@ { "data": { "text/plain": [ - "['GAME', 'AMALGAM', 'GLAM', 'MEGAPLEX', 'CACCIATORE', 'EROTICA']" + "['G', 'GL', 'GA', 'GM', 'GLA', 'GLM', 'GAM', 'GLAM']" ] }, "execution_count": 24, @@ -691,7 +675,7 @@ } ], "source": [ - "mini # Remind me again what the mini word list is?" + "letter_subsets(Honeycomb('GLAM', 'G')) " ] }, { @@ -706,6 +690,13 @@ "execution_count": 25, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mini = ['GAME', 'AMALGAM', 'GLAM', 'MEGAPLEX', 'CACCIATORE', 'EROTICA']\n" + ] + }, { "data": { "text/plain": [ @@ -718,6 +709,7 @@ } ], "source": [ + "print('mini =', mini)\n", "tabulate_points(mini)" ] }, @@ -762,8 +754,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 1.75 s, sys: 2.03 ms, total: 1.75 s\n", - "Wall time: 1.75 s\n" + "CPU times: user 1.65 s, sys: 2.19 ms, total: 1.65 s\n", + "Wall time: 1.65 s\n" ] } ], @@ -779,7 +771,7 @@ { "data": { "text/plain": [ - "(Honeycomb('AEGINRT', 'R'), 3898)" + "(Honeycomb(letters='AEGINRT', center='R'), 3898)" ] }, "execution_count": 28, @@ -797,39 +789,76 @@ "source": [ "**Wow! 3898 is a high score!** \n", "\n", - "And it took less than 2 seconds of computation to find the best honeycomb!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Step 7: Even Faster Algorithm: Branch and Bound\n", + "And it took **less than 2 seconds** of computation to find the best honeycomb!\n", "\n", - "A run time of 2 seconds is pretty good! But what if the word list were 100 times bigger? What if a honeycomb had 12 letters around the outside, not just 6? We might still be looking for ideas to speed up the computation. I happen to have one.\n", - "\n", - "Consider the word 'EQUIVOKE'. It is a pangram, but what with the 'Q' and 'V' and 'K', it is not a high-scoring honeycomb, regardless of what center is used:" + "Where does the time go? There's the initial time to do `tabulate_points` once, and then calls to `game_score2` for each candidate honeycomb: " ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 66.1 ms, sys: 852 µs, total: 66.9 ms\n", + "Wall time: 66.2 ms\n" + ] + } + ], + "source": [ + "%time points_table = tabulate_points(enable1)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "25.6 µs ± 149 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" + ] + } + ], + "source": [ + "%timeit game_score2(Honeycomb('AEGINRT', 'R'), points_table)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Step 7: Even Faster Algorithm: Branch and Bound\n", + "\n", + "A run time of less than 2 seconds is pretty good! But I'm not ready to stop now.\n", + "\n", + "Consider the word 'EQUIVOKE'. It is a pangram, but what with the 'Q' and 'V' and 'K', it is not a high-scoring honeycomb, regardless of what center is used:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'E': 48, 'Q': 29, 'U': 29, 'I': 32, 'V': 35, 'O': 36, 'K': 34}" + "{'E': 48, 'I': 32, 'K': 34, 'O': 36, 'Q': 29, 'U': 29, 'V': 35}" ] }, - "execution_count": 29, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "{C: game_score(Honeycomb('EIKOQUV', C), enable1)\n", - " for C in 'EQUIVOKE'}" + "letters = letterset('EQUIVOKE')\n", + "{C: game_score(Honeycomb(letters, C), enable1) for C in letters}" ] }, { @@ -849,7 +878,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -870,24 +899,24 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 382 ms, sys: 864 µs, total: 383 ms\n", - "Wall time: 383 ms\n" + "CPU times: user 373 ms, sys: 1.32 ms, total: 374 ms\n", + "Wall time: 374 ms\n" ] }, { "data": { "text/plain": [ - "Honeycomb('AEGINRT', 'R')" + "Honeycomb(letters='AEGINRT', center='R')" ] }, - "execution_count": 31, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -900,7 +929,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Same honeycomb for the answer, but four times faster—less than half a second.\n", + "Same honeycomb for the answer, but four times faster–less than 0.4 second.\n", "\n", "# Step 8: Curiosity\n", "\n", @@ -911,7 +940,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -920,7 +949,7 @@ "'ANTITOTALITARIAN'" ] }, - "execution_count": 32, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -938,7 +967,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -976,7 +1005,7 @@ " 'WINDAGE']" ] }, - "execution_count": 33, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -994,40 +1023,45 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Counter({'< 4': 922, 'valid': 44585, 'has S': 103913, '> 7': 23400})" + "[('too many distinct letters', 73611),\n", + " ('contains an S', 53556),\n", + " ('valid', 44585),\n", + " ('too short', 1068)]" ] }, - "execution_count": 34, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Counter('has S' if 'S' in w else \n", - " '< 4' if len(w) < 4 else \n", - " '> 7' if len(set(w)) > 7 else \n", - " 'valid'\n", - " for w in valid_words(open('enable1.txt').read(), lambda w: True))" + "def common(items): return Counter(items).most_common()\n", + "\n", + "common('too short' if len(w) < 4 else \n", + " 'too many distinct letters' if len(set(w)) > 7 else \n", + " 'contains an S' if 'S' in w else\n", + " 'valid'\n", + " for w in valid_words(open('enable1.txt').read(), lambda w: True))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "There are more than twice as many words with an 'S' as there are valid words.\n", + "There are more words with an 'S' than there are valid words.\n", "\n", "### About the points table: How many different letter subsets are there? " ] }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 37, "metadata": {}, "outputs": [ { @@ -1036,7 +1070,7 @@ "21661" ] }, - "execution_count": 35, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -1057,7 +1091,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 38, "metadata": {}, "outputs": [ { @@ -1075,7 +1109,7 @@ " ('ACDEIRT', 307)]" ] }, - "execution_count": 36, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -1088,7 +1122,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The best honeycomb, `'AEGINRT`, is also the highest scoring letter subset on its own (although it only gets 832 of the 3,898 total points from using all seven letters)." + "The best honeycomb, `'AEGINRT'`, is also the highest scoring letter subset on its own (although it only gets 832 of the 3,898 total points from using all seven letters)." ] }, { @@ -1102,7 +1136,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -1111,7 +1145,7 @@ "8084" ] }, - "execution_count": 37, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -1140,7 +1174,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 40, "metadata": {}, "outputs": [ { @@ -1149,7 +1183,7 @@ "14.0" ] }, - "execution_count": 38, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -1169,7 +1203,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -1179,19 +1213,19 @@ " \"\"\"Print stats, words, and word scores for the given honeycomb (or the best\n", " honeycomb if no honeycomb is given) over the given word list.\"\"\"\n", " bins = group_by(words, letterset)\n", - " adj = (\"best \" if honeycomb is None else \"\")\n", + " adj = (\"Best \" if honeycomb is None else \"\")\n", " honeycomb = honeycomb or best_honeycomb(words)\n", " points = game_score(honeycomb, words)\n", " subsets = letter_subsets(honeycomb)\n", " nwords = sum(len(bins[s]) for s in subsets)\n", - " print(f'The {adj}{honeycomb} scores {Ns(points, \"point\")} on {Ns(nwords, \"word\")}',\n", + " print(f'{adj}{honeycomb} scores {Ns(points, \"point\")} on {Ns(nwords, \"word\")}',\n", " f'from a {len(words)} word list:\\n')\n", " for s in sorted(subsets, key=lambda s: (-len(s), s)):\n", " if bins[s]:\n", " pts = sum(word_score(w) for w in bins[s])\n", " wcount = Ns(len(bins[s]), \"pangram\" if len(s) == 7 else \"word\")\n", " intro = f'{s:>7} {Ns(pts, \"point\"):>10} {wcount:>8} '\n", - " words = [f'{w}:{word_score(w)}' for w in sorted(bins[s])]\n", + " words = [f'{w}({word_score(w)})' for w in sorted(bins[s])]\n", " print(fill(' '.join(words), width=110, \n", " initial_indent=intro, subsequent_indent=' '*8))\n", " \n", @@ -1210,18 +1244,18 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "The Honeycomb('AEGLMPX', 'G') scores 24 points on 4 words from a 6 word list:\n", + "Honeycomb(letters='AEGLMPX', center='G') scores 24 points on 4 words from a 6 word list:\n", "\n", - "AEGLMPX 15 points 1 pangram MEGAPLEX:15\n", - " AEGM 1 point 1 word GAME:1\n", - " AGLM 8 points 2 words AMALGAM:7 GLAM:1\n" + "AEGLMPX 15 points 1 pangram MEGAPLEX(15)\n", + " AEGM 1 point 1 word GAME(1)\n", + " AGLM 8 points 2 words AMALGAM(7) GLAM(1)\n" ] } ], @@ -1231,7 +1265,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 43, "metadata": { "scrolled": false }, @@ -1240,96 +1274,101 @@ "name": "stdout", "output_type": "stream", "text": [ - "The best Honeycomb('AEGINRT', 'R') scores 3898 points on 537 words from a 44585 word list:\n", + "Best Honeycomb(letters='AEGINRT', center='R') scores 3898 points on 537 words from a 44585 word list:\n", "\n", - "AEGINRT 832 points 50 pangrams AERATING:15 AGGREGATING:18 ARGENTINE:16 ARGENTITE:16 ENTERTAINING:19\n", - " ENTRAINING:17 ENTREATING:17 GARNIERITE:17 GARTERING:16 GENERATING:17 GNATTIER:15 GRANITE:14 GRATINE:14\n", - " GRATINEE:15 GRATINEEING:18 GREATENING:17 INGRATE:14 INGRATIATE:17 INTEGRATE:16 INTEGRATING:18\n", - " INTENERATING:19 INTERAGE:15 INTERGANG:16 INTERREGNA:17 INTREATING:17 ITERATING:16 ITINERATING:18\n", - " NATTERING:16 RATTENING:16 REAGGREGATING:20 REATTAINING:18 REGENERATING:19 REGRANTING:17 REGRATING:16\n", - " REINITIATING:19 REINTEGRATE:18 REINTEGRATING:20 REITERATING:18 RETAGGING:16 RETAINING:16\n", - " RETARGETING:18 RETEARING:16 RETRAINING:17 RETREATING:17 TANGERINE:16 TANGIER:14 TARGETING:16\n", - " TATTERING:16 TEARING:14 TREATING:15\n", - " AEGINR 270 points 35 words AGINNER:7 AGREEING:8 ANEARING:8 ANERGIA:7 ANGERING:8 ANGRIER:7 ARGININE:8 EARING:6\n", - " EARNING:7 EARRING:7 ENGRAIN:7 ENGRAINING:10 ENRAGING:8 GAINER:6 GANGRENING:10 GARNERING:9 GEARING:7\n", - " GRAINER:7 GRAINIER:8 GRANNIE:7 GREGARINE:9 NAGGIER:7 NEARING:7 RANGIER:7 REAGIN:6 REARING:7\n", - " REARRANGING:11 REEARNING:9 REENGAGING:10 REGAIN:6 REGAINER:8 REGAINING:9 REGEARING:9 REGINA:6\n", - " REGINAE:7\n", - " AEGIRT 34 points 5 words AIGRET:6 AIGRETTE:8 GAITER:6 IRRIGATE:8 TRIAGE:6\n", - " AEGNRT 94 points 13 words ARGENT:6 GARNET:6 GENERATE:8 GRANTEE:7 GRANTER:7 GREATEN:7 NEGATER:7 REAGENT:7\n", - " REGENERATE:10 REGNANT:7 REGRANT:7 TANAGER:7 TEENAGER:8\n", - " AEINRT 232 points 30 words ARENITE:7 ATTAINER:8 ENTERTAIN:9 ENTERTAINER:11 ENTRAIN:7 ENTRAINER:9 INERRANT:8\n", - " INERTIA:7 INERTIAE:8 INTENERATE:10 INTREAT:7 ITERANT:7 ITINERANT:9 ITINERATE:9 NATTIER:7 NITRATE:7\n", - " RATINE:6 REATTAIN:8 REINITIATE:10 RETAIN:6 RETAINER:8 RETINA:6 RETINAE:7 RETIRANT:8 RETRAIN:7\n", - " TERRAIN:7 TERTIAN:7 TRAINEE:7 TRAINER:7 TRIENNIA:8\n", - " AGINRT 167 points 21 words AIRTING:7 ATTIRING:8 GRANITA:7 GRANTING:8 GRATIN:6 GRATING:7 INGRATIATING:12\n", - " INTRIGANT:9 IRRIGATING:10 IRRITATING:10 NARRATING:9 NITRATING:9 RANTING:7 RATING:6 RATTING:7 TARING:6\n", - " TARRING:7 TARTING:7 TITRATING:9 TRAINING:8 TRIAGING:8\n", - " EGINRT 218 points 26 words ENGIRT:6 ENTERING:8 GETTERING:9 GITTERN:7 GREETING:8 IGNITER:7 INTEGER:7\n", - " INTERNING:9 INTERRING:9 REENTERING:10 REGREETING:10 REGRETTING:10 REIGNITE:8 REIGNITING:10\n", - " REINTERRING:11 RENTING:7 RETINTING:9 RETIRING:8 RETTING:7 RINGENT:7 TEETERING:9 TENTERING:9 TIERING:7\n", - " TITTERING:9 TREEING:7 TRIGGERING:10\n", - " AEGNR 120 points 18 words ANGER:5 ARRANGE:7 ARRANGER:8 ENGAGER:7 ENRAGE:6 GANGER:6 GANGRENE:8 GARNER:6\n", - " GENERA:6 GRANGE:6 GRANGER:7 GREENGAGE:9 NAGGER:6 RANGE:5 RANGER:6 REARRANGE:9 REENGAGE:8 REGNA:5\n", - " AEGRT 123 points 19 words AGGREGATE:9 ERGATE:6 ETAGERE:7 GARGET:6 GARRET:6 GARTER:6 GRATE:5 GRATER:6 GREAT:5\n", - " GREATER:7 REAGGREGATE:11 REGATTA:7 REGRATE:7 RETAG:5 RETARGET:8 TAGGER:6 TARGE:5 TARGET:6 TERGA:5\n", - " AEINR 19 points 3 words INANER:6 NARINE:6 RAINIER:7\n", - " AEIRT 135 points 20 words ARIETTA:7 ARIETTE:7 ARTIER:6 ATTIRE:6 ATTRITE:7 IRATE:5 IRATER:6 IRRITATE:8\n", - " ITERATE:7 RATITE:6 RATTIER:7 REITERATE:9 RETIA:5 RETIARII:8 TARRIER:7 TATTIER:7 TEARIER:7 TERAI:5\n", - " TERRARIA:8 TITRATE:7\n", - " AENRT 132 points 19 words ANTEATER:8 ANTRE:5 ENTERA:6 ENTRANT:7 ENTREAT:7 ERRANT:6 NARRATE:7 NARRATER:8\n", - " NATTER:6 NEATER:6 RANTER:6 RATTEEN:7 RATTEN:6 RATTENER:8 REENTRANT:9 RETREATANT:10 TANNER:6 TERNATE:7\n", - " TERRANE:7\n", - " AGINR 138 points 19 words AGRARIAN:8 AIRING:6 ANGARIA:7 ARRAIGN:7 ARRAIGNING:10 ARRANGING:9 GARAGING:8\n", - " GARNI:5 GARRING:7 GNARRING:8 GRAIN:5 GRAINING:8 INGRAIN:7 INGRAINING:10 RAGGING:7 RAGING:6 RAINING:7\n", - " RANGING:7 RARING:6\n", - " AGIRT 5 points 1 word TRAGI:5\n", - " AGNRT 5 points 1 word GRANT:5\n", - " AINRT 64 points 9 words ANTIAIR:7 ANTIAR:6 ANTIARIN:8 INTRANT:7 IRRITANT:8 RIANT:5 TITRANT:7 TRAIN:5\n", - " TRINITARIAN:11\n", - " EGINR 186 points 24 words ENGINEER:8 ENGINEERING:11 ERRING:6 GINGER:6 GINGERING:9 GINNER:6 GINNIER:7\n", - " GREEING:7 GREENIE:7 GREENIER:8 GREENING:8 GRINNER:7 NIGGER:6 REENGINEER:10 REENGINEERING:13\n", - " REGREENING:10 REIGN:5 REIGNING:8 REINING:7 RENEGING:8 RENIG:5 RENIGGING:9 RERIGGING:9 RINGER:6\n", - " EGIRT 27 points 4 words GRITTIER:8 TERGITE:7 TIGER:5 TRIGGER:7\n", - " EGNRT 12 points 2 words GERENT:6 REGENT:6\n", - " EINRT 190 points 29 words ENTIRE:6 INERT:5 INTER:5 INTERN:6 INTERNE:7 INTERNEE:8 INTERTIE:8 NETTIER:7\n", - " NITER:5 NITERIE:7 NITRE:5 NITRITE:7 NITTIER:7 REINTER:7 RENITENT:8 RENTIER:7 RETINE:6 RETINENE:8\n", - " RETINITE:8 RETINT:6 TEENIER:7 TENTIER:7 TERRINE:7 TINIER:6 TINNER:6 TINNIER:7 TINTER:6 TRIENE:6\n", - " TRINE:5\n", - " GINRT 43 points 6 words GIRTING:7 GRITTING:8 RINGGIT:7 TIRING:6 TRIGGING:8 TRINING:7\n", - " AEGR 84 points 17 words AGER:1 AGGER:5 AGREE:5 ARREARAGE:9 EAGER:5 EAGERER:7 EAGRE:5 EGGAR:5 GAGER:5\n", - " GAGGER:6 GARAGE:6 GEAR:1 RAGE:1 RAGEE:5 RAGGEE:6 REGEAR:6 REGGAE:6\n", - " AEIR 22 points 4 words AERIE:5 AERIER:6 AIRER:5 AIRIER:6\n", - " AENR 40 points 9 words ANEAR:5 ARENA:5 EARN:1 EARNER:6 NEAR:1 NEARER:6 RANEE:5 REEARN:6 RERAN:5\n", - " AERT 127 points 24 words AERATE:6 ARETE:5 EATER:5 ERRATA:6 RATE:1 RATER:5 RATTER:6 REATA:5 RETEAR:6\n", - " RETREAT:7 RETREATER:9 TARE:1 TARRE:5 TARTER:6 TARTRATE:8 TATER:5 TATTER:6 TEAR:1 TEARER:6 TERRA:5\n", - " TERRAE:6 TETRA:5 TREAT:5 TREATER:7\n", - " AGIR 6 points 2 words AGRIA:5 RAGI:1\n", - " AGNR 13 points 5 words GNAR:1 GNARR:5 GRAN:1 GRANA:5 RANG:1\n", - " AGRT 13 points 3 words GRAT:1 RAGTAG:6 TAGRAG:6\n", - " AINR 8 points 4 words AIRN:1 NAIRA:5 RAIN:1 RANI:1\n", - " AIRT 21 points 5 words AIRT:1 ATRIA:5 RIATA:5 TIARA:5 TRAIT:5\n", - " ANRT 50 points 10 words ANTRA:5 ARRANT:6 RANT:1 RATAN:5 RATTAN:6 TANTARA:7 TANTRA:6 TARN:1 TARTAN:6\n", - " TARTANA:7\n", - " EGIR 17 points 3 words GREIGE:6 RERIG:5 RIGGER:6\n", - " EGNR 37 points 6 words GENRE:5 GREEN:5 GREENER:7 REGREEN:7 RENEGE:6 RENEGER:7\n", - " EGRT 45 points 7 words EGRET:5 GETTER:6 GREET:5 GREETER:7 REGREET:7 REGRET:6 REGRETTER:9\n", - " EINR 17 points 4 words INNER:5 REIN:1 RENIN:5 RENNIN:6\n", - " EIRT 87 points 17 words RETIE:5 RETIRE:6 RETIREE:7 RETIRER:7 RITE:1 RITTER:6 TERRIER:7 TERRIT:6 TIER:1\n", - " TIRE:1 TITER:5 TITRE:5 TITTER:6 TITTERER:8 TRIER:5 TRITE:5 TRITER:6\n", - " ENRT 104 points 19 words ENTER:5 ENTERER:7 ENTREE:6 ETERNE:6 NETTER:6 REENTER:7 RENNET:6 RENT:1 RENTE:5\n", - " RENTER:6 RETENE:6 TEENER:6 TENNER:6 TENTER:6 TERN:1 TERNE:5 TERREEN:7 TERRENE:7 TREEN:5\n", - " GINR 44 points 9 words GIRN:1 GIRNING:7 GRIN:1 GRINNING:8 IRING:5 RIGGING:7 RING:1 RINGING:7 RINNING:7\n", - " GIRT 3 points 3 words GIRT:1 GRIT:1 TRIG:1\n", - " AER 25 points 7 words AREA:1 AREAE:5 ARREAR:6 RARE:1 RARER:5 REAR:1 REARER:6\n", - " AGR 2 points 2 words AGAR:1 RAGA:1\n", - " AIR 2 points 2 words ARIA:1 RAIA:1\n", - " ART 24 points 5 words ATTAR:5 RATATAT:7 TART:1 TARTAR:6 TATAR:5\n", - " EGR 15 points 4 words EGER:1 EGGER:5 GREE:1 GREEGREE:8\n", - " EIR 11 points 2 words EERIE:5 EERIER:6\n", - " ENR 1 point 1 word ERNE:1\n", - " ERT 27 points 7 words RETE:1 TEETER:6 TERETE:6 TERRET:6 TETTER:6 TREE:1 TRET:1\n", - " GIR 7 points 2 words GRIG:1 GRIGRI:6\n" + "AEGINRT 832 points 50 pangrams AERATING(15) AGGREGATING(18) ARGENTINE(16) ARGENTITE(16) ENTERTAINING(19)\n", + " ENTRAINING(17) ENTREATING(17) GARNIERITE(17) GARTERING(16) GENERATING(17) GNATTIER(15) GRANITE(14)\n", + " GRATINE(14) GRATINEE(15) GRATINEEING(18) GREATENING(17) INGRATE(14) INGRATIATE(17) INTEGRATE(16)\n", + " INTEGRATING(18) INTENERATING(19) INTERAGE(15) INTERGANG(16) INTERREGNA(17) INTREATING(17)\n", + " ITERATING(16) ITINERATING(18) NATTERING(16) RATTENING(16) REAGGREGATING(20) REATTAINING(18)\n", + " REGENERATING(19) REGRANTING(17) REGRATING(16) REINITIATING(19) REINTEGRATE(18) REINTEGRATING(20)\n", + " REITERATING(18) RETAGGING(16) RETAINING(16) RETARGETING(18) RETEARING(16) RETRAINING(17)\n", + " RETREATING(17) TANGERINE(16) TANGIER(14) TARGETING(16) TATTERING(16) TEARING(14) TREATING(15)\n", + " AEGINR 270 points 35 words AGINNER(7) AGREEING(8) ANEARING(8) ANERGIA(7) ANGERING(8) ANGRIER(7) ARGININE(8)\n", + " EARING(6) EARNING(7) EARRING(7) ENGRAIN(7) ENGRAINING(10) ENRAGING(8) GAINER(6) GANGRENING(10)\n", + " GARNERING(9) GEARING(7) GRAINER(7) GRAINIER(8) GRANNIE(7) GREGARINE(9) NAGGIER(7) NEARING(7)\n", + " RANGIER(7) REAGIN(6) REARING(7) REARRANGING(11) REEARNING(9) REENGAGING(10) REGAIN(6) REGAINER(8)\n", + " REGAINING(9) REGEARING(9) REGINA(6) REGINAE(7)\n", + " AEGIRT 34 points 5 words AIGRET(6) AIGRETTE(8) GAITER(6) IRRIGATE(8) TRIAGE(6)\n", + " AEGNRT 94 points 13 words ARGENT(6) GARNET(6) GENERATE(8) GRANTEE(7) GRANTER(7) GREATEN(7) NEGATER(7)\n", + " REAGENT(7) REGENERATE(10) REGNANT(7) REGRANT(7) TANAGER(7) TEENAGER(8)\n", + " AEINRT 232 points 30 words ARENITE(7) ATTAINER(8) ENTERTAIN(9) ENTERTAINER(11) ENTRAIN(7) ENTRAINER(9)\n", + " INERRANT(8) INERTIA(7) INERTIAE(8) INTENERATE(10) INTREAT(7) ITERANT(7) ITINERANT(9) ITINERATE(9)\n", + " NATTIER(7) NITRATE(7) RATINE(6) REATTAIN(8) REINITIATE(10) RETAIN(6) RETAINER(8) RETINA(6) RETINAE(7)\n", + " RETIRANT(8) RETRAIN(7) TERRAIN(7) TERTIAN(7) TRAINEE(7) TRAINER(7) TRIENNIA(8)\n", + " AGINRT 167 points 21 words AIRTING(7) ATTIRING(8) GRANITA(7) GRANTING(8) GRATIN(6) GRATING(7)\n", + " INGRATIATING(12) INTRIGANT(9) IRRIGATING(10) IRRITATING(10) NARRATING(9) NITRATING(9) RANTING(7)\n", + " RATING(6) RATTING(7) TARING(6) TARRING(7) TARTING(7) TITRATING(9) TRAINING(8) TRIAGING(8)\n", + " EGINRT 218 points 26 words ENGIRT(6) ENTERING(8) GETTERING(9) GITTERN(7) GREETING(8) IGNITER(7) INTEGER(7)\n", + " INTERNING(9) INTERRING(9) REENTERING(10) REGREETING(10) REGRETTING(10) REIGNITE(8) REIGNITING(10)\n", + " REINTERRING(11) RENTING(7) RETINTING(9) RETIRING(8) RETTING(7) RINGENT(7) TEETERING(9) TENTERING(9)\n", + " TIERING(7) TITTERING(9) TREEING(7) TRIGGERING(10)\n", + " AEGNR 120 points 18 words ANGER(5) ARRANGE(7) ARRANGER(8) ENGAGER(7) ENRAGE(6) GANGER(6) GANGRENE(8)\n", + " GARNER(6) GENERA(6) GRANGE(6) GRANGER(7) GREENGAGE(9) NAGGER(6) RANGE(5) RANGER(6) REARRANGE(9)\n", + " REENGAGE(8) REGNA(5)\n", + " AEGRT 123 points 19 words AGGREGATE(9) ERGATE(6) ETAGERE(7) GARGET(6) GARRET(6) GARTER(6) GRATE(5) GRATER(6)\n", + " GREAT(5) GREATER(7) REAGGREGATE(11) REGATTA(7) REGRATE(7) RETAG(5) RETARGET(8) TAGGER(6) TARGE(5)\n", + " TARGET(6) TERGA(5)\n", + " AEINR 19 points 3 words INANER(6) NARINE(6) RAINIER(7)\n", + " AEIRT 135 points 20 words ARIETTA(7) ARIETTE(7) ARTIER(6) ATTIRE(6) ATTRITE(7) IRATE(5) IRATER(6)\n", + " IRRITATE(8) ITERATE(7) RATITE(6) RATTIER(7) REITERATE(9) RETIA(5) RETIARII(8) TARRIER(7) TATTIER(7)\n", + " TEARIER(7) TERAI(5) TERRARIA(8) TITRATE(7)\n", + " AENRT 132 points 19 words ANTEATER(8) ANTRE(5) ENTERA(6) ENTRANT(7) ENTREAT(7) ERRANT(6) NARRATE(7)\n", + " NARRATER(8) NATTER(6) NEATER(6) RANTER(6) RATTEEN(7) RATTEN(6) RATTENER(8) REENTRANT(9) RETREATANT(10)\n", + " TANNER(6) TERNATE(7) TERRANE(7)\n", + " AGINR 138 points 19 words AGRARIAN(8) AIRING(6) ANGARIA(7) ARRAIGN(7) ARRAIGNING(10) ARRANGING(9)\n", + " GARAGING(8) GARNI(5) GARRING(7) GNARRING(8) GRAIN(5) GRAINING(8) INGRAIN(7) INGRAINING(10) RAGGING(7)\n", + " RAGING(6) RAINING(7) RANGING(7) RARING(6)\n", + " AGIRT 5 points 1 word TRAGI(5)\n", + " AGNRT 5 points 1 word GRANT(5)\n", + " AINRT 64 points 9 words ANTIAIR(7) ANTIAR(6) ANTIARIN(8) INTRANT(7) IRRITANT(8) RIANT(5) TITRANT(7)\n", + " TRAIN(5) TRINITARIAN(11)\n", + " EGINR 186 points 24 words ENGINEER(8) ENGINEERING(11) ERRING(6) GINGER(6) GINGERING(9) GINNER(6) GINNIER(7)\n", + " GREEING(7) GREENIE(7) GREENIER(8) GREENING(8) GRINNER(7) NIGGER(6) REENGINEER(10) REENGINEERING(13)\n", + " REGREENING(10) REIGN(5) REIGNING(8) REINING(7) RENEGING(8) RENIG(5) RENIGGING(9) RERIGGING(9)\n", + " RINGER(6)\n", + " EGIRT 27 points 4 words GRITTIER(8) TERGITE(7) TIGER(5) TRIGGER(7)\n", + " EGNRT 12 points 2 words GERENT(6) REGENT(6)\n", + " EINRT 190 points 29 words ENTIRE(6) INERT(5) INTER(5) INTERN(6) INTERNE(7) INTERNEE(8) INTERTIE(8)\n", + " NETTIER(7) NITER(5) NITERIE(7) NITRE(5) NITRITE(7) NITTIER(7) REINTER(7) RENITENT(8) RENTIER(7)\n", + " RETINE(6) RETINENE(8) RETINITE(8) RETINT(6) TEENIER(7) TENTIER(7) TERRINE(7) TINIER(6) TINNER(6)\n", + " TINNIER(7) TINTER(6) TRIENE(6) TRINE(5)\n", + " GINRT 43 points 6 words GIRTING(7) GRITTING(8) RINGGIT(7) TIRING(6) TRIGGING(8) TRINING(7)\n", + " AEGR 84 points 17 words AGER(1) AGGER(5) AGREE(5) ARREARAGE(9) EAGER(5) EAGERER(7) EAGRE(5) EGGAR(5)\n", + " GAGER(5) GAGGER(6) GARAGE(6) GEAR(1) RAGE(1) RAGEE(5) RAGGEE(6) REGEAR(6) REGGAE(6)\n", + " AEIR 22 points 4 words AERIE(5) AERIER(6) AIRER(5) AIRIER(6)\n", + " AENR 40 points 9 words ANEAR(5) ARENA(5) EARN(1) EARNER(6) NEAR(1) NEARER(6) RANEE(5) REEARN(6) RERAN(5)\n", + " AERT 127 points 24 words AERATE(6) ARETE(5) EATER(5) ERRATA(6) RATE(1) RATER(5) RATTER(6) REATA(5)\n", + " RETEAR(6) RETREAT(7) RETREATER(9) TARE(1) TARRE(5) TARTER(6) TARTRATE(8) TATER(5) TATTER(6) TEAR(1)\n", + " TEARER(6) TERRA(5) TERRAE(6) TETRA(5) TREAT(5) TREATER(7)\n", + " AGIR 6 points 2 words AGRIA(5) RAGI(1)\n", + " AGNR 13 points 5 words GNAR(1) GNARR(5) GRAN(1) GRANA(5) RANG(1)\n", + " AGRT 13 points 3 words GRAT(1) RAGTAG(6) TAGRAG(6)\n", + " AINR 8 points 4 words AIRN(1) NAIRA(5) RAIN(1) RANI(1)\n", + " AIRT 21 points 5 words AIRT(1) ATRIA(5) RIATA(5) TIARA(5) TRAIT(5)\n", + " ANRT 50 points 10 words ANTRA(5) ARRANT(6) RANT(1) RATAN(5) RATTAN(6) TANTARA(7) TANTRA(6) TARN(1)\n", + " TARTAN(6) TARTANA(7)\n", + " EGIR 17 points 3 words GREIGE(6) RERIG(5) RIGGER(6)\n", + " EGNR 37 points 6 words GENRE(5) GREEN(5) GREENER(7) REGREEN(7) RENEGE(6) RENEGER(7)\n", + " EGRT 45 points 7 words EGRET(5) GETTER(6) GREET(5) GREETER(7) REGREET(7) REGRET(6) REGRETTER(9)\n", + " EINR 17 points 4 words INNER(5) REIN(1) RENIN(5) RENNIN(6)\n", + " EIRT 87 points 17 words RETIE(5) RETIRE(6) RETIREE(7) RETIRER(7) RITE(1) RITTER(6) TERRIER(7) TERRIT(6)\n", + " TIER(1) TIRE(1) TITER(5) TITRE(5) TITTER(6) TITTERER(8) TRIER(5) TRITE(5) TRITER(6)\n", + " ENRT 104 points 19 words ENTER(5) ENTERER(7) ENTREE(6) ETERNE(6) NETTER(6) REENTER(7) RENNET(6) RENT(1)\n", + " RENTE(5) RENTER(6) RETENE(6) TEENER(6) TENNER(6) TENTER(6) TERN(1) TERNE(5) TERREEN(7) TERRENE(7)\n", + " TREEN(5)\n", + " GINR 44 points 9 words GIRN(1) GIRNING(7) GRIN(1) GRINNING(8) IRING(5) RIGGING(7) RING(1) RINGING(7)\n", + " RINNING(7)\n", + " GIRT 3 points 3 words GIRT(1) GRIT(1) TRIG(1)\n", + " AER 25 points 7 words AREA(1) AREAE(5) ARREAR(6) RARE(1) RARER(5) REAR(1) REARER(6)\n", + " AGR 2 points 2 words AGAR(1) RAGA(1)\n", + " AIR 2 points 2 words ARIA(1) RAIA(1)\n", + " ART 24 points 5 words ATTAR(5) RATATAT(7) TART(1) TARTAR(6) TATAR(5)\n", + " EGR 15 points 4 words EGER(1) EGGER(5) GREE(1) GREEGREE(8)\n", + " EIR 11 points 2 words EERIE(5) EERIER(6)\n", + " ENR 1 point 1 word ERNE(1)\n", + " ERT 27 points 7 words RETE(1) TEETER(6) TERETE(6) TERRET(6) TETTER(6) TREE(1) TRET(1)\n", + " GIR 7 points 2 words GRIG(1) GRIGRI(6)\n" ] } ], @@ -1343,12 +1382,12 @@ "source": [ "# Step 10: What honeycombs have a high score without a lot of words?\n", "\n", - "Michael Braverman said he dislikes puzzles with a lot of low-scoring four-letter words. Can we find succint puzzles with lots of points but few words? With two objectives there won't be a single best answer to this question; rather we can ask: what honeycombs are there such that there are no other honeycombs with both more points and fewer words? We say such honeycombs are [**Pareto optimal**](https://en.wikipedia.org/wiki/Pareto_efficiency) and are on the **Pareto frontier**. We can find them as follows:" + "[Michael Braverman](https://www.linkedin.com/in/michael-braverman-2b32721/) said he dislikes puzzles with a lot of low-scoring four-letter words. Can we find succint puzzles with lots of points and fewer words? With two objectives there won't be a single best answer to this question; rather we can ask: what honeycombs are there such that there are no other honeycombs with both more points and fewer words? We say such honeycombs are [**Pareto optimal**](https://en.wikipedia.org/wiki/Pareto_efficiency) and are on the **Pareto frontier**. We can find them as follows:" ] }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ @@ -1379,7 +1418,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 45, "metadata": {}, "outputs": [ { @@ -1388,7 +1427,7 @@ "108" ] }, - "execution_count": 43, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } @@ -1402,148 +1441,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "So there are 108 (out of 55,902) honeycombs on the Pareto frontier. Here they are:" + "So there are 108 (out of 55,902) honeycombs on the Pareto frontier. Let's see what the frontier looks like by plotting word counts versus points scored:" ] }, { "cell_type": "code", - "execution_count": 44, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[(1, 15, Honeycomb('BIMNRUV', 'V'), 15.0),\n", - " (2, 26, Honeycomb('DHNORTX', 'X'), 13.0),\n", - " (3, 31, Honeycomb('CILMOQU', 'Q'), 10.33),\n", - " (4, 32, Honeycomb('BGINOUX', 'X'), 8.0),\n", - " (5, 45, Honeycomb('CEGIPTX', 'G'), 9.0),\n", - " (6, 50, Honeycomb('DELNPUZ', 'Z'), 8.33),\n", - " (7, 62, Honeycomb('BGILNOX', 'X'), 8.86),\n", - " (8, 67, Honeycomb('DGINOXZ', 'X'), 8.38),\n", - " (9, 70, Honeycomb('EFNQRTU', 'Q'), 7.78),\n", - " (10, 84, Honeycomb('CENOQRU', 'Q'), 8.4),\n", - " (11, 86, Honeycomb('GINOTUV', 'V'), 7.82),\n", - " (12, 100, Honeycomb('GILMNUZ', 'Z'), 8.33),\n", - " (13, 108, Honeycomb('GINOQTU', 'Q'), 8.31),\n", - " (14, 113, Honeycomb('CINOTXY', 'X'), 8.07),\n", - " (15, 115, Honeycomb('DGINOXZ', 'Z'), 7.67),\n", - " (16, 116, Honeycomb('DEIOPXZ', 'Z'), 7.25),\n", - " (17, 124, Honeycomb('CGINOUV', 'V'), 7.29),\n", - " (18, 136, Honeycomb('GHORTUW', 'W'), 7.56),\n", - " (19, 157, Honeycomb('DEIORXZ', 'X'), 8.26),\n", - " (22, 172, Honeycomb('DEGINPZ', 'Z'), 7.82),\n", - " (23, 184, Honeycomb('ACELQRU', 'Q'), 8.0),\n", - " (25, 189, Honeycomb('DELOPRX', 'X'), 7.56),\n", - " (26, 198, Honeycomb('AILNOTZ', 'Z'), 7.62),\n", - " (28, 224, Honeycomb('DEGINRZ', 'Z'), 8.0),\n", - " (33, 238, Honeycomb('DEIORXZ', 'Z'), 7.21),\n", - " (37, 243, Honeycomb('CGILNYZ', 'G'), 6.57),\n", - " (38, 275, Honeycomb('AGINOTZ', 'Z'), 7.24),\n", - " (41, 279, Honeycomb('CFGHILN', 'G'), 6.8),\n", - " (43, 324, Honeycomb('ACGINTV', 'V'), 7.53),\n", - " (45, 374, Honeycomb('ACINOTV', 'V'), 8.31),\n", - " (55, 385, Honeycomb('ACINOTU', 'U'), 7.0),\n", - " (59, 392, Honeycomb('GHINOTU', 'U'), 6.64),\n", - " (60, 396, Honeycomb('ACGILNZ', 'C'), 6.6),\n", - " (61, 404, Honeycomb('CGHINTW', 'G'), 6.62),\n", - " (62, 424, Honeycomb('CGHINRU', 'G'), 6.84),\n", - " (63, 426, Honeycomb('GINOPRU', 'U'), 6.76),\n", - " (65, 470, Honeycomb('CEINOTV', 'V'), 7.23),\n", - " (69, 485, Honeycomb('ACGINTV', 'C'), 7.03),\n", - " (70, 493, Honeycomb('CEGILNR', 'C'), 7.04),\n", - " (72, 495, Honeycomb('ACGHINR', 'H'), 6.88),\n", - " (73, 496, Honeycomb('CENORTV', 'V'), 6.79),\n", - " (74, 526, Honeycomb('ACILRTU', 'U'), 7.11),\n", - " (79, 530, Honeycomb('FGHILNT', 'G'), 6.71),\n", - " (81, 537, Honeycomb('BEFGINT', 'G'), 6.63),\n", - " (83, 552, Honeycomb('EGIMNPT', 'G'), 6.65),\n", - " (84, 571, Honeycomb('ACGIKNT', 'G'), 6.8),\n", - " (86, 606, Honeycomb('ACGINTV', 'G'), 7.05),\n", - " (89, 615, Honeycomb('ACILNTY', 'Y'), 6.91),\n", - " (92, 617, Honeycomb('CGINOTU', 'G'), 6.71),\n", - " (94, 646, Honeycomb('DELOPRV', 'V'), 6.87),\n", - " (98, 651, Honeycomb('FGILNPU', 'N'), 6.64),\n", - " (99, 674, Honeycomb('ACGINTV', 'T'), 6.81),\n", - " (102, 739, Honeycomb('GINORTU', 'U'), 7.25),\n", - " (110, 746, Honeycomb('CEGILNT', 'G'), 6.78),\n", - " (111, 764, Honeycomb('ACGHINT', 'G'), 6.88),\n", - " (113, 803, Honeycomb('CEGINOT', 'G'), 7.11),\n", - " (120, 823, Honeycomb('ACILRTU', 'C'), 6.86),\n", - " (122, 860, Honeycomb('ACGILNT', 'C'), 7.05),\n", - " (126, 879, Honeycomb('CEGINOT', 'C'), 6.98),\n", - " (131, 882, Honeycomb('CEGINPR', 'G'), 6.73),\n", - " (135, 929, Honeycomb('CEGILNR', 'G'), 6.88),\n", - " (136, 955, Honeycomb('EGINRTV', 'V'), 7.02),\n", - " (140, 994, Honeycomb('CENORTU', 'U'), 7.1),\n", - " (149, 1064, Honeycomb('CEGINRT', 'G'), 7.14),\n", - " (152, 1065, Honeycomb('ACGINOT', 'G'), 7.01),\n", - " (159, 1118, Honeycomb('ACGILNT', 'G'), 7.03),\n", - " (162, 1159, Honeycomb('CENORTU', 'C'), 7.15),\n", - " (166, 1161, Honeycomb('EFGILNR', 'G'), 6.99),\n", - " (169, 1187, Honeycomb('CDEINRT', 'C'), 7.02),\n", - " (172, 1199, Honeycomb('EGINRTW', 'G'), 6.97),\n", - " (180, 1207, Honeycomb('BEGINRT', 'G'), 6.71),\n", - " (181, 1288, Honeycomb('EFGINRT', 'G'), 7.12),\n", - " (183, 1323, Honeycomb('EGINRTV', 'G'), 7.23),\n", - " (188, 1367, Honeycomb('EGIMNRT', 'G'), 7.27),\n", - " (202, 1405, Honeycomb('EGINRTU', 'G'), 6.96),\n", - " (212, 1430, Honeycomb('EGINRTV', 'T'), 6.75),\n", - " (214, 1431, Honeycomb('DEGINRV', 'G'), 6.69),\n", - " (219, 1448, Honeycomb('ACINORT', 'C'), 6.61),\n", - " (221, 1472, Honeycomb('ACILNOT', 'L'), 6.66),\n", - " (223, 1562, Honeycomb('DEFGINR', 'G'), 7.0),\n", - " (230, 1566, Honeycomb('BDEGINR', 'G'), 6.81),\n", - " (231, 1598, Honeycomb('ACGINOT', 'I'), 6.92),\n", - " (234, 1685, Honeycomb('EGILNRT', 'G'), 7.2),\n", - " (238, 1691, Honeycomb('ACILNOT', 'C'), 7.11),\n", - " (244, 1737, Honeycomb('CEINORT', 'C'), 7.12),\n", - " (251, 1774, Honeycomb('ACILNOT', 'I'), 7.07),\n", - " (254, 1787, Honeycomb('ACILNOT', 'N'), 7.04),\n", - " (264, 1788, Honeycomb('DEGILNR', 'N'), 6.77),\n", - " (265, 1830, Honeycomb('DEGINRT', 'G'), 6.91),\n", - " (269, 1847, Honeycomb('CEINORT', 'I'), 6.87),\n", - " (275, 1856, Honeycomb('EGILNRT', 'R'), 6.75),\n", - " (277, 1882, Honeycomb('ACILNOT', 'A'), 6.79),\n", - " (280, 1893, Honeycomb('DEGILNR', 'G'), 6.76),\n", - " (282, 1968, Honeycomb('AGINORT', 'I'), 6.98),\n", - " (283, 1982, Honeycomb('ACEINRT', 'C'), 7.0),\n", - " (288, 2011, Honeycomb('AEGILNT', 'G'), 6.98),\n", - " (297, 2038, Honeycomb('CEINORT', 'N'), 6.86),\n", - " (312, 2117, Honeycomb('AGINORT', 'N'), 6.79),\n", - " (320, 2193, Honeycomb('AEGILNT', 'I'), 6.85),\n", - " (330, 2311, Honeycomb('ACEINRT', 'I'), 7.0),\n", - " (354, 2414, Honeycomb('ACEINRT', 'N'), 6.82),\n", - " (370, 2575, Honeycomb('ADEGINR', 'I'), 6.96),\n", - " (397, 2626, Honeycomb('ADEGINR', 'D'), 6.61),\n", - " (403, 3095, Honeycomb('AEGINRT', 'G'), 7.68),\n", - " (442, 3406, Honeycomb('AEGINRT', 'I'), 7.71),\n", - " (466, 3421, Honeycomb('AEGINRT', 'T'), 7.34),\n", - " (512, 3782, Honeycomb('AEGINRT', 'N'), 7.39),\n", - " (537, 3898, Honeycomb('AEGINRT', 'R'), 7.26)]" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ph # (word count, points, honeycomb, points/wcount) " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's see what the frontier looks like by plotting word counts versus points scored:" - ] - }, - { - "cell_type": "code", - "execution_count": 45, + "execution_count": 46, "metadata": {}, "outputs": [ { @@ -1572,12 +1475,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "That's somewhat surprising; usually a Pareto frontier looks like a quarter-circle; here it looks like an almost straight line. Maybe we can get a better view by plotting word counts versus the number of points per word:" + "That was somewhat surprising to me; usually a Pareto frontier looks like a quarter-circle; here it looks like an almost straight line. Maybe we can get a better view by plotting word counts versus the number of points per word:" ] }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 47, "metadata": {}, "outputs": [ { @@ -1606,38 +1509,38 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[(1, 15, Honeycomb('BIMNRUV', 'V'), 15.0),\n", - " (2, 26, Honeycomb('DHNORTX', 'X'), 13.0),\n", - " (3, 31, Honeycomb('CILMOQU', 'Q'), 10.33),\n", - " (4, 32, Honeycomb('BGINOUX', 'X'), 8.0),\n", - " (5, 45, Honeycomb('CEGIPTX', 'G'), 9.0),\n", - " (6, 50, Honeycomb('DELNPUZ', 'Z'), 8.33),\n", - " (7, 62, Honeycomb('BGILNOX', 'X'), 8.86),\n", - " (8, 67, Honeycomb('DGINOXZ', 'X'), 8.38),\n", - " (9, 70, Honeycomb('EFNQRTU', 'Q'), 7.78),\n", - " (10, 84, Honeycomb('CENOQRU', 'Q'), 8.4),\n", - " (11, 86, Honeycomb('GINOTUV', 'V'), 7.82),\n", - " (12, 100, Honeycomb('GILMNUZ', 'Z'), 8.33),\n", - " (13, 108, Honeycomb('GINOQTU', 'Q'), 8.31),\n", - " (14, 113, Honeycomb('CINOTXY', 'X'), 8.07),\n", - " (15, 115, Honeycomb('DGINOXZ', 'Z'), 7.67),\n", - " (19, 157, Honeycomb('DEIORXZ', 'X'), 8.26),\n", - " (22, 172, Honeycomb('DEGINPZ', 'Z'), 7.82),\n", - " (23, 184, Honeycomb('ACELQRU', 'Q'), 8.0),\n", - " (26, 198, Honeycomb('AILNOTZ', 'Z'), 7.62),\n", - " (28, 224, Honeycomb('DEGINRZ', 'Z'), 8.0),\n", - " (45, 374, Honeycomb('ACINOTV', 'V'), 8.31),\n", - " (403, 3095, Honeycomb('AEGINRT', 'G'), 7.68),\n", - " (442, 3406, Honeycomb('AEGINRT', 'I'), 7.71)]" + "[(1, 15, Honeycomb(letters='BIMNRUV', center='V'), 15.0),\n", + " (2, 26, Honeycomb(letters='DHNORTX', center='X'), 13.0),\n", + " (3, 31, Honeycomb(letters='CILMOQU', center='Q'), 10.33),\n", + " (4, 32, Honeycomb(letters='BGINOUX', center='X'), 8.0),\n", + " (5, 45, Honeycomb(letters='CEGIPTX', center='G'), 9.0),\n", + " (6, 50, Honeycomb(letters='DELNPUZ', center='Z'), 8.33),\n", + " (7, 62, Honeycomb(letters='BGILNOX', center='X'), 8.86),\n", + " (8, 67, Honeycomb(letters='DGINOXZ', center='X'), 8.38),\n", + " (9, 70, Honeycomb(letters='EFNQRTU', center='Q'), 7.78),\n", + " (10, 84, Honeycomb(letters='CENOQRU', center='Q'), 8.4),\n", + " (11, 86, Honeycomb(letters='GINOTUV', center='V'), 7.82),\n", + " (12, 100, Honeycomb(letters='GILMNUZ', center='Z'), 8.33),\n", + " (13, 108, Honeycomb(letters='GINOQTU', center='Q'), 8.31),\n", + " (14, 113, Honeycomb(letters='CINOTXY', center='X'), 8.07),\n", + " (15, 115, Honeycomb(letters='DGINOXZ', center='Z'), 7.67),\n", + " (19, 157, Honeycomb(letters='DEIORXZ', center='X'), 8.26),\n", + " (22, 172, Honeycomb(letters='DEGINPZ', center='Z'), 7.82),\n", + " (23, 184, Honeycomb(letters='ACELQRU', center='Q'), 8.0),\n", + " (26, 198, Honeycomb(letters='AILNOTZ', center='Z'), 7.62),\n", + " (28, 224, Honeycomb(letters='DEGINRZ', center='Z'), 8.0),\n", + " (45, 374, Honeycomb(letters='ACINOTV', center='V'), 8.31),\n", + " (403, 3095, Honeycomb(letters='AEGINRT', center='G'), 7.68),\n", + " (442, 3406, Honeycomb(letters='AEGINRT', center='I'), 7.71)]" ] }, - "execution_count": 47, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -1657,30 +1560,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here are reports on what I think are the most interesting low-word-count, higher-score honeycombs. I would not have been able to find any words at all for the first one, I think:" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The Honeycomb('CEGIPTX', 'G') scores 45 points on 5 words from a 44585 word list:\n", - "\n", - "CEGIPTX 17 points 1 pangram EPEXEGETIC:17\n", - " CEGITX 8 points 1 word EXEGETIC:8\n", - " CEGIP 7 points 1 word EPIGEIC:7\n", - " EGIP 6 points 1 word PIGGIE:6\n", - " EGTX 7 points 1 word EXEGETE:7\n" - ] - } - ], - "source": [ - "report(Honeycomb('CEGIPTX', 'G'))" + "Here are reports on what I think are the most interesting high-score/few-words honeycombs:" ] }, { @@ -1692,27 +1572,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "The Honeycomb('DEIORXZ', 'X') scores 157 points on 19 words from a 44585 word list:\n", + "Honeycomb(letters='CEGIPTX', center='G') scores 45 points on 5 words from a 44585 word list:\n", "\n", - "DEIORXZ 65 points 4 pangrams DEOXIDIZER:17 OXIDIZER:15 REOXIDIZE:16 REOXIDIZED:17\n", - " DEIOXZ 34 points 4 words DEOXIDIZE:9 DEOXIDIZED:10 OXIDIZE:7 OXIDIZED:8\n", - " DEIOX 23 points 4 words DIOXIDE:7 DOXIE:5 EXODOI:6 OXIDE:5\n", - " DEORX 12 points 2 words REDOX:5 XEROXED:7\n", - " DEIX 5 points 1 word DEXIE:5\n", - " DIOX 13 points 3 words DIOXID:6 IXODID:6 OXID:1\n", - " EORX 5 points 1 word XEROX:5\n" + "CEGIPTX 17 points 1 pangram EPEXEGETIC(17)\n", + " CEGITX 8 points 1 word EXEGETIC(8)\n", + " CEGIP 7 points 1 word EPIGEIC(7)\n", + " EGIP 6 points 1 word PIGGIE(6)\n", + " EGTX 7 points 1 word EXEGETE(7)\n" ] } ], "source": [ - "report(Honeycomb('DEIORXZ', 'X'))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following I think are decent puzzles:" + "report(Honeycomb('CEGIPTX', 'G'))" ] }, { @@ -1724,35 +1595,20 @@ "name": "stdout", "output_type": "stream", "text": [ - "The Honeycomb('ACINOTV', 'V') scores 374 points on 45 words from a 44585 word list:\n", + "Honeycomb(letters='DEIORXZ', center='X') scores 157 points on 19 words from a 44585 word list:\n", "\n", - "ACINOTV 171 points 10 pangrams ACTIVATION:17 AVOCATION:16 CAVITATION:17 CONVOCATION:18 INACTIVATION:19\n", - " INVOCATION:17 VACATION:15 VACCINATION:18 VATICINATION:19 VOCATION:15\n", - " ACINOV 7 points 1 word AVIONIC:7\n", - " ACINTV 8 points 1 word CAVATINA:8\n", - " AINOTV 62 points 7 words AVIATION:8 INNOVATION:10 INVITATION:10 NOVATION:8 OVATION:7 TITIVATION:10\n", - " VITIATION:9\n", - " CINOTV 17 points 2 words CONVICT:7 CONVICTION:10\n", - " ACINV 20 points 3 words VACCINA:7 VACCINIA:8 VINCA:5\n", - " ACITV 24 points 4 words ATAVIC:6 VATIC:5 VIATIC:6 VIATICA:7\n", - " ACNTV 6 points 1 word VACANT:6\n", - " ACOTV 6 points 1 word OCTAVO:6\n", - " AINOV 5 points 1 word AVION:5\n", - " CINOV 11 points 2 words COVIN:5 OVONIC:6\n", - " AINV 7 points 3 words AVIAN:5 VAIN:1 VINA:1\n", - " AITV 6 points 2 words VITA:1 VITTA:5\n", - " ANOV 1 point 1 word NOVA:1\n", - " ANTV 5 points 1 word AVANT:5\n", - " AOTV 6 points 1 word OTTAVA:6\n", - " CINV 5 points 1 word VINIC:5\n", - " INOV 1 point 1 word VINO:1\n", - " AIV 1 point 1 word VIVA:1\n", - " CIV 5 points 1 word CIVIC:5\n" + "DEIORXZ 65 points 4 pangrams DEOXIDIZER(17) OXIDIZER(15) REOXIDIZE(16) REOXIDIZED(17)\n", + " DEIOXZ 34 points 4 words DEOXIDIZE(9) DEOXIDIZED(10) OXIDIZE(7) OXIDIZED(8)\n", + " DEIOX 23 points 4 words DIOXIDE(7) DOXIE(5) EXODOI(6) OXIDE(5)\n", + " DEORX 12 points 2 words REDOX(5) XEROXED(7)\n", + " DEIX 5 points 1 word DEXIE(5)\n", + " DIOX 13 points 3 words DIOXID(6) IXODID(6) OXID(1)\n", + " EORX 5 points 1 word XEROX(5)\n" ] } ], "source": [ - "report(Honeycomb('ACINOTV', 'V'))" + "report(Honeycomb('DEIORXZ', 'X'))" ] }, { @@ -1764,37 +1620,77 @@ "name": "stdout", "output_type": "stream", "text": [ - "The Honeycomb('ACINOTU', 'U') scores 385 points on 55 words from a 44585 word list:\n", + "Honeycomb(letters='ACINOTV', center='V') scores 374 points on 45 words from a 44585 word list:\n", "\n", - "ACINOTU 162 points 10 pangrams ACTUATION:16 ANNUNCIATION:19 AUCTION:14 CAUTION:14 CONTINUA:15 CONTINUANT:17\n", - " CONTINUATION:19 COUNTIAN:15 CUNCTATION:17 INCAUTION:16\n", - " ACINTU 6 points 1 word TUNICA:6\n", - " ACNOTU 31 points 4 words ACCOUNT:7 ACCOUNTANT:10 COCOANUT:8 TOUCAN:6\n", - " AINOTU 17 points 2 words ANTIUNION:9 NUTATION:8\n", - " CINOTU 24 points 3 words CONTINUO:8 INUNCTION:9 UNCTION:7\n", - " ACINU 5 points 1 word UNCIA:5\n", - " ACOTU 6 points 1 word OUTACT:6\n", - " AINTU 9 points 1 word ANNUITANT:9\n", - " CINOU 13 points 2 words INCONNU:7 NUNCIO:6\n", - " CINTU 10 points 2 words CUTIN:5 TUNIC:5\n", - " CNOTU 20 points 3 words COCONUT:7 COUNT:5 OUTCOUNT:8\n", - " INOTU 16 points 2 words INTUITION:9 TUITION:7\n", - " AINU 1 point 1 word UNAI:1\n", - " ANTU 13 points 4 words AUNT:1 NUTANT:6 TAUNT:5 TUNA:1\n", - " AOTU 1 point 1 word AUTO:1\n", - " CINU 7 points 2 words UNCI:1 UNCINI:6\n", - " CNOU 1 point 1 word UNCO:1\n", - " CNTU 6 points 2 words CUNT:1 UNCUT:5\n", - " COTU 6 points 1 word CUTOUT:6\n", - " INOU 13 points 2 words NONUNION:8 UNION:5\n", - " INTU 7 points 2 words INTUIT:6 UNIT:1\n", - " NOTU 1 point 1 word UNTO:1\n", - " ANU 1 point 1 word UNAU:1\n", - " ATU 1 point 1 word TAUT:1\n", - " ITU 5 points 1 word TUTTI:5\n", - " NOU 1 point 1 word NOUN:1\n", - " OTU 1 point 1 word TOUT:1\n", - " TU 1 point 1 word TUTU:1\n" + "ACINOTV 171 points 10 pangrams ACTIVATION(17) AVOCATION(16) CAVITATION(17) CONVOCATION(18) INACTIVATION(19)\n", + " INVOCATION(17) VACATION(15) VACCINATION(18) VATICINATION(19) VOCATION(15)\n", + " ACINOV 7 points 1 word AVIONIC(7)\n", + " ACINTV 8 points 1 word CAVATINA(8)\n", + " AINOTV 62 points 7 words AVIATION(8) INNOVATION(10) INVITATION(10) NOVATION(8) OVATION(7) TITIVATION(10)\n", + " VITIATION(9)\n", + " CINOTV 17 points 2 words CONVICT(7) CONVICTION(10)\n", + " ACINV 20 points 3 words VACCINA(7) VACCINIA(8) VINCA(5)\n", + " ACITV 24 points 4 words ATAVIC(6) VATIC(5) VIATIC(6) VIATICA(7)\n", + " ACNTV 6 points 1 word VACANT(6)\n", + " ACOTV 6 points 1 word OCTAVO(6)\n", + " AINOV 5 points 1 word AVION(5)\n", + " CINOV 11 points 2 words COVIN(5) OVONIC(6)\n", + " AINV 7 points 3 words AVIAN(5) VAIN(1) VINA(1)\n", + " AITV 6 points 2 words VITA(1) VITTA(5)\n", + " ANOV 1 point 1 word NOVA(1)\n", + " ANTV 5 points 1 word AVANT(5)\n", + " AOTV 6 points 1 word OTTAVA(6)\n", + " CINV 5 points 1 word VINIC(5)\n", + " INOV 1 point 1 word VINO(1)\n", + " AIV 1 point 1 word VIVA(1)\n", + " CIV 5 points 1 word CIVIC(5)\n" + ] + } + ], + "source": [ + "report(Honeycomb('ACINOTV', 'V'))" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Honeycomb(letters='ACINOTU', center='U') scores 385 points on 55 words from a 44585 word list:\n", + "\n", + "ACINOTU 162 points 10 pangrams ACTUATION(16) ANNUNCIATION(19) AUCTION(14) CAUTION(14) CONTINUA(15)\n", + " CONTINUANT(17) CONTINUATION(19) COUNTIAN(15) CUNCTATION(17) INCAUTION(16)\n", + " ACINTU 6 points 1 word TUNICA(6)\n", + " ACNOTU 31 points 4 words ACCOUNT(7) ACCOUNTANT(10) COCOANUT(8) TOUCAN(6)\n", + " AINOTU 17 points 2 words ANTIUNION(9) NUTATION(8)\n", + " CINOTU 24 points 3 words CONTINUO(8) INUNCTION(9) UNCTION(7)\n", + " ACINU 5 points 1 word UNCIA(5)\n", + " ACOTU 6 points 1 word OUTACT(6)\n", + " AINTU 9 points 1 word ANNUITANT(9)\n", + " CINOU 13 points 2 words INCONNU(7) NUNCIO(6)\n", + " CINTU 10 points 2 words CUTIN(5) TUNIC(5)\n", + " CNOTU 20 points 3 words COCONUT(7) COUNT(5) OUTCOUNT(8)\n", + " INOTU 16 points 2 words INTUITION(9) TUITION(7)\n", + " AINU 1 point 1 word UNAI(1)\n", + " ANTU 13 points 4 words AUNT(1) NUTANT(6) TAUNT(5) TUNA(1)\n", + " AOTU 1 point 1 word AUTO(1)\n", + " CINU 7 points 2 words UNCI(1) UNCINI(6)\n", + " CNOU 1 point 1 word UNCO(1)\n", + " CNTU 6 points 2 words CUNT(1) UNCUT(5)\n", + " COTU 6 points 1 word CUTOUT(6)\n", + " INOU 13 points 2 words NONUNION(8) UNION(5)\n", + " INTU 7 points 2 words INTUIT(6) UNIT(1)\n", + " NOTU 1 point 1 word UNTO(1)\n", + " ANU 1 point 1 word UNAU(1)\n", + " ATU 1 point 1 word TAUT(1)\n", + " ITU 5 points 1 word TUTTI(5)\n", + " NOU 1 point 1 word NOUN(1)\n", + " OTU 1 point 1 word TOUT(1)\n", + " TU 1 point 1 word TUTU(1)\n" ] } ], @@ -1806,215 +1702,684 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Step 11: S Words\n", + "# Step 11: NY Times Archives\n", "\n", - "What if we allowed honeycombs and words to have an 'S' in them?" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(98141, 44585)" - ] - }, - "execution_count": 52, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "enable1s = valid_words(open('enable1.txt').read(), \n", - " lambda w: len(w) >= 4 and len(set(w)) <= 7)\n", - "\n", - "len(enable1s), len(enable1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Allowing 'S' more than doubles the number of words. Will it double the score of the best honeycomb?" + "What do the official honeycombs in the NY Times look like? I looked for an archive of past puzzles and found a nice [github repository](https://github.com/philshem/scrape_bee) by [Philip Shemella](https://smalldata.dev/#about), from which I extracted the following honeycombs, where the first letter is the center:" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "226" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nyt_archive = '''\n", + "ABCGILO ABGNORZ ACDIMNY ACEGHNX ACEHIMN ACINTVY ACIPTVY ACLNTVY ADEGIKN ADFLMPU ADFPRTU ADHILNR ADMOPRU AEFHLTU \n", + "AEGHLNO AEHLPUV BACILMY BACLNOR BAELNOP BAGHINT BAILNRT BEFILNO BEFILNX BEGIKNP BILMOTY CABEILT CABJKOT CADGINR \n", + "CADHINP CAEGHIN CAGILNW CAHIMRT CAINOTZ CAIPTVY CDEKNTU CGHINOP CHIKMNU CINOTUY DAGINPT DAGINTU DAKORWY DAMNORT \n", + "DBILNOW DCENOVY DHILNOW DIMORTY EABCHLW EABGMTY EABKLNO EACGHLN EACNTUX EAGHIMT EAHILVY EBLOPTY ECDLMOY ECFHILY \n", + "ECFLNOU ECHNTUY ECLMOPX ECNOPTY EDFLOUY EFGLNUV EGHILNW FAEGLOP FAELMOT GDEILTU GFHLOTU HACEMNT HACGILR HACILNR \n", + "HACLNTU HADGILN HAELPTY HCDEIKL HDEGITW IABCFKL IABEMNT IADFLNW IADLNTW IADLPTU IAEMNTY IAFNRTY IAJMNRU IALOTVY \n", + "ICDEFNO ICENOTV ICFNORT ICFNOTU ICGHLOR ICGLORW ICHKOPT ICLMPTY ICLNORY IDGNOXZ IEGHLOP IEHLNOT KAEGINP KCEHINT \n", + "LABCKOW LABIMOX LACENOW LACFINU LACHINO LACIKOT LACIMOY LADFMPU LADGRUY LAEIJNV LAFMORU LAGINOZ LAHIRTY LAIRUXY \n", + "LBCENOV LBEFINX LBEFIXY LDHINOP LDIQTUY LEHMNOT LEIPTVX LEMNOTU LENOPTU LFGHOTU LGHMOTY LGIMOXY LHIOPRW MABDINR \n", + "MABEKNT MACILNT MADILOR MAHNOPT MAILNPT MAILORT MALNOWY MFLNORU MILPTUY NABDHKO NACDHIP NACIMTY NADFILW NADHIOT \n", + "NALMOPR NALOPRU NBEILMT NCFIMOR NDOPRUW NMOPRTY OACELTY OADEFHN OADEGLY OADELNW OAEGHMP OAEHMNP OAGHMNY OAGILNY \n", + "OAGLMRU OAKLMNW OBDGHUY OBDHLNU OBGHRTU OBHILRY OCDMNPU OCEHKLM OCFHINR OCFIMRU OCHIMNP OCIMPRY ODEIJNT ODFGHIT \n", + "ODGHNRU OEGHMNY OEGIKNV OFHIRTW OFIKLRT OFINPRT OGHRTUW OHILMNT PABEILM PACDEHI PACEFTY PACINTY PADINOR PAEGHIN \n", + "PAGHORT PAIRTUY PCELMOX PEGLOTY PELNOTY PHILORW PIMNORT RABCDKW RABCDKY RACINPT RAFHKOY RAGHOPT RCGHOUY RFLMNOU \n", + "TACDKPR TACDLOR TACHLOP TADILVY TBCEILO TBILMOY TCNORUY TEGHNOU TGNORUW UACNORT UADFLMP UAJLNOR UBGILTY UCGILNO \n", + "UCNORTY VABEGLT VAEGLUY WADHIRT WADKLRY WAEGIKN WCDELNO WDILORY WEGHILT YABLNOT YACEINT YADEHLT YADGHLR YADINOR \n", + "YAEGILT YAGLMOP'''.split()\n", + "\n", + "len(nyt_archive)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Spelling Bee has been online since mid-2018, so there should be around 1,000 puzzles, but the 226 in this archive is enough for my purposes. We can determine some characteristics of the past puzzles:" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('A', 130),\n", + " ('L', 126),\n", + " ('O', 126),\n", + " ('I', 125),\n", + " ('N', 122),\n", + " ('T', 101),\n", + " ('C', 84),\n", + " ('E', 84),\n", + " ('H', 75),\n", + " ('R', 68),\n", + " ('Y', 68),\n", + " ('D', 63),\n", + " ('G', 62),\n", + " ('M', 62),\n", + " ('P', 59),\n", + " ('U', 54),\n", + " ('B', 41),\n", + " ('F', 38),\n", + " ('W', 29),\n", + " ('K', 26),\n", + " ('V', 17),\n", + " ('X', 12),\n", + " ('J', 5),\n", + " ('Z', 4),\n", + " ('Q', 1)]" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "common(''.join(nyt_archive)) # Most common letters" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('O', 30),\n", + " ('L', 27),\n", + " ('I', 21),\n", + " ('E', 17),\n", + " ('A', 16),\n", + " ('C', 13),\n", + " ('P', 13),\n", + " ('N', 11),\n", + " ('M', 10),\n", + " ('B', 9),\n", + " ('T', 9),\n", + " ('D', 8),\n", + " ('H', 8),\n", + " ('R', 7),\n", + " ('Y', 7),\n", + " ('U', 6),\n", + " ('W', 6),\n", + " ('F', 2),\n", + " ('G', 2),\n", + " ('K', 2),\n", + " ('V', 2)]" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "common(letters[0] for letters in nyt_archive) # Most common centers" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('AI', 21),\n", + " ('IO', 20),\n", + " ('AO', 13),\n", + " ('AEI', 12),\n", + " ('OU', 11),\n", + " ('AIO', 10),\n", + " ('AIY', 10),\n", + " ('AEO', 10),\n", + " ('EI', 10),\n", + " ('EIO', 8),\n", + " ('IOY', 8),\n", + " ('EOY', 7),\n", + " ('AE', 6),\n", + " ('AOU', 6),\n", + " ('AOY', 6),\n", + " ('EO', 6),\n", + " ('AU', 5),\n", + " ('AY', 4),\n", + " ('AIU', 4),\n", + " ('AEY', 4),\n", + " ('AEIY', 4),\n", + " ('EOU', 4),\n", + " ('AIOY', 4),\n", + " ('OUY', 4),\n", + " ('AEU', 3),\n", + " ('IOU', 3),\n", + " ('IUY', 3),\n", + " ('EU', 2),\n", + " ('EIY', 2),\n", + " ('AIUY', 2),\n", + " ('OY', 2),\n", + " ('AEOY', 2),\n", + " ('A', 2),\n", + " ('IU', 1),\n", + " ('IOUY', 1),\n", + " ('EUY', 1),\n", + " ('EOUY', 1),\n", + " ('EIU', 1),\n", + " ('IY', 1),\n", + " ('AUY', 1),\n", + " ('AEUY', 1)]" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def vowels(letters): return letterset(v for v in 'AEIOUY'if v in letters)\n", + "\n", + "common(vowels(p) for p in nyt_archive) # Vowels used" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(3, 107), (2, 102), (4, 15), (1, 2)]" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "common(len(vowels(p)) for p in nyt_archive) # Number of vowels used" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(2, 141), (3, 76), (1, 9)]" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "common(len(vowels(p).replace('Y', '')) for p in nyt_archive) # Number of vowels used, not counting 'Y'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can find the game score for all honeycombs in the archive and report some statistics:" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'N': 226, 'min': 107, 'max': 837, 'mean': 301.8407079646018}" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nyt_honeycombs = [Honeycomb(letterset(letters), letters[0]) for letters in nyt_archive]\n", + "\n", + "points_table = tabulate_points(enable1)\n", + "scores = [game_score2(h, points_table) for h in nyt_honeycombs]\n", + "\n", + "dict(N=len(scores), min=min(scores), max=max(scores), mean=sum(scores)/len(scores))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So the scores of NY Times honeycombs range from 100 to 800 with a mean of 300; far less than the 3,898 of the optimal honeycomb. Here's a plot and a histogram of all the scores:" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(sorted(scores), range(len(scores)), '.')\n", + "plt.xlabel('Game Score'); plt.ylabel('Game Number');\n", + "\n", + "plt.show()\n", + "\n", + "plt.hist(scores, bins=15, rwidth=0.9)\n", + "plt.xlabel('Game Score'); plt.ylabel('Frequency');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here's a report on the outlier, the only honeycomb to score over 800 points:" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "The best Honeycomb('AEINRST', 'E') scores 8681 points on 1179 words from a 98141 word list:\n", + "Honeycomb(letters='CEINOTV', center='I') scores 837 points on 125 words from a 44585 word list:\n", "\n", - "AEINRST 1381 points 86 pangrams ANESTRI:14 ANTISERA:15 ANTISTRESS:17 ANTSIER:14 ARENITES:15 ARSENITE:15\n", - " ARSENITES:16 ARTINESS:15 ARTINESSES:17 ATTAINERS:16 ENTERTAINERS:19 ENTERTAINS:17 ENTRAINERS:17\n", - " ENTRAINS:15 ENTREATIES:17 ERRANTRIES:17 INERTIAS:15 INSTANTER:16 INTENERATES:18 INTERSTATE:17\n", - " INTERSTATES:18 INTERSTRAIN:18 INTERSTRAINS:19 INTRASTATE:17 INTREATS:15 IRATENESS:16 IRATENESSES:18\n", - " ITINERANTS:17 ITINERARIES:18 ITINERATES:17 NASTIER:14 NITRATES:15 RAINIEST:15 RATANIES:15 RATINES:14\n", - " REATTAINS:16 REINITIATES:18 REINSTATE:16 REINSTATES:17 RESINATE:15 RESINATES:16 RESISTANT:16\n", - " RESISTANTS:17 RESTRAIN:15 RESTRAINER:17 RESTRAINERS:18 RESTRAINS:16 RESTRAINT:16 RESTRAINTS:17\n", - " RETAINERS:16 RETAINS:14 RETINAS:14 RETIRANTS:16 RETRAINS:15 RETSINA:14 RETSINAS:15 SANITARIES:17\n", - " SEATRAIN:15 SEATRAINS:16 STAINER:14 STAINERS:15 STANNARIES:17 STEARIN:14 STEARINE:15 STEARINES:16\n", - " STEARINS:15 STRAINER:15 STRAINERS:16 STRAITEN:15 STRAITENS:16 STRAITNESS:17 STRAITNESSES:19\n", - " TANISTRIES:17 TANNERIES:16 TEARSTAIN:16 TEARSTAINS:17 TENANTRIES:17 TERNARIES:16 TERRAINS:15\n", - " TERTIANS:15 TRAINEES:15 TRAINERS:15 TRANSIENT:16 TRANSIENTS:17 TRISTEARIN:17 TRISTEARINS:18\n", - " AEINRS 124 points 16 words AIRINESS:8 AIRINESSES:10 ANSERINE:8 ANSERINES:9 ARISEN:6 ARSINE:6 ARSINES:7\n", - " INSANER:7 INSNARE:7 INSNARER:8 INSNARERS:9 INSNARES:8 SENARII:7 SIERRAN:7 SIRENIAN:8 SIRENIANS:9\n", - " AEINRT 232 points 30 words ARENITE:7 ATTAINER:8 ENTERTAIN:9 ENTERTAINER:11 ENTRAIN:7 ENTRAINER:9 INERRANT:8\n", - " INERTIA:7 INERTIAE:8 INTENERATE:10 INTREAT:7 ITERANT:7 ITINERANT:9 ITINERATE:9 NATTIER:7 NITRATE:7\n", - " RATINE:6 REATTAIN:8 REINITIATE:10 RETAIN:6 RETAINER:8 RETINA:6 RETINAE:7 RETIRANT:8 RETRAIN:7\n", - " TERRAIN:7 TERTIAN:7 TRAINEE:7 TRAINER:7 TRIENNIA:8\n", - " AEINST 713 points 80 words ANISETTE:8 ANISETTES:9 ANTISENSE:9 ANTISTATE:9 ANTSIEST:8 ASININITIES:11\n", - " ASSASSINATE:11 ASSASSINATES:12 ASTATINE:8 ASTATINES:9 ENTASIA:7 ENTASIAS:8 ENTASIS:7 ETESIAN:7\n", - " ETESIANS:8 INANEST:7 INANITIES:9 INITIATES:9 INNATENESS:10 INNATENESSES:12 INSANEST:8 INSANITIES:10\n", - " INSATIATE:9 INSATIATENESS:13 INSATIATENESSES:15 INSENSATE:9 INSTANTANEITIES:15 INSTANTIATE:11\n", - " INSTANTIATES:12 INSTANTNESS:11 INSTANTNESSES:13 INSTATE:7 INSTATES:8 INTESTATE:9 INTESTATES:10\n", - " ISATINE:7 ISATINES:8 NASTIES:7 NASTIEST:8 NASTINESS:9 NASTINESSES:11 NATTIEST:8 NATTINESS:9\n", - " NATTINESSES:11 SANITATE:8 SANITATES:9 SANITIES:8 SANITISE:8 SANITISES:9 SATINET:7 SATINETS:8\n", - " SENTENTIA:9 SENTENTIAE:10 SESTINA:7 SESTINAS:8 STANINE:7 STANINES:8 STANNITE:8 STANNITES:9 TAENIAS:7\n", - " TAENIASES:9 TAENIASIS:9 TANSIES:7 TASTINESS:9 TASTINESSES:11 TATTINESS:9 TATTINESSES:11 TENIAS:6\n", - " TENIASES:8 TENIASIS:8 TETANIES:8 TETANISE:8 TETANISES:9 TINEAS:6 TISANE:6 TISANES:7 TITANATES:9\n", - " TITANESS:8 TITANESSES:10 TITANITES:9\n", - " AEIRST 473 points 60 words AERIEST:7 AIREST:6 AIRIEST:7 ARIETTAS:8 ARIETTES:8 ARISTAE:7 ARISTATE:8 ARTERIES:8\n", - " ARTERITIS:9 ARTIEST:7 ARTISTE:7 ARTISTES:8 ARTISTRIES:10 ARTSIER:7 ARTSIEST:8 ASSISTER:8 ASSISTERS:9\n", - " ASTERIA:7 ASTERIAS:8 ATRESIA:7 ATRESIAS:8 ATTIRES:7 EATERIES:8 IRATEST:7 IRRITATES:9 ITERATES:8\n", - " RARITIES:8 RATITES:7 RATTIEST:8 REITERATES:10 SATIRE:6 SATIRES:7 SATIRISE:8 SATIRISES:9 SERIATE:7\n", - " SERIATES:8 SESTERTIA:9 STARRIER:8 STARRIEST:9 STRAITER:8 STRAITEST:9 STRIAE:6 STRIATE:7 STRIATES:8\n", - " TARRIERS:8 TARRIES:7 TARRIEST:8 TARSIER:7 TARSIERS:8 TASTIER:7 TEARIEST:8 TERAIS:6 TERTIARIES:10\n", - " TITRATES:8 TRAITRESS:9 TRAITRESSES:11 TREATIES:8 TREATISE:8 TREATISES:9 TRISTATE:8\n", - " AENRST 336 points 40 words ANTEATERS:9 ANTRES:6 ARRESTANT:9 ARRESTANTS:10 ARSENATE:8 ARSENATES:9 ASSENTER:8\n", - " ASSENTERS:9 ASTERN:6 EARNEST:7 EARNESTNESS:11 EARNESTNESSES:13 EARNESTS:8 EASTERN:7 EASTERNER:9\n", - " EASTERNERS:10 ENTRANTS:8 ENTREATS:8 ERRANTS:7 NARRATERS:9 NARRATES:8 NATTERS:7 NEAREST:7 RANTERS:7\n", - " RATTEENS:8 RATTENERS:9 RATTENS:7 REENTRANTS:10 RETREATANTS:11 SARSENET:8 SARSENETS:9 SERENATA:8\n", - " SERENATAS:9 SERENATE:8 STERNA:6 TANNERS:7 TARANTASES:10 TARTNESS:8 TARTNESSES:10 TERRANES:8\n", - " EINRST 582 points 70 words ENTERITIS:9 ENTERITISES:11 ENTIRENESS:10 ENTIRENESSES:12 ENTIRES:7 ENTIRETIES:10\n", - " ENTRIES:7 ESTRIN:6 ESTRINS:7 ETERNISE:8 ETERNISES:9 ETERNITIES:10 INERTNESS:9 INERTNESSES:11 INERTS:6\n", - " INSERT:6 INSERTER:8 INSERTERS:9 INSERTS:7 INSETTER:8 INSETTERS:9 INSISTER:8 INSISTERS:9 INTENSER:8\n", - " INTEREST:8 INTERESTS:9 INTERNEES:9 INTERNES:8 INTERNIST:9 INTERNISTS:10 INTERNS:7 INTERS:6 INTERTIES:9\n", - " NITERIES:8 NITERS:6 NITRES:6 NITRITES:8 REENTRIES:9 REINSERT:8 REINSERTS:9 REINTERS:8 RENTIERS:8\n", - " RETINENES:9 RETINES:7 RETINITES:9 RETINITIS:9 RETINTS:7 SENTRIES:8 SERENITIES:10 SINISTER:8\n", - " SINISTERNESS:12 SINISTERNESSES:14 SINTER:6 SINTERS:7 STERNITE:8 STERNITES:9 STINTER:7 STINTERS:8\n", - " TEENSIER:8 TEENTSIER:9 TERRINES:8 TINNERS:7 TINTERS:7 TRIENES:7 TRIENS:6 TRIENTES:8 TRINES:6\n", - " TRINITIES:9 TRITENESS:9 TRITENESSES:11\n", - " AEINR 19 points 3 words INANER:6 NARINE:6 RAINIER:7\n", - " AEINS 129 points 17 words ANISE:5 ANISES:6 ASININE:7 EASINESS:8 EASINESSES:10 INANENESS:9 INANENESSES:11\n", - " INANES:6 INSANE:6 INSANENESS:10 INSANENESSES:12 NANNIES:7 SANIES:6 SANSEI:6 SANSEIS:7 SIENNA:6\n", - " SIENNAS:7\n", - " AEINT 64 points 10 words ENTIA:5 INITIATE:8 INNATE:6 TAENIA:6 TAENIAE:7 TENIA:5 TENIAE:6 TINEA:5 TITANATE:8\n", - " TITANITE:8\n", - " AEIRS 106 points 17 words AERIES:6 AIRERS:6 ARISE:5 ARISES:6 ARRISES:7 EASIER:6 RAISE:5 RAISER:6 RAISERS:7\n", - " RAISES:6 RERAISE:7 RERAISES:8 SASSIER:7 SERAI:5 SERAIS:6 SIERRA:6 SIERRAS:7\n", - " AEIRT 135 points 20 words ARIETTA:7 ARIETTE:7 ARTIER:6 ATTIRE:6 ATTRITE:7 IRATE:5 IRATER:6 IRRITATE:8\n", - " ITERATE:7 RATITE:6 RATTIER:7 REITERATE:9 RETIA:5 RETIARII:8 TARRIER:7 TATTIER:7 TEARIER:7 TERAI:5\n", - " TERRARIA:8 TITRATE:7\n", - " AEIST 112 points 15 words EASIEST:7 ETATIST:7 SASSIEST:8 SATIATE:7 SATIATES:8 SATIETIES:9 SIESTA:6 SIESTAS:7\n", - " STEATITE:8 STEATITES:9 TASSIE:6 TASSIES:7 TASTIEST:8 TATTIES:7 TATTIEST:8\n", - " AENRS 172 points 25 words ANEARS:6 ARENAS:6 EARNERS:7 EARNS:5 ENSNARE:7 ENSNARER:8 ENSNARERS:9 ENSNARES:8\n", - " NARES:5 NEARNESS:8 NEARNESSES:10 NEARS:5 RANEES:6 RARENESS:8 RARENESSES:10 REEARNS:7 RENNASE:7\n", - " RENNASES:8 SANER:5 SARSEN:6 SARSENS:7 SNARE:5 SNARER:6 SNARERS:7 SNARES:6\n", - " AENRT 132 points 19 words ANTEATER:8 ANTRE:5 ENTERA:6 ENTRANT:7 ENTREAT:7 ERRANT:6 NARRATE:7 NARRATER:8\n", - " NATTER:6 NEATER:6 RANTER:6 RATTEEN:7 RATTEN:6 RATTENER:8 REENTRANT:9 RETREATANT:10 TANNER:6 TERNATE:7\n", - " TERRANE:7\n", - " AENST 217 points 32 words ANATASE:7 ANATASES:8 ANENST:6 ANNATES:7 ANSATE:6 ANTENNAS:8 ANTES:5 ASSENT:6\n", - " ASSENTS:7 ENATES:6 ENTASES:7 ETNAS:5 NATES:5 NEATENS:7 NEATEST:7 NEATNESS:8 NEATNESSES:10 NEATS:5\n", - " SANEST:6 SATEEN:6 SATEENS:7 SENATE:6 SENATES:7 SENSATE:7 SENSATES:8 SETENANT:8 SETENANTS:9 STANE:5\n", - " STANES:6 TANNATES:8 TANNEST:7 TENANTS:7\n", - " AERST 604 points 85 words AERATES:7 ARETES:6 ARREST:6 ARRESTEE:8 ARRESTEES:9 ARRESTER:8 ARRESTERS:9\n", - " ARRESTS:7 ASSERT:6 ASSERTER:8 ASSERTERS:9 ASSERTS:7 ASTER:5 ASTERS:6 ATTESTER:8 ATTESTERS:9 EASTER:6\n", - " EASTERS:7 EATERS:6 ERRATAS:7 ESTERASE:8 ESTERASES:9 ESTREAT:7 ESTREATS:8 RAREST:6 RASTER:6 RASTERS:7\n", - " RATERS:6 RATES:5 RATTERS:7 REARREST:8 REARRESTS:9 REASSERT:8 REASSERTS:9 REATAS:6 RESEAT:6 RESEATS:7\n", - " RESTART:7 RESTARTS:8 RESTATE:7 RESTATES:8 RETASTE:7 RETASTES:8 RETEARS:7 RETREATERS:10 RETREATS:8\n", - " SEAREST:7 SEATER:6 SEATERS:7 SERRATE:7 SERRATES:8 STARE:5 STARER:6 STARERS:7 STARES:6 STARETS:7\n", - " STARTER:7 STARTERS:8 STATER:6 STATERS:7 STEARATE:8 STEARATES:9 STRASSES:8 STRETTA:7 STRETTAS:8 TARES:5\n", - " TARRES:6 TARTEST:7 TARTRATES:9 TASTER:6 TASTERS:7 TATERS:6 TATTERS:7 TEARERS:7 TEARS:5 TEASER:6\n", - " TEASERS:7 TERRAS:6 TERRASES:8 TESSERA:7 TESSERAE:8 TETRAS:6 TRASSES:7 TREATERS:8 TREATS:6\n", - " EINRS 184 points 29 words EERINESS:8 EERINESSES:10 ESERINE:7 ESERINES:8 INNERS:6 NEREIS:6 REINS:5 RENINS:6\n", - " RENNINS:7 RERISEN:7 RESIN:5 RESINS:6 RINSE:5 RINSER:6 RINSERS:7 RINSES:6 RISEN:5 SEINER:6 SEINERS:7\n", - " SEREIN:6 SEREINS:7 SERIN:5 SERINE:6 SERINES:7 SERINS:6 SINNER:6 SINNERS:7 SIREN:5 SIRENS:6\n", - " EINRT 190 points 29 words ENTIRE:6 INERT:5 INTER:5 INTERN:6 INTERNE:7 INTERNEE:8 INTERTIE:8 NETTIER:7\n", - " NITER:5 NITERIE:7 NITRE:5 NITRITE:7 NITTIER:7 REINTER:7 RENITENT:8 RENTIER:7 RETINE:6 RETINENE:8\n", - " RETINITE:8 RETINT:6 TEENIER:7 TENTIER:7 TERRINE:7 TINIER:6 TINNER:6 TINNIER:7 TINTER:6 TRIENE:6\n", - " TRINE:5\n", - " EINST 469 points 58 words EINSTEIN:8 EINSTEINS:9 ENTITIES:8 INSENTIENT:10 INSET:5 INSETS:6 INSISTENT:9\n", - " INTENSE:7 INTENSENESS:11 INTENSENESSES:13 INTENSEST:9 INTENSITIES:11 INTENTNESS:10 INTENTNESSES:12\n", - " INTENTS:7 INTESTINE:9 INTESTINES:10 INTINES:7 NEIST:5 NETTIEST:8 NINETEENS:9 NINETIES:8 NITES:5\n", - " NITTIEST:8 SENITI:6 SENNIT:6 SENNITS:7 SENSITISE:9 SENSITISES:10 SENTI:5 SENTIENT:8 SENTIENTS:9\n", - " SESTINE:7 SESTINES:8 SIENITE:7 SIENITES:8 SITTEN:6 STEIN:5 STEINS:6 TEENIEST:8 TEENSIEST:9\n", - " TEENTSIEST:10 TENNIES:7 TENNIS:6 TENNISES:8 TENNIST:7 TENNISTS:8 TENSITIES:9 TENTIEST:8 TESTINESS:9\n", - " TESTINESSES:11 TINES:5 TINIEST:7 TININESS:8 TININESSES:10 TINNIEST:8 TINNINESS:9 TINNINESSES:11\n", - " EIRST 262 points 38 words EERIEST:7 IRITISES:8 RESIST:6 RESISTER:8 RESISTERS:9 RESISTS:7 RESITE:6 RESITES:7\n", - " RETIES:6 RETIREES:8 RETIRERS:8 RETIRES:7 RETRIES:7 RITES:5 RITTERS:7 SISTER:6 SISTERS:7 SITTER:6\n", - " SITTERS:7 STIRRER:7 STIRRERS:8 STRETTI:7 TERRIERS:8 TERRIES:7 TERRITS:7 TESTIER:7 TIERS:5 TIRES:5\n", - " TITERS:6 TITRES:6 TITTERERS:9 TITTERS:7 TRESSIER:8 TRESSIEST:9 TRIERS:6 TRIES:5 TRISTE:6 TRITEST:7\n", - " ENRST 246 points 35 words ENTERERS:8 ENTERS:6 ENTREES:7 NERTS:5 NESTER:6 NESTERS:7 NETTERS:7 REENTERS:8\n", - " RENEST:6 RENESTS:7 RENNETS:7 RENTERS:7 RENTES:6 RENTS:5 RESENT:6 RESENTS:7 RETENES:7 SERENEST:8\n", - " STERN:5 STERNER:7 STERNEST:8 STERNNESS:9 STERNNESSES:11 STERNS:6 TEENERS:7 TENNERS:7 TENSER:6\n", - " TENTERS:7 TERNES:6 TERNS:5 TERREENS:8 TERRENES:8 TERSENESS:9 TERSENESSES:11 TREENS:6\n", - " AEIN 11 points 2 words INANE:5 NANNIE:6\n", - " AEIR 22 points 4 words AERIE:5 AERIER:6 AIRER:5 AIRIER:6\n", - " AEIS 13 points 2 words EASIES:6 SASSIES:7\n", - " AEIT 6 points 1 word TATTIE:6\n", - " AENR 40 points 9 words ANEAR:5 ARENA:5 EARN:1 EARNER:6 NEAR:1 NEARER:6 RANEE:5 REEARN:6 RERAN:5\n", - " AENS 46 points 9 words ANES:1 ANSAE:5 SANE:1 SANENESS:8 SANENESSES:10 SANES:5 SENNA:5 SENNAS:6 SENSA:5\n", - " AENT 63 points 13 words ANENT:5 ANTAE:5 ANTE:1 ANTENNA:7 ANTENNAE:8 ATTENT:6 EATEN:5 ENATE:5 ETNA:1 NEAT:1\n", - " NEATEN:6 TANNATE:7 TENANT:6\n", - " AERS 121 points 26 words AREAS:5 ARES:1 ARREARS:7 ARSE:1 ARSES:5 EARS:1 ERAS:1 ERASE:5 ERASER:6 ERASERS:7\n", - " ERASES:6 RARES:5 RASE:1 RASER:5 RASERS:6 RASES:5 REARERS:7 REARS:5 REASSESS:8 REASSESSES:10 SAREE:5\n", - " SAREES:6 SEAR:1 SEARER:6 SEARS:5 SERA:1\n", - " AERT 127 points 24 words AERATE:6 ARETE:5 EATER:5 ERRATA:6 RATE:1 RATER:5 RATTER:6 REATA:5 RETEAR:6\n", - " RETREAT:7 RETREATER:9 TARE:1 TARRE:5 TARTER:6 TARTRATE:8 TATER:5 TATTER:6 TEAR:1 TEARER:6 TERRA:5\n", - " TERRAE:6 TETRA:5 TREAT:5 TREATER:7\n", - " AEST 164 points 35 words ASSET:5 ASSETS:6 ATES:1 ATTEST:6 ATTESTS:7 EAST:1 EASTS:5 EATS:1 ESTATE:6\n", - " ESTATES:7 ETAS:1 SATE:1 SATES:5 SEAT:1 SEATS:5 SETA:1 SETAE:5 STASES:6 STATE:5 STATES:6 TASSE:5\n", - " TASSES:6 TASSET:6 TASSETS:7 TASTE:5 TASTES:6 TATES:5 TEAS:1 TEASE:5 TEASES:6 TEATS:5 TESTA:5 TESTAE:6\n", - " TESTATE:7 TESTATES:8\n", - " EINR 17 points 4 words INNER:5 REIN:1 RENIN:5 RENNIN:6\n", - " EINS 53 points 10 words NINES:5 NINNIES:7 NISEI:5 NISEIS:6 SEINE:5 SEINES:6 SEISIN:6 SEISINS:7 SINE:1\n", - " SINES:5\n", - " EINT 28 points 6 words INTENT:6 INTINE:6 NINETEEN:8 NITE:1 TENTIE:6 TINE:1\n", - " EIRS 101 points 20 words IRES:1 IRISES:6 REIS:1 RERISE:6 RERISES:7 RISE:1 RISER:5 RISERS:6 RISES:5 SEISER:6\n", - " SEISERS:7 SERIES:6 SERRIES:7 SIRE:1 SIREE:5 SIREES:6 SIRES:5 SIRREE:6 SIRREES:7 SISSIER:7\n", - " EIRT 87 points 17 words RETIE:5 RETIRE:6 RETIREE:7 RETIRER:7 RITE:1 RITTER:6 TERRIER:7 TERRIT:6 TIER:1\n", - " TIRE:1 TITER:5 TITRE:5 TITTER:6 TITTERER:8 TRIER:5 TRITE:5 TRITER:6\n", - " EIST 41 points 8 words SISSIEST:8 SITE:1 SITES:5 STIES:5 TESTIEST:8 TESTIS:6 TIES:1 TITTIES:7\n", - " ENRS 80 points 12 words ERNES:5 ERNS:1 RESEEN:6 SERENE:6 SERENENESS:10 SERENENESSES:12 SERENER:7 SERENES:7\n", - " SNEER:5 SNEERER:7 SNEERERS:8 SNEERS:6\n", - " ENRT 104 points 19 words ENTER:5 ENTERER:7 ENTREE:6 ETERNE:6 NETTER:6 REENTER:7 RENNET:6 RENT:1 RENTE:5\n", - " RENTER:6 RETENE:6 TEENER:6 TENNER:6 TENTER:6 TERN:1 TERNE:5 TERREEN:7 TERRENE:7 TREEN:5\n", - " ENST 94 points 18 words ENTENTES:8 NEST:1 NESTS:5 NETS:1 NETTS:5 SENNET:6 SENNETS:7 SENT:1 SENTE:5 TEENS:5\n", - " TENETS:6 TENS:1 TENSE:5 TENSENESS:9 TENSENESSES:11 TENSES:6 TENSEST:7 TENTS:5\n", - " ERST 266 points 44 words ERST:1 ESTER:5 ESTERS:6 REEST:5 REESTS:6 RESET:5 RESETS:6 RESETTER:8 RESETTERS:9\n", - " REST:1 RESTER:6 RESTERS:7 RESTRESS:8 RESTRESSES:10 RESTS:5 RETEST:6 RETESTS:7 RETS:1 SEREST:6 SETTER:6\n", - " SETTERS:7 STEER:5 STEERER:7 STEERERS:8 STEERS:6 STERE:5 STERES:6 STREET:6 STREETS:7 STRESS:6\n", - " STRESSES:8 STRETTE:7 TEETERS:7 TERRETS:7 TERSE:5 TERSER:6 TERSEST:7 TESTER:6 TESTERS:7 TETTERS:7\n", - " TREES:5 TRESS:5 TRESSES:7 TRETS:5\n", - " AER 25 points 7 words AREA:1 AREAE:5 ARREAR:6 RARE:1 RARER:5 REAR:1 REARER:6\n", - " AES 33 points 8 words ASEA:1 ASSES:5 ASSESS:6 ASSESSES:8 EASE:1 EASES:5 SASSES:6 SEAS:1\n", - " AET 2 points 2 words TATE:1 TEAT:1\n", - " EIN 1 point 1 word NINE:1\n", - " EIR 11 points 2 words EERIE:5 EERIER:6\n", - " EIS 35 points 7 words ISSEI:5 ISSEIS:6 SEIS:1 SEISE:5 SEISES:6 SISES:5 SISSIES:7\n", - " EIT 6 points 1 word TITTIE:6\n", - " ENR 1 point 1 word ERNE:1\n", - " ENS 20 points 6 words NESS:1 NESSES:6 SEEN:1 SENE:1 SENSE:5 SENSES:6\n", - " ENT 15 points 5 words ENTENTE:7 NETT:1 TEEN:1 TENET:5 TENT:1\n", - " ERS 52 points 13 words ERRS:1 ERSES:5 REES:1 RESEE:5 RESEES:6 SEER:1 SEERESS:7 SEERESSES:9 SEERS:5 SERE:1\n", - " SERER:5 SERES:5 SERS:1\n", - " ERT 27 points 7 words RETE:1 TEETER:6 TERETE:6 TERRET:6 TETTER:6 TREE:1 TRET:1\n", - " EST 79 points 18 words SESTET:6 SESTETS:7 SETS:1 SETT:1 SETTEE:6 SETTEES:7 SETTS:5 STET:1 STETS:5 TEES:1\n", - " TEST:1 TESTEE:6 TESTEES:7 TESTES:6 TESTS:5 TETS:1 TSETSE:6 TSETSES:7\n", - " EN 1 point 1 word NENE:1\n", - " ES 7 points 3 words ESES:1 ESSES:5 SEES:1\n" + "CEINOTV 182 points 11 pangrams COINVENT(15) CONCOCTIVE(17) CONNECTIVE(17) CONNIVENT(16) CONVECTION(17)\n", + " CONVECTIVE(17) CONVENIENT(17) CONVENTION(17) EVECTION(15) EVICTION(15) INCONVENIENT(19)\n", + " CEINOT 140 points 16 words CONCEIT(7) CONNECTION(10) CONTENTION(10) CONTINENCE(10) CONTINENT(9) COONTIE(7)\n", + " INCONTINENCE(12) INCONTINENT(11) INNOCENT(8) NEOTENIC(8) NICOTINE(8) NOETIC(6) NONCONNECTION(13)\n", + " NOTICE(6) TECTONIC(8) TONETIC(7)\n", + " CEINOV 60 points 7 words CONCEIVE(8) CONNIVE(7) CONVENIENCE(11) CONVINCE(8) INCONVENIENCE(13) INVOICE(7)\n", + " NOVICE(6)\n", + " CEINTV 18 points 2 words INCENTIVE(9) INVECTIVE(9)\n", + " CINOTV 17 points 2 words CONVICT(7) CONVICTION(10)\n", + " EINOTV 9 points 1 word INVENTION(9)\n", + " CEINO 22 points 3 words CONIINE(7) CONINE(6) INNOCENCE(9)\n", + " CEINT 20 points 3 words ENCEINTE(8) ENTICE(6) INCITE(6)\n", + " CEINV 14 points 2 words EVINCE(6) EVINCIVE(8)\n", + " CEIOT 6 points 1 word COOTIE(6)\n", + " CEIOV 5 points 1 word VOICE(5)\n", + " CEITV 17 points 3 words CIVET(5) EVICT(5) EVICTEE(7)\n", + " CINOT 53 points 7 words COITION(7) CONCOCTION(10) INTINCTION(10) NICOTIN(7) NICOTINIC(9) ONTIC(5) TONIC(5)\n", + " CINOV 11 points 2 words COVIN(5) OVONIC(6)\n", + " EINOT 22 points 3 words INTENTION(9) INTONE(6) TONTINE(7)\n", + " EINOV 10 points 2 words ENVOI(5) OVINE(5)\n", + " EINTV 28 points 4 words INVENT(6) INVENTIVE(9) INVITE(6) INVITEE(7)\n", + " EIOTV 6 points 1 word VOTIVE(6)\n", + " CEIN 7 points 3 words CINE(1) NICE(1) NIECE(5)\n", + " CEIT 9 points 3 words CITE(1) ETIC(1) TECTITE(7)\n", + " CEIV 6 points 2 words CIVIE(5) VICE(1)\n", + " CINO 33 points 9 words CION(1) COIN(1) CONI(1) CONIC(5) CONIN(5) ICON(1) ICONIC(6) IONIC(5) NONIONIC(8)\n", + " CINT 5 points 1 word TINCT(5)\n", + " CINV 5 points 1 word VINIC(5)\n", + " CIOT 13 points 3 words OTIC(1) OTITIC(6) TICTOC(6)\n", + " EINO 6 points 1 word IONONE(6)\n", + " EINT 28 points 6 words INTENT(6) INTINE(6) NINETEEN(8) NITE(1) TENTIE(6) TINE(1)\n", + " EINV 19 points 6 words NEVI(1) NIEVE(5) VEIN(1) VENIN(5) VENINE(6) VINE(1)\n", + " EITV 5 points 1 word EVITE(5)\n", + " INOT 12 points 3 words INTO(1) NITON(5) NOTION(6)\n", + " INOV 1 point 1 word VINO(1)\n", + " CIO 11 points 2 words COCCI(5) COCCIC(6)\n", + " CIT 5 points 1 word ICTIC(5)\n", + " CIV 5 points 1 word CIVIC(5)\n", + " EIN 1 point 1 word NINE(1)\n", + " EIT 6 points 1 word TITTIE(6)\n", + " EIV 1 point 1 word VIVE(1)\n", + " INO 15 points 3 words INION(5) NINON(5) ONION(5)\n", + " INT 2 points 2 words INTI(1) TINT(1)\n", + " IOT 1 point 1 word TOIT(1)\n", + " IT 1 point 1 word TITI(1)\n" ] } ], "source": [ + "report(max(nyt_honeycombs, key=lambda h: game_score2(h, points_table)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And here are the most common words, along with all the pangram words that appeared more than once:" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('LOLL', 28),\n", + " ('NOON', 23),\n", + " ('LALL', 21),\n", + " ('OLIO', 18),\n", + " ('AALII', 16),\n", + " ('ANNA', 16),\n", + " ('CACA', 16),\n", + " ('ILIA', 16),\n", + " ('ILIAL', 16),\n", + " ('INION', 16),\n", + " ('NAAN', 16),\n", + " ('NANA', 16),\n", + " ('NINON', 16),\n", + " ('OLLA', 16),\n", + " ('ONION', 16),\n", + " ('OTTO', 16),\n", + " ('TOOT', 16),\n", + " ('COCCI', 15),\n", + " ('COCCIC', 15),\n", + " ('DODO', 15),\n", + " ('INIA', 15),\n", + " ('LOOM', 15),\n", + " ('LOON', 15),\n", + " ('MOLL', 15),\n", + " ('MOOL', 15),\n", + " ('NOLO', 15),\n", + " ('OLEO', 15),\n", + " ('TITI', 15),\n", + " ('ACACIA', 14),\n", + " ('TOIT', 14)]" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nyt_words = common(w for w in enable1 for h in nyt_honeycombs if can_make(h, w))\n", + "nyt_words[:30]" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('CAPTIVITY', 2),\n", + " ('COMPLEX', 2),\n", + " ('COUNTRY', 2),\n", + " ('HANDICAP', 2),\n", + " ('IMMOBILITY', 2),\n", + " ('INFLEXIBLE', 2),\n", + " ('MOBILITY', 2),\n", + " ('MOURNFUL', 2),\n", + " ('NONCOUNTRY', 2),\n", + " ('PHOTOGRAPH', 2),\n", + " ('THOUGHTFUL', 2),\n", + " ('WHIPPOORWILL', 2),\n", + " ('WHIRLPOOL', 2),\n", + " ('WINDFALL', 2),\n", + " ('WINDFLAW', 2)]" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[(w, c) for w, c in nyt_words if c > 1 and pangram_bonus(w)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Step 12: 'S' Words\n", + "\n", + "What if we allowed honeycombs and words to have an 'S' in them? We already saw that 53,556 words were rejected because they contain an 'S'; how much more could a honeycomb score if we allow 'S' words?" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Best Honeycomb(letters='AEINRST', center='E') scores 8681 points on 1179 words from a 98141 word list:\n", + "\n", + "AEINRST 1381 points 86 pangrams ANESTRI(14) ANTISERA(15) ANTISTRESS(17) ANTSIER(14) ARENITES(15) ARSENITE(15)\n", + " ARSENITES(16) ARTINESS(15) ARTINESSES(17) ATTAINERS(16) ENTERTAINERS(19) ENTERTAINS(17) ENTRAINERS(17)\n", + " ENTRAINS(15) ENTREATIES(17) ERRANTRIES(17) INERTIAS(15) INSTANTER(16) INTENERATES(18) INTERSTATE(17)\n", + " INTERSTATES(18) INTERSTRAIN(18) INTERSTRAINS(19) INTRASTATE(17) INTREATS(15) IRATENESS(16)\n", + " IRATENESSES(18) ITINERANTS(17) ITINERARIES(18) ITINERATES(17) NASTIER(14) NITRATES(15) RAINIEST(15)\n", + " RATANIES(15) RATINES(14) REATTAINS(16) REINITIATES(18) REINSTATE(16) REINSTATES(17) RESINATE(15)\n", + " RESINATES(16) RESISTANT(16) RESISTANTS(17) RESTRAIN(15) RESTRAINER(17) RESTRAINERS(18) RESTRAINS(16)\n", + " RESTRAINT(16) RESTRAINTS(17) RETAINERS(16) RETAINS(14) RETINAS(14) RETIRANTS(16) RETRAINS(15)\n", + " RETSINA(14) RETSINAS(15) SANITARIES(17) SEATRAIN(15) SEATRAINS(16) STAINER(14) STAINERS(15)\n", + " STANNARIES(17) STEARIN(14) STEARINE(15) STEARINES(16) STEARINS(15) STRAINER(15) STRAINERS(16)\n", + " STRAITEN(15) STRAITENS(16) STRAITNESS(17) STRAITNESSES(19) TANISTRIES(17) TANNERIES(16) TEARSTAIN(16)\n", + " TEARSTAINS(17) TENANTRIES(17) TERNARIES(16) TERRAINS(15) TERTIANS(15) TRAINEES(15) TRAINERS(15)\n", + " TRANSIENT(16) TRANSIENTS(17) TRISTEARIN(17) TRISTEARINS(18)\n", + " AEINRS 124 points 16 words AIRINESS(8) AIRINESSES(10) ANSERINE(8) ANSERINES(9) ARISEN(6) ARSINE(6) ARSINES(7)\n", + " INSANER(7) INSNARE(7) INSNARER(8) INSNARERS(9) INSNARES(8) SENARII(7) SIERRAN(7) SIRENIAN(8)\n", + " SIRENIANS(9)\n", + " AEINRT 232 points 30 words ARENITE(7) ATTAINER(8) ENTERTAIN(9) ENTERTAINER(11) ENTRAIN(7) ENTRAINER(9)\n", + " INERRANT(8) INERTIA(7) INERTIAE(8) INTENERATE(10) INTREAT(7) ITERANT(7) ITINERANT(9) ITINERATE(9)\n", + " NATTIER(7) NITRATE(7) RATINE(6) REATTAIN(8) REINITIATE(10) RETAIN(6) RETAINER(8) RETINA(6) RETINAE(7)\n", + " RETIRANT(8) RETRAIN(7) TERRAIN(7) TERTIAN(7) TRAINEE(7) TRAINER(7) TRIENNIA(8)\n", + " AEINST 713 points 80 words ANISETTE(8) ANISETTES(9) ANTISENSE(9) ANTISTATE(9) ANTSIEST(8) ASININITIES(11)\n", + " ASSASSINATE(11) ASSASSINATES(12) ASTATINE(8) ASTATINES(9) ENTASIA(7) ENTASIAS(8) ENTASIS(7) ETESIAN(7)\n", + " ETESIANS(8) INANEST(7) INANITIES(9) INITIATES(9) INNATENESS(10) INNATENESSES(12) INSANEST(8)\n", + " INSANITIES(10) INSATIATE(9) INSATIATENESS(13) INSATIATENESSES(15) INSENSATE(9) INSTANTANEITIES(15)\n", + " INSTANTIATE(11) INSTANTIATES(12) INSTANTNESS(11) INSTANTNESSES(13) INSTATE(7) INSTATES(8) INTESTATE(9)\n", + " INTESTATES(10) ISATINE(7) ISATINES(8) NASTIES(7) NASTIEST(8) NASTINESS(9) NASTINESSES(11) NATTIEST(8)\n", + " NATTINESS(9) NATTINESSES(11) SANITATE(8) SANITATES(9) SANITIES(8) SANITISE(8) SANITISES(9) SATINET(7)\n", + " SATINETS(8) SENTENTIA(9) SENTENTIAE(10) SESTINA(7) SESTINAS(8) STANINE(7) STANINES(8) STANNITE(8)\n", + " STANNITES(9) TAENIAS(7) TAENIASES(9) TAENIASIS(9) TANSIES(7) TASTINESS(9) TASTINESSES(11) TATTINESS(9)\n", + " TATTINESSES(11) TENIAS(6) TENIASES(8) TENIASIS(8) TETANIES(8) TETANISE(8) TETANISES(9) TINEAS(6)\n", + " TISANE(6) TISANES(7) TITANATES(9) TITANESS(8) TITANESSES(10) TITANITES(9)\n", + " AEIRST 473 points 60 words AERIEST(7) AIREST(6) AIRIEST(7) ARIETTAS(8) ARIETTES(8) ARISTAE(7) ARISTATE(8)\n", + " ARTERIES(8) ARTERITIS(9) ARTIEST(7) ARTISTE(7) ARTISTES(8) ARTISTRIES(10) ARTSIER(7) ARTSIEST(8)\n", + " ASSISTER(8) ASSISTERS(9) ASTERIA(7) ASTERIAS(8) ATRESIA(7) ATRESIAS(8) ATTIRES(7) EATERIES(8)\n", + " IRATEST(7) IRRITATES(9) ITERATES(8) RARITIES(8) RATITES(7) RATTIEST(8) REITERATES(10) SATIRE(6)\n", + " SATIRES(7) SATIRISE(8) SATIRISES(9) SERIATE(7) SERIATES(8) SESTERTIA(9) STARRIER(8) STARRIEST(9)\n", + " STRAITER(8) STRAITEST(9) STRIAE(6) STRIATE(7) STRIATES(8) TARRIERS(8) TARRIES(7) TARRIEST(8)\n", + " TARSIER(7) TARSIERS(8) TASTIER(7) TEARIEST(8) TERAIS(6) TERTIARIES(10) TITRATES(8) TRAITRESS(9)\n", + " TRAITRESSES(11) TREATIES(8) TREATISE(8) TREATISES(9) TRISTATE(8)\n", + " AENRST 336 points 40 words ANTEATERS(9) ANTRES(6) ARRESTANT(9) ARRESTANTS(10) ARSENATE(8) ARSENATES(9)\n", + " ASSENTER(8) ASSENTERS(9) ASTERN(6) EARNEST(7) EARNESTNESS(11) EARNESTNESSES(13) EARNESTS(8) EASTERN(7)\n", + " EASTERNER(9) EASTERNERS(10) ENTRANTS(8) ENTREATS(8) ERRANTS(7) NARRATERS(9) NARRATES(8) NATTERS(7)\n", + " NEAREST(7) RANTERS(7) RATTEENS(8) RATTENERS(9) RATTENS(7) REENTRANTS(10) RETREATANTS(11) SARSENET(8)\n", + " SARSENETS(9) SERENATA(8) SERENATAS(9) SERENATE(8) STERNA(6) TANNERS(7) TARANTASES(10) TARTNESS(8)\n", + " TARTNESSES(10) TERRANES(8)\n", + " EINRST 582 points 70 words ENTERITIS(9) ENTERITISES(11) ENTIRENESS(10) ENTIRENESSES(12) ENTIRES(7)\n", + " ENTIRETIES(10) ENTRIES(7) ESTRIN(6) ESTRINS(7) ETERNISE(8) ETERNISES(9) ETERNITIES(10) INERTNESS(9)\n", + " INERTNESSES(11) INERTS(6) INSERT(6) INSERTER(8) INSERTERS(9) INSERTS(7) INSETTER(8) INSETTERS(9)\n", + " INSISTER(8) INSISTERS(9) INTENSER(8) INTEREST(8) INTERESTS(9) INTERNEES(9) INTERNES(8) INTERNIST(9)\n", + " INTERNISTS(10) INTERNS(7) INTERS(6) INTERTIES(9) NITERIES(8) NITERS(6) NITRES(6) NITRITES(8)\n", + " REENTRIES(9) REINSERT(8) REINSERTS(9) REINTERS(8) RENTIERS(8) RETINENES(9) RETINES(7) RETINITES(9)\n", + " RETINITIS(9) RETINTS(7) SENTRIES(8) SERENITIES(10) SINISTER(8) SINISTERNESS(12) SINISTERNESSES(14)\n", + " SINTER(6) SINTERS(7) STERNITE(8) STERNITES(9) STINTER(7) STINTERS(8) TEENSIER(8) TEENTSIER(9)\n", + " TERRINES(8) TINNERS(7) TINTERS(7) TRIENES(7) TRIENS(6) TRIENTES(8) TRINES(6) TRINITIES(9) TRITENESS(9)\n", + " TRITENESSES(11)\n", + " AEINR 19 points 3 words INANER(6) NARINE(6) RAINIER(7)\n", + " AEINS 129 points 17 words ANISE(5) ANISES(6) ASININE(7) EASINESS(8) EASINESSES(10) INANENESS(9)\n", + " INANENESSES(11) INANES(6) INSANE(6) INSANENESS(10) INSANENESSES(12) NANNIES(7) SANIES(6) SANSEI(6)\n", + " SANSEIS(7) SIENNA(6) SIENNAS(7)\n", + " AEINT 64 points 10 words ENTIA(5) INITIATE(8) INNATE(6) TAENIA(6) TAENIAE(7) TENIA(5) TENIAE(6) TINEA(5)\n", + " TITANATE(8) TITANITE(8)\n", + " AEIRS 106 points 17 words AERIES(6) AIRERS(6) ARISE(5) ARISES(6) ARRISES(7) EASIER(6) RAISE(5) RAISER(6)\n", + " RAISERS(7) RAISES(6) RERAISE(7) RERAISES(8) SASSIER(7) SERAI(5) SERAIS(6) SIERRA(6) SIERRAS(7)\n", + " AEIRT 135 points 20 words ARIETTA(7) ARIETTE(7) ARTIER(6) ATTIRE(6) ATTRITE(7) IRATE(5) IRATER(6)\n", + " IRRITATE(8) ITERATE(7) RATITE(6) RATTIER(7) REITERATE(9) RETIA(5) RETIARII(8) TARRIER(7) TATTIER(7)\n", + " TEARIER(7) TERAI(5) TERRARIA(8) TITRATE(7)\n", + " AEIST 112 points 15 words EASIEST(7) ETATIST(7) SASSIEST(8) SATIATE(7) SATIATES(8) SATIETIES(9) SIESTA(6)\n", + " SIESTAS(7) STEATITE(8) STEATITES(9) TASSIE(6) TASSIES(7) TASTIEST(8) TATTIES(7) TATTIEST(8)\n", + " AENRS 172 points 25 words ANEARS(6) ARENAS(6) EARNERS(7) EARNS(5) ENSNARE(7) ENSNARER(8) ENSNARERS(9)\n", + " ENSNARES(8) NARES(5) NEARNESS(8) NEARNESSES(10) NEARS(5) RANEES(6) RARENESS(8) RARENESSES(10)\n", + " REEARNS(7) RENNASE(7) RENNASES(8) SANER(5) SARSEN(6) SARSENS(7) SNARE(5) SNARER(6) SNARERS(7)\n", + " SNARES(6)\n", + " AENRT 132 points 19 words ANTEATER(8) ANTRE(5) ENTERA(6) ENTRANT(7) ENTREAT(7) ERRANT(6) NARRATE(7)\n", + " NARRATER(8) NATTER(6) NEATER(6) RANTER(6) RATTEEN(7) RATTEN(6) RATTENER(8) REENTRANT(9) RETREATANT(10)\n", + " TANNER(6) TERNATE(7) TERRANE(7)\n", + " AENST 217 points 32 words ANATASE(7) ANATASES(8) ANENST(6) ANNATES(7) ANSATE(6) ANTENNAS(8) ANTES(5)\n", + " ASSENT(6) ASSENTS(7) ENATES(6) ENTASES(7) ETNAS(5) NATES(5) NEATENS(7) NEATEST(7) NEATNESS(8)\n", + " NEATNESSES(10) NEATS(5) SANEST(6) SATEEN(6) SATEENS(7) SENATE(6) SENATES(7) SENSATE(7) SENSATES(8)\n", + " SETENANT(8) SETENANTS(9) STANE(5) STANES(6) TANNATES(8) TANNEST(7) TENANTS(7)\n", + " AERST 604 points 85 words AERATES(7) ARETES(6) ARREST(6) ARRESTEE(8) ARRESTEES(9) ARRESTER(8) ARRESTERS(9)\n", + " ARRESTS(7) ASSERT(6) ASSERTER(8) ASSERTERS(9) ASSERTS(7) ASTER(5) ASTERS(6) ATTESTER(8) ATTESTERS(9)\n", + " EASTER(6) EASTERS(7) EATERS(6) ERRATAS(7) ESTERASE(8) ESTERASES(9) ESTREAT(7) ESTREATS(8) RAREST(6)\n", + " RASTER(6) RASTERS(7) RATERS(6) RATES(5) RATTERS(7) REARREST(8) REARRESTS(9) REASSERT(8) REASSERTS(9)\n", + " REATAS(6) RESEAT(6) RESEATS(7) RESTART(7) RESTARTS(8) RESTATE(7) RESTATES(8) RETASTE(7) RETASTES(8)\n", + " RETEARS(7) RETREATERS(10) RETREATS(8) SEAREST(7) SEATER(6) SEATERS(7) SERRATE(7) SERRATES(8) STARE(5)\n", + " STARER(6) STARERS(7) STARES(6) STARETS(7) STARTER(7) STARTERS(8) STATER(6) STATERS(7) STEARATE(8)\n", + " STEARATES(9) STRASSES(8) STRETTA(7) STRETTAS(8) TARES(5) TARRES(6) TARTEST(7) TARTRATES(9) TASTER(6)\n", + " TASTERS(7) TATERS(6) TATTERS(7) TEARERS(7) TEARS(5) TEASER(6) TEASERS(7) TERRAS(6) TERRASES(8)\n", + " TESSERA(7) TESSERAE(8) TETRAS(6) TRASSES(7) TREATERS(8) TREATS(6)\n", + " EINRS 184 points 29 words EERINESS(8) EERINESSES(10) ESERINE(7) ESERINES(8) INNERS(6) NEREIS(6) REINS(5)\n", + " RENINS(6) RENNINS(7) RERISEN(7) RESIN(5) RESINS(6) RINSE(5) RINSER(6) RINSERS(7) RINSES(6) RISEN(5)\n", + " SEINER(6) SEINERS(7) SEREIN(6) SEREINS(7) SERIN(5) SERINE(6) SERINES(7) SERINS(6) SINNER(6) SINNERS(7)\n", + " SIREN(5) SIRENS(6)\n", + " EINRT 190 points 29 words ENTIRE(6) INERT(5) INTER(5) INTERN(6) INTERNE(7) INTERNEE(8) INTERTIE(8)\n", + " NETTIER(7) NITER(5) NITERIE(7) NITRE(5) NITRITE(7) NITTIER(7) REINTER(7) RENITENT(8) RENTIER(7)\n", + " RETINE(6) RETINENE(8) RETINITE(8) RETINT(6) TEENIER(7) TENTIER(7) TERRINE(7) TINIER(6) TINNER(6)\n", + " TINNIER(7) TINTER(6) TRIENE(6) TRINE(5)\n", + " EINST 469 points 58 words EINSTEIN(8) EINSTEINS(9) ENTITIES(8) INSENTIENT(10) INSET(5) INSETS(6)\n", + " INSISTENT(9) INTENSE(7) INTENSENESS(11) INTENSENESSES(13) INTENSEST(9) INTENSITIES(11) INTENTNESS(10)\n", + " INTENTNESSES(12) INTENTS(7) INTESTINE(9) INTESTINES(10) INTINES(7) NEIST(5) NETTIEST(8) NINETEENS(9)\n", + " NINETIES(8) NITES(5) NITTIEST(8) SENITI(6) SENNIT(6) SENNITS(7) SENSITISE(9) SENSITISES(10) SENTI(5)\n", + " SENTIENT(8) SENTIENTS(9) SESTINE(7) SESTINES(8) SIENITE(7) SIENITES(8) SITTEN(6) STEIN(5) STEINS(6)\n", + " TEENIEST(8) TEENSIEST(9) TEENTSIEST(10) TENNIES(7) TENNIS(6) TENNISES(8) TENNIST(7) TENNISTS(8)\n", + " TENSITIES(9) TENTIEST(8) TESTINESS(9) TESTINESSES(11) TINES(5) TINIEST(7) TININESS(8) TININESSES(10)\n", + " TINNIEST(8) TINNINESS(9) TINNINESSES(11)\n", + " EIRST 262 points 38 words EERIEST(7) IRITISES(8) RESIST(6) RESISTER(8) RESISTERS(9) RESISTS(7) RESITE(6)\n", + " RESITES(7) RETIES(6) RETIREES(8) RETIRERS(8) RETIRES(7) RETRIES(7) RITES(5) RITTERS(7) SISTER(6)\n", + " SISTERS(7) SITTER(6) SITTERS(7) STIRRER(7) STIRRERS(8) STRETTI(7) TERRIERS(8) TERRIES(7) TERRITS(7)\n", + " TESTIER(7) TIERS(5) TIRES(5) TITERS(6) TITRES(6) TITTERERS(9) TITTERS(7) TRESSIER(8) TRESSIEST(9)\n", + " TRIERS(6) TRIES(5) TRISTE(6) TRITEST(7)\n", + " ENRST 246 points 35 words ENTERERS(8) ENTERS(6) ENTREES(7) NERTS(5) NESTER(6) NESTERS(7) NETTERS(7)\n", + " REENTERS(8) RENEST(6) RENESTS(7) RENNETS(7) RENTERS(7) RENTES(6) RENTS(5) RESENT(6) RESENTS(7)\n", + " RETENES(7) SERENEST(8) STERN(5) STERNER(7) STERNEST(8) STERNNESS(9) STERNNESSES(11) STERNS(6)\n", + " TEENERS(7) TENNERS(7) TENSER(6) TENTERS(7) TERNES(6) TERNS(5) TERREENS(8) TERRENES(8) TERSENESS(9)\n", + " TERSENESSES(11) TREENS(6)\n", + " AEIN 11 points 2 words INANE(5) NANNIE(6)\n", + " AEIR 22 points 4 words AERIE(5) AERIER(6) AIRER(5) AIRIER(6)\n", + " AEIS 13 points 2 words EASIES(6) SASSIES(7)\n", + " AEIT 6 points 1 word TATTIE(6)\n", + " AENR 40 points 9 words ANEAR(5) ARENA(5) EARN(1) EARNER(6) NEAR(1) NEARER(6) RANEE(5) REEARN(6) RERAN(5)\n", + " AENS 46 points 9 words ANES(1) ANSAE(5) SANE(1) SANENESS(8) SANENESSES(10) SANES(5) SENNA(5) SENNAS(6)\n", + " SENSA(5)\n", + " AENT 63 points 13 words ANENT(5) ANTAE(5) ANTE(1) ANTENNA(7) ANTENNAE(8) ATTENT(6) EATEN(5) ENATE(5)\n", + " ETNA(1) NEAT(1) NEATEN(6) TANNATE(7) TENANT(6)\n", + " AERS 121 points 26 words AREAS(5) ARES(1) ARREARS(7) ARSE(1) ARSES(5) EARS(1) ERAS(1) ERASE(5) ERASER(6)\n", + " ERASERS(7) ERASES(6) RARES(5) RASE(1) RASER(5) RASERS(6) RASES(5) REARERS(7) REARS(5) REASSESS(8)\n", + " REASSESSES(10) SAREE(5) SAREES(6) SEAR(1) SEARER(6) SEARS(5) SERA(1)\n", + " AERT 127 points 24 words AERATE(6) ARETE(5) EATER(5) ERRATA(6) RATE(1) RATER(5) RATTER(6) REATA(5)\n", + " RETEAR(6) RETREAT(7) RETREATER(9) TARE(1) TARRE(5) TARTER(6) TARTRATE(8) TATER(5) TATTER(6) TEAR(1)\n", + " TEARER(6) TERRA(5) TERRAE(6) TETRA(5) TREAT(5) TREATER(7)\n", + " AEST 164 points 35 words ASSET(5) ASSETS(6) ATES(1) ATTEST(6) ATTESTS(7) EAST(1) EASTS(5) EATS(1) ESTATE(6)\n", + " ESTATES(7) ETAS(1) SATE(1) SATES(5) SEAT(1) SEATS(5) SETA(1) SETAE(5) STASES(6) STATE(5) STATES(6)\n", + " TASSE(5) TASSES(6) TASSET(6) TASSETS(7) TASTE(5) TASTES(6) TATES(5) TEAS(1) TEASE(5) TEASES(6)\n", + " TEATS(5) TESTA(5) TESTAE(6) TESTATE(7) TESTATES(8)\n", + " EINR 17 points 4 words INNER(5) REIN(1) RENIN(5) RENNIN(6)\n", + " EINS 53 points 10 words NINES(5) NINNIES(7) NISEI(5) NISEIS(6) SEINE(5) SEINES(6) SEISIN(6) SEISINS(7)\n", + " SINE(1) SINES(5)\n", + " EINT 28 points 6 words INTENT(6) INTINE(6) NINETEEN(8) NITE(1) TENTIE(6) TINE(1)\n", + " EIRS 101 points 20 words IRES(1) IRISES(6) REIS(1) RERISE(6) RERISES(7) RISE(1) RISER(5) RISERS(6) RISES(5)\n", + " SEISER(6) SEISERS(7) SERIES(6) SERRIES(7) SIRE(1) SIREE(5) SIREES(6) SIRES(5) SIRREE(6) SIRREES(7)\n", + " SISSIER(7)\n", + " EIRT 87 points 17 words RETIE(5) RETIRE(6) RETIREE(7) RETIRER(7) RITE(1) RITTER(6) TERRIER(7) TERRIT(6)\n", + " TIER(1) TIRE(1) TITER(5) TITRE(5) TITTER(6) TITTERER(8) TRIER(5) TRITE(5) TRITER(6)\n", + " EIST 41 points 8 words SISSIEST(8) SITE(1) SITES(5) STIES(5) TESTIEST(8) TESTIS(6) TIES(1) TITTIES(7)\n", + " ENRS 80 points 12 words ERNES(5) ERNS(1) RESEEN(6) SERENE(6) SERENENESS(10) SERENENESSES(12) SERENER(7)\n", + " SERENES(7) SNEER(5) SNEERER(7) SNEERERS(8) SNEERS(6)\n", + " ENRT 104 points 19 words ENTER(5) ENTERER(7) ENTREE(6) ETERNE(6) NETTER(6) REENTER(7) RENNET(6) RENT(1)\n", + " RENTE(5) RENTER(6) RETENE(6) TEENER(6) TENNER(6) TENTER(6) TERN(1) TERNE(5) TERREEN(7) TERRENE(7)\n", + " TREEN(5)\n", + " ENST 94 points 18 words ENTENTES(8) NEST(1) NESTS(5) NETS(1) NETTS(5) SENNET(6) SENNETS(7) SENT(1)\n", + " SENTE(5) TEENS(5) TENETS(6) TENS(1) TENSE(5) TENSENESS(9) TENSENESSES(11) TENSES(6) TENSEST(7)\n", + " TENTS(5)\n", + " ERST 266 points 44 words ERST(1) ESTER(5) ESTERS(6) REEST(5) REESTS(6) RESET(5) RESETS(6) RESETTER(8)\n", + " RESETTERS(9) REST(1) RESTER(6) RESTERS(7) RESTRESS(8) RESTRESSES(10) RESTS(5) RETEST(6) RETESTS(7)\n", + " RETS(1) SEREST(6) SETTER(6) SETTERS(7) STEER(5) STEERER(7) STEERERS(8) STEERS(6) STERE(5) STERES(6)\n", + " STREET(6) STREETS(7) STRESS(6) STRESSES(8) STRETTE(7) TEETERS(7) TERRETS(7) TERSE(5) TERSER(6)\n", + " TERSEST(7) TESTER(6) TESTERS(7) TETTERS(7) TREES(5) TRESS(5) TRESSES(7) TRETS(5)\n", + " AER 25 points 7 words AREA(1) AREAE(5) ARREAR(6) RARE(1) RARER(5) REAR(1) REARER(6)\n", + " AES 33 points 8 words ASEA(1) ASSES(5) ASSESS(6) ASSESSES(8) EASE(1) EASES(5) SASSES(6) SEAS(1)\n", + " AET 2 points 2 words TATE(1) TEAT(1)\n", + " EIN 1 point 1 word NINE(1)\n", + " EIR 11 points 2 words EERIE(5) EERIER(6)\n", + " EIS 35 points 7 words ISSEI(5) ISSEIS(6) SEIS(1) SEISE(5) SEISES(6) SISES(5) SISSIES(7)\n", + " EIT 6 points 1 word TITTIE(6)\n", + " ENR 1 point 1 word ERNE(1)\n", + " ENS 20 points 6 words NESS(1) NESSES(6) SEEN(1) SENE(1) SENSE(5) SENSES(6)\n", + " ENT 15 points 5 words ENTENTE(7) NETT(1) TEEN(1) TENET(5) TENT(1)\n", + " ERS 52 points 13 words ERRS(1) ERSES(5) REES(1) RESEE(5) RESEES(6) SEER(1) SEERESS(7) SEERESSES(9)\n", + " SEERS(5) SERE(1) SERER(5) SERES(5) SERS(1)\n", + " ERT 27 points 7 words RETE(1) TEETER(6) TERETE(6) TERRET(6) TETTER(6) TREE(1) TRET(1)\n", + " EST 79 points 18 words SESTET(6) SESTETS(7) SETS(1) SETT(1) SETTEE(6) SETTEES(7) SETTS(5) STET(1)\n", + " STETS(5) TEES(1) TEST(1) TESTEE(6) TESTEES(7) TESTES(6) TESTS(5) TETS(1) TSETSE(6) TSETSES(7)\n", + " EN 1 point 1 word NENE(1)\n", + " ES 7 points 3 words ESES(1) ESSES(5) SEES(1)\n" + ] + } + ], + "source": [ + "enable1s = valid_words(open('enable1.txt').read(), \n", + " lambda w: len(w) >= 4 and len(set(w)) <= 7)\n", + "\n", "report(words=enable1s)" ] }, @@ -2022,18 +2387,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Yes it does (roughly) double the score!\n", - "\n", - "# Summary\n", - "\n", - "This notebook showed how to find the highest-scoring honeycomb. Thanks to a series of ideas, we were able to achieve a substantial reduction in the number of honeycombs that need to be examined (a factor of 400), the run time needed for `game_score` (a factor of about 200), and the overall run time (a factor of about 70,000).\n", - "\n", - "- **Brute Force Enumeration** (3,364,900 honeycombs; 10 hours (estimate) run time)
Try every possible honeycomb.\n", - "- **Pangram Lettersets** (55,902 honeycombs; 10 minutes (estimate) run time)
Try just the honeycombs that are pangram lettersets (with every center).\n", - "- **Points Table** (55,902 honeycombs; under 2 seconds run time)
Precompute the score for each letterset, and sum the 64 letter subsets of each honeycomb.\n", - "- **Branch and Bound** (8,084 honeycombs; under 1/2 second run time)
Try every center only for lettersets that score better than the best score so far.\n", - "\n", - "\n", + "Allowing 'S' words more than doubles the score!\n", "\n", "Here are pictures for the highest-scoring honeycombs, with and without an S:\n", "\n", @@ -2043,6 +2397,30 @@ "
\n", "" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Summary\n", + "\n", + "This notebook showed how to find the highest-scoring honeycomb. Four ideas led to four approaches:\n", + "\n", + "1. **Brute Force Enumeration**: Compute the game score for every possible honeycomb; return the best.\n", + "2. **Pangram Lettersets**: Compute the game score for just the honeycombs that are pangram lettersets (with all possible centers).\n", + "3. **Points Table**: Precompute the score for each letterset; then for each candidate honeycomb, sum the scores of the 64 letter subsets.\n", + "4. **Branch and Bound**: Try all 7 centers only for lettersets that score better than the best score so far.\n", + "\n", + "These ideas led to a substantial reduction in the number of honeycombs examined (a factor of 400), the run time of a call to `game_score` (a factor of 300), and the overall run time of `best_honeycomb` (a factor of 75,000, although I didn't actually run the first two cases, just estimated the time).\n", + "\n", + "|Approach|Honeycombs|`game_score` Time|Total Run Time|\n", + "|--------|----------|--------|----|\n", + "|**1. Brute Force Enumeration**|3,364,900|9000 microseconds|8.5 hours|\n", + "|**2. Pangram Lettersets**|55,902|9000 microseconds|500 seconds|\n", + "|**3. Points Table**|55,902|26 microseconds|1.6 seconds|\n", + "|**4. Branch and Bound**|8,084 |26 microseconds|0.4 seconds|\n", + "\n" + ] } ], "metadata": {