X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=riddle_definitions.md;h=a9242f362736e34ad44c42aa259e1952705d1e4e;hb=52008779b0281639e17a6570271dc7d5a3227b03;hp=fb025c46a224a986231653ec511512bb1cf6b3b2;hpb=ce34915246926441c163272e09f1343db3fd1955;p=riddle-generator.git diff --git a/riddle_definitions.md b/riddle_definitions.md index fb025c4..a9242f3 100644 --- a/riddle_definitions.md +++ b/riddle_definitions.md @@ -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)]