diff --git a/js_client/english.html b/js_client/english.html new file mode 100644 index 0000000..805ef92 --- /dev/null +++ b/js_client/english.html @@ -0,0 +1,57 @@ + + + + + + + + + + // Original viewport definition, note the "id" that I use to modify the viewport later on: + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/js_client/german.html b/js_client/german.html new file mode 100644 index 0000000..d035c95 --- /dev/null +++ b/js_client/german.html @@ -0,0 +1,57 @@ + + + + + + + + + + // Original viewport definition, note the "id" that I use to modify the viewport later on: + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/js_client/grid.js b/js_client/grid.js index dd24986..4381d33 100644 --- a/js_client/grid.js +++ b/js_client/grid.js @@ -116,7 +116,13 @@ customElements.define('grid-box', GridBox); export class GridLetter extends GridBox { static get styles() { return css` + .letter { + display: grid; + } + input[type='text'] { + grid-column: 1; + grid-row: 1; width: 1.5em; height: 1.5em; text-align: center; @@ -131,18 +137,47 @@ export class GridLetter extends GridBox { } input[type='text']:focus { + grid-column: 1; + grid-row: 1; background-color: LightYellow; border-color: Yellow; } input[type='text']:read-only { + grid-column: 1; + grid-row: 1; background-color: LightGreen; } input[type='text']:selection { + grid-column: 1; + grid-row: 1; background: transparent; } + + .circle { + margin: auto; + text-align: left; + vertical-align: bottom; + bottom: 0; + position: relative; + z-index: 0; + pointer-events: none; + grid-column: 1; + grid-row: 1; + border: 0.1em solid lightGray; + color: black; + height: 80%; + width: 80%; + border-radius:50%; + -moz-border-radius:50%; + -webkit-border-radius:50%; + background: transparent; + + } + + `; } @@ -159,7 +194,10 @@ export class GridLetter extends GridBox { super(); this._value = ''; this.is_focused = false; - this.key_delay = false; + this.firstKeydownOver = true; + this.solutionIndex = -1; + this.lastFocusedX = -1; + this.lastFocusedY = -1; this.updateGridElement(); } @@ -179,7 +217,9 @@ export class GridLetter extends GridBox { onInput(e) { + if (!this.is_focused) { + e.handled = true; return; } @@ -213,7 +253,7 @@ export class GridLetter extends GridBox { } if (!e.handled && this.value.length === 0) { - this.crosswordGrid.focusPrevCell(this.x, this.y); + this.crosswordGrid.focusPrevCell(this.x, this.y, this.lastFocusedX, this.lastFocusedY); e.handled = true; } else if (!e.handled && this.value.length === 1) { @@ -233,7 +273,7 @@ export class GridLetter extends GridBox { }) } - if (!e.handled){ + if (!e.handled) { e.target.select(); @@ -242,28 +282,43 @@ export class GridLetter extends GridBox { } - onKeydown(e) { - - if (!this.is_focused) { + onKeyup(e) { + if (!this.firstKeydownOver) { + e.handled = true; + return; + } + + var key = e.key; + if (this.value.length == 0 || this.revealed) { + if (key === 'Backspace' || key === 'Delete') { + this.crosswordGrid.focusPrevCell(this.x, this.y, this.lastFocusedX, this.lastFocus); + e.handled = true; + return + + } + else if (this.revealed) { + this.crosswordGrid.focusNextCell(this.x, this.y); + e.handled = true; + return + } + } + } + + onKeydown(e) { + + this.firstKeydownOver = true; + + if (!this.is_focused) { + e.handled = true; return; } - this.key_delay = false; var key = e.key; console.log(key); - if (this.value.length == 0) { - if (key === 'Backspace' || key === 'Delete') { - this.crosswordGrid.focusPrevCell(this.x, this.y); - e.handled = true; - return - - } - } - // check arrow keys if (key === "ArrowLeft") { // left @@ -301,14 +356,20 @@ export class GridLetter extends GridBox { onFocus(e) { e.target.select(); this.is_focused = true; - this.key_delay = true; this.crosswordGrid.updateHints(this.x, this.y); } - focus() { + focus(lastX = -1, lastY = -1) { + if (lastX > 0 && lastY > 0) { + this.lastFocusedX = lastX; + this.lastFocusedY = lastY; + } var element = this.shadowRoot.getElementById(`${this.x}-${this.y}`); + this.firstKeydownOver = false; element.focus(); + + } blur() { @@ -323,13 +384,13 @@ export class GridLetter extends GridBox { if (this.grid_id != null) { this.gridElement.setGridLetter(this); + this.solutionIndex = this.crosswordGrid.getSolutionIndex(this.x, this.y) } } updateLetter(letter, revealed) { this.value = letter; - if (revealed) - { + if (revealed) { console.log("rec") this.shadowRoot.getElementById(`${this.x}-${this.y}`).readOnly = true; this.blur(); @@ -339,7 +400,25 @@ export class GridLetter extends GridBox { render() { this.updateGridElement(); - return html``; + + var solutionOverlay = html``; + + if (this.solutionIndex >= 0) { + solutionOverlay = html` +
${this.solutionIndex}
+ `; + } + + + + return html` +
+ + ${solutionOverlay} +
+ + `; + } } @@ -508,7 +587,7 @@ export class CrosswordGrid extends LitElement { id: { type: String }, width: { type: Number }, height: { type: Number }, - infobox_id: {type: String} + infobox_id: { type: String } } } @@ -527,6 +606,18 @@ export class CrosswordGrid extends LitElement { this.infobox = null; this.serverConnection = null; + + this.solution_locations = null; + } + + getSolutionIndex(x, y) { + var i = 0; + for (i = 0; i < this.solution_locations.length; i++) { + if (this.solution_locations[i][0] == y && this.solution_locations[i][1] == x) { + return i; + } + } + return -1; } createGridByJson(json_grid) { @@ -536,6 +627,8 @@ export class CrosswordGrid extends LitElement { const width = json_grid.w; const height = json_grid.h; + this.solution_locations = json_grid.solution + var y; var x; @@ -573,7 +666,7 @@ export class CrosswordGrid extends LitElement { if (this.grid[y][x].getGridType() === gridType.LETTER) { this.grid[y][x].getGridLetter().blur(); } - this.grid[y][x + 1].getGridLetter().focus(); + this.grid[y][x + 1].getGridLetter().focus(x, y); return true; @@ -601,7 +694,7 @@ export class CrosswordGrid extends LitElement { if (this.grid[y][x].getGridType() === gridType.LETTER) { this.grid[y][x].getGridLetter().blur(); } - this.grid[y + 1][x].getGridLetter().focus(); + this.grid[y + 1][x].getGridLetter().focus(x, y); return true; } @@ -642,7 +735,11 @@ export class CrosswordGrid extends LitElement { } } - focusPrevCell(x, y) { + focusPrevCell(x, y, lastX = -1, lastY = -1) { + if (lastX > 0 && lastY > 0) { + this.grid[lastY][lastX].getGridLetter().focus(); + return; + } if (this.lastMoveVertical) { if (this.focusPrevCellVertical(x, y)) { return; @@ -675,9 +772,9 @@ export class CrosswordGrid extends LitElement { super.update() } - updateHints(x,y) { + updateHints(x, y) { var cell = this.grid[y][x]; - if (cell.getGridType() != gridType.LETTER){ + if (cell.getGridType() != gridType.LETTER) { console.error("cannot get hints for non letter cell"); } @@ -697,15 +794,14 @@ export class CrosswordGrid extends LitElement { if (left_end.getGridType() === gridType.HINT) { var box = left_end.getHintBox(); - if (box.hasHorizontalHint()){ + if (box.hasHorizontalHint()) { hHint = `${box.hint_id}: ${box.horizontal_hint}`; } } if (upper_end.getGridType() === gridType.HINT) { var box = upper_end.getHintBox() - if (box.hasVerticalHint()) - { + if (box.hasVerticalHint()) { vHint = `${box.hint_id}: ${box.vertical_hint}`; } } @@ -728,10 +824,10 @@ export class CrosswordGrid extends LitElement { var vertical_hint = this.json_grid.grid[el.y][el.x].vertical_hint; var horizontal_hint = this.json_grid.grid[el.y][el.x].horizontal_hint; - if (!vertical_hint){ + if (!vertical_hint) { vertical_hint = ""; } - if (!horizontal_hint){ + if (!horizontal_hint) { horizontal_hint = ""; } @@ -745,19 +841,19 @@ export class CrosswordGrid extends LitElement { } } - updateLetter(x,y,letter,revealed) { + updateLetter(x, y, letter, revealed) { var cell = this.grid[y][x]; - if (cell.getGridType() != gridType.LETTER){ + if (cell.getGridType() != gridType.LETTER) { console.error("received bad message"); } cell.getGridLetter().updateLetter(letter, revealed) } - registerServerConnection(connection){ + registerServerConnection(connection) { this.serverConnection = connection } - sendMessage(msg){ + sendMessage(msg) { if (!this.serverConnection) { console.error("no server connection registered") } diff --git a/js_client/index.html b/js_client/index.html index 06ad975..1b95484 100644 --- a/js_client/index.html +++ b/js_client/index.html @@ -10,47 +10,71 @@ - // Original viewport definition, note the "id" that I use to modify the viewport later on: - - - - + - - - - + +
+

Select the language:

+
German
+
English
+
diff --git a/js_client/infoBox.js b/js_client/infoBox.js index 4710b5b..b314f93 100644 --- a/js_client/infoBox.js +++ b/js_client/infoBox.js @@ -79,13 +79,13 @@ export class InfoBox extends LitElement { this.horizontal_hint = ""; } - onNew(){ - setCookie("session",""); + onNew() { + setCookie("session", ""); window.location.href = location.protocol + '//' + location.host + location.pathname; } - onShare(){ + onShare() { copyToClipboard(window.location.href); } diff --git a/js_client/serverConnection.js b/js_client/serverConnection.js index af8a8a9..2ea7e73 100644 --- a/js_client/serverConnection.js +++ b/js_client/serverConnection.js @@ -10,7 +10,8 @@ export class ServerConnection extends WebsocketConnection { static get properties() { return { url: { type: String }, - grid_id: { type: String } + grid_id: { type: String }, + lang: { type: String } } } @@ -19,6 +20,7 @@ export class ServerConnection extends WebsocketConnection { this.sessionId = null; this.isRegistered = false; this.crossword_grid = null; + } update(props) { @@ -51,7 +53,8 @@ export class ServerConnection extends WebsocketConnection { console.log("register", this.sessionId); this.sendMessage({ 'type': 'register', - 'sessionId': this.sessionId + 'sessionId': this.sessionId, + 'lang': this.lang }); } diff --git a/js_client/solutionBox.js b/js_client/solutionBox.js new file mode 100644 index 0000000..9e9de4c --- /dev/null +++ b/js_client/solutionBox.js @@ -0,0 +1,90 @@ +import { html, css, LitElement, unsafeCSS } from 'https://unpkg.com/lit-element/lit-element.js?module'; + +export class SolutionBox extends LitElement { + static get styles() { + return css` + .letter { + display: grid; + } + + input[type='text'] { + grid-column: 1; + grid-row: 1; + width: 1.5em; + height: 1.5em; + text-align: center; + border-style: solid; + border-width: 0.2 em; + border-color: black; + outline: none; + color: black; + font-size: 2em; + user-select: none; + text-transform: capitalize; + } + + input[type='text']:read-only { + grid-column: 1; + grid-row: 1; + background-color: LightGreen; + } + + input[type='text']:selection { + grid-column: 1; + grid-row: 1; + background: transparent; + } + + .circle { + margin: auto; + text-align: left; + vertical-align: bottom; + bottom: 0; + position: relative; + z-index: 0; + pointer-events: none; + grid-column: 1; + grid-row: 1; + border: 0.1em solid lightGray; + color: black; + height: 80%; + width: 80%; + border-radius:50%; + -moz-border-radius:50%; + -webkit-border-radius:50%; + background: transparent; + + } + + `; + + } + + static get properties() { + return { + length: { type: Number } + }; + } + + constructor() { + super(); + this.length = 0; + this.values = []; + } + + update(props) { + if (props.has("length")) { + var i; + for (i = 0; i < this.length; i++) { + this.values.push(""); + } + } + super.update() + } + + render() { + return html` + + `; + } +} \ No newline at end of file diff --git a/server/crossword.py b/server/crossword.py index 53783ab..ece4bc9 100644 --- a/server/crossword.py +++ b/server/crossword.py @@ -105,12 +105,13 @@ class LetterField(Field): class Grid(object): - def __init__(self, width: int, height: int, lang_code: str, density=0.5): + def __init__(self, width: int, height: int, lang_code: str, density=0.55): self._width = width self._height = height self._lang_code = lang_code self._density = density self._grid = [] + self._solution_locations = None try: self._build_grid() except Exception as e: @@ -245,12 +246,21 @@ class Grid(object): 'y': y, 'user_input': cell.get_user_content() }] - + return revealed_changes + def get_solution_locations(self): + return self._solution_locations + def _build_grid(self): - raw_grid, word_infos = crossword_generator.create_word_grid( - self._width - 1, self._height - 1, lang_code="en", target_density=self._density) + raw_grid, word_infos, solution_locations = crossword_generator.create_word_grid( + self._width - 1, self._height - 1, lang_code=self._lang_code, target_density=self._density) + + self._solution_locations = solution_locations + # fix solution locations offsets + for i in range(len(self._solution_locations)): + self._solution_locations[i][0] += 1 + self._solution_locations[i][1] += 1 # note: we will append an additional row and column, to have enough space to place hint fields @@ -304,17 +314,18 @@ class Crossword(object): return { 'w': self._width, 'h': self._height, - 'grid': self._grid.serialize() + 'grid': self._grid.serialize(), + 'solution': self._grid.get_solution_locations() } def user_input(self, x: int, y: int, letter: str) -> list: return self._grid.user_input(x=x, y=y, letter=letter) - + def get_status(self) -> list: return self._grid.get_status() if __name__ == "__main__": logging.basicConfig(level=logging.INFO) - cw = Crossword(15, 15, "en") + cw = Crossword(30, 30, "de") print(cw.serialize()) diff --git a/server/crossword_connection.py b/server/crossword_connection.py index f51502c..40eb49a 100644 --- a/server/crossword_connection.py +++ b/server/crossword_connection.py @@ -14,7 +14,7 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection): self._session = None - async def send_crossword(self, sessionId: str): + async def send_crossword(self, sessionId: str, lang:str = "en"): if sessionId not in CrosswordConnection.sessions: await self.send_error(msg="unknown session") return @@ -28,7 +28,7 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection): await self.send_error(msg="you are not registered to given session") return - crossword = sess.get_crossword() + crossword = sess.get_crossword(lang=lang) await self.send({ 'type': 'crossword', 'crossword': crossword.serialize() @@ -53,7 +53,7 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection): 'updates': update_message }) - async def register(self, sessionId: str = None): + async def register(self, sessionId: str = None, lang: str = "en"): if sessionId is None: @@ -68,7 +68,7 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection): await self.send_error("unknown session id") # register with new id: - await self.register() + await self.register(lang=lang) return sess = CrosswordConnection.sessions[sessionId] @@ -81,7 +81,7 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection): 'sessionId': sessionId }) - await self.send_crossword(sessionId) + await self.send_crossword(sessionId, lang=lang) async def send_error(self, msg: str): await self.send({ @@ -98,9 +98,12 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection): if message['type'] == 'register': sessionId = None + lang = "en" if 'sessionId' in message: sessionId = message['sessionId'] - await self.register(sessionId=sessionId) + if "lang" in message: + lang = message['lang'] + await self.register(sessionId=sessionId, lang = lang) return if self._session is None: diff --git a/server/crossword_generator.py b/server/crossword_generator.py index 0248f57..d5f5990 100644 --- a/server/crossword_generator.py +++ b/server/crossword_generator.py @@ -16,157 +16,190 @@ def get_database(lang: str = "en") -> dict: with open(db_file, "r") as f: db = json.load(f) get_database._dbs[lang] = db - + logging.info("database loaded") - + return get_database._dbs[lang] + get_database._dbs = {} + class NoDataException(Exception): pass + class WordInfo(object): - def __init__(self, word:str, y:int, x:int, is_vertical: bool, database: dict): + def __init__(self, word: str, y: int, x: int, is_vertical: bool, database: dict, opposite_prefix: str = "opposite of", synonym_prefix: str = "other word for"): self._dictionary_database = database self._y = y self._x = x self._word = word self._hint = None self._is_vertical = is_vertical - + + self.opposite_prefix = opposite_prefix + self.synonym_prefix = synonym_prefix + self.choose_info() def get_attribute(self, attr: str): attr = self._dictionary_database[self._word][attr] - if attr is None: + if attr is None or len(attr) == 0: raise NoDataException return attr - + def get_best_antonym(self) -> str: antonyms = self.get_attribute("antonyms") return random.choice(antonyms) - + def get_best_synonym(self) -> str: synonyms = self.get_attribute("synonyms") return random.choice(synonyms) - + def get_best_sense(self) -> str: senses = self.get_attribute("senses") return random.choice(senses) - - def choose_info(self): + + def choose_info(self, n: int = 1): + assert n <= 4 # first choose antonyms, then synonyms, then senses - + + hints = [] + try: - self._hint = f"opposite of {self.get_best_antonym()}" - return + antonyms = self.get_attribute("antonyms") + antonyms = [f"{self.opposite_prefix} {w}" for w in antonyms] + hints = hints + antonyms except NoDataException: pass - + try: - self._hint = f"other word for {self.get_best_synonym()}" - return + synonyms = self.get_attribute("synonyms") + synonyms = [f"{self.synonym_prefix} {w}" for w in synonyms] + + hints = hints + synonyms except NoDataException: pass - - self._hint = self.get_best_sense() - + + try: + senses = self.get_attribute("senses") + hints = hints + senses + except NoDataException: + pass + + final_hints = [] + for i in range(n): + choice = random.choice(hints) + hints.remove(choice) + final_hints.append(choice) + + if n == 1: + self._hint = final_hints[0] + return + + hint_symbols = ['a)', 'b)', 'c)', 'd)'] + + self._hint = "" + for i in range(n): + self._hint += hint_symbols[i] + " " + final_hints[i] + ". " + def get_hint(self) -> str: return self._hint - + def get_hint_location(self): x = self._x if self._is_vertical else self._x - 1 y = self._y - 1 if self._is_vertical else self._y return (y, x) - + def is_vertical(self): return self._is_vertical -def create_word_grid(w: int, h: int, lang_code: str = "en", target_density = 0.5): + +def create_word_grid(w: int, h: int, lang_code: str = "en", target_density=0.5): logging.info("generate new crossword") - database = get_database(lang = lang_code) + database = get_database(lang=lang_code) list_words = list(database.keys()) - grid = np.full(shape=(h,w), dtype=np.unicode, fill_value = ' ') - + grid = np.full(shape=(h, w), dtype=np.unicode, fill_value=' ') + locations = {} - + word_hints = {} - + def store_location(char: str, y: int, x: int): assert len(char) == 1 - + if char not in locations: locations[char] = [] - - locations[char].append([y,x]) - + + locations[char].append([y, x]) + remove_digits = str.maketrans('', '', digits) n_words = len(list_words) - - def get_word(max_length: int, min_length = 0): + + def get_word(max_length: int, min_length=0): assert max_length > 1 - - index = random.randint(0,n_words-1) + + index = random.randint(0, n_words-1) word = list_words[index][:] - + while len(word) >= max_length or not word.isalnum() or len(word) <= min_length: - index = random.randint(0,n_words-1) + index = random.randint(0, n_words-1) word = list_words[index][:] - + return word - - def normalize_word(word:str): + + def normalize_word(word: str): word = word.translate(remove_digits) return word.lower() - - def place_word(word:str, y: int, x:int, vertical:bool = False): + + opposite_prefix = "opposite of" if lang_code == "en" else "Gegenteil von" + synonym_prefix = "other word for" if lang_code == "en" else "anderes Wort für" + + def place_word(word: str, y: int, x: int, vertical: bool = False): normalized_word = normalize_word(word) n = len(normalized_word) if vertical: assert grid.shape[0] - n >= y for i, char in enumerate(normalized_word): - grid[y + i,x] = char + grid[y + i, x] = char store_location(char, y+i, x) else: assert grid.shape[1] - n >= x for i, char in enumerate(normalized_word): - grid[y,x + i] = char + grid[y, x + i] = char store_location(char, y, x+i) - - word_hints[normalized_word] = WordInfo(word, y, x, vertical, database) - - - + + word_hints[normalized_word] = WordInfo( + word, y, x, vertical, database, opposite_prefix, synonym_prefix) + def density(): return 1 - (grid == " ").sum() / (w * h) - - - - def check_if_fits(word:str, y:int, x:int, vertical:bool): + + def check_if_fits(word: str, y: int, x: int, vertical: bool): n = len(word) if vertical: - + # check if there is space before and after if y - 1 >= 0 and grid[y - 1, x] != " ": return False - if y + n < grid.shape[0] - 1 and grid[y+n,x] != " ": + if y + n < grid.shape[0] - 1 and grid[y+n, x] != " ": return False - + if grid.shape[0] - n < y or y < 0: # print("over board") return False - + for i, char in enumerate(word): char_x = x char_y = y + i - + if not (grid[char_y, char_x] == " " or grid[char_y, char_x] == char): # print("not matching") return False - + if grid[char_y, char_x] == " ": # check for horizonatal neighbors: if char_x - 1 >= 0 and grid[char_y, char_x - 1] != " ": @@ -175,27 +208,27 @@ def create_word_grid(w: int, h: int, lang_code: str = "en", target_density = 0.5 if char_x + 1 < grid.shape[1] and grid[char_y, char_x + 1] != " ": # print("4") return False - + else: - + # check if there is space before and after if x - 1 >= 0 and grid[y, x - 1] != " ": return False - if x + n < grid.shape[1] - 1 and grid[y,x + n] != " ": + if x + n < grid.shape[1] - 1 and grid[y, x + n] != " ": return False - + if grid.shape[1] - n < x or x < 0: # print("over board") return False - + for i, char in enumerate(word): char_x = x + i char_y = y - + if not (grid[char_y, char_x] == " " or grid[char_y, char_x] == char): # print("not matching") return False - + if grid[char_y, char_x] == " ": # check for vertical neighbors: if char_y - 1 >= 0 and grid[char_y - 1, char_x] != " ": @@ -204,83 +237,106 @@ def create_word_grid(w: int, h: int, lang_code: str = "en", target_density = 0.5 if char_y + 1 < grid.shape[0] and grid[char_y + 1, char_x] != " ": # print("2") return False - + return True - - + def get_crossover(word: str): # returns Tuple of: (y,x, is_vertical?) or None - + shuffled_order = list(range(len(word))) random.shuffle(shuffled_order) - + for index in shuffled_order: # check for existing locations char = word[index] if char in locations: char_locations = locations[char] - + for char_loc in char_locations: # test vertical y = char_loc[0] - index x = char_loc[1] - + if check_if_fits(word, y, x, vertical=True): - return (y,x,True) - + return (y, x, True) + # test horizontal y = char_loc[0] x = char_loc[1] - index - + if check_if_fits(word, y, x, vertical=False): - return (y,x,False) - + return (y, x, False) + return None - - min_shape = min(w,h,30) - + + def get_solution_word(min_length=8, max_length=100): + word = get_word(min_length=min_length, max_length=max_length) + + # search for matching characters in locations + locations_cpy = dict(locations) + solution_locations = [] + + for char in word: + if char not in locations_cpy or len(locations_cpy[char]) == 0: + # next try: + get_solution_word(min_length=min_length, max_length=max_length) + + location_candidates = locations_cpy[char] + + n = len(location_candidates) + + i = random.randint(0, n-1) + + solution_locations.append(location_candidates[i]) + del(location_candidates[i]) + + return solution_locations + + min_shape = min(w, h, 30) + # place first word: - first_word = get_word(max_length=min_shape, min_length=min(10,grid.shape[1] - 2)) - + first_word = get_word(max_length=min_shape, + min_length=min(10, grid.shape[1] - 2)) + # find random place: x = random.randint(0, grid.shape[1] - len(first_word) - 1) y = random.randint(0, grid.shape[0] - 1) - + place_word(first_word, y, x, vertical=False) - + i = 0 - - + current_density = density() - + while current_density < target_density: word = get_word(max_length=(1 - current_density ** 0.4) * min_shape, min_length=max(min(10, 0.5 * (1 - current_density ** 0.3) * min_shape), 2)) - + normalized_word = normalize_word(word) - + if normalized_word in word_hints: continue - + # check if matching characters exist: crossover = get_crossover(normalized_word) - + i += 1 if i % 100000 == 0: print(i) if i > 100000: break - + if crossover == None: current_density = density() continue - - y,x,is_vertical = crossover - - place_word(word, y,x, is_vertical) - + + y, x, is_vertical = crossover + + place_word(word, y, x, is_vertical) + current_density = density() - + + solution_word_locations = get_solution_word() + logging.info("crossword generation done after %s iterations", str(i)) - return grid, word_hints - + return grid, word_hints, solution_word_locations diff --git a/server/de.json b/server/de.json new file mode 100644 index 0000000..85c1f01 --- /dev/null +++ b/server/de.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c043827e36783046fc91cf069cc7e60d580f2a666bc0f0f002a3f9c73ce7915 +size 22199093 diff --git a/server/session.py b/server/session.py index e8bfcce..7a92a4b 100644 --- a/server/session.py +++ b/server/session.py @@ -36,13 +36,13 @@ class Session(object): def get_datetime_created(self) -> dt.datetime: return self.datetime_created - def create_crossword(self, width: int = 30, height: int = 30): + def create_crossword(self, width: int = 20, height: int = 20, lang: str = "en"): self.crossword = crossword.Crossword(width=width, height=height, - lang_code="en") + lang_code=lang) - def get_crossword(self) -> crossword.Crossword: + def get_crossword(self, lang: str = "en") -> crossword.Crossword: if self.crossword is None: - self.create_crossword() + self.create_crossword(lang = lang) return self.crossword