Skip to content

Instantly share code, notes, and snippets.

@hughdbrown
Last active March 30, 2022 12:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hughdbrown/3fe653c8020afb40a1b4d0ecfd0bdd38 to your computer and use it in GitHub Desktop.
Save hughdbrown/3fe653c8020afb40a1b4d0ecfd0bdd38 to your computer and use it in GitHub Desktop.
Calculate and score all possible answers for twentyletters.com puzzle
Sample output
~/twenty.py preventionthionalist
process dictionary 0:00:00.057675
lookup tables 0:00:00.354845
search solutions 0:00:00.004037
[(304, 'trephinations', 'violent'),
(304, 'interventional', 'pithos'),
(304, 'interpolative', 'tonnish'),
(292, 'trephination', 'violents'),
(292, 'trephination', 'novelist'),
(292, 'nontransitive', 'hoplite'),
(292, 'innovationist', 'telpher'),
(292, 'antistrophon', 'lenitive'),
(284, 'ventilations', 'triphone'),
(284, 'intervention', 'hospital'),
(284, 'antiphoners', 'volitient'),
(284, 'antinovelist', 'triphone'),
(280, 'ventilation', 'triphones'),
(280, 'nontrivial', 'epithetons'),
(280, 'inventorial', 'phonetist'),
(280, 'epithetons', 'nontrivial')]
#!/usr/bin/env python3
from collections import Counter, defaultdict
from datetime import datetime
from pprint import pprint
from string import ascii_lowercase
from sys import argv
from typing import Dict, List
LETTER_SCORES = {
1: 'aeioulnrst',
2: 'dg',
3: 'bcmp',
4: 'fhvwy',
5: 'k',
8: 'jxq',
10: 'z',
}
INV_LETTER_SCORES = {c: v for v, k in LETTER_SCORES.items() for c in k}
def score(word):
return len(word) * sum(INV_LETTER_SCORES[c] for c in word)
def score_counter(word, counter):
return len(word) * sum(INV_LETTER_SCORES[c] * _len for c, _len in counter.items())
def scored(word, solution):
c, word_list = solution
word0 = word_list[0]
total_score = score(word) + score_counter(word0, c)
for w in word_list:
yield (total_score, word, w)
def key_fn(word):
return "".join(sorted(word))
def main(twenty: str, dict_file: str):
twenty_cs = set(twenty)
twenty_c = Counter(twenty)
# Words grouped by word length
lengths: Dict[int, List[str]] = defaultdict(list)
# Lists of words grouped by letters in word
x = defaultdict(list)
words: Dict[str, Counter] = {}
s = datetime.now()
with open(dict_file) as handle:
allwords = [
line.rstrip()
for line in handle
if line[0] in twenty_cs and line[len(line) // 2] in twenty_cs
]
e = datetime.now()
print(f'process dictionary {e - s}')
s = datetime.now()
for word in allwords:
wordlen = len(word)
if 3 <= wordlen <= 20:
c = Counter(word)
if twenty_cs.issuperset(c) and all(twenty_c[k] >= v for k, v in c.items()):
lengths[wordlen].append(word)
words[word] = c
x[key_fn(word)].append(word)
e = datetime.now()
print(f'lookup tables {e - s}')
s = datetime.now()
solutions: Dict[str, List[str]] = {}
indexes = list(range(10, 17 + 1)) + [20]
for i in indexes:
for word in (lengths[i] or []):
left = twenty_c - words[word]
k = key_fn("".join(k * v for k, v in left.items()))
# Is there any possible word with these letters?
if k in x:
solutions[word] = (left, x[k])
e = datetime.now()
print(f'search solutions {e - s}')
pprint(
sorted(
{s for word1, solution in solutions.items() for s in scored(word1, solution)},
reverse=True,
)
)
if __name__ == '__main__':
for dict_file in ("words_alpha.txt", "sowpods.txt"):
main(argv[1], dict_file)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment