# 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
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
```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]
```
```
```python
-def collapse_riddle_clues(elems : Dict[int, Tuple[RiddleClue, RiddleClue]]) -> RiddleElems:
+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:
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:
```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}
```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)]
position_word = [t for t in stripped_tokens if t in ordinals][0]
pos = from_ordinal(position_word)
- first_index, first_word = [(i, t) for i, t in enumerate(stripped_tokens)
+ indexed_words = [(i, t) for i, t in enumerate(stripped_tokens)
if t not in ordinals
- if t not in negative_words][0]
- second_index, second_word = [(i, t) for i, t in enumerate(stripped_tokens)
- if t not in ordinals
- if t not in negative_words][1]
+ if t not in negative_words]
+
+ first_index, first_word = indexed_words[0]
+ second_index, second_word = indexed_words[1]
+
neg_indices = [i for i, t in enumerate(stripped_tokens) if t in negative_words]
first_clue = None
```python
def parse_riddle(riddle_text: str) -> Riddle:
- riddle_lines = {i: elem
- for i, elem in
- [parse_line(tokenise(l)) for l in riddle_text.split('\n')]}
- return collapse_riddle_clues(riddle_lines)
+ return {i: elem
+ for i, elem in
+ [parse_line(tokenise(l))
+ for l in riddle_text.split('\n')]}
```
```python
```python
def parse_and_solve_riddle(riddle_text: str) -> List[str]:
riddle = parse_riddle(riddle_text)
- return solve_riddle(riddle)
+ elems = collapse_riddle_clues(riddle)
+ return solve_riddle(elems)
```
```python