(function(){ const WORDS = [ {word:'ОЛИВЬЕ',hint:'Любимый тейвирский салатик'}, {word:'СЕЛЕДКА ПОД ШУБОЙ',hint:'Любимый тейвирский салатик номер 2'}, {word:'ХОЛОДЕЦ ИЗ МАМОНТА',hint:'Голиафье странное блюдо'}, {word:'ШАМПАНСКОЕ',hint:'Пузырики!'}, {word:'БОЙ КУРАНТОВ',hint:'То, что считают'}, {word:'ПЕПЕЛ В БОКАЛЕ',hint:'Странная специя для шампанского'}, {word:'СЕРПАНТИН',hint:'Новогодний торжественный мусор'}, {word:'ЗАПИСКА',hint:'Для желаний'}, {word:'БЕНГАЛЬСКИЕ ОГНИ',hint:'Пиротехника'}, {word:'БАЛ МАСКАРАД',hint:'Мероприятие'}, {word:'ВЫГОРАНИЕ',hint:'То, что оставим в прошлом году'}, {word:'КОЖУРА',hint:'Ароматное'}, {word:'КРАСНЫЙ НОС',hint:'Дед Мороз -'}, {word:'РОЖДЕСТВЕНСКИЙ ГНОМ',hint:'помощник'}, {word:'ДЕД МОРОЗ',hint:'его все знают'}, {word:'ПАЛЬМА',hint:'Что наряжают в Султанате'}, {word:'КОЛЯДЫ',hint:'Поет народ в Тейвире'}, {word:'СВЯТКИ',hint:'Недели зимних праздников'} ]; const MAX_MISS = 6; // ====== DOM ====== const root = document.getElementById('wg2'); const wordEl = root.querySelector('#wg2-word'); const keyEl = root.querySelector('#wg2-key'); const missEl = root.querySelector('#wg2-miss'); const maxEl = root.querySelector('#wg2-max'); const statusEl = root.querySelector('#wg2-status'); const hintBtn = root.querySelector('#wg2-hint'); const hintBox = root.querySelector('#wg2-hintbox'); const newBtn = root.querySelector('#wg2-new'); const winsEl = root.querySelector('#wg2-wins'); const lossEl = root.querySelector('#wg2-loss'); const streakEl= root.querySelector('#wg2-streak'); const parts = { 1: root.querySelector('.p1'), 2: root.querySelector('.p2'), 3: root.querySelector('.p3'), 4: root.querySelector('.p4'), 5: root.querySelector('.p5'), 6: root.querySelector('.p6'), }; maxEl.textContent = String(MAX_MISS); // ====== State ====== const LS = 'wg2-stats'; let stats = JSON.parse(localStorage.getItem(LS) || '{"w":0,"l":0,"s":0}'); let answer = null; // строка (нормализованная, UPPER) let display = []; // массив символов (для вывода) let opened = new Set(); let used = new Set(); let misses = 0; let hint = ''; renderStats(); // ====== Helpers ====== const RU_ALPHABET = 'ЙЦУКЕНГШЩЗХЪЁФЫВАПРОЛДЖЭЯЧСМИТЬБЮ'; const KEYS = ['Й','Ц','У','К','Е','Н','Г','Ш','Щ','З','Х','Ъ','Ё', 'Ф','Ы','В','А','П','Р','О','Л','Д','Ж','Э', 'Я','Ч','С','М','И','Т','Ь','Б','Ю','-']; function normalize(s){ return s.toUpperCase() .replaceAll('Ё','Е') // Ё == Е .replace(/[A-Z]/g, m => m); // латиницу оставим как есть (не используется в словах) } function isLetter(ch){ return RU_ALPHABET.includes(ch) || ch==='-'; } function randomWord(){ return WORDS[Math.floor(Math.random()*WORDS.length)]; } // ====== Game ====== function newGame(){ const pick = randomWord(); answer = normalize(pick.word); hint = pick.hint || ''; opened.clear(); used.clear(); misses = 0; hintBox.hidden = true; hintBox.textContent = hint ? ('Подсказка: '+hint) : ''; missEl.textContent = '0'; for(let k=1;k<=MAX_MISS;k++){ parts[k].classList.remove('on'); } buildDisplay(); buildKeyboard(); statusEl.hidden = true; statusEl.textContent = ''; } function buildDisplay(){ display = []; wordEl.innerHTML = ''; for(const ch of answer){ if(ch===' '){ display.push(' '); const span = document.createElement('span'); span.className='chr space'; span.textContent=' '; wordEl.appendChild(span); continue; } if(!isLetter(ch)){ // символы сразу открываем display.push(ch); const span = document.createElement('span'); span.className='chr'; span.textContent=ch; wordEl.appendChild(span); continue; } const openedNow = opened.has(ch); display.push(openedNow ? ch : '•'); const span = document.createElement('span'); span.className='chr'; span.textContent = openedNow ? ch : '·'; wordEl.appendChild(span); } } function buildKeyboard(){ keyEl.innerHTML = ''; for(const k of KEYS){ const btn = document.createElement('button'); btn.type='button'; btn.className='wg2-k'; btn.textContent = k; btn.disabled = used.has(k); if(used.has(k)){ btn.classList.add(opened.has(k) ? 'good' : 'bad'); } btn.addEventListener('click', ()=>guess(k)); keyEl.appendChild(btn); } } function guess(letter){ if(!letter) return; letter = normalize(letter); if(letter==='-'){ /* допустим дефис */ } if(used.has(letter)) return; used.add(letter); const was = opened.size; // заменяем Ё на Е в проверке const matchLetter = (ch)=> (ch===letter) || (ch==='Е'&&letter==='Ё') || (ch==='Ё'&&letter==='Е'); let found = false; for(const ch of new Set(answer)){ if(matchLetter(ch) && isLetter(ch)){ if(answer.includes(letter) || (letter==='Е' && answer.includes('Е'))){ opened.add('Е'); // для унификации } } } if(answer.includes(letter) || (letter==='Е' && answer.includes('Е'))){ found = true; } if(found){ // открыть все вхождения for(let i=0;i=MAX_MISS){ stats.l++; stats.s=0; localStorage.setItem(LS, JSON.stringify(stats)); renderStats(); statusEl.hidden=false; statusEl.style.color='var(--bad)'; statusEl.textContent='Поражение. Слово было: «'+answer+'».'; disableKeys(); // открыть слово opened = new Set(answer.split('').filter(isLetter)); buildDisplay(); } } function isWin(){ for(const ch of answer){ if(isLetter(ch) && !opened.has(ch)) return false; } return true; } function disableKeys(){ const btns = keyEl.querySelectorAll('.wg2-k'); btns.forEach(b=> b.disabled=true); } function renderStats(){ winsEl.textContent = stats.w; lossEl.textContent = stats.l; streakEl.textContent = stats.s; } // ====== Keyboard input (физическая) ====== document.addEventListener('keydown', (e)=>{ if(statusEl && !statusEl.hidden) return; // игра окончена let ch = e.key.toUpperCase(); // нормализуем русскую раскладку (и символы) if(ch==='Ё') ch='Е'; if(ch.length===1 && (RU_ALPHABET.includes(ch) || ch==='-')){ guess(ch); } }); // ====== UI ====== newBtn.addEventListener('click', newGame); hintBtn.addEventListener('click', ()=>{ if(!hint) return; hintBox.hidden = !hintBox.hidden; }); // ====== Boot ====== newGame(); })();