[html]
<div class="game-frame" id="gameRoot" data-words="Виски, Водка, Бар, Пивас, Коньяк, Абсент">
<div class="game-header">
<div class="word-bank">
<ul class="word-list" id="wordsContainer"></ul>
</div>
</div>
<div class="grid-wrapper">
<div class="wordsearch-grid" id="gridContainer"></div>
</div>
<div class="info-bar">
<div class="status" id="statusMsg">Найди все слова</div>
<div class="button-group">
<button class="btn" id="resetBtn">Новая игра</button>
</div>
</div>
<div class="legend">
ВЫДЕЛЯЙ ТОЛЬКО СЛЕВА НАПРАВО (→) ИЛИ СВЕРХУ ВНИЗ (↓) — игра определит слово автоматически
</div>
</div>
<script>
// Ждём 1 секунду, чтобы обойти сломанный jQuery форума
setTimeout(function() {
(function() {
var container = document.getElementById('gameRoot');
if (!container) return;
var TARGET_WORDS = [];
function fetchWords() {
var attr = container.getAttribute('data-words');
if (attr && attr.trim()) {
var raw = attr.split(',').map(function(w) { return w.trim().toUpperCase(); }).filter(function(w) { return w.length >= 2; });
TARGET_WORDS = [];
var seen = {};
for (var i = 0; i < raw.length; i++) {
if (!seen[raw[i]]) {
seen[raw[i]] = true;
TARGET_WORDS.push(raw[i]);
}
}
}
if (TARGET_WORDS.length === 0) TARGET_WORDS = ["ЗАМОК", "КОКУЁЙ", "КОРОНА"];
}
var ROWS = 14;
var COLS = 14;
var gridData = [];
var placedWords = [];
var foundSet = {};
var foundMask = [];
var isSelecting = false;
var selectedOrder = [];
var selectedSet = {};
var startCell = null;
var gridEl = document.getElementById("gridContainer");
var wordsListEl = document.getElementById("wordsContainer");
var statusSpan = document.getElementById("statusMsg");
var resetBtn = document.getElementById("resetBtn");
function randomRussian() {
var letters = "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
return letters[Math.floor(Math.random() * letters.length)];
}
var RIGHT = [0, 1];
var DOWN = [1, 0];
var PLACEMENT_DIRS = [RIGHT, DOWN];
function tryPlaceWord(word, grid, rows, cols) {
var len = word.length;
var dirs = PLACEMENT_DIRS.slice();
for (var i = dirs.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = dirs[i]; dirs[i] = dirs[j]; dirs[j] = temp;
}
for (var d = 0; d < dirs.length; d++) {
var dir = dirs[d];
var dr = dir[0], dc = dir[1];
var startPositions = [];
for (var r = 0; r < rows; r++) {
for (var c = 0; c < cols; c++) {
var endRow = r + dr * (len - 1);
var endCol = c + dc * (len - 1);
if (endRow >= 0 && endRow < rows && endCol >= 0 && endCol < cols) {
var conflict = false;
for (var k = 0; k < len; k++) {
var nr = r + dr * k;
var nc = c + dc * k;
if (grid[nr][nc] !== '' && grid[nr][nc] !== word[k]) {
conflict = true;
break;
}
}
if (!conflict) {
startPositions.push([r, c]);
}
}
}
}
if (startPositions.length > 0) {
var randPos = startPositions[Math.floor(Math.random() * startPositions.length)];
var startRow = randPos[0], startCol = randPos[1];
var positions = [];
for (var k = 0; k < len; k++) {
var nr = startRow + dr * k;
var nc = startCol + dc * k;
grid[nr][nc] = word[k];
positions.push([nr, nc]);
}
return { word: word, positions: positions };
}
}
return null;
}
function buildFreshGrid() {
var fresh = [];
for (var i = 0; i < ROWS; i++) {
fresh.push(new Array(COLS).fill(''));
}
var shuffledWords = TARGET_WORDS.slice();
for (var i = shuffledWords.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = shuffledWords[i]; shuffledWords[i] = shuffledWords[j]; shuffledWords[j] = temp;
}
var placedList = [];
for (var w = 0; w < shuffledWords.length; w++) {
var word = shuffledWords[w];
var placedObj = tryPlaceWord(word, fresh, ROWS, COLS);
if (!placedObj) {
for (var attempt = 0; attempt < 35; attempt++) {
var copyGrid = fresh.map(function(row) { return row.slice(); });
var retry = tryPlaceWord(word, copyGrid, ROWS, COLS);
if (retry) {
for (var p = 0; p < retry.positions.length; p++) {
fresh[retry.positions[p][0]][retry.positions[p][1]] = retry.word;
}
placedList.push(retry);
break;
}
if (attempt === 34) {
var forced = false;
for (var r = 0; r < ROWS && !forced; r++) {
for (var c = 0; c <= COLS - word.length; c++) {
var ok = true;
for (var q = 0; q < word.length; q++) {
if (fresh[r][c+q] !== '' && fresh[r][c+q] !== word[q]) { ok = false; break; }
}
if (ok) {
var pos = [];
for (var q = 0; q < word.length; q++) {
fresh[r][c+q] = word[q];
pos.push([r, c+q]);
}
placedList.push({ word: word, positions: pos });
forced = true;
break;
}
}
}
if (!forced) {
for (var c = 0; c < COLS && !forced; c++) {
for (var r = 0; r <= ROWS - word.length; r++) {
var ok = true;
for (var q = 0; q < word.length; q++) {
if (fresh[r+q][c] !== '' && fresh[r+q][c] !== word[q]) { ok = false; break; }
}
if (ok) {
var pos = [];
for (var q = 0; q < word.length; q++) {
fresh[r+q][c] = word[q];
pos.push([r+q, c]);
}
placedList.push({ word: word, positions: pos });
forced = true;
break;
}
}
}
}
if (!forced) {
placedList.push({ word: word, positions: [] });
}
}
}
} else {
placedList.push(placedObj);
}
}
for (var i = 0; i < ROWS; i++) {
for (var j = 0; j < COLS; j++) {
if (fresh[i][j] === '') {
fresh[i][j] = randomRussian();
}
}
}
return { grid: fresh, placedWords: placedList };
}
function updateMask() {
var mask = [];
for (var i = 0; i < ROWS; i++) {
mask.push(new Array(COLS).fill(false));
}
for (var p = 0; p < placedWords.length; p++) {
if (foundSet[placedWords[p].word]) {
for (var pos = 0; pos < placedWords[p].positions.length; pos++) {
var r = placedWords[p].positions[pos][0];
var c = placedWords[p].positions[pos][1];
if (r >= 0 && r < ROWS && c >= 0 && c < COLS) mask[r][c] = true;
}
}
}
foundMask = mask;
}
function isValidSelection(order) {
if (order.length <= 1) return true;
var first = order[0].split(',').map(Number);
var last = order[order.length-1].split(',').map(Number);
var dr = last[0] - first[0];
var dc = last[1] - first[1];
if (dr !== 0 && dc !== 0) return false;
if (dr === 0 && dc <= 0) return false;
if (dc === 0 && dr <= 0) return false;
for (var i = 1; i < order.length; i++) {
var prev = order[i-1].split(',').map(Number);
var curr = order[i].split(',').map(Number);
var stepR = curr[0] - prev[0];
var stepC = curr[1] - prev[1];
if (stepR !== 0 && stepC !== 0) return false;
if (dr === 0 && stepC !== 1) return false;
if (dc === 0 && stepR !== 1) return false;
}
return true;
}
function render() {
if (!gridEl) return;
gridEl.style.display = "inline-grid";
gridEl.style.gridTemplateColumns = "repeat(" + COLS + ", minmax(0, auto))";
gridEl.innerHTML = '';
for (var i = 0; i < ROWS; i++) {
for (var j = 0; j < COLS; j++) {
var cell = document.createElement('div');
cell.className = 'cell';
if (foundMask[i][j]) cell.classList.add('cell--found');
var key = i + "," + j;
if (selectedSet[key]) cell.classList.add('cell--selected');
cell.textContent = gridData[i][j];
cell.setAttribute('data-row', i);
cell.setAttribute('data-col', j);
cell.addEventListener('mousedown', (function(i, j) {
return function(e) {
e.preventDefault();
startSelection(i, j);
};
})(i, j));
cell.addEventListener('mouseenter', (function(i, j) {
return function() {
if (isSelecting && startCell) addToSelection(i, j);
};
})(i, j));
cell.addEventListener('mouseup', endSelection);
cell.addEventListener('touchstart', (function(i, j) {
return function(e) {
e.preventDefault();
startSelection(i, j);
};
})(i, j));
cell.addEventListener('touchmove', function(e) {
e.preventDefault();
if (!isSelecting) return;
var touch = e.touches[0];
var elem = document.elementFromPoint(touch.clientX, touch.clientY);
if (elem && elem.classList && elem.classList.contains('cell')) {
var r = parseInt(elem.getAttribute('data-row'));
var c = parseInt(elem.getAttribute('data-col'));
if (!isNaN(r)) addToSelection(r, c);
}
});
cell.addEventListener('touchend', function(e) {
e.preventDefault();
endSelection();
});
gridEl.appendChild(cell);
}
}
}
function startSelection(row, col) {
isSelecting = true;
selectedOrder = [];
selectedSet = {};
startCell = [row, col];
addCell(row, col);
render();
}
function addToSelection(row, col) {
if (!startCell) return;
var newKey = row + "," + col;
if (selectedSet[newKey]) return;
if (selectedOrder.length === 0) {
addCell(row, col);
render();
return;
}
var sR = startCell[0], sC = startCell[1];
var drCandidate = Math.sign(row - sR);
var dcCandidate = Math.sign(col - sC);
if (!(drCandidate === 0 && dcCandidate === 1) && !(drCandidate === 1 && dcCandidate === 0)) {
return;
}
var lastKey = selectedOrder[selectedOrder.length - 1];
var parts = lastKey.split(',');
var lR = parseInt(parts[0]), lC = parseInt(parts[1]);
var expectedRow = lR + drCandidate;
var expectedCol = lC + dcCandidate;
if (expectedRow === row && expectedCol === col) {
addCell(row, col);
render();
return;
}
var backRow = lR - drCandidate;
var backCol = lC - dcCandidate;
if (backRow === row && backCol === col && selectedOrder.length > 1) {
var removed = selectedOrder.pop();
delete selectedSet[removed];
render();
return;
}
}
function addCell(row, col) {
var key = row + "," + col;
if (!selectedSet[key]) {
selectedOrder.push(key);
selectedSet[key] = true;
}
}
function endSelection() {
if (!isSelecting) return;
isSelecting = false;
if (selectedOrder.length === 0) {
startCell = null;
return;
}
if (!isValidSelection(selectedOrder)) {
statusSpan.innerText = "Разрешено выделять только вправо (→) или вниз (↓)";
setTimeout(function() { updateStatusMsg(); }, 1300);
clearVisual();
startCell = null;
return;
}
var selectedWord = getSelectedWord();
if (selectedWord && !foundSet[selectedWord] && TARGET_WORDS.indexOf(selectedWord) !== -1) {
foundSet[selectedWord] = true;
updateMask();
render();
updateWordListUI();
updateStatusMsg();
statusSpan.innerText = "Найдено: " + selectedWord;
setTimeout(function() { updateStatusMsg(); }, 1500);
clearVisual();
}
else if (selectedWord && TARGET_WORDS.indexOf(selectedWord) !== -1 && foundSet[selectedWord]) {
statusSpan.innerText = " \"" + selectedWord + "\" уже найдено";
setTimeout(function() { updateStatusMsg(); }, 1000);
clearVisual();
}
else if (selectedWord && TARGET_WORDS.indexOf(selectedWord) === -1) {
statusSpan.innerText = "\"" + selectedWord + "\" нет в списке";
setTimeout(function() { updateStatusMsg(); }, 1200);
clearVisual();
}
else {
clearVisual();
}
startCell = null;
}
function getSelectedWord() {
if (selectedOrder.length === 0) return null;
if (selectedOrder.length === 1) {
var parts = selectedOrder[0].split(',');
var r = parseInt(parts[0]), c = parseInt(parts[1]);
return gridData[r][c];
}
return selectedOrder.map(function(key) {
var parts = key.split(',');
var r = parseInt(parts[0]), c = parseInt(parts[1]);
return gridData[r][c];
}).join('');
}
function clearVisual() {
selectedOrder = [];
selectedSet = {};
startCell = null;
render();
}
function updateWordListUI() {
wordsListEl.innerHTML = '';
for (var i = 0; i < TARGET_WORDS.length; i++) {
var w = TARGET_WORDS[i];
var li = document.createElement('li');
li.className = 'word-badge';
if (foundSet[w]) li.classList.add('word-badge--found');
li.textContent = w;
wordsListEl.appendChild(li);
}
}
function updateStatusMsg() {
var total = TARGET_WORDS.length;
var foundCount = 0;
for (var w in foundSet) {
if (foundSet.hasOwnProperty(w)) foundCount++;
}
if (foundCount === total) statusSpan.innerText = "Победа! Все слова найдены!";
else statusSpan.innerText = "Найдено: " + foundCount + " / " + total;
}
function resetGame() {
var fresh = buildFreshGrid();
gridData = fresh.grid;
placedWords = fresh.placedWords;
foundSet = {};
updateMask();
clearVisual();
updateWordListUI();
updateStatusMsg();
}
function init() {
fetchWords();
if (TARGET_WORDS.length === 0) TARGET_WORDS = ["ЗАМОК", "КОКУЁЙ", "КОРОНА"];
resetGame();
resetBtn.addEventListener('click', resetGame);
window.addEventListener('mouseup', function() {
if (isSelecting) endSelection();
});
}
init();
})();
}, 1000); // Задержка 1 секунда
</script>
[/html]


















![de other side [crossover]](https://i.imgur.com/BQboz9c.png)












