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:
-
-
-
-
+
-
-
-
-
+
+
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