adding german option
This commit is contained in:
parent
8a8fc90a1f
commit
5e6d69835f
57
js_client/english.html
Normal file
57
js_client/english.html
Normal 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
57
js_client/german.html
Normal 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>
|
@ -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) {
|
||||||
@ -233,7 +273,7 @@ export class GridLetter extends GridBox {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!e.handled){
|
if (!e.handled) {
|
||||||
|
|
||||||
e.target.select();
|
e.target.select();
|
||||||
|
|
||||||
@ -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>
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,7 +587,7 @@ export class CrosswordGrid extends LitElement {
|
|||||||
id: { type: String },
|
id: { type: String },
|
||||||
width: { type: Number },
|
width: { type: Number },
|
||||||
height: { 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.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;
|
||||||
@ -675,9 +772,9 @@ export class CrosswordGrid extends LitElement {
|
|||||||
super.update()
|
super.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHints(x,y) {
|
updateHints(x, y) {
|
||||||
var cell = this.grid[y][x];
|
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");
|
console.error("cannot get hints for non letter cell");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -697,15 +794,14 @@ export class CrosswordGrid extends LitElement {
|
|||||||
|
|
||||||
if (left_end.getGridType() === gridType.HINT) {
|
if (left_end.getGridType() === gridType.HINT) {
|
||||||
var box = left_end.getHintBox();
|
var box = left_end.getHintBox();
|
||||||
if (box.hasHorizontalHint()){
|
if (box.hasHorizontalHint()) {
|
||||||
hHint = `${box.hint_id}: ${box.horizontal_hint}`;
|
hHint = `${box.hint_id}: ${box.horizontal_hint}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -728,10 +824,10 @@ export class CrosswordGrid extends LitElement {
|
|||||||
var vertical_hint = this.json_grid.grid[el.y][el.x].vertical_hint;
|
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;
|
var horizontal_hint = this.json_grid.grid[el.y][el.x].horizontal_hint;
|
||||||
|
|
||||||
if (!vertical_hint){
|
if (!vertical_hint) {
|
||||||
vertical_hint = "";
|
vertical_hint = "";
|
||||||
}
|
}
|
||||||
if (!horizontal_hint){
|
if (!horizontal_hint) {
|
||||||
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];
|
var cell = this.grid[y][x];
|
||||||
if (cell.getGridType() != gridType.LETTER){
|
if (cell.getGridType() != gridType.LETTER) {
|
||||||
console.error("received bad message");
|
console.error("received bad message");
|
||||||
}
|
}
|
||||||
cell.getGridLetter().updateLetter(letter, revealed)
|
cell.getGridLetter().updateLetter(letter, revealed)
|
||||||
}
|
}
|
||||||
|
|
||||||
registerServerConnection(connection){
|
registerServerConnection(connection) {
|
||||||
this.serverConnection = connection
|
this.serverConnection = connection
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage(msg){
|
sendMessage(msg) {
|
||||||
if (!this.serverConnection) {
|
if (!this.serverConnection) {
|
||||||
console.error("no server connection registered")
|
console.error("no server connection registered")
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -79,13 +79,13 @@ export class InfoBox extends LitElement {
|
|||||||
this.horizontal_hint = "";
|
this.horizontal_hint = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
onNew(){
|
onNew() {
|
||||||
setCookie("session","");
|
setCookie("session", "");
|
||||||
window.location.href = location.protocol + '//' + location.host + location.pathname;
|
window.location.href = location.protocol + '//' + location.host + location.pathname;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onShare(){
|
onShare() {
|
||||||
copyToClipboard(window.location.href);
|
copyToClipboard(window.location.href);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
90
js_client/solutionBox.js
Normal 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`
|
||||||
|
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
@ -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:
|
||||||
@ -245,12 +246,21 @@ class Grid(object):
|
|||||||
'y': y,
|
'y': y,
|
||||||
'user_input': cell.get_user_content()
|
'user_input': cell.get_user_content()
|
||||||
}]
|
}]
|
||||||
|
|
||||||
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,17 +314,18 @@ 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:
|
||||||
return self._grid.user_input(x=x, y=y, letter=letter)
|
return self._grid.user_input(x=x, y=y, letter=letter)
|
||||||
|
|
||||||
def get_status(self) -> list:
|
def get_status(self) -> list:
|
||||||
return self._grid.get_status()
|
return self._grid.get_status()
|
||||||
|
|
||||||
|
|
||||||
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())
|
||||||
|
@ -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:
|
||||||
|
@ -16,157 +16,190 @@ def get_database(lang: str = "en") -> dict:
|
|||||||
with open(db_file, "r") as f:
|
with open(db_file, "r") as f:
|
||||||
db = json.load(f)
|
db = json.load(f)
|
||||||
get_database._dbs[lang] = db
|
get_database._dbs[lang] = db
|
||||||
|
|
||||||
logging.info("database loaded")
|
logging.info("database loaded")
|
||||||
|
|
||||||
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
|
||||||
self._word = word
|
self._word = word
|
||||||
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
|
||||||
|
|
||||||
def get_best_antonym(self) -> str:
|
def get_best_antonym(self) -> str:
|
||||||
antonyms = self.get_attribute("antonyms")
|
antonyms = self.get_attribute("antonyms")
|
||||||
return random.choice(antonyms)
|
return random.choice(antonyms)
|
||||||
|
|
||||||
def get_best_synonym(self) -> str:
|
def get_best_synonym(self) -> str:
|
||||||
synonyms = self.get_attribute("synonyms")
|
synonyms = self.get_attribute("synonyms")
|
||||||
return random.choice(synonyms)
|
return random.choice(synonyms)
|
||||||
|
|
||||||
def get_best_sense(self) -> str:
|
def get_best_sense(self) -> str:
|
||||||
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
|
||||||
|
|
||||||
def get_hint_location(self):
|
def get_hint_location(self):
|
||||||
x = self._x if self._is_vertical else self._x - 1
|
x = self._x if self._is_vertical else self._x - 1
|
||||||
y = self._y - 1 if self._is_vertical else self._y
|
y = self._y - 1 if self._is_vertical else self._y
|
||||||
return (y, x)
|
return (y, x)
|
||||||
|
|
||||||
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")
|
||||||
|
|
||||||
database = get_database(lang = lang_code)
|
database = get_database(lang=lang_code)
|
||||||
list_words = list(database.keys())
|
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 = {}
|
locations = {}
|
||||||
|
|
||||||
word_hints = {}
|
word_hints = {}
|
||||||
|
|
||||||
def store_location(char: str, y: int, x: int):
|
def store_location(char: str, y: int, x: int):
|
||||||
assert len(char) == 1
|
assert len(char) == 1
|
||||||
|
|
||||||
if char not in locations:
|
if char not in locations:
|
||||||
locations[char] = []
|
locations[char] = []
|
||||||
|
|
||||||
locations[char].append([y,x])
|
locations[char].append([y, x])
|
||||||
|
|
||||||
remove_digits = str.maketrans('', '', digits)
|
remove_digits = str.maketrans('', '', digits)
|
||||||
n_words = len(list_words)
|
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
|
assert max_length > 1
|
||||||
|
|
||||||
index = random.randint(0,n_words-1)
|
index = random.randint(0, n_words-1)
|
||||||
word = list_words[index][:]
|
word = list_words[index][:]
|
||||||
|
|
||||||
while len(word) >= max_length or not word.isalnum() or len(word) <= min_length:
|
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][:]
|
word = list_words[index][:]
|
||||||
|
|
||||||
return word
|
return word
|
||||||
|
|
||||||
def normalize_word(word:str):
|
def normalize_word(word: str):
|
||||||
word = word.translate(remove_digits)
|
word = word.translate(remove_digits)
|
||||||
return word.lower()
|
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)
|
normalized_word = normalize_word(word)
|
||||||
n = len(normalized_word)
|
n = len(normalized_word)
|
||||||
if vertical:
|
if vertical:
|
||||||
assert grid.shape[0] - n >= y
|
assert grid.shape[0] - n >= y
|
||||||
for i, char in enumerate(normalized_word):
|
for i, char in enumerate(normalized_word):
|
||||||
grid[y + i,x] = char
|
grid[y + i, x] = char
|
||||||
store_location(char, y+i, x)
|
store_location(char, y+i, x)
|
||||||
else:
|
else:
|
||||||
assert grid.shape[1] - n >= x
|
assert grid.shape[1] - n >= x
|
||||||
for i, char in enumerate(normalized_word):
|
for i, char in enumerate(normalized_word):
|
||||||
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:
|
||||||
|
|
||||||
# check if there is space before and after
|
# check if there is space before and after
|
||||||
if y - 1 >= 0 and grid[y - 1, x] != " ":
|
if y - 1 >= 0 and grid[y - 1, x] != " ":
|
||||||
return False
|
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
|
return False
|
||||||
|
|
||||||
if grid.shape[0] - n < y or y < 0:
|
if grid.shape[0] - n < y or y < 0:
|
||||||
# print("over board")
|
# print("over board")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for i, char in enumerate(word):
|
for i, char in enumerate(word):
|
||||||
char_x = x
|
char_x = x
|
||||||
char_y = y + i
|
char_y = y + i
|
||||||
|
|
||||||
if not (grid[char_y, char_x] == " " or grid[char_y, char_x] == char):
|
if not (grid[char_y, char_x] == " " or grid[char_y, char_x] == char):
|
||||||
# print("not matching")
|
# print("not matching")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if grid[char_y, char_x] == " ":
|
if grid[char_y, char_x] == " ":
|
||||||
# check for horizonatal neighbors:
|
# check for horizonatal neighbors:
|
||||||
if char_x - 1 >= 0 and grid[char_y, char_x - 1] != " ":
|
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] != " ":
|
if char_x + 1 < grid.shape[1] and grid[char_y, char_x + 1] != " ":
|
||||||
# print("4")
|
# print("4")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
# check if there is space before and after
|
# check if there is space before and after
|
||||||
if x - 1 >= 0 and grid[y, x - 1] != " ":
|
if x - 1 >= 0 and grid[y, x - 1] != " ":
|
||||||
return False
|
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
|
return False
|
||||||
|
|
||||||
if grid.shape[1] - n < x or x < 0:
|
if grid.shape[1] - n < x or x < 0:
|
||||||
# print("over board")
|
# print("over board")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for i, char in enumerate(word):
|
for i, char in enumerate(word):
|
||||||
char_x = x + i
|
char_x = x + i
|
||||||
char_y = y
|
char_y = y
|
||||||
|
|
||||||
if not (grid[char_y, char_x] == " " or grid[char_y, char_x] == char):
|
if not (grid[char_y, char_x] == " " or grid[char_y, char_x] == char):
|
||||||
# print("not matching")
|
# print("not matching")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if grid[char_y, char_x] == " ":
|
if grid[char_y, char_x] == " ":
|
||||||
# check for vertical neighbors:
|
# check for vertical neighbors:
|
||||||
if char_y - 1 >= 0 and grid[char_y - 1, char_x] != " ":
|
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] != " ":
|
if char_y + 1 < grid.shape[0] and grid[char_y + 1, char_x] != " ":
|
||||||
# print("2")
|
# print("2")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
shuffled_order = list(range(len(word)))
|
shuffled_order = list(range(len(word)))
|
||||||
random.shuffle(shuffled_order)
|
random.shuffle(shuffled_order)
|
||||||
|
|
||||||
for index in shuffled_order:
|
for index in shuffled_order:
|
||||||
# check for existing locations
|
# check for existing locations
|
||||||
char = word[index]
|
char = word[index]
|
||||||
if char in locations:
|
if char in locations:
|
||||||
char_locations = locations[char]
|
char_locations = locations[char]
|
||||||
|
|
||||||
for char_loc in char_locations:
|
for char_loc in char_locations:
|
||||||
# test vertical
|
# test vertical
|
||||||
y = char_loc[0] - index
|
y = char_loc[0] - index
|
||||||
x = char_loc[1]
|
x = char_loc[1]
|
||||||
|
|
||||||
if check_if_fits(word, y, x, vertical=True):
|
if check_if_fits(word, y, x, vertical=True):
|
||||||
return (y,x,True)
|
return (y, x, True)
|
||||||
|
|
||||||
# test horizontal
|
# test horizontal
|
||||||
y = char_loc[0]
|
y = char_loc[0]
|
||||||
x = char_loc[1] - index
|
x = char_loc[1] - index
|
||||||
|
|
||||||
if check_if_fits(word, y, x, vertical=False):
|
if check_if_fits(word, y, x, vertical=False):
|
||||||
return (y,x,False)
|
return (y, x, False)
|
||||||
|
|
||||||
return None
|
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:
|
# 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)
|
||||||
y = random.randint(0, grid.shape[0] - 1)
|
y = random.randint(0, grid.shape[0] - 1)
|
||||||
|
|
||||||
place_word(first_word, y, x, vertical=False)
|
place_word(first_word, y, x, vertical=False)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
|
|
||||||
|
|
||||||
current_density = density()
|
current_density = density()
|
||||||
|
|
||||||
while current_density < target_density:
|
while current_density < target_density:
|
||||||
word = get_word(max_length=(1 - current_density ** 0.4) * min_shape,
|
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))
|
min_length=max(min(10, 0.5 * (1 - current_density ** 0.3) * min_shape), 2))
|
||||||
|
|
||||||
normalized_word = normalize_word(word)
|
normalized_word = normalize_word(word)
|
||||||
|
|
||||||
if normalized_word in word_hints:
|
if normalized_word in word_hints:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# check if matching characters exist:
|
# check if matching characters exist:
|
||||||
crossover = get_crossover(normalized_word)
|
crossover = get_crossover(normalized_word)
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
if i % 100000 == 0:
|
if i % 100000 == 0:
|
||||||
print(i)
|
print(i)
|
||||||
if i > 100000:
|
if i > 100000:
|
||||||
break
|
break
|
||||||
|
|
||||||
if crossover == None:
|
if crossover == None:
|
||||||
current_density = density()
|
current_density = density()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
y,x,is_vertical = crossover
|
y, x, is_vertical = crossover
|
||||||
|
|
||||||
place_word(word, y,x, is_vertical)
|
place_word(word, y, x, is_vertical)
|
||||||
|
|
||||||
current_density = density()
|
current_density = density()
|
||||||
|
|
||||||
|
solution_word_locations = get_solution_word()
|
||||||
|
|
||||||
logging.info("crossword generation done after %s iterations", str(i))
|
logging.info("crossword generation done after %s iterations", str(i))
|
||||||
return grid, word_hints
|
return grid, word_hints, solution_word_locations
|
||||||
|
|
||||||
|
BIN
server/de.json
(Stored with Git LFS)
Normal file
BIN
server/de.json
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user