Removed neighbour generation out of the core library
[riddle-generator.git] / riddle_creator_lazy.md
1 ---
2 jupyter:
3 jupytext:
4 formats: ipynb,md
5 text_representation:
6 extension: .md
7 format_name: markdown
8 format_version: '1.3'
9 jupytext_version: 1.14.5
10 kernelspec:
11 display_name: Python 3 (ipykernel)
12 language: python
13 name: python3
14 ---
15
16 ```python
17 from riddle_definitions import *
18
19 from typing import Dict, Tuple, List, Set
20 from enum import Enum, auto
21 import random
22 import gzip
23 ```
24
25 ```python
26 dictionary_neighbours = {}
27
28 for line in gzip.open('dictionary_neighbours.txt.gz', 'rt').readlines():
29 words = line.strip().split(',')
30 dictionary_neighbours[words[0]] = words[1:]
31
32 possible_riddle_clues = list(dictionary_neighbours.keys())
33 ```
34
35 ```python
36 len(dictionary_neighbours['sonnet'])
37 ```
38
39 ```python
40 def include_exclude_clue(letter: str, limit: int = 3) -> (RiddleClue, RiddleClue):
41 finished = False
42 while not finished:
43
44 has_first = False
45 while not has_first:
46 with_letter = random.choice(possible_riddle_clues)
47 has_first = letter in with_letter
48
49 others = dictionary_neighbours[with_letter][:]
50 random.shuffle(others)
51
52 while not finished and others:
53 other = others[0]
54
55 if letter not in other and edit_distance(with_letter, other) <= limit:
56 finished = True
57 else:
58 others = others[1:]
59
60 return (RiddleClue(word=with_letter, valence=RiddleValence.Include),
61 RiddleClue(word=other, valence=RiddleValence.Exclude))
62
63 a, b = include_exclude_clue('s')
64 a, b, set(a.word) - set(b.word), edit_distance(a.word, b.word)
65 ```
66
67 ```python
68 def include_include_clue(letter: str, limit: int = 3) -> (RiddleClue, RiddleClue):
69 finished = False
70 while not finished:
71
72 has_first = False
73 while not has_first:
74 with_letter = random.choice(possible_riddle_clues)
75 has_first = letter in with_letter
76
77 others = dictionary_neighbours[with_letter][:]
78 random.shuffle(others)
79
80 while not finished and others:
81 other = others[0]
82
83 if letter in other and edit_distance(with_letter, other) <= limit:
84 finished = True
85 else:
86 others = others[1:]
87
88 return (RiddleClue(word=with_letter, valence=RiddleValence.Include),
89 RiddleClue(word=other, valence=RiddleValence.Include))
90
91 a, b = include_include_clue('s')
92 a, b, set(a.word) | set(b.word), edit_distance(a.word, b.word)
93 ```
94
95 ```python
96 def exclude_exclude_clue(letter: str, limit: int = 3) -> (RiddleClue, RiddleClue):
97 finished = False
98 while not finished:
99
100 has_first = False
101 while not has_first:
102 without_letter = random.choice(possible_riddle_clues)
103 has_first = letter not in without_letter
104
105 others = dictionary_neighbours[without_letter][:]
106 random.shuffle(others)
107
108 while not finished and others:
109 other = others[0]
110
111 if letter not in other and edit_distance(without_letter, other) <= limit:
112 finished = True
113 else:
114 others = others[1:]
115
116
117 return (RiddleClue(word=without_letter, valence=RiddleValence.Exclude),
118 RiddleClue(word=other, valence=RiddleValence.Exclude))
119
120 a, b = exclude_exclude_clue('s')
121 a, b, set(a.word) | set(b.word), edit_distance(a.word, b.word)
122 ```
123
124 ```python
125 def random_clue( letter: str
126 , ie_limit: int = 3
127 , ii_limit: int = 2
128 , ee_limit: int = 2) -> (RiddleClue, RiddleClue):
129 clue_type = random.choices(['include_exclude', 'include_include', 'exclude_exclude'],
130 weights=[7, 2, 1],
131 k=1)[0]
132 if clue_type == 'include_exclude':
133 return include_exclude_clue(letter, limit=ie_limit)
134 elif clue_type =='include_include':
135 return include_include_clue(letter, limit=ii_limit)
136 else:
137 return exclude_exclude_clue(letter, limit=ee_limit)
138 ```
139
140 ```python
141 def random_riddle( word: str
142 , ie_limit: int = 3
143 , ii_limit: int = 2
144 , ee_limit: int = 2
145 ) -> Riddle:
146 return {i+1 :
147 random_clue(l,
148 ie_limit=ie_limit, ii_limit=ii_limit, ee_limit=ee_limit)
149 for i, l in enumerate(word)}
150 ```
151
152 ```python
153 sample_riddle = random_riddle('teacup')
154 sample_riddle
155 ```
156
157 ```python
158 collapse_riddle_clues(sample_riddle)
159 ```
160
161 ```python
162 solve_riddle(collapse_riddle_clues(sample_riddle))
163 ```
164
165 ```python
166 # write_riddle(sample_riddle)
167 ```
168
169 ```python
170 # sample_riddle = random_riddle('sonnet', limit=4)
171 # sample_riddle
172 ```
173
174 ```python
175 sample_riddle
176 ```
177
178 ```python
179 collapse_riddle_clues(sample_riddle)
180 ```
181
182 ```python
183 solve_riddle(collapse_riddle_clues(sample_riddle))
184 ```
185
186 ```python
187 def valid_random_riddle(word: str) -> Riddle:
188 finished = False
189 while not finished:
190 riddle = random_riddle(word)
191 solns = solve_riddle(collapse_riddle_clues(riddle))
192 finished = (len(solns) == 1)
193 return riddle
194 ```
195
196 ```python
197 import time
198 import csv
199 reports = []
200 for _ in range(1000):
201 w1, c1 = time.perf_counter(), time.process_time()
202 r = valid_random_riddle(random.choice(possible_riddle_clues))
203 w2, c2 = time.perf_counter(), time.process_time()
204 linecount = len(r)
205 reports.append({'wall_time': w2 - w1,
206 'cpu_time': c2 - c1,
207 'riddle_lines': linecount})
208
209 with open('metrics_lazy.csv', 'w', newline='') as csvfile:
210 fieldnames = list(reports[0].keys())
211 writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
212
213 writer.writeheader()
214 for r in reports:
215 writer.writerow(r)
216 ```
217
218 ```python
219 def write_include_exclude_line(clue_a: RiddleClue, clue_b: RiddleClue) -> str:
220 line = f"is in {clue_a.word} but not in {clue_b.word}"
221 return line
222 ```
223
224 ```python
225 def write_include_include_line(clue_a: RiddleClue, clue_b: RiddleClue) -> str:
226 if random.randrange(2) == 0:
227 line = f"is in {clue_a.word} and also in {clue_b.word}"
228 else:
229 line = f"is in both {clue_a.word} and {clue_b.word}"
230 return line
231 ```
232
233 ```python
234 def write_exclude_exclude_line(clue_a: RiddleClue, clue_b: RiddleClue) -> str:
235 line = f"is neither in {clue_a.word} nor in {clue_b.word}"
236 return line
237 ```
238
239 ```python
240 def write_line(a: RiddleClue, b: RiddleClue) -> str:
241 if a.valence == RiddleValence.Include and b.valence == RiddleValence.Include:
242 return write_include_include_line(a, b)
243 elif a.valence == RiddleValence.Include and b.valence == RiddleValence.Exclude:
244 return write_include_exclude_line(a, b)
245 elif a.valence == RiddleValence.Exclude and b.valence == RiddleValence.Exclude:
246 return write_exclude_exclude_line(a, b)
247 else:
248 return "illegal line"
249 ```
250
251 ```python
252 def write_riddle(riddle: Riddle) -> List[str]:
253 output = []
254 for i, (clue_a, clue_b) in sorted(riddle.items()):
255 pos = reverse_ordinals[i]
256 if i == len(riddle) and random.random() <= 0.3:
257 pos = reverse_ordinals[-1]
258 line = write_line(clue_a, clue_b)
259 full_line = f"My {pos} {line}"
260 output.append(full_line)
261 return output
262 ```
263
264 ```python
265
266 ```
267
268 ```python
269 sample_riddle = valid_random_riddle("riddle")
270 sample_riddle
271 ```
272
273 ```python
274 write_riddle(sample_riddle)
275 ```
276
277 ```python
278 solve_riddle(collapse_riddle_clues(sample_riddle))
279 ```
280
281 ```python
282 with open("generated-riddles-lazy.txt", 'w') as file:
283 between = False
284 for _ in range(10):
285 if between:
286 file.write('\n')
287 between = True
288 target = random.choice(possible_riddle_clues)
289 riddle = valid_random_riddle(target)
290 lines = write_riddle(riddle)
291 file.writelines(l + '\n' for l in lines)
292 file.write(f'Target: {target}\n')
293
294 ```
295
296 ```python
297 print('\n'.join(write_riddle(valid_random_riddle("faster"))))
298 ```
299
300 ```python
301 len(dictionary_neighbours['sonnet'])
302 ```
303
304 ```python
305 ndls = sum(len(ws) for ws in dictionary_neighbours.values())
306 ndls
307 ```
308
309 ```python
310 ndls / len(dictionary_neighbours)
311 ```
312
313 ```python
314 dn_trimmed = {w : [o for o in dictionary_neighbours[w] if edit_distance(w, o) <= 3]
315 for w in dictionary_neighbours}
316 ```
317
318 ```python
319 ndlts = sum(len(ws) for ws in dn_trimmed.values())
320 ndlts
321 ```
322
323 ```python
324 ndlts / len(dn_trimmed)
325 ```
326
327 ```python
328 148 / 940
329 ```
330
331 ```python
332 1/7
333 ```
334
335 ```python
336 1/6
337 ```
338
339 ```python
340
341 ```