Created separate library
[riddle-generator.git] / riddle_definitions.md
diff --git a/riddle_definitions.md b/riddle_definitions.md
new file mode 100644 (file)
index 0000000..fb025c4
--- /dev/null
@@ -0,0 +1,171 @@
+---
+jupyter:
+  jupytext:
+    formats: ipynb,md,py:percent
+    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
+---
+
+# Definitions generally useful for the riddle solver
+
+```python
+import unicodedata
+import re
+from dataclasses import dataclass
+from typing import Dict, Tuple, List, Set
+from enum import Enum, auto
+import functools
+import random
+```
+
+```python
+stop_words = set('my is in within lies and also always you will find the found'.split())
+negative_words = set('but not never neither nor'.split())
+```
+
+```python
+ordinals : Dict[str, int] =  { 'last': -1
+            , 'first': 1
+            , 'second': 2
+            , 'third': 3
+            , 'fourth': 4
+            , 'fifth': 5
+            , 'sixth': 6
+            , 'seventh': 7
+            , 'eighth': 8
+            , 'ninth': 9
+            , 'tenth': 10
+            , 'eleventh': 11
+            , 'twelfth': 12
+            }
+
+reverse_ordinals : Dict[int, str] = {n: w for w, n in ordinals.items()}
+
+def from_ordinal(word: str) -> int:
+  return ordinals[word]
+
+def to_ordinal(number: int) -> str:
+  return reverse_ordinals[number]
+```
+
+These are the words that can be the solution to a riddle, and used as the clue for a riddle.
+
+```python
+dictionary : List[str] = [unicodedata.normalize('NFKD', w.strip()).\
+                 encode('ascii', 'ignore').\
+                 decode('utf-8')
+              for w in open('/usr/share/dict/british-english').readlines()
+              if w.strip().islower()
+              if w.strip().isalpha()
+              if len(w.strip()) >= 5
+              if w not in stop_words
+              if w not in negative_words
+              if w not in ordinals
+             ]
+```
+
+Some types that will be used throughout the library
+
+```python
+class RiddleValence(Enum):
+  Include = auto()
+  Exclude = auto()
+
+@dataclass
+class RiddleClue:
+  valence : RiddleValence
+  word : str
+  
+@dataclass
+class RiddleElement:
+  valence : RiddleValence
+  letters : Set[str]
+
+Riddle = Dict[int, Tuple[RiddleClue, RiddleClue]]
+RiddleElems =  Dict[int, RiddleElement]
+```
+
+```python
+@functools.lru_cache
+def edit_distance(s: str, t: str) -> int:
+  if s == "":
+    return len(t)
+  if t == "":
+    return len(s)
+  if s[-1] == t[-1]:
+    cost = 0
+  else:
+    cost = 1
+       
+  res = min(
+    [ edit_distance(s[:-1], t)+1
+    , edit_distance(s, t[:-1])+1
+    , edit_distance(s[:-1], t[:-1]) + cost
+    ])
+
+  return res
+```
+
+```python
+def collapse_riddle_clues(elems : Dict[int, Tuple[RiddleClue, RiddleClue]]) -> RiddleElems:
+  def combine_clues(a: RiddleClue, b: RiddleClue) -> RiddleElement:
+    if a.valence == b.valence:
+      if a.valence == RiddleValence.Include:
+        return RiddleElement(letters = set(a.word) & set(b.word), 
+                             valence = RiddleValence.Include)
+      else:
+        return RiddleElement(letters = set(a.word) | set(b.word), 
+                             valence = RiddleValence.Exclude)
+    else:
+      if a.valence == RiddleValence.Include:
+        p, q = a, b
+      else:
+        p, q = b, a
+      return RiddleElement(letters = set(p.word) - set(q.word), 
+                           valence = RiddleValence.Include)
+  
+  return {i: combine_clues(a, b) for i, (a, b) in elems.items()}
+```
+
+```python
+
+```
+
+```python
+def matches_element(pos: int, elem: RiddleElement, word: str) -> bool:
+  if len(word) < pos:
+    return False
+  if elem.valence == RiddleValence.Include:
+    return word[pos-1] in elem.letters
+  else:
+    return word[pos-1] not in elem.letters
+```
+
+```python
+def matches_all_elements(riddle: RiddleElems, word: str) -> bool:
+  if -1 in riddle:
+    last_elem = riddle[-1]
+    new_riddle = {p: e for p, e in riddle.items() if p != -1}
+    new_riddle[len(word)] = last_elem
+  else:
+    new_riddle = riddle
+  return all(matches_element(i, elem, word) for i, elem in new_riddle.items())
+```
+
+```python
+def solve_riddle(riddle: RiddleElems) -> List[str]:
+  return [w for w in dictionary 
+          if len(w) == len(riddle)
+          if matches_all_elements(riddle, w)]    
+```
+
+```python
+
+```