diff --git a/.gitignore b/.gitignore index 7f7cccc..6af70f0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ __pycache__/ *.py[cod] *$py.class +*.lock + # C extensions *.so @@ -58,3 +60,8 @@ docs/_build/ # PyBuilder target/ +# archives +*.bz2 + +# jupyter +.ipynb_checkpoints/ diff --git a/Pipfile b/Pipfile index 0b7d4ac..164a87f 100644 --- a/Pipfile +++ b/Pipfile @@ -9,6 +9,8 @@ numpy = "*" pylint = "*" flake8 = "*" rope = "*" +pandas = "*" +tqdm = "*" [dev-packages] diff --git a/data/.gitattributes b/data/.gitattributes new file mode 100644 index 0000000..23b98b1 --- /dev/null +++ b/data/.gitattributes @@ -0,0 +1,4 @@ +english.words filter=lfs diff=lfs merge=lfs -text +german.words filter=lfs diff=lfs merge=lfs -text +english.json filter=lfs diff=lfs merge=lfs -text +german.json filter=lfs diff=lfs merge=lfs -text diff --git a/data/data_tests.ipynb b/data/data_tests.ipynb new file mode 100644 index 0000000..9fbb84d --- /dev/null +++ b/data/data_tests.ipynb @@ -0,0 +1,1101 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import re\n", + "import tqdm\n", + "from IPython.display import Markdown" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## prepare raw wikiextract" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "%%bash\n", + "cat english.words| jq -s . > english.json" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "with open('english.json', 'r') as f:\n", + " english_vocab_db = json.load(f)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## helper functions to filter senses, synonyms and antonyms" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def filter_senses(senses_list, word):\n", + " filtered_senses = []\n", + " for entry in senses_list:\n", + " if 'glosses' in entry:\n", + " glosses = entry['glosses']\n", + " for gloss_entry in glosses:\n", + " for gloss in gloss_entry.split(';'):\n", + " s = re.sub('\\s+', ' ', re.sub(\"[\\(\\[].*?[\\)\\]]\", \"\", gloss).replace(\".\", \"\")).strip()\n", + " if len(s) > 0 and word not in s:\n", + " filtered_senses.append(s)\n", + " return filtered_senses" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def filter_synonyms(synonyms_list):\n", + " filtered_synonyms = []\n", + " for entry in synonyms_list:\n", + " if 'word' in entry:\n", + " filtered_synonyms.append(entry['word'])\n", + " return filtered_synonyms" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def filter_antonyms(antonym_list):\n", + " filtered_antonyms = []\n", + " for entry in antonym_list:\n", + " if 'word' in entry:\n", + " filtered_antonyms.append(entry['word'])\n", + " return filtered_antonyms" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def add_wiktionary_item(item, db):\n", + " item = {}\n", + " if 'word' not in item.keys():\n", + " return\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## build a dictionary_database from the raw extract\n", + "using words as keys, enumerate those keys (e.g. `house`, `house1`, `house2` etc.) on duplicates" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 1023195/1023195 [00:00<00:00, 1386092.63it/s]\n" + ] + } + ], + "source": [ + "dictionary_database = {}\n", + "for item in tqdm.tqdm(english_vocab_db):\n", + " if 'word' in item:\n", + " word = item['word']\n", + " i = 0\n", + " while word in dictionary_database:\n", + " i += 1\n", + " word = f\"{item['word']}{i}\"\n", + " dictionary_database[word] = item\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "class NoDataException(Exception):\n", + " pass\n", + "\n", + "def get_attribute(word:str, attr:str, db = dictionary_database):\n", + " if word not in db:\n", + " raise NoDataException\n", + " item = db[word]\n", + " if attr not in item:\n", + " raise NoDataException\n", + " return item[attr]\n", + "\n", + "def get_senses(word: str):\n", + " \n", + " senses = get_attribute(word, 'senses')\n", + " \n", + " return filter_senses(senses, word)\n", + "\n", + "def get_synonyms(word: str):\n", + " syns = get_attribute(word, 'synonyms')\n", + " \n", + " return filter_synonyms(syns)\n", + "\n", + "def get_antonyms(word: str):\n", + " ants = get_attribute(word, 'antonyms')\n", + " \n", + " return filter_antonyms(ants)\n", + "\n", + "def display_info(word: str):\n", + " \n", + " if word not in dictionary_database:\n", + " raise NoDataException\n", + " \n", + " try:\n", + " senses = get_senses(word)\n", + " except NoDataException:\n", + " senses = []\n", + " \n", + " try:\n", + " synonyms = get_synonyms(word)\n", + " except NoDataException:\n", + " synonyms = []\n", + " \n", + " try:\n", + " antonyms = get_antonyms(word)\n", + " except NoDataException:\n", + " antonyms = []\n", + " \n", + " senses_str = '\\n'.join([f\"* {sense}\" for sense in senses])\n", + " \n", + " synonyms_str = '\\n'.join([f\"* {syn}\" for syn in synonyms])\n", + " \n", + " antonyms_str = '\\n'.join([f\"* {ant}\" for ant in antonyms])\n", + " \n", + " markdown_string = f\"\"\"\n", + "## {word}\n", + "\n", + "### Senses:\n", + "\n", + "{senses_str}\n", + "\n", + "### Synonyms:\n", + "\n", + "{synonyms_str}\n", + "\n", + "### Antonyms:\n", + "\n", + "{antonyms_str}\n", + " \n", + " \"\"\"\n", + " \n", + " return Markdown(markdown_string)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 1023195/1023195 [00:06<00:00, 159434.15it/s]\n" + ] + } + ], + "source": [ + "import random\n", + "list_words = []\n", + "for word in tqdm.tqdm(dictionary_database.keys()):\n", + " item = dictionary_database[word]\n", + " try:\n", + " senses = get_senses(word)\n", + " if len(senses) > 1:\n", + " list_words.append(word)\n", + " \n", + " except NoDataException:\n", + " pass\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "\n", + "## cockmonger\n", + "\n", + "### Senses:\n", + "\n", + "* A frequent consumer of cocks\n", + "* A procurer of cocks\n", + "* A person with a large penis\n", + "\n", + "### Synonyms:\n", + "\n", + "\n", + "\n", + "### Antonyms:\n", + "\n", + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(display_info(\"cockmonger\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "----\n", + "## algorithm to build a grid of words" + ] + }, + { + "cell_type": "code", + "execution_count": 358, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import random\n", + "from string import digits\n", + "\n", + "class WordInfo(object):\n", + " def __init__(self, word:str, y:int, x:int, is_vertical: bool, database=dictionary_database):\n", + " self._dictionary_database = dictionary_database\n", + " self._y = y\n", + " self._x = x\n", + " self._word = word\n", + " self._hint = None\n", + " self._is_vertical = is_vertical\n", + " \n", + " self.choose_info()\n", + " \n", + " def get_best_antonym(self) -> str:\n", + " antonyms = get_antonyms(self._word)\n", + " return random.choice(antonyms)\n", + " \n", + " def get_best_synonym(self) -> str:\n", + " synonyms = get_synonyms(self._word)\n", + " return random.choice(synonyms)\n", + " \n", + " def get_best_sense(self) -> str:\n", + " senses = get_senses(self._word)\n", + " return random.choice(senses)\n", + " \n", + " def choose_info(self):\n", + " # first choose antonyms, then synonyms, then senses\n", + " \n", + " try:\n", + " self._hint = f\"opposite of {self.get_best_antonym()}\"\n", + " except NoDataException:\n", + " pass\n", + " \n", + " try:\n", + " self._hint = f\"other word for {self.get_best_synonym()}\"\n", + " except NoDataException:\n", + " pass\n", + " \n", + " self._hint = self.get_best_sense()\n", + " \n", + " def get_hint(self) -> str:\n", + " return self._hint\n", + " \n", + " def get_hint_location(self):\n", + " x = self._x if self._is_vertical else self._x - 1\n", + " y = self._y if self._is_vertical else self._y - 1\n", + " return (y, x)\n", + " \n", + " def is_vertical(self):\n", + " return self._is_vertical\n", + "\n", + "def create_word_grid(w: int, h: int, word_list = list_words, target_density = 0.5):\n", + " grid = np.full(shape=(h,w), dtype=np.unicode, fill_value = ' ')\n", + " \n", + " locations = {}\n", + " \n", + " word_hints = {}\n", + " \n", + " def store_location(char: str, y: int, x: int):\n", + " assert len(char) == 1\n", + " \n", + " if char not in locations:\n", + " locations[char] = []\n", + " \n", + " locations[char].append([y,x])\n", + " \n", + " remove_digits = str.maketrans('', '', digits)\n", + " n_words = len(list_words)\n", + " \n", + " def get_word(max_length: int, min_length = 0):\n", + " assert max_length > 1\n", + " \n", + " index = random.randint(0,n_words-1)\n", + " word = list_words[index][:]\n", + " \n", + " while len(word) >= max_length or not word.isalnum() or len(word) <= min_length:\n", + " index = random.randint(0,n_words-1)\n", + " word = list_words[index][:]\n", + " \n", + " return word\n", + " \n", + " def normalize_word(word:str):\n", + " word = word.translate(remove_digits)\n", + " return word.lower()\n", + " \n", + " def place_word(word:str, y: int, x:int, vertical:bool = False):\n", + " normalized_word = normalize_word(word)\n", + " n = len(normalized_word)\n", + " if vertical:\n", + " assert grid.shape[0] - n >= y\n", + " for i, char in enumerate(normalized_word):\n", + " grid[y + i,x] = char\n", + " store_location(char, y+i, x)\n", + " else:\n", + " assert grid.shape[1] - n >= x\n", + " for i, char in enumerate(normalized_word):\n", + " grid[y,x + i] = char\n", + " store_location(char, y, x+i)\n", + " \n", + " word_hints[normalized_word] = WordInfo(word, y, x, vertical)\n", + " \n", + " \n", + " \n", + " def density():\n", + " return 1 - (grid == \" \").sum() / (w * h)\n", + " \n", + " \n", + " \n", + " def check_if_fits(word:str, y:int, x:int, vertical:bool):\n", + " n = len(word)\n", + " if vertical:\n", + " \n", + " # check if there is space before and after\n", + " if y - 1 >= 0 and grid[y - 1, x] != \" \":\n", + " return False\n", + " if y + n < grid.shape[0] - 1 and grid[y+n,x] != \" \":\n", + " return False\n", + " \n", + " if grid.shape[0] - n < y or y < 0:\n", + " #print(\"over board\")\n", + " return False\n", + " \n", + " for i, char in enumerate(word):\n", + " char_x = x\n", + " char_y = y + i\n", + " \n", + " if not (grid[char_y, char_x] == \" \" or grid[char_y, char_x] == char):\n", + " #print(\"not matching\")\n", + " return False\n", + " \n", + " if grid[char_y, char_x] == \" \":\n", + " # check for horizonatal neighbors:\n", + " if char_x - 1 >= 0 and grid[char_y, char_x - 1] != \" \":\n", + " #print(\"3\")\n", + " return False\n", + " if char_x + 1 < grid.shape[1] and grid[char_y, char_x + 1] != \" \":\n", + " #print(\"4\")\n", + " return False\n", + " \n", + " else:\n", + " \n", + " # check if there is space before and after\n", + " if x - 1 >= 0 and grid[y, x - 1] != \" \":\n", + " return False\n", + " if x + n < grid.shape[1] - 1 and grid[y,x + n] != \" \":\n", + " return False\n", + " \n", + " if grid.shape[1] - n < x or x < 0:\n", + " #print(\"over board\")\n", + " return False\n", + " \n", + " for i, char in enumerate(word):\n", + " char_x = x + i\n", + " char_y = y\n", + " \n", + " if not (grid[char_y, char_x] == \" \" or grid[char_y, char_x] == char):\n", + " #print(\"not matching\")\n", + " return False\n", + " \n", + " if grid[char_y, char_x] == \" \":\n", + " # check for vertical neighbors:\n", + " if char_y - 1 >= 0 and grid[char_y - 1, char_x] != \" \":\n", + " #print(\"1\")\n", + " return False\n", + " if char_y + 1 < grid.shape[0] and grid[char_y + 1, char_x] != \" \":\n", + " #print(\"2\")\n", + " return False\n", + " \n", + " return True\n", + " \n", + " \n", + " def get_crossover(word: str):\n", + " # returns Tuple of: (y,x, is_vertical?) or None\n", + " \n", + " shuffled_order = list(range(len(word)))\n", + " random.shuffle(shuffled_order)\n", + " \n", + " for index in shuffled_order:\n", + " # check for existing locations\n", + " char = word[index]\n", + " if char in locations:\n", + " char_locations = locations[char]\n", + " \n", + " for char_loc in char_locations:\n", + " # test vertical\n", + " y = char_loc[0] - index\n", + " x = char_loc[1]\n", + " \n", + " if check_if_fits(word, y, x, vertical=True):\n", + " return (y,x,True)\n", + " \n", + " # test horizontal\n", + " y = char_loc[0]\n", + " x = char_loc[1] - index\n", + " \n", + " if check_if_fits(word, y, x, vertical=False):\n", + " return (y,x,False)\n", + " \n", + " return None\n", + " \n", + " min_shape = min(w,h,30)\n", + " \n", + " # place first word:\n", + " first_word = get_word(max_length=min_shape, min_length=min(10,grid.shape[1] - 2))\n", + " \n", + " # find random place:\n", + " x = random.randint(0, grid.shape[1] - len(first_word) - 1)\n", + " y = random.randint(0, grid.shape[0] - 1)\n", + " \n", + " place_word(first_word, y, x, vertical=False)\n", + " \n", + " i = 0\n", + " \n", + " \n", + " current_density = density()\n", + " \n", + " while current_density < target_density:\n", + " word = get_word(max_length=(1 - current_density ** 0.4) * min_shape,\n", + " min_length=max(min(10, 0.5 * (1 - current_density ** 0.3) * min_shape), 2))\n", + " \n", + " normalized_word = normalize_word(word)\n", + " \n", + " if normalized_word in word_hints:\n", + " continue\n", + " \n", + " # check if matching characters exist:\n", + " crossover = get_crossover(normalized_word)\n", + " \n", + " i += 1\n", + " if i % 100000 == 0:\n", + " print(i)\n", + " if i > 100000:\n", + " break\n", + " \n", + " if crossover == None:\n", + " current_density = density()\n", + " continue\n", + " \n", + " y,x,is_vertical = crossover\n", + " \n", + " place_word(word, y,x, is_vertical)\n", + " \n", + " current_density = density()\n", + " \n", + " print(i)\n", + " return grid, word_hints\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 359, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "968\n" + ] + }, + { + "data": { + "text/markdown": [ + "| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |\n", + "|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n", + "| | | | |**c**| |**a**|**e**|**r**|**o**|**p**|**h**|**o**|**b**|**i**|**c**| |**m**|**g**|**m**| |**f**|**a**|**d**|**d**|**y**| |**f**| | |**o**| |**c**| | |**g**|**r**|**a**|**y**| | | | |**q**|**u**|**a**|**y**| |\n", + "|**f**|**b**|**i**| |**l**| | | |**h**| | |**i**| |**f**| | | |**m**| | |**r**| |**c**| |**a**| | |**o**|**v**|**e**|**r**|**w**|**o**|**r**|**d**| |**p**| | |**t**| |**s**| |**u**| | | |**r**|\n", + "|**i**| | |**s**|**m**|**s**| | |**i**| | |**a**| | |**m**| | |**p**|**a**|**c**|**i**|**f**|**i**|**s**|**m**| | |**o**| |**x**| | |**m**| | |**p**|**a**|**i**|**g**|**e**| |**i**| |**a**| |**h**| |**e**|\n", + "|**g**|**l**|**e**|**e**| |**y**| |**c**|**a**|**r**|**o**|**t**|**e**|**n**|**o**|**l**| | | |**o**| | |**c**| | |**a**|**u**|**f**| |**p**|**o**|**p**|**p**|**l**|**e**| | |**m**| |**n**| |**l**| |**d**|**r**|**o**|**f**|**f**|\n", + "|**u**| | |**c**| |**m**| | |**n**| | |**t**| | |**r**| | |**d**|**o**|**g**|**g**|**i**|**l**|**y**| | | | |**s**| | | |**r**| | |**g**|**y**|**p**| |**t**| |**v**| |**r**| |**r**| |**a**|\n", + "|**r**| | |**o**|**r**|**p**|**h**|**a**|**n**|**r**|**y**| | | |**t**| |**e**| | |**n**| | |**e**| | | |**s**|**p**|**o**|**n**|**g**|**i**|**o**|**s**|**e**| | |**r**| | | |**e**| |**u**| |**s**| |**n**|\n", + "|**a**| | |**n**| |**t**| | |**o**| | | |**r**| |**a**|**f**|**r**|**o**|**d**|**i**|**t**|**e**| |**a**| |**a**| | |**l**| | | |**m**| | | |**c**|**o**|**l**| |**d**|**r**|**i**|**p**| |**e**| | |\n", + "|**l**| |**e**|**d**|**d**|**o**| | |**n**|**g**|**p**| |**y**| |**l**| |**v**| | |**z**| |**v**| |**m**| |**f**| | |**a**|**f**|**v**| |**i**| | | | |**p**| |**e**| |**i**| |**l**| |**l**| | |\n", + "| |**e**| |**e**| |**m**| |**s**| | | | |**n**| | |**n**| | |**w**|**a**|**g**|**e**|**n**|**b**|**o**|**o**|**m**| |**r**| |**a**|**r**|**s**|**e**|**w**|**i**|**p**|**e**| |**m**|**i**|**n**|**d**|**e**|**l**|**o**| |**u**|\n", + "|**u**|**l**|**l**|**r**| |**a**|**n**|**t**|**e**|**c**|**r**|**i**|**t**|**i**|**c**|**a**|**l**| | |**n**| |**n**| |**i**| |**u**| | |**y**| |**t**| |**e**| | |**m**| |**r**|**o**|**b**| |**g**| | | |**r**| |**n**|\n", + "| |**e**| | | |**t**| |**e**| |**r**| | | | | |**n**| |**p**| |**c**|**u**|**e**| |**t**| |**l**|**a**|**t**| |**m**| | | | | |**m**| | | |**r**| | |**t**| |**l**|**e**|**s**|**s**|\n", + "| |**c**|**u**|**r**|**r**|**i**|**e**|**r**| |**a**| |**o**|**x**|**a**|**z**|**o**|**l**|**i**|**n**|**e**| | | | |**w**| | | |**s**|**e**|**i**|**s**|**m**|**o**|**l**|**o**|**g**|**i**|**c**|**a**|**l**|**l**|**y**| | | | |**t**|\n", + "| |**t**| | | |**c**| |**c**| |**c**| | | | | |**m**| |**l**| | |**m**| |**s**| |**o**| | | |**h**| | |**c**| | | |**r**| | | |**i**| | |**p**| | | | |**i**|\n", + "|**c**|**r**|**a**|**m**|**p**| |**m**|**o**|**c**|**k**|**e**|**r**|**y**| |**a**|**a**|**a**|**e**| |**t**|**o**|**s**|**h**|**e**|**r**| |**r**|**h**|**a**|**p**|**s**|**o**|**d**|**i**|**s**|**t**|**i**|**c**| |**d**| |**p**|**a**|**p**|**p**|**y**| |**t**|\n", + "| |**e**| | | | | |**r**| |**i**| | | |**d**| |**c**| | | | |**g**| |**e**| |**m**| |**f**| | |**e**| | | | | |**a**| |**d**| | |**s**| |**l**| |**r**| |**o**|**c**|\n", + "| | |**d**|**m**| |**c**|**r**|**y**|**i**|**n**|**g**| |**p**|**u**|**s**|**h**|**b**|**a**|**l**|**l**| |**u**|**n**|**p**|**h**|**o**|**t**|**o**|**g**|**r**|**a**|**p**|**h**|**a**|**b**|**l**|**e**| |**m**| |**h**| | | |**i**| | |**h**|\n", + "| |**g**| |**u**| |**m**| | | |**g**| | | |**e**| |**i**| |**a**| | |**m**| |**a**| |**o**| | | | | |**a**| | |**l**| |**i**| | |**u**| |**e**|**s**|**k**|**i**|**m**|**o**| | |\n", + "| |**c**|**o**|**l**|**e**|**a**|**d**| | | | |**p**| | | |**n**| |**b**| | |**i**| |**n**| |**l**| |**u**|**n**|**v**|**i**|**s**|**u**|**a**|**l**|**i**|**z**|**a**|**b**|**l**|**e**| |**i**| | |**i**| | | |\n", + "| |**e**| |**t**| | | | | |**s**| |**l**|**e**|**t**|**t**|**e**|**r**| |**p**|**u**|**n**|**t**|**i**|**t**|**e**| | | | | | | |**d**| | |**e**| | |**t**| | |**m**| | |**g**| | | |\n", + "| | |**l**|**i**|**g**|**h**|**t**|**s**|**c**|**a**|**p**|**e**| | | |**r**| | | | | | |**g**| | | | |**c**|**t**|**c**| | |**s**|**i**|**e**| | |**l**|**a**|**m**|**b**|**a**|**s**|**t**|**e**|**d**| | |\n", + "| | | |**t**| | |**b**| | |**d**| |**a**| | |**p**|**y**|**r**|**a**|**z**|**o**|**l**|**e**|**a**|**m**|**i**|**d**|**e**| |**o**| |**f**| | | | | |**l**| |**n**| | | | | |**n**| | |**g**|\n", + "| | | |**h**| |**m**| |**b**| |**d**| |**d**| | |**a**| | | |**e**| | | |**n**| | |**r**| | |**i**| |**a**|**z**|**a**|**c**|**y**|**t**|**o**|**s**|**i**|**n**|**e**| | |**d**|**o**|**t**|**t**|**y**|\n", + "| | |**s**|**e**|**r**|**i**|**c**|**a**|**t**|**e**| |**i**| | |**l**| | | |**r**| |**n**| |**r**| | |**u**| | |**n**| |**w**| | | | | |**u**| |**m**| | | | | |**u**| | |**m**|\n", + "| |**j**| |**i**| |**n**| |**n**| |**n**| |**n**| |**d**|**e**|**p**|**o**|**s**|**i**|**t**|**a**|**r**|**y**| | |**m**|**o**|**r**|**g**|**e**|**n**| |**c**|**a**|**i**|**r**|**n**|**g**|**o**|**r**|**m**|**s**| | |**s**|**k**|**i**|**n**|\n", + "| |**o**| |**s**| |**y**| |**d**| | | |**g**| | | | |**u**| |**b**| |**i**| | |**r**| |**b**| |**a**| |**d**| |**v**| |**m**| | | | |**u**| | |**u**| |**r**| |**o**| |**i**|\n", + "| |**h**|**a**|**m**|**f**|**a**|**t**| |**c**|**y**|**c**|**l**|**o**|**p**|**a**|**e**|**d**|**i**|**a**| |**r**| |**c**|**o**|**l**|**l**|**a**|**r**| |**e**| |**h**|**e**|**a**|**d**|**h**|**o**|**u**|**s**|**e**| |**r**|**c**|**s**| |**k**| |**c**|\n", + "| |**n**| | | |**n**| | | | | |**y**| | | | | |**t**| |**t**|**a**|**u**| |**e**| |**e**| |**e**| |**n**| |**d**| |**r**| | | | | | | |**e**| |**o**| |**o**| | |\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " " + ] + }, + { + "data": { + "text/plain": [ + " 854104 function calls (854099 primitive calls) in 0.447 seconds\n", + "\n", + " Ordered by: internal time\n", + "\n", + " ncalls tottime percall cumtime percall filename:lineno(function)\n", + " 385008 0.289 0.000 0.306 0.000 :111(check_if_fits)\n", + " 968 0.072 0.000 0.383 0.000 :174(get_crossover)\n", + " 395346 0.018 0.000 0.018 0.000 {built-in method builtins.len}\n", + " 969 0.017 0.000 0.027 0.000 :106(density)\n", + " 969 0.008 0.000 0.008 0.000 {method 'reduce' of 'numpy.ufunc' objects}\n", + " 973 0.007 0.000 0.016 0.000 :72(get_word)\n", + " 1 0.005 0.005 0.444 0.444 :54(create_word_grid)\n", + " 9573 0.005 0.000 0.007 0.000 random.py:238(_randbelow_with_getrandbits)\n", + " 5549 0.003 0.000 0.007 0.000 random.py:291(randrange)\n", + " 968 0.002 0.000 0.005 0.000 random.py:349(shuffle)\n", + " 1123 0.002 0.000 0.002 0.000 {method 'sub' of 're.Pattern' objects}\n", + " 17858 0.002 0.000 0.002 0.000 {method 'getrandbits' of '_random.Random' objects}\n", + " 5549 0.002 0.000 0.009 0.000 random.py:335(randint)\n", + " 162 0.001 0.000 0.005 0.000 :1(filter_senses)\n", + " 162 0.001 0.000 0.010 0.000 :88(place_word)\n", + " 1134 0.001 0.000 0.001 0.000 {method 'translate' of 'str' objects}\n", + " 18 0.001 0.000 0.001 0.000 {method 'acquire' of '_thread.lock' objects}\n", + " 9573 0.001 0.000 0.001 0.000 {method 'bit_length' of 'int' objects}\n", + " 922 0.001 0.000 0.001 0.000 :61(store_location)\n", + " 969 0.001 0.000 0.009 0.000 {method 'sum' of 'numpy.ndarray' objects}\n", + " 1134 0.001 0.000 0.002 0.000 :84(normalize_word)\n", + " 974 0.001 0.000 0.001 0.000 {built-in method builtins.min}\n", + " 1092 0.001 0.000 0.001 0.000 re.py:289(_compile)\n", + " 27 0.001 0.000 0.001 0.000 :9()\n", + " 1092 0.001 0.000 0.003 0.000 re.py:203(sub)\n", + " 162 0.000 0.000 0.007 0.000 :28(choose_info)\n", + " 973 0.000 0.000 0.000 0.000 {built-in method builtins.max}\n", + " 486 0.000 0.000 0.000 0.000 :4(get_attribute)\n", + " 969 0.000 0.000 0.009 0.000 _methods.py:45(_sum)\n", + " 1 0.000 0.000 0.447 0.447 {built-in method builtins.exec}\n", + " 1229 0.000 0.000 0.000 0.000 {built-in method builtins.isinstance}\n", + " 162 0.000 0.000 0.008 0.000 :6(__init__)\n", + " 162 0.000 0.000 0.001 0.000 :16(get_best_antonym)\n", + " 1083 0.000 0.000 0.000 0.000 {method 'isalnum' of 'str' objects}\n", + " 162 0.000 0.000 0.006 0.000 :24(get_best_sense)\n", + " 1134 0.000 0.000 0.000 0.000 {method 'lower' of 'str' objects}\n", + " 1542 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects}\n", + " 548 0.000 0.000 0.000 0.000 {method 'replace' of 'str' objects}\n", + " 162 0.000 0.000 0.006 0.000 :12(get_senses)\n", + " 424 0.000 0.000 0.000 0.000 {method 'split' of 'str' objects}\n", + " 188 0.000 0.000 0.000 0.000 random.py:344(choice)\n", + " 162 0.000 0.000 0.000 0.000 :23(get_antonyms)\n", + " 546 0.000 0.000 0.000 0.000 {method 'strip' of 'str' objects}\n", + " 162 0.000 0.000 0.000 0.000 :20(get_best_synonym)\n", + " 162 0.000 0.000 0.000 0.000 :18(get_synonyms)\n", + " 12 0.000 0.000 0.000 0.000 inspect.py:2926(_bind)\n", + " 8 0.000 0.000 0.000 0.000 socket.py:438(send)\n", + " 155 0.000 0.000 0.000 0.000 traitlets.py:564(__get__)\n", + " 1 0.000 0.000 0.446 0.446 :1()\n", + " 11 0.000 0.000 0.000 0.000 formatters.py:397(lookup_by_type)\n", + " 1 0.000 0.000 0.001 0.001 :2(grid2mdown)\n", + " 155 0.000 0.000 0.000 0.000 traitlets.py:533(get)\n", + " 4 0.000 0.000 0.000 0.000 encoder.py:304(iterencode)\n", + " 6/1 0.000 0.000 0.000 0.000 jsonutil.py:73(json_clean)\n", + " 21 0.000 0.000 0.000 0.000 :1(filter_synonyms)\n", + " 5 0.000 0.000 0.000 0.000 {method 'update' of '_hashlib.HASH' objects}\n", + " 44 0.000 0.000 0.000 0.000 formatters.py:550(_in_deferred_types)\n", + " 1 0.000 0.000 0.001 0.001 formatters.py:89(format)\n", + " 12 0.000 0.000 0.000 0.000 inspect.py:2665(args)\n", + " 12 0.000 0.000 0.000 0.000 inspect.py:2718(apply_defaults)\n", + " 12 0.000 0.000 0.000 0.000 decorator.py:199(fix)\n", + " 8 0.000 0.000 0.000 0.000 iostream.py:195(schedule)\n", + " 1 0.000 0.000 0.000 0.000 {built-in method numpy.core._multiarray_umath.implement_array_function}\n", + " 129 0.000 0.000 0.000 0.000 {built-in method builtins.getattr}\n", + " 1 0.000 0.000 0.000 0.000 {built-in method numpy.empty}\n", + " 1 0.000 0.000 0.002 0.002 display.py:131(display)\n", + " 11 0.000 0.000 0.000 0.000 formatters.py:374(lookup)\n", + " 9 0.000 0.000 0.000 0.000 formatters.py:331(__call__)\n", + " 31 0.000 0.000 0.000 0.000 encoder.py:38(encode_basestring)\n", + " 12 0.000 0.000 0.001 0.000 decorator.py:229(fun)\n", + " 12 0.000 0.000 0.000 0.000 inspect.py:2688(kwargs)\n", + " 12 0.000 0.000 0.000 0.000 formatters.py:220(catch_format_error)\n", + " 1 0.000 0.000 0.001 0.001 zmqshell.py:97(publish)\n", + " 2 0.000 0.000 0.001 0.000 iostream.py:335(flush)\n", + " 1 0.000 0.000 0.000 0.000 {built-in method posix.stat}\n", + " 33 0.000 0.000 0.000 0.000 {method 'join' of 'str' objects}\n", + " 11 0.000 0.000 0.000 0.000 dir2.py:54(get_real_method)\n", + " 74 0.000 0.000 0.000 0.000 {built-in method builtins.next}\n", + " 1 0.000 0.000 0.000 0.000 pretty.py:356(pretty)\n", + " 134 0.000 0.000 0.000 0.000 inspect.py:2577(kind)\n", + " 4 0.000 0.000 0.000 0.000 __init__.py:294(dumps)\n", + " 10 0.000 0.000 0.000 0.000 threading.py:1093(is_alive)\n", + " 2 0.000 0.000 0.000 0.000 iostream.py:384(write)\n", + " 1 0.000 0.000 0.000 0.000 session.py:662(send)\n", + " 1 0.000 0.000 0.000 0.000 formatters.py:689(__call__)\n", + " 12 0.000 0.000 0.000 0.000 {method 'format' of 'str' objects}\n", + " 1 0.000 0.000 0.000 0.000 numeric.py:268(full)\n", + " 2 0.000 0.000 0.000 0.000 {method 'isoformat' of 'datetime.datetime' objects}\n", + " 4 0.000 0.000 0.000 0.000 jsonapi.py:32(dumps)\n", + " 2 0.000 0.000 0.001 0.000 threading.py:280(wait)\n", + " 2 0.000 0.000 0.000 0.000 threading.py:228(__init__)\n", + " 1 0.000 0.000 0.000 0.000 session.py:603(serialize)\n", + " 1 0.000 0.000 0.001 0.001 display.py:81(publish_display_data)\n", + " 4 0.000 0.000 0.000 0.000 encoder.py:275(encode)\n", + " 1 0.000 0.000 0.000 0.000 pretty.py:697(_repr_pprint)\n", + " 29 0.000 0.000 0.000 0.000 encoder.py:60(replace)\n", + " 1 0.000 0.000 0.000 0.000 <__array_function__ internals>:2(copyto)\n", + " 1 0.000 0.000 0.000 0.000 hmac.py:115(copy)\n", + " 1 0.000 0.000 0.000 0.000 pretty.py:185(__init__)\n", + " 1 0.000 0.000 0.000 0.000 display.py:591(__init__)\n", + " 10 0.000 0.000 0.000 0.000 threading.py:1039(_wait_for_tstate_lock)\n", + " 12 0.000 0.000 0.000 0.000 inspect.py:3057(bind)\n", + " 4 0.000 0.000 0.000 0.000 encoder.py:141(__init__)\n", + " 5 0.000 0.000 0.000 0.000 :1(filter_antonyms)\n", + " 48 0.000 0.000 0.000 0.000 inspect.py:2882(parameters)\n", + " 12 0.000 0.000 0.000 0.000 abc.py:96(__instancecheck__)\n", + " 12 0.000 0.000 0.000 0.000 {built-in method _abc._abc_instancecheck}\n", + " 8 0.000 0.000 0.000 0.000 iostream.py:91(_event_pipe)\n", + " 22 0.000 0.000 0.000 0.000 {built-in method builtins.hasattr}\n", + " 2 0.000 0.000 0.001 0.000 threading.py:556(wait)\n", + " 1 0.000 0.000 0.000 0.000 {built-in method builtins.print}\n", + " 1 0.000 0.000 0.000 0.000 session.py:588(sign)\n", + " 1 0.000 0.000 0.001 0.001 zmqshell.py:80(_flush_streams)\n", + " 51 0.000 0.000 0.000 0.000 inspect.py:2565(name)\n", + " 36 0.000 0.000 0.000 0.000 {method 'items' of 'mappingproxy' objects}\n", + " 12 0.000 0.000 0.000 0.000 pretty.py:305(_get_mro)\n", + " 12 0.000 0.000 0.000 0.000 inspect.py:2657(__init__)\n", + " 11 0.000 0.000 0.000 0.000 formatters.py:274(_get_type)\n", + " 1 0.000 0.000 0.000 0.000 formatters.py:903(__call__)\n", + " 2 0.000 0.000 0.000 0.000 threading.py:521(__init__)\n", + " 24 0.000 0.000 0.000 0.000 {built-in method builtins.iter}\n", + " 1 0.000 0.000 0.000 0.000 session.py:149(utcnow)\n", + " 3 0.000 0.000 0.000 0.000 pretty.py:479(enq)\n", + " 12 0.000 0.000 0.000 0.000 {method 'values' of 'mappingproxy' objects}\n", + " 4 0.000 0.000 0.000 0.000 session.py:82()\n", + " 2 0.000 0.000 0.000 0.000 jsonutil.py:84(date_default)\n", + " 1 0.000 0.000 0.000 0.000 session.py:569(msg)\n", + " 1 0.000 0.000 0.000 0.000 contextlib.py:86(__init__)\n", + " 2 0.000 0.000 0.000 0.000 threading.py:259(__exit__)\n", + " 2 0.000 0.000 0.000 0.000 iostream.py:308(_is_master_process)\n", + " 2 0.000 0.000 0.000 0.000 pretty.py:264(begin_group)\n", + " 2 0.000 0.000 0.000 0.000 pretty.py:288(end_group)\n", + " 11 0.000 0.000 0.000 0.000 inspect.py:73(isclass)\n", + " 4 0.000 0.000 0.000 0.000 hmac.py:111(update)\n", + " 1 0.000 0.000 0.000 0.000 genericpath.py:16(exists)\n", + " 29 0.000 0.000 0.000 0.000 {method 'group' of 're.Match' objects}\n", + " 1 0.000 0.000 0.000 0.000 pretty.py:216(text)\n", + " 3 0.000 0.000 0.000 0.000 {method 'copy' of '_hashlib.HASH' objects}\n", + " 11 0.000 0.000 0.000 0.000 formatters.py:359(_check_return)\n", + " 1 0.000 0.000 0.000 0.000 {method 'replace' of 'datetime.datetime' objects}\n", + " 1 0.000 0.000 0.000 0.000 formatters.py:949(__call__)\n", + " 1 0.000 0.000 0.000 0.000 pretty.py:339(__init__)\n", + " 3 0.000 0.000 0.000 0.000 pretty.py:466(__init__)\n", + " 12 0.000 0.000 0.000 0.000 {built-in method builtins.id}\n", + " 1 0.000 0.000 0.000 0.000 {built-in method maketrans}\n", + " 3 0.000 0.000 0.000 0.000 pretty.py:401(_in_deferred_types)\n", + " 1 0.000 0.000 0.000 0.000 display.py:34(_safe_exists)\n", + " 1 0.000 0.000 0.000 0.000 session.py:566(msg_header)\n", + " 1 0.000 0.000 0.000 0.000 zmqshell.py:90(_hooks)\n", + " 10 0.000 0.000 0.000 0.000 threading.py:529(is_set)\n", + " 1 0.000 0.000 0.000 0.000 hmac.py:128(_current)\n", + " 2 0.000 0.000 0.000 0.000 threading.py:256(__enter__)\n", + " 1 0.000 0.000 0.000 0.000 pretty.py:297(flush)\n", + " 1 0.000 0.000 0.000 0.000 contextlib.py:242(helper)\n", + " 1 0.000 0.000 0.000 0.000 iostream.py:260(send_multipart)\n", + " 1 0.000 0.000 0.000 0.000 configurable.py:551(initialized)\n", + " 5 0.000 0.000 0.000 0.000 {method 'encode' of 'str' objects}\n", + " 1 0.000 0.000 0.000 0.000 display.py:640(__repr__)\n", + " 2 0.000 0.000 0.000 0.000 threading.py:268(_acquire_restore)\n", + " 1 0.000 0.000 0.000 0.000 formatters.py:940(_check_return)\n", + " 2 0.000 0.000 0.000 0.000 pretty.py:168(group)\n", + " 14 0.000 0.000 0.000 0.000 {built-in method builtins.callable}\n", + " 1 0.000 0.000 0.000 0.000 session.py:224(extract_header)\n", + " 1 0.000 0.000 0.000 0.000 displaypub.py:43(_validate_data)\n", + " 1 0.000 0.000 0.000 0.000 session.py:513(msg_id)\n", + " 7 0.000 0.000 0.000 0.000 pretty.py:102(_safe_getattr)\n", + " 3 0.000 0.000 0.000 0.000 {built-in method posix.getpid}\n", + " 2 0.000 0.000 0.000 0.000 configurable.py:507(instance)\n", + " 1 0.000 0.000 0.000 0.000 session.py:744()\n", + " 2 0.000 0.000 0.000 0.000 iostream.py:321(_schedule_flush)\n", + " 2 0.000 0.000 0.000 0.000 pretty.py:496(remove)\n", + " 2 0.000 0.000 0.000 0.000 threading.py:265(_release_save)\n", + " 10 0.000 0.000 0.000 0.000 {method 'append' of 'collections.deque' objects}\n", + " 1 0.000 0.000 0.000 0.000 hmac.py:147(hexdigest)\n", + " 1 0.000 0.000 0.000 0.000 session.py:218(msg_header)\n", + " 1 0.000 0.000 0.000 0.000 {built-in method builtins.repr}\n", + " 1 0.000 0.000 0.000 0.000 contextlib.py:121(__exit__)\n", + " 1 0.000 0.000 0.000 0.000 pretty.py:474(__init__)\n", + " 1 0.000 0.000 0.000 0.000 contextlib.py:112(__enter__)\n", + " 4 0.000 0.000 0.000 0.000 {built-in method _thread.allocate_lock}\n", + " 5 0.000 0.000 0.000 0.000 {method 'items' of 'dict' objects}\n", + " 1 0.000 0.000 0.000 0.000 {method 'digest' of '_hashlib.HASH' objects}\n", + " 1 0.000 0.000 0.000 0.000 display.py:741(_repr_markdown_)\n", + " 4 0.000 0.000 0.000 0.000 {method 'clear' of 'dict' objects}\n", + " 2 0.000 0.000 0.000 0.000 jsonutil.py:31(_ensure_tzinfo)\n", + " 1 0.000 0.000 0.000 0.000 {built-in method utcnow}\n", + " 3 0.000 0.000 0.000 0.000 {method 'get' of 'mappingproxy' objects}\n", + " 2 0.000 0.000 0.000 0.000 threading.py:271(_is_owned)\n", + " 1 0.000 0.000 0.000 0.000 display.py:659(reload)\n", + " 1 0.000 0.000 0.000 0.000 multiarray.py:1043(copyto)\n", + " 1 0.000 0.000 0.000 0.000 iostream.py:207(send_multipart)\n", + " 1 0.000 0.000 0.000 0.000 {built-in method builtins.locals}\n", + " 2 0.000 0.000 0.000 0.000 {method 'remove' of 'list' objects}\n", + " 1 0.000 0.000 0.000 0.000 {method 'hexdigest' of '_hashlib.HASH' objects}\n", + " 3 0.000 0.000 0.000 0.000 {method 'pop' of 'list' objects}\n", + " 1 0.000 0.000 0.000 0.000 display.py:652(_data_and_metadata)\n", + " 1 0.000 0.000 0.000 0.000 {method 'splitlines' of 'str' objects}\n", + " 3 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects}\n", + " 4 0.000 0.000 0.000 0.000 {method 'pop' of 'dict' objects}\n", + " 1 0.000 0.000 0.000 0.000 display.py:698(_check_data)\n", + " 2 0.000 0.000 0.000 0.000 {built-in method _imp.lock_held}\n", + " 2 0.000 0.000 0.000 0.000 {method '__enter__' of '_thread.lock' objects}\n", + " 1 0.000 0.000 0.000 0.000 {built-in method __new__ of type object at 0x90efa0}\n", + " 2 0.000 0.000 0.000 0.000 {method 'release' of '_thread.lock' objects}\n", + " 1 0.000 0.000 0.000 0.000 {method 'getvalue' of '_io.StringIO' objects}\n", + " 2 0.000 0.000 0.000 0.000 {method '__exit__' of '_thread.lock' objects}\n", + " 1 0.000 0.000 0.000 0.000 {method 'startswith' of 'str' objects}\n", + " 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}\n", + " 1 0.000 0.000 0.000 0.000 {method 'write' of '_io.StringIO' objects}\n", + " 2 0.000 0.000 0.000 0.000 {method 'extend' of 'list' objects}\n", + " 1 0.000 0.000 0.000 0.000 {method 'clear' of 'collections.deque' objects}\n", + " 1 0.000 0.000 0.000 0.000 jsonutil.py:46(encode_images)\n", + " 1 0.000 0.000 0.000 0.000 formatters.py:824(_check_return)\n", + " 1 0.000 0.000 0.000 0.000 tz.py:74(utcoffset)\n", + " 1 0.000 0.000 0.000 0.000 {method 'copy' of 'dict' objects}" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%%prun\n", + "gr, infos = create_word_grid(16*3,9*3, target_density=0.55)\n", + "def grid2mdown(grid):\n", + " h,w = grid.shape\n", + " \n", + " md_str = \" \".join([\"|\"] * (w+1)) + \"\\n\"\n", + " md_str += \":---:\".join([\"|\"] * (w+1)) + \"\\n\"\n", + " \n", + " for y in range(h):\n", + " md_str += \"|\" + \"|\".join([(f\"**{grid[y,x]}**\" if grid[y,x] != \" \" else \" \") for x in range(w)]) + \"|\\n\"\n", + " \n", + " return Markdown(md_str)\n", + "\n", + "display(grid2mdown(gr))\n", + " \n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 279, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "edie -- surname\n" + ] + } + ], + "source": [ + "i=220\n", + "\n", + "word = list(infos.keys())[i]\n", + "\n", + "print(word, \"--\", infos[word].get_hint())" + ] + }, + { + "cell_type": "code", + "execution_count": 360, + "metadata": {}, + "outputs": [ + { + "data": { + "text/markdown": [ + "\n", + "## wagenboom\n", + "\n", + "### Senses:\n", + "\n", + "* A South African proteaceous tree\n", + "* The tough wood of this tree, used for making wagon wheels\n", + "\n", + "### Synonyms:\n", + "\n", + "* waboom\n", + "* wagon tree\n", + "\n", + "### Antonyms:\n", + "\n", + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(display_info(\"wagenboom\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 374, + "metadata": {}, + "outputs": [], + "source": [ + "from enum import Enum\n", + "\n", + "class HintOrientation(Enum):\n", + " VERTICAL = 0\n", + " HORIZONTAL = 1\n", + " BOTH = 2\n", + " \n", + "class FieldType(Enum):\n", + " EMPTY = 0\n", + " HINT = 1\n", + " LETTER = 2\n", + "\n", + "class Field(object):\n", + " def __init__(self, field_type: FieldType = FieldType.EMPTY):\n", + " self._field_type = field_type\n", + " \n", + " def get_type(self) -> FieldType:\n", + " return self._field_type\n", + " \n", + " def get_content(self) -> str:\n", + " return None\n", + "\n", + "class HintField(Field):\n", + " def __init__(self, horizontal_hint: str = None, vertical_hint: str = None):\n", + " super().__init__(field_type=FieldType.HINT)\n", + " \n", + " self._horizontal_hint = horizontal_hint\n", + " self._vertical_hint = vertical_hint\n", + " \n", + " def get_horizontal_hint(self) -> str:\n", + " return self._horizontal_hint\n", + " \n", + " def get_vertical_hint(self) -> str:\n", + " return self._vertical_hint\n", + " \n", + " def set_horizintal_hint(hint: str):\n", + " self._horizontal_hint = hint\n", + " \n", + " def set_vertical_hint(hint:str):\n", + " self._vertical_hint = hint\n", + "\n", + "class LetterField(Field):\n", + " def __init__(self, letter: str):\n", + " assert len(letter) <= 1\n", + " \n", + " super().__init__(field_type = FieldType.LETTER)\n", + " \n", + " self._letter = letter\n", + " self._revealed = False\n", + " \n", + " def get_content(self) -> str:\n", + " return self._letter\n", + " \n", + " def reveal(self):\n", + " self._revealed = True\n", + " \n", + " def is_revealed(self) -> bool:\n", + " return self._revealed\n", + "\n", + "class Grid(object):\n", + " def __init__(self, width: int, height: int, density = 0.5):\n", + " self._width = width\n", + " self._height = height\n", + " self._density = density\n", + " self._grid = []\n", + " self._build_grid()\n", + " \n", + " def _build_grid(self):\n", + " raw_grid, word_infos = create_word_grid(self._width - 1, self._height - 1, target_density=self._density)\n", + " \n", + " # note: we will append an additional row and column, to have enough space to place hint fields\n", + " \n", + " self._grid = [[Field()] * self._width] # initialize with empty row\n", + " \n", + " for y in range(self._height):\n", + " row = [Field()] # initialize row with empty column\n", + " for x in range(self._width):\n", + " raw_cell = raw_grid[y-1,x-1]\n", + " if raw_cell == \" \":\n", + " row.append(Field())\n", + " else:\n", + " row.append(LetterField(raw_cell))\n", + " \n", + " self._grid.append(row)\n", + " \n", + " # place hint fields:\n", + " for word, info in word_infos.items():\n", + " y,x = info.get_hint_location()\n", + " # correct offset\n", + " y += 1\n", + " x += 1\n", + " \n", + " cell = self._grid[y][x]\n", + " \n", + " # check if we already have a hint here:\n", + " if cell.get_type() == FieldType.HINT:\n", + " if info.is_vertical():\n", + " cell.set_vertical_hint(info.get_hint())\n", + " else:\n", + " cell.set_horizintal_hint(info.get_hint())\n", + " elif cell.get_type() == FieldType.LETTER:\n", + " # edge case: a word has \"eaten up\" another one, skipping that case\n", + " pass\n", + " \n", + " else:\n", + " if info.is_vertical():\n", + " self._grid[y][x] = HintField(vertical_hint=info.get_hint())\n", + " else:\n", + " self._grid[y][x] = HintField(horizontal_hint=info.get_hint())\n", + "\n", + " \n", + "class Crossword(object):\n", + " def __init__(self, width: int, height: int, database: dict):\n", + " self._width = width\n", + " self._height = height\n", + " self._database = database\n", + " self._grid = Grid(width, height)\n", + " \n", + " \n", + " \n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 375, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "234\n" + ] + } + ], + "source": [ + "cw = Crossword(16*3, 9*3, english_vocab_db)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/data/english.json b/data/english.json new file mode 100644 index 0000000..c2e05f7 --- /dev/null +++ b/data/english.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d43a3e3ae7fe526647b03357906af4507f0063712904c19a30969340442e111e +size 867763465 diff --git a/data/english.words b/data/english.words new file mode 100644 index 0000000..d357123 --- /dev/null +++ b/data/english.words @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9611e86ff7cc0b1af95cf08b174be46b6e101dff9cd41525f898c34345c4ab95 +size 561150044 diff --git a/data/german.json b/data/german.json new file mode 100644 index 0000000..88ecc13 --- /dev/null +++ b/data/german.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:121e4732f46356420fba69048e7e844b3e11202d6b078b0116cc8471660ef252 +size 218895204 diff --git a/data/german.words b/data/german.words new file mode 100644 index 0000000..e4f537e --- /dev/null +++ b/data/german.words @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5106240b799dffafbcabb45ddb94dca4d3b49eb2d5df9086b7ed7285e18b3892 +size 125528056