9 jupytext_version: 1.14.5
11 display_name: Python 3 (ipykernel)
17 from riddle_definitions import *
19 from typing import Dict, Tuple, List, Set
20 from enum import Enum, auto
26 dictionary_neighbours = {}
28 for line in gzip.open('dictionary_neighbours.txt.gz', 'rt').readlines():
29 words = line.strip().split(',')
30 dictionary_neighbours[words[0]] = words[1:]
32 possible_riddle_solutions = list(dictionary_neighbours.keys())
36 len(dictionary_neighbours['sonnet'])
40 def include_exclude_clue(letter: str, limit: int = 3) -> (RiddleClue, RiddleClue):
43 with_letter = random.choice([w for w in dictionary_neighbours if letter in w])
44 without_letter = [w for w in dictionary_neighbours[with_letter]
46 if edit_distance(with_letter, w) <= limit]
48 other = random.choice(without_letter)
51 return (RiddleClue(word=with_letter, valence=RiddleValence.Include),
52 RiddleClue(word=other, valence=RiddleValence.Exclude))
54 a, b = include_exclude_clue('s')
55 a, b, set(a.word) - set(b.word), edit_distance(a.word, b.word)
59 def include_include_clue(letter: str, limit: int = 3) -> (RiddleClue, RiddleClue):
62 with_letter = random.choice([w for w in dictionary_neighbours if letter in w])
63 others = [w for w in dictionary_neighbours[with_letter]
65 if edit_distance(with_letter, w) <= limit]
67 other = random.choice(others)
69 return (RiddleClue(word=with_letter, valence=RiddleValence.Include),
70 RiddleClue(word=other, valence=RiddleValence.Include))
72 a, b = include_include_clue('s')
73 a, b, set(a.word) | set(b.word), edit_distance(a.word, b.word)
77 def exclude_exclude_clue(letter: str, limit: int = 3) -> (RiddleClue, RiddleClue):
80 without_letter = random.choice([w for w in dictionary_neighbours if letter not in w])
81 others = [w for w in dictionary_neighbours[without_letter]
83 if edit_distance(without_letter, w) <= limit]
85 other = random.choice(others)
88 return (RiddleClue(word=without_letter, valence=RiddleValence.Exclude),
89 RiddleClue(word=other, valence=RiddleValence.Exclude))
91 a, b = exclude_exclude_clue('s')
92 a, b, set(a.word) | set(b.word), edit_distance(a.word, b.word)
96 def random_clue( letter: str
99 , ee_limit: int = 2) -> (RiddleClue, RiddleClue):
100 clue_type = random.choices(['include_exclude', 'include_include', 'exclude_exclude'],
103 if clue_type == 'include_exclude':
104 return include_exclude_clue(letter, limit=ie_limit)
105 elif clue_type =='include_include':
106 return include_include_clue(letter, limit=ii_limit)
108 return exclude_exclude_clue(letter, limit=ee_limit)
112 def random_riddle( word: str
119 ie_limit=ie_limit, ii_limit=ii_limit, ee_limit=ee_limit)
120 for i, l in enumerate(word)}
124 sample_riddle = random_riddle('teacup')
129 solve_riddle(collapse_riddle_clues(sample_riddle))
133 # write_riddle(sample_riddle)
137 # sample_riddle = random_riddle('sonnet', limit=4)
146 collapse_riddle_clues(sample_riddle)
150 solve_riddle(collapse_riddle_clues(sample_riddle))
154 def valid_random_riddle(word: str) -> Riddle:
157 riddle = random_riddle(word)
158 solns = solve_riddle(collapse_riddle_clues(riddle))
159 finished = (len(solns) == 1)
167 for _ in range(1000):
169 w1, c1 = time.perf_counter(), time.process_time()
170 r = valid_random_riddle(random.choice(dictionary))
171 w2, c2 = time.perf_counter(), time.process_time()
173 reports.append({'wall_time': w2 - w1,
175 'generated_lines': gencount,
176 'riddle_lines': linecount})
177 glcounts.append((gencount, linecount))
178 w_times.append(w2 - w1)
179 c_times.append(c2 - c1)
181 with open('metrics_related.csv', 'w', newline='') as csvfile:
182 fieldnames = list(reports[0].keys())
183 writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
191 def write_include_exclude_line(clue_a: RiddleClue, clue_b: RiddleClue) -> str:
192 line = f"is in {clue_a.word} but not in {clue_b.word}"
197 def write_include_include_line(clue_a: RiddleClue, clue_b: RiddleClue) -> str:
198 if random.randrange(2) == 0:
199 line = f"is in {clue_a.word} and also in {clue_b.word}"
201 line = f"is in both {clue_a.word} and {clue_b.word}"
206 def write_exclude_exclude_line(clue_a: RiddleClue, clue_b: RiddleClue) -> str:
207 line = f"is neither in {clue_a.word} nor in {clue_b.word}"
212 def write_line(a: RiddleClue, b: RiddleClue) -> str:
213 if a.valence == RiddleValence.Include and b.valence == RiddleValence.Include:
214 return write_include_include_line(a, b)
215 elif a.valence == RiddleValence.Include and b.valence == RiddleValence.Exclude:
216 return write_include_exclude_line(a, b)
217 elif a.valence == RiddleValence.Exclude and b.valence == RiddleValence.Exclude:
218 return write_exclude_exclude_line(a, b)
220 return "illegal line"
224 def write_riddle(riddle: Riddle) -> List[str]:
226 for i, (clue_a, clue_b) in sorted(riddle.items()):
227 pos = reverse_ordinals[i]
228 if i == len(riddle) and random.random() <= 0.3:
229 pos = reverse_ordinals[-1]
230 line = write_line(clue_a, clue_b)
231 full_line = f"My {pos} {line}"
232 output.append(full_line)
241 sample_riddle = valid_random_riddle("elephant")
246 write_riddle(sample_riddle)
250 solve_riddle(collapse_riddle_clues(sample_riddle))
254 with open("generated-riddles.txt", 'w') as file:
260 target = random.choice(dictionary)
261 riddle = valid_random_riddle(target)
262 lines = write_riddle(riddle)
263 file.writelines(l + '\n' for l in lines)
264 file.write(f'Target: {target}\n')