Working on blog posts
[riddle-generator.git] / riddle_definitions.md
index fb025c46a224a986231653ec511512bb1cf6b3b2..a9242f362736e34ad44c42aa259e1952705d1e4e 100644 (file)
@@ -6,7 +6,7 @@ jupyter:
       extension: .md
       format_name: markdown
       format_version: '1.3'
-      jupytext_version: 1.14.5
+      jupytext_version: 1.15.0
   kernelspec:
     display_name: Python 3 (ipykernel)
     language: python
@@ -15,6 +15,8 @@ jupyter:
 
 # Definitions generally useful for the riddle solver
 
+While this file is here as a Markdown file, it's intended that Jupytext will save this file as a "percent" Python file, so that it can be imported by other notebooks here.
+
 ```python
 import unicodedata
 import re
@@ -26,8 +28,8 @@ 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())
+stop_words = set('my is in within lies and also always you will find the found but'.split())
+negative_words = set('not never neither nor'.split())
 ```
 
 ```python
@@ -65,6 +67,7 @@ dictionary : List[str] = [unicodedata.normalize('NFKD', w.strip()).\
               if w.strip().islower()
               if w.strip().isalpha()
               if len(w.strip()) >= 5
+              if len(w.strip()) <= 12
               if w not in stop_words
               if w not in negative_words
               if w not in ordinals
@@ -75,20 +78,29 @@ Some types that will be used throughout the library
 
 ```python
 class RiddleValence(Enum):
+  """Does this part of the riddle include or exclude letters?"""
   Include = auto()
   Exclude = auto()
 
 @dataclass
 class RiddleClue:
+  """A half line of a riddle, like 'is in dreams' or 'not in octet'"""
   valence : RiddleValence
   word : str
   
 @dataclass
 class RiddleElement:
+  """A representation of the constraints that come from a whole line of 
+  a riddle"""
   valence : RiddleValence
   letters : Set[str]
 
+# A riddle that's been read and parsed. 
+# Note that the numbering is one-based, as per the numbers in the riddle text
 Riddle = Dict[int, Tuple[RiddleClue, RiddleClue]]
+
+# A riddle that's been processed ready for solving
+# Note that the numbering is one-based, as per the numbers in the riddle text
 RiddleElems =  Dict[int, RiddleElement]
 ```
 
@@ -99,22 +111,37 @@ def edit_distance(s: str, t: str) -> int:
     return len(t)
   if t == "":
     return len(s)
-  if s[-1] == t[-1]:
+  if s[0] == t[0]:
     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
+    [ 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:
+dictionary_neighbours = {
+  w: [o for o in dictionary
+      if edit_distance(w, o) <= 5
+      if not set(w) <= set(o)
+      if not set(o) <= set(w)]
+  for w in dictionary}
+
+dictionary_neighbours = {w: ns 
+                         for w, ns in dictionary_neighbours.items()
+                         if ns}
+```
+
+```python
+def collapse_riddle_clues(elems : Riddle) -> RiddleElems:
+  """Combine the two parts of a riddle line into one element for solving.
+  This takes account of the valence of the two parts."""
   def combine_clues(a: RiddleClue, b: RiddleClue) -> RiddleElement:
     if a.valence == b.valence:
       if a.valence == RiddleValence.Include:
@@ -134,12 +161,11 @@ def collapse_riddle_clues(elems : Dict[int, Tuple[RiddleClue, RiddleClue]]) -> R
   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:
+  """Does this element match this position of the the word?
+  Note that positions are one-based, as in the numbering system of the 
+  puzzle."""
   if len(word) < pos:
     return False
   if elem.valence == RiddleValence.Include:
@@ -150,6 +176,7 @@ def matches_element(pos: int, elem: RiddleElement, word: str) -> bool:
 
 ```python
 def matches_all_elements(riddle: RiddleElems, word: str) -> bool:
+  """Do all the elements of a riddle match the appropriate parts of a word?"""
   if -1 in riddle:
     last_elem = riddle[-1]
     new_riddle = {p: e for p, e in riddle.items() if p != -1}
@@ -161,6 +188,7 @@ def matches_all_elements(riddle: RiddleElems, word: str) -> bool:
 
 ```python
 def solve_riddle(riddle: RiddleElems) -> List[str]:
+  """Find all words that match this riddle"""
   return [w for w in dictionary 
           if len(w) == len(riddle)
           if matches_all_elements(riddle, w)]