adding german option

This commit is contained in:
Jonas Weinz 2021-06-23 19:11:34 +02:00
parent 8a8fc90a1f
commit 5e6d69835f
12 changed files with 590 additions and 190 deletions

57
js_client/english.html Normal file
View File

@ -0,0 +1,57 @@
<!doctype html>
<html>
<head>
<!-- Polyfills only needed for Firefox and Edge. -->
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script>
<!-- Works only on browsers that support Javascript modules like
Chrome, Safari, Firefox 60, Edge 17 -->
<style>
body {
background-color: black;
font-size: 1.5vh;
}
@media (max-aspect-ratio: 3/2) {
body {
font-size: 1.5vh;
}
}
</style>
// Original viewport definition, note the "id" that I use to modify the viewport later on:
<meta name="viewport" id="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
<script>
// Global boolean variable that holds the current orientation
var pageInPortraintMode;
// Listen for window resizes to detect orientation changes
window.addEventListener("resize", windowSizeChanged);
// Set the global orientation variable as soon as the page loads
addEventListener("load", function() {
pageInPortraintMode = window.innerHeight > window.innerWidth;
document.getElementById("viewport").setAttribute("content", "width=" + window.innerWidth + ", height=" + window.innerHeight + ", initial-scale=1.0, maximum-scale=1.0, user-scalable=0");
})
// Adjust viewport values only if orientation has changed (not on every window resize)
function windowSizeChanged() {
if (((pageInPortraintMode === true) && (window.innerHeight < window.innerWidth)) || ((pageInPortraintMode === false) && (window.innerHeight > window.innerWidth))) {
pageInPortraintMode = window.innerHeight > window.innerWidth;
document.getElementById("viewport").setAttribute("content", "width=" + window.innerWidth + ", height=" + window.innerHeight + ", initial-scale=1.0, maximum-scale=1.0, user-scalable=0");
}
}
</script>
</head>
<body>
<info-box id="infobox"></info-box>
<server-connection id="server-connection" lang="en" grid_id="grid" url="wss://the-cake-is-a-lie.net:8765"></server-connection>
<crossword-grid id="grid" infobox_id="infobox" width="10" height="10"></crossword-grid>
<script type="module" src="./main.js"></script>
</body>
</html>

57
js_client/german.html Normal file
View File

@ -0,0 +1,57 @@
<!doctype html>
<html>
<head>
<!-- Polyfills only needed for Firefox and Edge. -->
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script>
<!-- Works only on browsers that support Javascript modules like
Chrome, Safari, Firefox 60, Edge 17 -->
<style>
body {
background-color: black;
font-size: 1.5vh;
}
@media (max-aspect-ratio: 3/2) {
body {
font-size: 1.5vh;
}
}
</style>
// Original viewport definition, note the "id" that I use to modify the viewport later on:
<meta name="viewport" id="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
<script>
// Global boolean variable that holds the current orientation
var pageInPortraintMode;
// Listen for window resizes to detect orientation changes
window.addEventListener("resize", windowSizeChanged);
// Set the global orientation variable as soon as the page loads
addEventListener("load", function() {
pageInPortraintMode = window.innerHeight > window.innerWidth;
document.getElementById("viewport").setAttribute("content", "width=" + window.innerWidth + ", height=" + window.innerHeight + ", initial-scale=1.0, maximum-scale=1.0, user-scalable=0");
})
// Adjust viewport values only if orientation has changed (not on every window resize)
function windowSizeChanged() {
if (((pageInPortraintMode === true) && (window.innerHeight < window.innerWidth)) || ((pageInPortraintMode === false) && (window.innerHeight > window.innerWidth))) {
pageInPortraintMode = window.innerHeight > window.innerWidth;
document.getElementById("viewport").setAttribute("content", "width=" + window.innerWidth + ", height=" + window.innerHeight + ", initial-scale=1.0, maximum-scale=1.0, user-scalable=0");
}
}
</script>
</head>
<body>
<info-box id="infobox"></info-box>
<server-connection id="server-connection" lang="de" grid_id="grid" url="ws://localhost:8765"></server-connection>
<crossword-grid id="grid" infobox_id="infobox" width="10" height="10"></crossword-grid>
<script type="module" src="./main.js"></script>
</body>
</html>

View File

@ -116,7 +116,13 @@ customElements.define('grid-box', GridBox);
export class GridLetter extends GridBox { export class GridLetter extends GridBox {
static get styles() { static get styles() {
return css` return css`
.letter {
display: grid;
}
input[type='text'] { input[type='text'] {
grid-column: 1;
grid-row: 1;
width: 1.5em; width: 1.5em;
height: 1.5em; height: 1.5em;
text-align: center; text-align: center;
@ -131,18 +137,47 @@ export class GridLetter extends GridBox {
} }
input[type='text']:focus { input[type='text']:focus {
grid-column: 1;
grid-row: 1;
background-color: LightYellow; background-color: LightYellow;
border-color: Yellow; border-color: Yellow;
} }
input[type='text']:read-only { input[type='text']:read-only {
grid-column: 1;
grid-row: 1;
background-color: LightGreen; background-color: LightGreen;
} }
input[type='text']:selection { input[type='text']:selection {
grid-column: 1;
grid-row: 1;
background: transparent; 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(); super();
this._value = ''; this._value = '';
this.is_focused = false; this.is_focused = false;
this.key_delay = false; this.firstKeydownOver = true;
this.solutionIndex = -1;
this.lastFocusedX = -1;
this.lastFocusedY = -1;
this.updateGridElement(); this.updateGridElement();
} }
@ -179,7 +217,9 @@ export class GridLetter extends GridBox {
onInput(e) { onInput(e) {
if (!this.is_focused) { if (!this.is_focused) {
e.handled = true;
return; return;
} }
@ -213,7 +253,7 @@ export class GridLetter extends GridBox {
} }
if (!e.handled && this.value.length === 0) { 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; e.handled = true;
} }
else if (!e.handled && this.value.length === 1) { else if (!e.handled && this.value.length === 1) {
@ -242,28 +282,43 @@ export class GridLetter extends GridBox {
} }
onKeydown(e) { onKeyup(e) {
if (!this.firstKeydownOver) {
if (!this.is_focused) { 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; return;
} }
this.key_delay = false;
var key = e.key; var key = e.key;
console.log(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 // check arrow keys
if (key === "ArrowLeft") { if (key === "ArrowLeft") {
// left // left
@ -301,14 +356,20 @@ export class GridLetter extends GridBox {
onFocus(e) { onFocus(e) {
e.target.select(); e.target.select();
this.is_focused = true; this.is_focused = true;
this.key_delay = true;
this.crosswordGrid.updateHints(this.x, this.y); 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}`); var element = this.shadowRoot.getElementById(`${this.x}-${this.y}`);
this.firstKeydownOver = false;
element.focus(); element.focus();
} }
blur() { blur() {
@ -323,13 +384,13 @@ export class GridLetter extends GridBox {
if (this.grid_id != null) { if (this.grid_id != null) {
this.gridElement.setGridLetter(this); this.gridElement.setGridLetter(this);
this.solutionIndex = this.crosswordGrid.getSolutionIndex(this.x, this.y)
} }
} }
updateLetter(letter, revealed) { updateLetter(letter, revealed) {
this.value = letter; this.value = letter;
if (revealed) if (revealed) {
{
console.log("rec") console.log("rec")
this.shadowRoot.getElementById(`${this.x}-${this.y}`).readOnly = true; this.shadowRoot.getElementById(`${this.x}-${this.y}`).readOnly = true;
this.blur(); this.blur();
@ -339,7 +400,25 @@ export class GridLetter extends GridBox {
render() { render() {
this.updateGridElement(); this.updateGridElement();
return html`<input type="text" id="${this.x}-${this.y}" value="${this.value}" @input=${this.onInput} @focus=${this.onFocus} @keydown=${this.onKeydown} autocomplete="off"></input>`;
var solutionOverlay = html``;
if (this.solutionIndex >= 0) {
solutionOverlay = html`
<div class="circle">${this.solutionIndex}</div>
`;
}
return html`
<div class="letter">
<input type="text" id="${this.x}-${this.y}" value="${this.value}" @input=${this.onInput} @focus=${this.onFocus} @keydown=${this.onKeydown} @keyup=${this.onKeyup} autocomplete="off"></input>
${solutionOverlay}
</div>
`;
} }
} }
@ -527,6 +606,18 @@ export class CrosswordGrid extends LitElement {
this.infobox = null; this.infobox = null;
this.serverConnection = 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) { createGridByJson(json_grid) {
@ -536,6 +627,8 @@ export class CrosswordGrid extends LitElement {
const width = json_grid.w; const width = json_grid.w;
const height = json_grid.h; const height = json_grid.h;
this.solution_locations = json_grid.solution
var y; var y;
var x; var x;
@ -573,7 +666,7 @@ export class CrosswordGrid extends LitElement {
if (this.grid[y][x].getGridType() === gridType.LETTER) { if (this.grid[y][x].getGridType() === gridType.LETTER) {
this.grid[y][x].getGridLetter().blur(); this.grid[y][x].getGridLetter().blur();
} }
this.grid[y][x + 1].getGridLetter().focus(); this.grid[y][x + 1].getGridLetter().focus(x, y);
return true; return true;
@ -601,7 +694,7 @@ export class CrosswordGrid extends LitElement {
if (this.grid[y][x].getGridType() === gridType.LETTER) { if (this.grid[y][x].getGridType() === gridType.LETTER) {
this.grid[y][x].getGridLetter().blur(); this.grid[y][x].getGridLetter().blur();
} }
this.grid[y + 1][x].getGridLetter().focus(); this.grid[y + 1][x].getGridLetter().focus(x, y);
return true; 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.lastMoveVertical) {
if (this.focusPrevCellVertical(x, y)) { if (this.focusPrevCellVertical(x, y)) {
return; return;
@ -704,8 +801,7 @@ export class CrosswordGrid extends LitElement {
if (upper_end.getGridType() === gridType.HINT) { if (upper_end.getGridType() === gridType.HINT) {
var box = upper_end.getHintBox() var box = upper_end.getHintBox()
if (box.hasVerticalHint()) if (box.hasVerticalHint()) {
{
vHint = `${box.hint_id}: ${box.vertical_hint}`; vHint = `${box.hint_id}: ${box.vertical_hint}`;
} }
} }

View File

@ -10,47 +10,71 @@
<style> <style>
body { body {
background-color: black; background-color: black;
font-size: 1vw; font-size: 1.5vh;
text-align: center;
margin: auto;
} }
@media (max-aspect-ratio: 3/2) { @media (max-aspect-ratio: 3/2) {
body { body {
font-size: 1vh; font-size: 1.5vh;
} }
} }
html {
width: 100%;
height: 100%;
}
body {
min-width: 100%;
min-height: 100%;
}
.centerbox {
background-color: black;
border-style: none;
box-shadow: none;
outline: none;
position: absolute;
width: 15em;
height: 10em;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
padding: 1em;
color: white;
font-size: 1em;
}
.box {
background-color: black;
border-color: white;
border-width: 0.2em;
border-style: solid;
max-width: 15em;
max-height: 3em;
box-shadow: none;
outline: none;
position: relative;
margin: 1em;
padding: 1em;
color: white;
font-size: 1em;
}
</style> </style>
// Original viewport definition, note the "id" that I use to modify the viewport later on:
<meta name="viewport" id="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
<script>
// Global boolean variable that holds the current orientation
var pageInPortraintMode;
// Listen for window resizes to detect orientation changes
window.addEventListener("resize", windowSizeChanged);
// Set the global orientation variable as soon as the page loads
addEventListener("load", function() {
pageInPortraintMode = window.innerHeight > window.innerWidth;
document.getElementById("viewport").setAttribute("content", "width=" + window.innerWidth + ", height=" + window.innerHeight + ", initial-scale=1.0, maximum-scale=1.0, user-scalable=0");
})
// Adjust viewport values only if orientation has changed (not on every window resize)
function windowSizeChanged() {
if (((pageInPortraintMode === true) && (window.innerHeight < window.innerWidth)) || ((pageInPortraintMode === false) && (window.innerHeight > window.innerWidth))) {
pageInPortraintMode = window.innerHeight > window.innerWidth;
document.getElementById("viewport").setAttribute("content", "width=" + window.innerWidth + ", height=" + window.innerHeight + ", initial-scale=1.0, maximum-scale=1.0, user-scalable=0");
}
}
</script>
</head> </head>
<body> <body>
<info-box id="infobox"></info-box>
<server-connection id="server-connection" grid_id="grid" url="wss://the-cake-is-a-lie.net:8765"></server-connection> <div class="centerbox">
<crossword-grid id="grid" infobox_id="infobox" width="10" height="10"></crossword-grid> <h3>Select the language:</h3>
<script type="module" src="./main.js"></script> <a href="./german.html"> <div class="box"> German </div> </a>
<a href="./english.html"> <div class="box"> English </div> </a>
</div>
</body> </body>

View File

@ -10,7 +10,8 @@ export class ServerConnection extends WebsocketConnection {
static get properties() { static get properties() {
return { return {
url: { type: String }, 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.sessionId = null;
this.isRegistered = false; this.isRegistered = false;
this.crossword_grid = null; this.crossword_grid = null;
} }
update(props) { update(props) {
@ -51,7 +53,8 @@ export class ServerConnection extends WebsocketConnection {
console.log("register", this.sessionId); console.log("register", this.sessionId);
this.sendMessage({ this.sendMessage({
'type': 'register', 'type': 'register',
'sessionId': this.sessionId 'sessionId': this.sessionId,
'lang': this.lang
}); });
} }

90
js_client/solutionBox.js Normal file
View File

@ -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`
`;
}
}

View File

@ -105,12 +105,13 @@ class LetterField(Field):
class Grid(object): 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._width = width
self._height = height self._height = height
self._lang_code = lang_code self._lang_code = lang_code
self._density = density self._density = density
self._grid = [] self._grid = []
self._solution_locations = None
try: try:
self._build_grid() self._build_grid()
except Exception as e: except Exception as e:
@ -248,9 +249,18 @@ class Grid(object):
return revealed_changes return revealed_changes
def get_solution_locations(self):
return self._solution_locations
def _build_grid(self): def _build_grid(self):
raw_grid, word_infos = crossword_generator.create_word_grid( raw_grid, word_infos, solution_locations = crossword_generator.create_word_grid(
self._width - 1, self._height - 1, lang_code="en", target_density=self._density) 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 # note: we will append an additional row and column, to have enough space to place hint fields
@ -304,7 +314,8 @@ class Crossword(object):
return { return {
'w': self._width, 'w': self._width,
'h': self._height, '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: def user_input(self, x: int, y: int, letter: str) -> list:
@ -316,5 +327,5 @@ class Crossword(object):
if __name__ == "__main__": if __name__ == "__main__":
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
cw = Crossword(15, 15, "en") cw = Crossword(30, 30, "de")
print(cw.serialize()) print(cw.serialize())

View File

@ -14,7 +14,7 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection):
self._session = None 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: if sessionId not in CrosswordConnection.sessions:
await self.send_error(msg="unknown session") await self.send_error(msg="unknown session")
return return
@ -28,7 +28,7 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection):
await self.send_error(msg="you are not registered to given session") await self.send_error(msg="you are not registered to given session")
return return
crossword = sess.get_crossword() crossword = sess.get_crossword(lang=lang)
await self.send({ await self.send({
'type': 'crossword', 'type': 'crossword',
'crossword': crossword.serialize() 'crossword': crossword.serialize()
@ -53,7 +53,7 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection):
'updates': update_message 'updates': update_message
}) })
async def register(self, sessionId: str = None): async def register(self, sessionId: str = None, lang: str = "en"):
if sessionId is None: if sessionId is None:
@ -68,7 +68,7 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection):
await self.send_error("unknown session id") await self.send_error("unknown session id")
# register with new id: # register with new id:
await self.register() await self.register(lang=lang)
return return
sess = CrosswordConnection.sessions[sessionId] sess = CrosswordConnection.sessions[sessionId]
@ -81,7 +81,7 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection):
'sessionId': sessionId 'sessionId': sessionId
}) })
await self.send_crossword(sessionId) await self.send_crossword(sessionId, lang=lang)
async def send_error(self, msg: str): async def send_error(self, msg: str):
await self.send({ await self.send({
@ -98,9 +98,12 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection):
if message['type'] == 'register': if message['type'] == 'register':
sessionId = None sessionId = None
lang = "en"
if 'sessionId' in message: if 'sessionId' in message:
sessionId = message['sessionId'] sessionId = message['sessionId']
await self.register(sessionId=sessionId) if "lang" in message:
lang = message['lang']
await self.register(sessionId=sessionId, lang = lang)
return return
if self._session is None: if self._session is None:

View File

@ -21,13 +21,16 @@ def get_database(lang: str = "en") -> dict:
return get_database._dbs[lang] return get_database._dbs[lang]
get_database._dbs = {} get_database._dbs = {}
class NoDataException(Exception): class NoDataException(Exception):
pass pass
class WordInfo(object): 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._dictionary_database = database
self._y = y self._y = y
self._x = x self._x = x
@ -35,11 +38,14 @@ class WordInfo(object):
self._hint = None self._hint = None
self._is_vertical = is_vertical self._is_vertical = is_vertical
self.opposite_prefix = opposite_prefix
self.synonym_prefix = synonym_prefix
self.choose_info() self.choose_info()
def get_attribute(self, attr: str): def get_attribute(self, attr: str):
attr = self._dictionary_database[self._word][attr] attr = self._dictionary_database[self._word][attr]
if attr is None: if attr is None or len(attr) == 0:
raise NoDataException raise NoDataException
return attr return attr
@ -55,22 +61,48 @@ class WordInfo(object):
senses = self.get_attribute("senses") senses = self.get_attribute("senses")
return random.choice(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 # first choose antonyms, then synonyms, then senses
hints = []
try: try:
self._hint = f"opposite of {self.get_best_antonym()}" antonyms = self.get_attribute("antonyms")
return antonyms = [f"{self.opposite_prefix} {w}" for w in antonyms]
hints = hints + antonyms
except NoDataException: except NoDataException:
pass pass
try: try:
self._hint = f"other word for {self.get_best_synonym()}" synonyms = self.get_attribute("synonyms")
return synonyms = [f"{self.synonym_prefix} {w}" for w in synonyms]
hints = hints + synonyms
except NoDataException: except NoDataException:
pass 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: def get_hint(self) -> str:
return self._hint return self._hint
@ -83,6 +115,7 @@ class WordInfo(object):
def is_vertical(self): def is_vertical(self):
return self._is_vertical 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") logging.info("generate new crossword")
@ -122,6 +155,9 @@ def create_word_grid(w: int, h: int, lang_code: str = "en", target_density = 0.5
word = word.translate(remove_digits) word = word.translate(remove_digits)
return word.lower() return word.lower()
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): def place_word(word: str, y: int, x: int, vertical: bool = False):
normalized_word = normalize_word(word) normalized_word = normalize_word(word)
n = len(normalized_word) n = len(normalized_word)
@ -136,15 +172,12 @@ def create_word_grid(w: int, h: int, lang_code: str = "en", target_density = 0.5
grid[y, x + i] = char grid[y, x + i] = char
store_location(char, y, x+i) 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(): def density():
return 1 - (grid == " ").sum() / (w * h) 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) n = len(word)
if vertical: if vertical:
@ -207,7 +240,6 @@ def create_word_grid(w: int, h: int, lang_code: str = "en", target_density = 0.5
return True return True
def get_crossover(word: str): def get_crossover(word: str):
# returns Tuple of: (y,x, is_vertical?) or None # returns Tuple of: (y,x, is_vertical?) or None
@ -237,10 +269,34 @@ def create_word_grid(w: int, h: int, lang_code: str = "en", target_density = 0.5
return None return None
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) min_shape = min(w, h, 30)
# place first word: # 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: # find random place:
x = random.randint(0, grid.shape[1] - len(first_word) - 1) x = random.randint(0, grid.shape[1] - len(first_word) - 1)
@ -250,7 +306,6 @@ def create_word_grid(w: int, h: int, lang_code: str = "en", target_density = 0.5
i = 0 i = 0
current_density = density() current_density = density()
while current_density < target_density: while current_density < target_density:
@ -281,6 +336,7 @@ def create_word_grid(w: int, h: int, lang_code: str = "en", target_density = 0.5
current_density = density() current_density = density()
logging.info("crossword generation done after %s iterations", str(i)) solution_word_locations = get_solution_word()
return grid, word_hints
logging.info("crossword generation done after %s iterations", str(i))
return grid, word_hints, solution_word_locations

BIN
server/de.json (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -36,13 +36,13 @@ class Session(object):
def get_datetime_created(self) -> dt.datetime: def get_datetime_created(self) -> dt.datetime:
return self.datetime_created 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, self.crossword = crossword.Crossword(width=width,
height=height, 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: if self.crossword is None:
self.create_crossword() self.create_crossword(lang = lang)
return self.crossword return self.crossword