Optimised riddle creation
[riddle-generator.git] / riddle_creator_filtered_dict.md
diff --git a/riddle_creator_filtered_dict.md b/riddle_creator_filtered_dict.md
new file mode 100644 (file)
index 0000000..9af3c93
--- /dev/null
@@ -0,0 +1,269 @@
+---
+jupyter:
+  jupytext:
+    formats: ipynb,md
+    text_representation:
+      extension: .md
+      format_name: markdown
+      format_version: '1.3'
+      jupytext_version: 1.14.5
+  kernelspec:
+    display_name: Python 3 (ipykernel)
+    language: python
+    name: python3
+---
+
+```python
+from riddle_definitions import *
+
+from typing import Dict, Tuple, List, Set
+from enum import Enum, auto
+import random
+import gzip
+```
+
+```python
+dictionary_neighbours = {}
+
+for line in gzip.open('dictionary_neighbours.txt.gz', 'rt').readlines():
+  words = line.strip().split(',')
+  dictionary_neighbours[words[0]] = words[1:]
+
+possible_riddle_solutions = list(dictionary_neighbours.keys())
+```
+
+```python
+len(dictionary_neighbours['sonnet'])
+```
+
+```python
+def include_exclude_clue(letter: str, limit: int = 3) -> (RiddleClue, RiddleClue):
+  finished = False
+  while not finished:
+    with_letter = random.choice([w for w in dictionary_neighbours if letter in w])
+    without_letter = [w for w in dictionary_neighbours[with_letter] 
+                      if letter not in w
+                      if edit_distance(with_letter, w) <= limit]
+    if without_letter:
+      other = random.choice(without_letter)
+      finished = True
+
+  return (RiddleClue(word=with_letter, valence=RiddleValence.Include),
+          RiddleClue(word=other, valence=RiddleValence.Exclude))
+
+a, b = include_exclude_clue('s')
+a, b, set(a.word) - set(b.word), edit_distance(a.word, b.word)
+```
+
+```python
+def include_include_clue(letter: str, limit: int = 3) -> (RiddleClue, RiddleClue):
+  finished = False
+  while not finished:
+    with_letter = random.choice([w for w in dictionary_neighbours if letter in w])
+    others = [w for w in dictionary_neighbours[with_letter] 
+                      if letter in w
+                      if edit_distance(with_letter, w) <= limit]
+    if others:
+      other = random.choice(others)
+      finished = True
+  return (RiddleClue(word=with_letter, valence=RiddleValence.Include),
+          RiddleClue(word=other, valence=RiddleValence.Include))
+
+a, b = include_include_clue('s')
+a, b, set(a.word) | set(b.word), edit_distance(a.word, b.word)
+```
+
+```python
+def exclude_exclude_clue(letter: str, limit: int = 3) -> (RiddleClue, RiddleClue):
+  finished = False
+  while not finished:
+    without_letter = random.choice([w for w in dictionary_neighbours if letter not in w])
+    others = [w for w in dictionary_neighbours[without_letter] 
+                      if letter not in w
+                      if edit_distance(without_letter, w) <= limit]
+    if others:
+      other = random.choice(others)
+      finished = True
+
+  return (RiddleClue(word=without_letter, valence=RiddleValence.Exclude),
+          RiddleClue(word=other, valence=RiddleValence.Exclude))
+
+a, b = exclude_exclude_clue('s')
+a, b, set(a.word) | set(b.word), edit_distance(a.word, b.word)
+```
+
+```python
+def random_clue( letter: str
+               , ie_limit: int = 3
+               , ii_limit: int = 2
+               , ee_limit: int = 2) -> (RiddleClue, RiddleClue):
+  clue_type = random.choices(['include_exclude', 'include_include', 'exclude_exclude'],
+                             weights=[7, 2, 1],
+                             k=1)[0]
+  if clue_type == 'include_exclude':
+    return include_exclude_clue(letter, limit=ie_limit)
+  elif clue_type =='include_include':
+    return include_include_clue(letter, limit=ii_limit)
+  else:
+    return exclude_exclude_clue(letter, limit=ee_limit)
+```
+
+```python
+def random_riddle( word: str
+                 , ie_limit: int = 3
+                 , ii_limit: int = 2
+                 , ee_limit: int = 2
+                 ) -> Riddle:
+  return {i+1 : 
+    random_clue(l, 
+                ie_limit=ie_limit, ii_limit=ii_limit, ee_limit=ee_limit)
+          for i, l in enumerate(word)}  
+```
+
+```python
+sample_riddle = random_riddle('teacup')
+sample_riddle
+```
+
+```python
+solve_riddle(collapse_riddle_clues(sample_riddle))
+```
+
+```python
+# write_riddle(sample_riddle)
+```
+
+```python
+# sample_riddle = random_riddle('sonnet', limit=4)
+# sample_riddle
+```
+
+```python
+sample_riddle
+```
+
+```python
+collapse_riddle_clues(sample_riddle)
+```
+
+```python
+solve_riddle(collapse_riddle_clues(sample_riddle))
+```
+
+```python
+def valid_random_riddle(word: str) -> Riddle:
+  finished = False
+  while not finished:
+    riddle = random_riddle(word)
+    solns = solve_riddle(collapse_riddle_clues(riddle))
+    finished = (len(solns) == 1)
+  return riddle
+```
+
+```python
+import time
+import csv
+reports = []
+for _ in range(1000):
+  gencount = 0
+  w1, c1 = time.perf_counter(), time.process_time()
+  r = valid_random_riddle(random.choice(dictionary))
+  w2, c2 = time.perf_counter(), time.process_time()
+  linecount = len(r)
+  reports.append({'wall_time': w2 - w1,
+                  'cpu_time': c2 - c1,
+                  'generated_lines': gencount,
+                  'riddle_lines': linecount})
+  glcounts.append((gencount, linecount))
+  w_times.append(w2 - w1)
+  c_times.append(c2 - c1)
+
+with open('metrics_related.csv', 'w', newline='') as csvfile:
+  fieldnames = list(reports[0].keys())
+  writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
+
+  writer.writeheader()
+  for r in reports:
+    writer.writerow(r)
+```
+
+```python
+def write_include_exclude_line(clue_a: RiddleClue, clue_b: RiddleClue) -> str:
+  line = f"is in {clue_a.word} but not in {clue_b.word}"
+  return line
+```
+
+```python
+def write_include_include_line(clue_a: RiddleClue, clue_b: RiddleClue) -> str:
+  if random.randrange(2) == 0:
+    line = f"is in {clue_a.word} and also in {clue_b.word}"
+  else:
+    line = f"is in both {clue_a.word} and {clue_b.word}"
+  return line
+```
+
+```python
+def write_exclude_exclude_line(clue_a: RiddleClue, clue_b: RiddleClue) -> str:
+  line = f"is neither in {clue_a.word} nor in {clue_b.word}"
+  return line
+```
+
+```python
+def write_line(a: RiddleClue, b: RiddleClue) -> str:
+  if a.valence == RiddleValence.Include and b.valence == RiddleValence.Include:
+    return write_include_include_line(a, b)
+  elif a.valence == RiddleValence.Include and b.valence == RiddleValence.Exclude:
+    return write_include_exclude_line(a, b)
+  elif a.valence == RiddleValence.Exclude and b.valence == RiddleValence.Exclude:
+    return write_exclude_exclude_line(a, b)
+  else:
+    return "illegal line"
+```
+
+```python
+def write_riddle(riddle: Riddle) -> List[str]:
+  output = []
+  for i, (clue_a, clue_b) in sorted(riddle.items()):
+    pos = reverse_ordinals[i]
+    if i == len(riddle) and random.random() <= 0.3:
+      pos = reverse_ordinals[-1]
+    line = write_line(clue_a, clue_b)
+    full_line = f"My {pos} {line}"
+    output.append(full_line)
+  return output  
+```
+
+```python
+
+```
+
+```python
+sample_riddle = valid_random_riddle("elephant")
+sample_riddle
+```
+
+```python
+write_riddle(sample_riddle)
+```
+
+```python
+solve_riddle(collapse_riddle_clues(sample_riddle))
+```
+
+```python
+with open("generated-riddles.txt", 'w') as file:
+  between = False
+  for _ in range(10):
+    if between:
+      file.write('\n')
+    between = True
+    target = random.choice(dictionary)
+    riddle = valid_random_riddle(target)
+    lines = write_riddle(riddle)
+    file.writelines(l + '\n' for l in lines)
+    file.write(f'Target: {target}\n')
+```
+
+```python
+
+```