9 from pyutils import bootstrap, config
10 from pyutils.unscrambler import Unscrambler
12 cfg = config.add_commandline_args(
13 f'Scrabble (aka Wordscapes)! ({__file__})',
14 'Find all words in a query letter set (proper and subset based)',
18 help='Minimum length permissable in results',
24 '--show_scrabble_score',
25 help='Should we compute and display Scrabble game scores?',
30 help='Set of letters available on the board (not in hand). e.g. --extra_letters s t r',
33 logger = logging.getLogger(__name__)
36 scrabble_score_by_letter = {
66 def fill_in_blanks(letters: str, skip: Set[str]) -> str:
67 if '_' not in letters:
68 logger.debug('Filled in blanks: %s', letters)
72 for replacement in string.ascii_lowercase:
73 filled_in = letters.replace('_', replacement, 1)
74 if filled_in not in skip:
76 'Orig: %s, replacement is %s, new: %s', letters, replacement, filled_in
79 yield from fill_in_blanks(filled_in, skip)
82 def lookup_letter_set(
84 unscrambler: Unscrambler,
88 sig = unscrambler.compute_word_sig(letters)
89 if sig not in seen_sigs:
90 logger.debug('%s => %s', letters, sig)
91 for (words, exact) in unscrambler.lookup_by_sig(sig).items():
93 for word in words.split(','):
94 if len(word) >= config.config['min_length']:
97 logger.debug('Skipping %s because it\'s too short.', word)
101 @bootstrap.initialize
103 if len(sys.argv) < 2:
104 print("Missing required query.", file=sys.stderr)
107 seen_sigs: Set[int] = set()
108 seen_words: Set[str] = set()
109 seen_fill_ins: Set[str] = set()
110 u: Unscrambler = Unscrambler()
112 query = sys.argv[1].lower()
113 orig_letters = set([x for x in query if x != '_'])
114 logger.debug('Initial query: %s (%s)', query, orig_letters)
116 if config.config['extra_letters']:
117 for extra in config.config['extra_letters']:
118 extra = extra.lower()
119 query = query + extra
120 logger.debug('Building with extra letter: %s; query is %s', extra, query)
121 for q in fill_in_blanks(query, seen_fill_ins):
122 logger.debug('...blanks filled in: %s', q)
123 for x in range(config.config['min_length'], len(q) + 1):
124 for tup in itertools.combinations(q, x):
125 letters = ''.join(tup)
126 logger.debug('...considering subset: %s', letters)
127 lookup_letter_set(letters, u, seen_sigs, seen_words)
129 logger.debug('Removing extra letter; query is %s', query)
131 for q in fill_in_blanks(query, seen_fill_ins):
132 logger.debug('...blanks filled in: %s', q)
133 for x in range(config.config['min_length'], len(q) + 1):
134 for tup in itertools.combinations(q, x):
135 letters = ''.join(tup)
136 logger.debug('...considering subset: %s', letters)
137 lookup_letter_set(letters, u, seen_sigs, seen_words)
140 for word in sorted(seen_words):
141 if config.config['show_scrabble_score']:
143 copy = orig_letters.copy()
147 score += scrabble_score_by_letter.get(letter, 0)
152 output[word] = len(word)
154 for word, n in sorted(output.items(), key=lambda x: x[1]):
155 if config.config['show_scrabble_score']:
156 print(f'{word} => {n}')
161 if __name__ == "__main__":