Skip to content

Instantly share code, notes, and snippets.

@jhoneycutt
Created October 13, 2023 08:06
Show Gist options
  • Save jhoneycutt/88915c96585712eb660f22e12bef21a7 to your computer and use it in GitHub Desktop.
Save jhoneycutt/88915c96585712eb660f22e12bef21a7 to your computer and use it in GitHub Desktop.
NY Times letter-boxed solver
import json
import sys
import time
# Lexicon must be a JSON array of strings.
LEXICON_PATH = "lexicon.json"
if len(sys.argv) != 5:
name = sys.argv[0]
print(f"Usage : {name} <side 1> <side 2> <side 3> <side 4>")
print(f"Example: {name} ERP SBW OID UHC")
sys.exit(-1)
sides = [side.lower() for side in sys.argv[1:]]
all_sides_string = "".join(sides)
full_letter_set = set(all_sides_string)
# Create a map from each letter to each playable letter, i.e. the letters on
# the other 3 sides.
side_map = {
sides[0][0]: set(sides[1] + sides[2] + sides[3]),
sides[0][1]: set(sides[1] + sides[2] + sides[3]),
sides[0][2]: set(sides[1] + sides[2] + sides[3]),
sides[1][0]: set(sides[0] + sides[2] + sides[3]),
sides[1][1]: set(sides[0] + sides[2] + sides[3]),
sides[1][2]: set(sides[0] + sides[2] + sides[3]),
sides[2][0]: set(sides[0] + sides[1] + sides[3]),
sides[2][1]: set(sides[0] + sides[1] + sides[3]),
sides[2][2]: set(sides[0] + sides[1] + sides[3]),
sides[3][0]: set(sides[0] + sides[1] + sides[2]),
sides[3][1]: set(sides[0] + sides[1] + sides[2]),
sides[3][2]: set(sides[0] + sides[1] + sides[2]),
}
def can_play(word):
for i, letter in enumerate(word):
if letter not in full_letter_set:
return False
if not i:
continue
if letter not in side_map[word[i - 1]]:
return False
return True
start_time = time.time()
lexicon = None
with open(LEXICON_PATH) as w:
lexicon = json.load(w)
playable_words = [word for word in lexicon if can_play(word)]
# Create a mapping from a letter to all playable words starting with that
# letter.
playable_words_map = { letter: [] for letter in full_letter_set }
for word in playable_words:
if word[0] in full_letter_set:
playable_words_map[word[0]].append(word)
candidate_count = 0
for first_word in playable_words:
last_letter = first_word[-1]
second_word_candidates = playable_words_map[last_letter]
for second_word in second_word_candidates:
if set(first_word + second_word) == full_letter_set:
candidate_count += 1
print(f"Candidate: {first_word}, {second_word}")
elapsed = time.time() - start_time
print(f"\n{candidate_count} candidates found in {elapsed:.2f} secs.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment