[html]
<!-- Стили лежат в style/style.css, секция WORDLE. -->
<div id="wg">
<div id="wg-loader">
<div id="wg-loader-spinner"></div>
<span id="wg-loader-text">Загрузка...</span>
</div>
</div>
<script>
(function () {
// ─── Настройки ─────────────────────────────────────────────────────────────
// Список слов по порядку: слово (5 заглавных букв, без Ё) и необязательная подсказка hint (нет поля = нет кнопки).
// Каждый день в 00:00 по Москве берётся следующее; после последнего список идёт по кругу.
const WORDS = [
{word: 'МАГИЯ'},
{word: 'ПЛАМЯ'},
{word: 'РУБЕЖ'},
{word: 'ОГОНЬ'},
{word: 'УГОЛЬ'},
{word: 'ТИТУЛ'},
{word: 'КРОВЬ'},
{word: 'ОБРЯД'},
{word: 'КУЛЬТ'},
{word: 'РУИНЫ'},
{word: 'БАШНЯ'},
{word: 'ОАЗИС'},
{word: 'ЗАМОК'},
{word: 'ФЬОРД'},
{word: 'АРХИВ'},
{word: 'ПЕПЕЛ'},
{word: 'ОРДЕН'},
{word: 'ЕРЕСЬ'},
{word: 'ДОЗОР'},
{word: 'АРЕНА'},
{word: 'ПЕСОК'},
{word: 'КНЯЗЬ'},
{word: 'КАЗНА'},
{word: 'СЕДЛО'},
{word: 'ДЕКАН'},
{word: 'ЛЕГАТ'},
{word: 'ДОГМА'},
{word: 'ОЗЕРО'},
{word: 'ВОЙНА'},
{word: 'ПЛАТО'},
];
const START_DATE = '2026-10-23'; // ← московский день, в который показывается WORDS[0]
const STRICT_DICT = true; // ← true = только слова из словаря; false = любые 5 букв
const VERSION = 1; // ← увеличьте, чтобы сбросить прогресс всех игроков
const API_KEY = `wordle_v${VERSION}`; // ключ хранилища (не содержит слова)
const DICT_URL = 'https://forumstatic.ru/files/001c/d1/ce/31144.js'; // ← URL загруженного dict.js
// ───────────────────────────────────────────────────────────────────────────
// Москва — UTC+3 без перехода на летнее время, поэтому смещаем время на 3 часа
// и считаем целые сутки; так смена слова не зависит от часового пояса игрока.
const DAY_MS = 864e5, MSK_OFFSET = 3 * 36e5;
const mskDay = t => Math.floor((t + MSK_OFFSET) / DAY_MS);
const dayIndex = mskDay(Date.now()) - mskDay(Date.parse(START_DATE));
const {word: SECRET, hint: HINT} = WORDS[((dayIndex % WORDS.length) + WORDS.length) % WORDS.length];
// Прогресс привязан к слову (s.secret === SECRET), поэтому новый день начинается с чистого поля.
// Страница, открытая через полночь, перезагружается, чтобы подхватить новое слово.
setTimeout(() => location.reload(), (mskDay(Date.now()) + 1) * DAY_MS - MSK_OFFSET - Date.now() + 1000);
const STORE = `wg_${SECRET}_v${VERSION}`;
const ROWS = 6, COLS = 5;
const KB = [
['Й','Ц','У','К','Е','Н','Г','Ш','Щ','З','Х','Ъ'],
['Ф','Ы','В','А','П','Р','О','Л','Д','Ж','Э'],
['Ввод','Я','Ч','С','М','И','Т','Ь','Б','Ю','⌫']
];
let grid = Array.from({length: ROWS}, () => Array(COLS).fill(''));
let hues = Array.from({length: ROWS}, () => Array(COLS).fill(''));
let row = 0, col = 0, over = false, busy = false;
let kmap = {};
let dict = null;
function applyState(s) {
({grid, hues, row, col, over, kmap} = s);
}
function load() {
setLoaderText('Данные...');
const handler = e => {
if (e.data?.type !== 'wordle-storage-response' || e.data?.key !== API_KEY) return;
window.removeEventListener('message', handler);
clearTimeout(timeout);
let applied = false;
try {
const s = JSON.parse(e.data.value);
if (s?.secret === SECRET) {
applyState(s);
applied = true;
}
} catch {}
if (!applied) {
try {
const s = JSON.parse(localStorage.getItem(STORE));
if (s?.secret === SECRET) applyState(s);
} catch {}
}
hideLoader();
renderAll();
if (over) {
const won = hues[row - 1]?.every(c => c === 'green');
msg(won ? 'Победа! 🎉' : SECRET, true);
if (won) document.getElementById('wg-share').style.display = 'block';
document.getElementById('wg-hint')?.style.setProperty('display', 'none');
}
};
window.addEventListener('message', handler);
const timeout = setTimeout(() => {
window.removeEventListener('message', handler);
try {
const s = JSON.parse(localStorage.getItem(STORE));
if (s?.secret === SECRET) applyState(s);
} catch {}
hideLoader();
renderAll();
}, 3000);
window.parent.postMessage({type: 'wordle-storage-get', key: API_KEY}, '*');
}
function save() {
const state = {secret: SECRET, grid, hues, row, col, over, kmap};
localStorage.setItem(STORE, JSON.stringify(state));
window.parent.postMessage({type: 'wordle-storage-set', key: API_KEY, value: JSON.stringify(state)}, '*');
}
const tile = (r, c) => document.getElementById(`wt${r}${c}`);
const kbtn = k => document.getElementById(`wk${k}`);
function setTile(r, c, pop) {
const t = tile(r, c);
const letter = grid[r][c], color = hues[r][c];
t.textContent = letter;
t.className = 'wg-tile' + (color ? ` wg-${color}` : letter ? ' wg-has-letter' : '');
if (pop && letter && !color) {
t.style.animation = 'none';
t.offsetHeight;
t.style.animation = 'wg-pop 0.12s ease';
}
}
function syncKeys() {
for (const [k, c] of Object.entries(kmap)) {
const b = kbtn(k);
if (b) b.className = `wg-key${['Ввод','⌫'].includes(k) ? ' wg-wide' : ''} wg-${c}`;
}
}
function renderAll() {
for (let r = 0; r < ROWS; r++) for (let c = 0; c < COLS; c++) setTile(r, c, false);
syncKeys();
}
function check(guess) {
const res = Array(COLS).fill('gray');
const sec = [...SECRET], used = Array(COLS).fill(false);
guess.forEach((l, i) => { if (l === sec[i]) { res[i] = 'green'; used[i] = true; } });
guess.forEach((l, i) => {
if (res[i] === 'green') return;
const j = sec.findIndex((s, idx) => !used[idx] && s === l);
if (j !== -1) { res[i] = 'yellow'; used[j] = true; }
});
return res;
}
function reveal(r, result, done) {
busy = true;
const DUR = 320, GAP = 110;
result.forEach((color, c) => {
setTimeout(() => {
const t = tile(r, c);
t.style.transition = `transform ${DUR / 2}ms ease`;
t.style.transform = 'scaleY(0)';
setTimeout(() => {
hues[r][c] = color;
setTile(r, c, false);
t.style.transform = 'scaleY(1)';
}, DUR / 2);
}, c * GAP);
});
setTimeout(() => { busy = false; done(); }, (COLS - 1) * GAP + DUR + 60);
}
function shake() {
const rowEl = document.querySelectorAll('.wg-row')[row];
rowEl.style.animation = 'none';
rowEl.offsetHeight;
rowEl.style.animation = 'wg-shake 0.4s ease';
setTimeout(() => { rowEl.style.animation = ''; }, 400);
}
function submit() {
if (col < COLS) { shake(); msg('Мало букв'); return; }
const guess = grid[row];
if (STRICT_DICT && dict && !dict.has(guess.join(''))) { shake(); msg('Нет в словаре'); return; }
const result = check(guess);
const pri = {green: 3, yellow: 2, gray: 1};
guess.forEach((l, i) => {
if (!kmap[l] || pri[result[i]] > pri[kmap[l]]) kmap[l] = result[i];
});
const won = result.every(c => c === 'green');
const r = row;
row++; col = 0;
reveal(r, result, () => {
syncKeys();
if (won) {
over = true; save();
window.parent.postMessage({type: 'wordle-solved'}, '*');
msg('Победа! 🎉', true);
document.getElementById('wg-share').style.display = 'block';
document.getElementById('wg-hint')?.style.setProperty('display', 'none');
} else if (row >= ROWS) {
over = true; save();
msg(SECRET, true);
document.getElementById('wg-hint')?.style.setProperty('display', 'none');
} else {
save();
}
});
}
function handle(key) {
if (over || busy) return;
if (key === 'Ввод' || key === 'Enter') {
submit();
} else if (key === '⌫' || key === 'Backspace') {
if (col > 0) { col--; grid[row][col] = ''; setTile(row, col, false); }
} else {
const up = key.toUpperCase();
if (/^[А-ЯЁ]$/.test(up) && col < COLS) {
grid[row][col] = up === 'Ё' ? 'Е' : up;
setTile(row, col, true);
col++;
}
}
}
let msgT;
function msg(text, perm) {
const el = document.getElementById('wg-msg');
el.textContent = text;
el.style.opacity = '1';
clearTimeout(msgT);
if (!perm) msgT = setTimeout(() => { el.style.opacity = '0'; }, 1800);
}
window.wgShare = function () {
const ob = '\x5B', cb = '\x5D';
const text = `${ob}hide=10005000${cb}\n${ob}b${cb}Ответ:${ob}/b${cb} ${SECRET}\n${ob}b${cb}Попыток:${ob}/b${cb} ${row}\n${ob}/hide${cb}`;
const fallback = () => {
const ta = document.createElement('textarea');
ta.value = text;
ta.setAttribute('readonly', '');
ta.style.cssText = 'position:fixed;top:-9999px;left:-9999px;opacity:0;pointer-events:none';
document.body.appendChild(ta);
ta.select();
const onCopy = e => { e.stopImmediatePropagation(); };
document.addEventListener('copy', onCopy, true);
try { document.execCommand('copy'); msg('Скопировано!'); } catch { msg('Ошибка копирования'); }
document.removeEventListener('copy', onCopy, true);
ta.remove();
};
if (navigator.clipboard?.writeText) {
navigator.clipboard.writeText(text).then(() => msg('Скопировано!')).catch(fallback);
} else {
fallback();
}
};
function build() {
const root = document.getElementById('wg');
const board = document.createElement('div');
board.id = 'wg-board';
for (let r = 0; r < ROWS; r++) {
const rEl = document.createElement('div');
rEl.className = 'wg-row';
for (let c = 0; c < COLS; c++) {
const t = document.createElement('div');
t.className = 'wg-tile';
t.id = `wt${r}${c}`;
rEl.appendChild(t);
}
board.appendChild(rEl);
}
root.appendChild(board);
const msgEl = document.createElement('div');
msgEl.id = 'wg-msg';
root.appendChild(msgEl);
const kb = document.createElement('div');
kb.id = 'wg-keyboard';
KB.forEach(krow => {
const rEl = document.createElement('div');
rEl.className = 'wg-kb-row';
krow.forEach(k => {
const b = document.createElement('button');
b.className = 'wg-key' + (['Ввод','⌫'].includes(k) ? ' wg-wide' : '');
b.id = `wk${k}`;
b.textContent = k;
b.addEventListener('click', () => handle(k));
rEl.appendChild(b);
});
kb.appendChild(rEl);
});
root.appendChild(kb);
const share = document.createElement('button');
share.id = 'wg-share';
share.className = 'button';
share.textContent = 'Поделиться результатом';
share.addEventListener('click', () => window.wgShare());
root.appendChild(share);
if (HINT) {
const hintBtn = document.createElement('button');
hintBtn.id = 'wg-hint';
hintBtn.className = 'button';
hintBtn.textContent = 'Подсказка';
hintBtn.style.display = 'block';
hintBtn.addEventListener('click', () => msg(HINT, false));
root.appendChild(hintBtn);
}
document.addEventListener('keydown', e => {
if (e.ctrlKey || e.altKey || e.metaKey) return;
if (e.key === 'Enter') { e.preventDefault(); handle('Enter'); }
else if (e.key === 'Backspace') handle('Backspace');
else handle(e.key);
});
}
function init() {
build();
load();
}
function setLoaderText(t) {
const el = document.getElementById('wg-loader-text');
if (el) el.textContent = t;
}
function hideLoader() {
const el = document.getElementById('wg-loader');
if (el) el.remove();
}
function loadDict() {
setLoaderText('Словарь...');
const s = document.createElement('script');
s.src = DICT_URL;
s.onload = () => {
const d = window.WG_DICT || '';
dict = new Set();
for (let i = 0; i < d.length; i += 5) dict.add(d.slice(i, i + 5));
init();
};
s.onerror = () => init();
document.head.appendChild(s);
}
document.readyState === 'loading'
? document.addEventListener('DOMContentLoaded', loadDict)
: loadDict();
})();
</script>
[/html]
[hideprofile]