Доработать код в генерации филвордов
код не мой, но помогите сделать чтобы при генерации филвордов отображалось номер слова и номера букв в слове. Пример на картинки. Необязательно реализовывать так, можно например вывести ввведите сюда код консоль.
Генератор филвордов из текстовых файлов
Слова для примера
ШОУ ЗЕЯ ЛЕВ УДАЛИТЬ
(function() {
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
const canvas = document.getElementById('board');
const fileInput = document.getElementById('file');
const regenButton = document.getElementById('regenButton');
const nextButton = document.getElementById('nextButton');
let currentFile = undefined;
let currentSentence = undefined;
let currentField = [];
let currentWords = [];
let currentSelectedPath = [];
const mouseState = {
x: 0,
y: 0,
pressed: false,
};
async function parseSentence(reader) {
let offset = undefined;
let allText = '';
while (true) {
const { value: bytes, done } = await reader.read(new Uint8Array(512));
if (bytes) {
allText += textDecoder.decode(bytes);
const endRegex = /[.!?]+/;
const match = endRegex.exec(allText);
if (match) {
const neededText = allText.substring(0, match.index);
offset = match.index + match[0].length;
offset = textEncoder.encode(allText.substring(0, offset)).length;
allText = neededText;
break;
}
}
if (done) {
break;
}
}
const nonAbRegex = /[^a-zа-я]+/ig;
const result = allText.split(nonAbRegex)
.filter(txt => txt.length > 0)
.map(txt => txt.toUpperCase());
return { result, offset };
}
async function readSentence() {
const reader = currentFile.stream().getReader();
const { result, offset } = await parseSentence(reader);
reader.releaseLock();
if (offset) {
currentFile = currentFile.slice(offset);
} else {
currentFile = undefined;
}
return result;
}
function calcFieldSize(minSize) {
let i = 0;
for (; i * i < minSize; ++i);
return i;
}
// https://stackoverflow.com/a/12646864
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
const cellPrototype = {
edges: function() {
return [this.top, this.left, this.right, this.bottom].filter(x => x);
},
};
function createEmptyField(fieldSize) {
const field = [];
for (let i = 0; i < fieldSize; ++i) {
const row = [];
for (let j = 0; j < fieldSize; ++j) {
const cell = {
idx: i * fieldSize + j,
char: undefined,
top: undefined,
left: undefined,
right: undefined,
bottom: undefined,
__proto__: cellPrototype,
};
if (i > 0) {
cell.top = field[i - 1][j];
field[i - 1][j].bottom = cell;
}
if (j > 0) {
cell.left = row[j - 1];
row[j - 1].right = cell;
}
row.push(cell);
}
field.push(row);
}
return field;
}
function getEmptyFieldComponents(field) {
const comps = [];
for (let i = 0; i < field.length * field.length; ++i) {
comps.push(undefined);
}
function dfs(v, comp) {
if (v.char !== undefined || comps[v.idx] !== undefined) {
return false;
}
comps[v.idx] = comp;
for (const u of v.edges()) {
dfs(u, comp);
}
return true;
}
let c = 0;
for (const row of field) {
for (const cell of row) {
if (dfs(cell, c)) {
++c;
}
}
}
return [c, comps];
}
function checkWordFit(field, wordLength) {
const [compsAmount, comps] = getEmptyFieldComponents(field);
const compSizes = [];
for (let i = 0; i < compsAmount; ++i) {
compSizes.push(comps.filter(x => x == i).length);
}
if (compSizes.every(x => x < wordLength)) {
return false;
}
// TODO here we assume that word fit because of checking hardness
return true;
}
function placeWordInField(field, word) {
if (!checkWordFit(field, word.length)) {
// TODO extend
console.log('don\'t fit!!!', field, word);
throw new Error(':(');
}
const neighbours = [];
for (const row of field) {
for (const cell of row) {
if (cell.char !== undefined) {
neighbours.push(Infinity); // ignore busy cells
} else {
neighbours.push(cell.edges().filter(x => x.char === undefined).length);
}
}
}
let firstCells = [];
for (const row of field) {
for (const cell of row) {
if (neighbours[cell.idx] < 4) {
firstCells.push(cell);
}
}
}
if (firstCells.length === 0) {
throw new Error('wtf');
}
const path = [];
function dfs(v) {
if (path.indexOf(v) !== -1) {
return false;
}
if (v.char !== undefined) {
return false;
}
path.push(v);
if (path.length === word.length) {
return true;
}
const us = v.edges();
shuffleArray(us);
us.sort((a, b) => neighbours[a.idx] - neighbours[b.idx]);
for (const u of us) {
if (dfs(u)) {
return true;
}
}
path.pop();
return false;
}
shuffleArray(firstCells);
firstCells.sort((a, b) => neighbours[a.idx] - neighbours[b.idx]);
for (const firstCell of firstCells) {
if (dfs(firstCell)) {
break;
}
}
if (path.length !== word.length) {
// TODO is this could happen?
throw new Error(':(');
}
const wordObject = {
word,
chars: [],
guessed: false,
};
for (let i = 0; i < word.length; ++i) {
const charObject = {
value: word[i],
word: wordObject,
cell: path[i],
};
path[i].char = charObject;
wordObject.chars.push(charObject);
}
for (const cell of path) {
if (cell.char === undefined) {
throw Error('wtf');
}
}
return wordObject;
}
function generateField(words) {
const wordsLength = words.map(s => s.length).reduce((a, b) => a + b, 0);
const fieldSize = calcFieldSize(wordsLength);
const wordsPermutation = [...words];
shuffleArray(wordsPermutation);
const field = createEmptyField(fieldSize);
const placedWords = [];
for (const word of wordsPermutation) {
placedWords.push(placeWordInField(field, word));
}
console.log(field.map(row => row.map(cell =>
cell.char ? cell.char.value : '#').join(' ')).join('\n'))
return { field, words: placedWords };
}
function renderField(field, words) {
const ctx = canvas.getContext('2d');
const { width, height } = canvas;
ctx.beginPath();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, width, height);
const size = Math.min(width, height);
let scale = 50;
const mathScale = (size - 20) / field.length;
if (scale > mathScale) {
scale = mathScale;
}
const startX = (width - scale * field.length) / 2;
const startY = (height - scale * field.length) / 2;
ctx.strokeStyle = '#333';
ctx.fillStyle = '#86dbf7';
ctx.lineWidth = 1;
ctx.rect(startX, startY, scale * field.length, scale * field.length);
ctx.stroke();
ctx.fill();
const fontSize = Math.max(12, scale - 20);
ctx.font = `bold ${fontSize}px sans-serif`;
const mouseCellX = Math.floor((mouseState.x - startX) / scale);
const mouseCellY = Math.floor((mouseState.y - startY) / scale);
const mouseOnCell = !(mouseCellX < 0 || mouseCellX >= field.length
|| mouseCellY < 0 || mouseCellY >= field.length
|| field[mouseCellY][mouseCellX].char === undefined
|| field[mouseCellY][mouseCellX].char.word.guessed);
const mouseCell = mouseOnCell ? field[mouseCellY][mouseCellX] : undefined;
if (mouseOnCell) {
canvas.style.cursor = 'pointer';
} else {
canvas.style.cursor = 'default';
}
if (mouseState.pressed) {
if (mouseOnCell) {
const indexInPath = currentSelectedPath.indexOf(mouseCell);
if (indexInPath === -1) {
let canPush = currentSelectedPath.length === 0;
if (currentSelectedPath.length > 0) {
const lastCell = currentSelectedPath[currentSelectedPath.length - 1];
if (lastCell.edges().indexOf(mouseCell) !== -1) {
canPush = true;
}
}
if (canPush) {
currentSelectedPath.push(mouseCell);
}
} else if (indexInPath === currentSelectedPath.length - 2) {
currentSelectedPath.pop();
}
}
} else if (currentSelectedPath.length > 0) {
const word = currentSelectedPath[0].char.word;
if (word.chars.length === currentSelectedPath.length
&& currentSelectedPath.every((c, idx) => word.chars[idx] === c.char)) {
word.guessed = true;
if (currentWords.every(w => w.guessed)) {
generateLevel();
}
}
currentSelectedPath = [];
}
let cellY = startY + scale / 2;
for (const row of field) {
let cellX = startX + scale / 2;
for (const cell of row) {
if (cell.char) {
if (cell.char.word.guessed) {
ctx.fillStyle = '#7cfc00';
ctx.fillRect(cellX - scale / 2, cellY - scale / 2, scale, scale);
} else if (currentSelectedPath.indexOf(cell) !== -1) {
ctx.fillStyle = '#ffa000';
ctx.fillRect(cellX - scale / 2, cellY - scale / 2, scale, scale);
} else if (cell === mouseCell) {
ctx.fillStyle = 'rgba(127, 127, 127, 0.2)';
ctx.fillRect(cellX - scale / 2, cellY - scale / 2, scale, scale);
}
const measure = ctx.measureText(cell.char.value);
const lineHeight = measure.actualBoundingBoxAscent
+ measure.actualBoundingBoxDescent;
const x = cellX - measure.width / 2;
const y = cellY - lineHeight / 2 + measure.actualBoundingBoxAscent;
ctx.fillStyle = '#111';
ctx.fillText(cell.char.value, x, y);
} else {
ctx.fillStyle = 'rgba(127, 127, 127, 0.4)';
ctx.fillRect(cellX - scale / 2, cellY - scale / 2, scale, scale);
}
cellX += scale;
}
cellY += scale;
}
ctx.beginPath();
for (let i = 1; i < field.length; ++i) {
ctx.moveTo(startX + i * scale, startY);
ctx.lineTo(startX + i * scale, startY + field.length * scale);
ctx.moveTo(startX, startY + i * scale);
ctx.lineTo(startX + field.length * scale, startY + i * scale);
}
ctx.stroke();
ctx.lineWidth = 2;
ctx.stokeStyle = '#000';
ctx.beginPath();
for (const word of words) {
if (!word.guessed) {
continue;
}
for (const char of word.chars) {
const { cell } = char;
const j = cell.idx % field.length;
const i = (cell.idx - j) / field.length;
const x = startX + j * scale;
const y = startY + i * scale;
const edges = [
[cell.top, [x, y], [x + scale, y]],
[cell.left, [x, y], [x, y + scale]],
[cell.right, [x + scale, y], [x + scale, y + scale]],
[cell.bottom, [x, y + scale], [x + scale, y + scale]],
];
for (const [v, move, line] of edges) {
if (v?.char === undefined || v.char.word !== cell.char.word) {
ctx.moveTo(...move);
ctx.lineTo(...line);
}
}
}
}
ctx.stroke();
}
async function generateLevel() {
if (currentFile === undefined) {
currentSentence = undefined;
renderField([], []);
currentField = [];
currentWords = [];
regenButton.disabled = true;
nextButton.disabled = true;
fileInput.value = '';
return;
}
let sentence = [];
do {
sentence = await readSentence();
} while (sentence.length === 0);
currentSentence = sentence;
// console.log(sentence);
const { field, words } = generateField(sentence);
renderField(field, words);
currentField = field;
currentWords = words;
regenButton.disabled = false;
nextButton.disabled = false;
}
function handleMouseEvent({ buttons, clientX, clientY }) {
const boundingRect = canvas.getBoundingClientRect();
const x = (clientX - boundingRect.left) / boundingRect.width * canvas.width;
const y = (clientY - boundingRect.top) / boundingRect.height * canvas.height;
mouseState.x = x;
mouseState.y = y;
mouseState.pressed = (buttons & 1) !== 0;
renderField(currentField, currentWords);
}
canvas.onmousemove = (ev) => {
handleMouseEvent(ev);
};
canvas.onmouseenter = (ev) => {
handleMouseEvent(ev);
};
canvas.onmouseleave = (ev) => {
handleMouseEvent(ev);
};
canvas.onmousedown = (ev) => {
handleMouseEvent(ev);
};
canvas.onmouseup = (ev) => {
handleMouseEvent(ev);
};
fileInput.onchange = ({ currentTarget: fileInput }) => {
if (fileInput.files.length !== 1) {
return;
}
currentFile = fileInput.files[0];
generateLevel();
};
regenButton.onclick = () => {
if (currentSentence === undefined) {
return;
}
const { field, words } = generateField(currentSentence);
renderField(field, words);
currentField = field;
currentWords = words;
};
nextButton.onclick = () => {
generateLevel();
};
})();
body {
font-family: system-ui;
background: #f06d06;
color: white;
text-align: center;
}
<html language="ru">
<head>
<title>Филворды</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="MainPanel">
<div class="MainPanel-File">
Выберите текстовый файл:
<input type="file" id="file">
</div>
<hr>
<canvas id="board" width="200" height="200" class="MainPanel-Board"></canvas>
<div class="MainPanel-Controls">
<button type="button" id="regenButton" disabled>Заново</button>
</div>
</div>
<script type="text/javascript" src="./fillwords.js"></script>
</body>
</html>
Ответы (1 шт):
Особой логики не требуется - достаточно в процессе подготовки данных в паре мест индексы слова и буквы в слове дальше передать, а затем вывести.
В функции function generateField(words) {}
изменить способ перебора чтобы передать дальше индекс слова:
wordsPermutation.forEach((word, idx) => {
placedWords.push(placeWordInField(field, word, idx));
})
и дальше в функции function placeWordInField(field, word, idx) {}
записать его, а так же далее при переборе и индекс буквы
const wordObject = {
word,
chars: [],
guessed: false,
indexWord: idx
};
for (let i = 0; i < word.length; ++i) {
const charObject = {
value: word[i],
word: wordObject,
cell: path[i],
indexChar: i
};
path[i].char = charObject;
wordObject.chars.push(charObject);
}
Далее в функции рендера function renderField(field, words) {}
вывести переданное:
ctx.fillText(`${cell.char.value}:[${cell.char.word.indexWord+1},${cell.char.indexChar+1}]` , x, y);
Ну и чтобы это влезло в ячейки:
// const fontSize = Math.max(12, scale - 20);
const fontSize = 10;
Т.е в рабочем виде так:
(function() {
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
const canvas = document.getElementById('board');
const fileInput = document.getElementById('file');
const regenButton = document.getElementById('regenButton');
const nextButton = document.getElementById('nextButton');
let currentFile = undefined;
let currentSentence = undefined;
let currentField = [];
let currentWords = [];
let currentSelectedPath = [];
const mouseState = {
x: 0,
y: 0,
pressed: false,
};
async function parseSentence(reader) {
let offset = undefined;
let allText = '';
while (true) {
const { value: bytes, done } = await reader.read(new Uint8Array(512));
if (bytes) {
allText += textDecoder.decode(bytes);
const endRegex = /[.!?]+/;
const match = endRegex.exec(allText);
if (match) {
const neededText = allText.substring(0, match.index);
offset = match.index + match[0].length;
offset = textEncoder.encode(allText.substring(0, offset)).length;
allText = neededText;
break;
}
}
if (done) {
break;
}
}
const nonAbRegex = /[^a-zа-я]+/ig;
const result = allText.split(nonAbRegex)
.filter(txt => txt.length > 0)
.map(txt => txt.toUpperCase());
return { result, offset };
}
async function readSentence() {
const reader = currentFile.stream().getReader();
const { result, offset } = await parseSentence(reader);
reader.releaseLock();
if (offset) {
currentFile = currentFile.slice(offset);
} else {
currentFile = undefined;
}
return result;
}
function calcFieldSize(minSize) {
let i = 0;
for (; i * i < minSize; ++i);
return i;
}
// https://stackoverflow.com/a/12646864
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
const cellPrototype = {
edges: function() {
return [this.top, this.left, this.right, this.bottom].filter(x => x);
},
};
function createEmptyField(fieldSize) {
const field = [];
for (let i = 0; i < fieldSize; ++i) {
const row = [];
for (let j = 0; j < fieldSize; ++j) {
const cell = {
idx: i * fieldSize + j,
char: undefined,
top: undefined,
left: undefined,
right: undefined,
bottom: undefined,
__proto__: cellPrototype,
};
if (i > 0) {
cell.top = field[i - 1][j];
field[i - 1][j].bottom = cell;
}
if (j > 0) {
cell.left = row[j - 1];
row[j - 1].right = cell;
}
row.push(cell);
}
field.push(row);
}
return field;
}
function getEmptyFieldComponents(field) {
const comps = [];
for (let i = 0; i < field.length * field.length; ++i) {
comps.push(undefined);
}
function dfs(v, comp) {
if (v.char !== undefined || comps[v.idx] !== undefined) {
return false;
}
comps[v.idx] = comp;
for (const u of v.edges()) {
dfs(u, comp);
}
return true;
}
let c = 0;
for (const row of field) {
for (const cell of row) {
if (dfs(cell, c)) {
++c;
}
}
}
return [c, comps];
}
function checkWordFit(field, wordLength) {
const [compsAmount, comps] = getEmptyFieldComponents(field);
const compSizes = [];
for (let i = 0; i < compsAmount; ++i) {
compSizes.push(comps.filter(x => x == i).length);
}
if (compSizes.every(x => x < wordLength)) {
return false;
}
// TODO here we assume that word fit because of checking hardness
return true;
}
function placeWordInField(field, word, idx) {
if (!checkWordFit(field, word.length)) {
// TODO extend
console.log('don\'t fit!!!', field, word);
throw new Error(':(');
}
const neighbours = [];
for (const row of field) {
for (const cell of row) {
if (cell.char !== undefined) {
neighbours.push(Infinity); // ignore busy cells
} else {
neighbours.push(cell.edges().filter(x => x.char === undefined).length);
}
}
}
let firstCells = [];
for (const row of field) {
for (const cell of row) {
if (neighbours[cell.idx] < 4) {
firstCells.push(cell);
}
}
}
if (firstCells.length === 0) {
throw new Error('wtf');
}
const path = [];
function dfs(v) {
if (path.indexOf(v) !== -1) {
return false;
}
if (v.char !== undefined) {
return false;
}
path.push(v);
if (path.length === word.length) {
return true;
}
const us = v.edges();
shuffleArray(us);
us.sort((a, b) => neighbours[a.idx] - neighbours[b.idx]);
for (const u of us) {
if (dfs(u)) {
return true;
}
}
path.pop();
return false;
}
shuffleArray(firstCells);
firstCells.sort((a, b) => neighbours[a.idx] - neighbours[b.idx]);
for (const firstCell of firstCells) {
if (dfs(firstCell)) {
break;
}
}
if (path.length !== word.length) {
// TODO is this could happen?
throw new Error(':(');
}
const wordObject = {
word,
chars: [],
guessed: false,
indexWord: idx
};
for (let i = 0; i < word.length; ++i) {
const charObject = {
value: word[i],
word: wordObject,
cell: path[i],
indexChar: i
};
path[i].char = charObject;
wordObject.chars.push(charObject);
}
for (const cell of path) {
if (cell.char === undefined) {
throw Error('wtf');
}
}
return wordObject;
}
function generateField(words) {
const wordsLength = words.map(s => s.length).reduce((a, b) => a + b, 0);
const fieldSize = calcFieldSize(wordsLength);
const wordsPermutation = [...words];
shuffleArray(wordsPermutation);
const field = createEmptyField(fieldSize);
const placedWords = [];
wordsPermutation.forEach((word, idx) => {
placedWords.push(placeWordInField(field, word, idx));
})
console.log(field.map(row => row.map(cell =>
cell.char ? cell.char.value : '#').join(' ')).join('\n'))
return { field, words: placedWords };
}
function renderField(field, words) {
const ctx = canvas.getContext('2d');
const { width, height } = canvas;
ctx.beginPath();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, width, height);
const size = Math.min(width, height);
let scale = 50;
const mathScale = (size - 20) / field.length;
if (scale > mathScale) {
scale = mathScale;
}
const startX = (width - scale * field.length) / 2;
const startY = (height - scale * field.length) / 2;
ctx.strokeStyle = '#333';
ctx.fillStyle = '#86dbf7';
ctx.lineWidth = 1;
ctx.rect(startX, startY, scale * field.length, scale * field.length);
ctx.stroke();
ctx.fill();
// const fontSize = Math.max(12, scale - 20);
const fontSize = 10;
ctx.font = `bold ${fontSize}px sans-serif`;
const mouseCellX = Math.floor((mouseState.x - startX) / scale);
const mouseCellY = Math.floor((mouseState.y - startY) / scale);
const mouseOnCell = !(mouseCellX < 0 || mouseCellX >= field.length
|| mouseCellY < 0 || mouseCellY >= field.length
|| field[mouseCellY][mouseCellX].char === undefined
|| field[mouseCellY][mouseCellX].char.word.guessed);
const mouseCell = mouseOnCell ? field[mouseCellY][mouseCellX] : undefined;
if (mouseOnCell) {
canvas.style.cursor = 'pointer';
} else {
canvas.style.cursor = 'default';
}
if (mouseState.pressed) {
if (mouseOnCell) {
const indexInPath = currentSelectedPath.indexOf(mouseCell);
if (indexInPath === -1) {
let canPush = currentSelectedPath.length === 0;
if (currentSelectedPath.length > 0) {
const lastCell = currentSelectedPath[currentSelectedPath.length - 1];
if (lastCell.edges().indexOf(mouseCell) !== -1) {
canPush = true;
}
}
if (canPush) {
currentSelectedPath.push(mouseCell);
}
} else if (indexInPath === currentSelectedPath.length - 2) {
currentSelectedPath.pop();
}
}
} else if (currentSelectedPath.length > 0) {
const word = currentSelectedPath[0].char.word;
if (word.chars.length === currentSelectedPath.length
&& currentSelectedPath.every((c, idx) => word.chars[idx] === c.char)) {
word.guessed = true;
if (currentWords.every(w => w.guessed)) {
generateLevel();
}
}
currentSelectedPath = [];
}
let cellY = startY + scale / 2;
for (const row of field) {
let cellX = startX + scale / 2;
for (const cell of row) {
if (cell.char) {
if (cell.char.word.guessed) {
ctx.fillStyle = '#7cfc00';
ctx.fillRect(cellX - scale / 2, cellY - scale / 2, scale, scale);
} else if (currentSelectedPath.indexOf(cell) !== -1) {
ctx.fillStyle = '#ffa000';
ctx.fillRect(cellX - scale / 2, cellY - scale / 2, scale, scale);
} else if (cell === mouseCell) {
ctx.fillStyle = 'rgba(127, 127, 127, 0.2)';
ctx.fillRect(cellX - scale / 2, cellY - scale / 2, scale, scale);
}
const measure = ctx.measureText(cell.char.value);
const lineHeight = measure.actualBoundingBoxAscent
+ measure.actualBoundingBoxDescent;
const x = cellX - measure.width / 2;
const y = cellY - lineHeight / 2 + measure.actualBoundingBoxAscent;
ctx.fillStyle = '#111';
ctx.fillText(`${cell.char.value}:[${cell.char.word.indexWord+1},${cell.char.indexChar+1}]` , x, y);
} else {
ctx.fillStyle = 'rgba(127, 127, 127, 0.4)';
ctx.fillRect(cellX - scale / 2, cellY - scale / 2, scale, scale);
}
cellX += scale;
}
cellY += scale;
}
ctx.beginPath();
for (let i = 1; i < field.length; ++i) {
ctx.moveTo(startX + i * scale, startY);
ctx.lineTo(startX + i * scale, startY + field.length * scale);
ctx.moveTo(startX, startY + i * scale);
ctx.lineTo(startX + field.length * scale, startY + i * scale);
}
ctx.stroke();
ctx.lineWidth = 2;
ctx.stokeStyle = '#000';
ctx.beginPath();
for (const word of words) {
if (!word.guessed) {
continue;
}
for (const char of word.chars) {
const { cell } = char;
const j = cell.idx % field.length;
const i = (cell.idx - j) / field.length;
const x = startX + j * scale;
const y = startY + i * scale;
const edges = [
[cell.top, [x, y], [x + scale, y]],
[cell.left, [x, y], [x, y + scale]],
[cell.right, [x + scale, y], [x + scale, y + scale]],
[cell.bottom, [x, y + scale], [x + scale, y + scale]],
];
for (const [v, move, line] of edges) {
if (v?.char === undefined || v.char.word !== cell.char.word) {
ctx.moveTo(...move);
ctx.lineTo(...line);
}
}
}
}
ctx.stroke();
}
async function generateLevel() {
if (currentFile === undefined) {
currentSentence = undefined;
renderField([], []);
currentField = [];
currentWords = [];
regenButton.disabled = true;
nextButton.disabled = true;
fileInput.value = '';
return;
}
let sentence = [];
do {
sentence = await readSentence();
} while (sentence.length === 0);
currentSentence = sentence;
const { field, words } = generateField(sentence);
renderField(field, words);
currentField = field;
currentWords = words;
regenButton.disabled = false;
nextButton.disabled = false;
}
function handleMouseEvent({ buttons, clientX, clientY }) {
const boundingRect = canvas.getBoundingClientRect();
const x = (clientX - boundingRect.left) / boundingRect.width * canvas.width;
const y = (clientY - boundingRect.top) / boundingRect.height * canvas.height;
mouseState.x = x;
mouseState.y = y;
mouseState.pressed = (buttons & 1) !== 0;
renderField(currentField, currentWords);
}
canvas.onmousemove = (ev) => {
handleMouseEvent(ev);
};
canvas.onmouseenter = (ev) => {
handleMouseEvent(ev);
};
canvas.onmouseleave = (ev) => {
handleMouseEvent(ev);
};
canvas.onmousedown = (ev) => {
handleMouseEvent(ev);
};
canvas.onmouseup = (ev) => {
handleMouseEvent(ev);
};
fileInput.onchange = ({ currentTarget: fileInput }) => {
if (fileInput.files.length !== 1) {
return;
}
currentFile = fileInput.files[0];
generateLevel();
};
regenButton.onclick = () => {
if (currentSentence === undefined) {
return;
}
const { field, words } = generateField(currentSentence);
renderField(field, words);
currentField = field;
currentWords = words;
};
nextButton.onclick = () => {
generateLevel();
};
})();
body {
font-family: system-ui;
background: #f06d06;
color: white;
text-align: center;
}
<html language="ru">
<head>
<title>Филворды</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="MainPanel">
<div class="MainPanel-File">
Выберите текстовый файл:
<input type="file" id="file">
</div>
<hr>
<canvas id="board" width="200" height="200" class="MainPanel-Board"></canvas>
<button id="nextButton">Далее</button>
<div class="MainPanel-Controls">
<button type="button" id="regenButton" disabled>Заново</button>
</div>
</div>
<script type="text/javascript" src="./fillwords.js"></script>
</body>
</html>