From aee927a3b13f5c2875f1196dce0e3b6cf050148a Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 29 May 2026 12:10:52 +0300 Subject: [PATCH] =?UTF-8?q?feat(alg11=20ch3=20wave1):=20=C2=A77=20=C2=AB?= =?UTF-8?q?=D0=A1=D0=B2=D0=BE=D0=B9=D1=81=D1=82=D0=B2=D0=B0=20=D0=BB=D0=BE?= =?UTF-8?q?=D0=B3=D0=B0=D1=80=D0=B8=D1=84=D0=BC=D0=BE=D0=B2=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/textbooks/algebra_11_ch3.html | 448 ++++++++++++++++++++++++- 1 file changed, 444 insertions(+), 4 deletions(-) diff --git a/frontend/textbooks/algebra_11_ch3.html b/frontend/textbooks/algebra_11_ch3.html index bbdfab3..f45bc7b 100644 --- a/frontend/textbooks/algebra_11_ch3.html +++ b/frontend/textbooks/algebra_11_ch3.html @@ -130,6 +130,26 @@ a{color:inherit;text-decoration:none} .spoiler[open] summary::before{content:'\2212'} .spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6} +.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s} +.dnd-pool.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid} +.dnd-pool.col{flex-direction:column;align-items:stretch} +.dnd-pool.col .dnd-chip{width:auto} +.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%} +.dnd-chip:hover{transform:translateY(-1px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)} +.dnd-chip:active{cursor:grabbing} +.dnd-chip.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(217,119,6,.22);transform:translateY(-1px)} +.dnd-chip.dragging{opacity:.28} +.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))} +.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer} +.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)} +.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s} +.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))} +.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em} +.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)} +.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px} +.dnd-hint{font-size:.82rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px} +.dnd-hint svg{width:14px;height:14px;flex-shrink:0} + .col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto} .sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)} .sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)} @@ -450,6 +470,19 @@ function makeCard(kind, title, num, body){ const labels = {repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно'}; return '
'+ICONS[kind]+'
'+(labels[kind]||'')+(title&&title!==labels[kind]?' \xb7 '+title:'')+'
'+(num?'
'+num+'
':'')+'
'+body+'
'; } +function setupSorter(cfg){ + const placed = {}; const pool = document.getElementById(cfg.poolId); const scope = document.querySelector(cfg.scopeSelector); + if(!pool||!scope) return {placed,render:()=>{},reset:()=>{}}; + pool.classList.add('dnd-pool'); if(cfg.columnLayout) pool.classList.add('col'); + let armed = null; + function buildChip(it,isPlaced){ const e=document.createElement('div'); e.className='dnd-chip'+(isPlaced?' placed':''); e.dataset.id=it.id; e.innerHTML=''+it.html+'\xd7'; attach(e,it.id); return e; } + function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); } + function attachBoxTaps(){ scope.querySelectorAll('.drop-box').forEach(box=>{ box.addEventListener('click',ev=>{ if(!armed)return; if(ev.target.closest('.dnd-chip'))return; const di=box.querySelector('[data-cat]'); if(di){placed[armed]=di.dataset.cat;armed=null;render();} }); }); } + function render(){ pool.innerHTML=''; cfg.items.forEach(it=>{if(placed[it.id])return;const c=buildChip(it,false);if(armed===it.id)c.classList.add('armed');pool.appendChild(c);}); cfg.cats.forEach(cat=>{const box=scope.querySelector('.drop-items[data-cat="'+cat+'"]');if(!box)return;box.innerHTML='';cfg.items.forEach(it=>{if(placed[it.id]!==cat)return;box.appendChild(buildChip(it,true));});}); if(window.renderMathInElement)try{renderMath(scope);}catch(_){} } + attachBoxTaps(); render(); + return {placed,render,reset(){ for(const k in placed)delete placed[k];armed=null;render(); }}; +} /* === SVG-хелперы (axes2D, plotFunc, pointWithDrop, asymptote, snapToValue, геом.) === */ @@ -604,15 +637,422 @@ function wireReadBtn(paraId){ function buildP7(){ const box = document.getElementById('p7-body'); let html = ''; - html += makeCard('theory', 'В разработке', '7.0', ` -

Содержание параграфа Свойства логарифмов будет добавлено в Phase 1+.

-

Раздел Phase 0 — skeleton. Здесь появятся теория, примеры и интерактивы.

-

Ключевая формула: $\log_a(bc) = \log_a b + \log_a c$

+ + /* === ТЕОРИЯ === */ + html += makeCard('theory', 'Произведение, частное, степень', '7.1', ` +

Пусть $a > 0$, $a \\ne 1$ и $b > 0$, $c > 0$. Тогда справедливы три основных свойства:

+

$\\log_a(bc) = \\log_a b + \\log_a c$

+

$\\log_a \\dfrac{b}{c} = \\log_a b - \\log_a c$

+

$\\log_a b^n = n \\cdot \\log_a b$

+
Доказательство свойства произведения
+

Пусть $\\log_a b = p$ и $\\log_a c = q$. По определению логарифма $b = a^p$ и $c = a^q$.

+

Тогда $bc = a^p \\cdot a^q = a^{p+q}$, значит $\\log_a(bc) = p + q = \\log_a b + \\log_a c$.

+

Свойства для частного и степени доказываются аналогично — через определение логарифма и свойства степеней.

+
+

Примеры:

+

$\\log_2 6 = \\log_2(2 \\cdot 3) = \\log_2 2 + \\log_2 3 = 1 + \\log_2 3$.

+

$\\log_5 \\dfrac{25}{8} = \\log_5 25 - \\log_5 8 = 2 - 3 \\log_5 2$.

+

$\\log_2 8^3 = 3 \\log_2 8 = 3 \\cdot 3 = 9$.

`); + + html += makeCard('rule', 'Корень и перенос показателя из основания', '7.2', ` +

Логарифм корня. Поскольку $\\sqrt[n]{b} = b^{1/n}$, по свойству степени:

+

$\\log_a \\sqrt[n]{b} = \\log_a b^{1/n} = \\dfrac{1}{n} \\log_a b$

+

Перенос показателя из основания.

+

$\\log_{a^n} b = \\dfrac{1}{n} \\log_a b$

+

Эти два свойства часто объединяют в одно:

+

$\\log_{a^k} b^m = \\dfrac{m}{k} \\log_a b$

+

Примеры:

+

$\\log_2 \\sqrt{8} = \\dfrac{1}{2} \\log_2 8 = \\dfrac{3}{2}$.

+

$\\log_4 16 = \\log_{2^2} 2^4 = \\dfrac{4}{2} \\log_2 2 = 2$.

+

$\\log_{27} \\sqrt[3]{9} = \\log_{3^3} 3^{2/3} = \\dfrac{2/3}{3} = \\dfrac{2}{9}$.

+ `); + + html += makeCard('example', 'Переход к новому основанию', '7.3', ` +

Иногда удобно перейти от $\\log_a b$ к логарифму по другому основанию $c$ ($c > 0$, $c \\ne 1$):

+

$\\log_a b = \\dfrac{\\log_c b}{\\log_c a}$

+

Чаще всего берут $c = 10$ (десятичные) или $c = e$ (натуральные). Полезное следствие при $a \\ne 1$, $b \\ne 1$:

+

$\\log_a b = \\dfrac{1}{\\log_b a}$

+

Применение:

+

$\\log_2 7 = \\dfrac{\\lg 7}{\\lg 2} \\approx \\dfrac{0{,}845}{0{,}301} \\approx 2{,}807$.

+

$\\log_3 5 = \\dfrac{1}{\\log_5 3}$.

+

Связь $\\lg$ и $\\ln$:

+

$\\lg b = \\dfrac{\\ln b}{\\ln 10} \\approx \\dfrac{\\ln b}{2{,}303}$, $\\quad \\ln b = \\dfrac{\\lg b}{\\lg e} \\approx \\dfrac{\\lg b}{0{,}4343}$.

+ `); + + /* === ИНТЕРАКТИВ 1 — пошаговый «применитель» свойств === */ + html += `
+
ИНТЕРАКТИВ 1
Применение свойств логарифма
+
Выбери выражение ползунком и нажимай «Следующий шаг ▶», чтобы открывать решение по одному шагу. Просмотри все 5 выражений — получишь XP.
+
+ +
+
+
+
+ + + +
+
`; + + /* === ИНТЕРАКТИВ 2 — калькулятор логарифмов === */ + html += `
+
ИНТЕРАКТИВ 2
Калькулятор логарифмов
+
Выбери свойство, заполни поля и нажми «Вычислить» — увидишь применение свойства пошагово и численное приближение.
+
+ + + + +
+
+
+
+ +
`; + + /* === ИНТЕРАКТИВ 3 — DnD-сортер === */ + html += `
+
ИНТЕРАКТИВ 3
Сопоставь выражение со значением
+
Перетащи каждое выражение в ящик с верным значением. Используй свойства логарифмов: сумму, разность, степень и корень.
+
8 выражений — 4 ящика
+
+
+
$=1$
+
$=2$
+
$=3$
+
$=5$
+
+
+ +
`; + + /* === ИНТЕРАКТИВ 4 — тренажёр === */ + html += `
+
ИНТЕРАКТИВ 4
Тренажёр свойств
+
6 задач на применение свойств. Введи число (целое или десятичное, допуск $\\pm 0{,}05$).
+
Задача 1 / 6Очки: 0 / 6
+
+
+ ответ = + + + +
+ +
`; + html += secNavFor('p7'); html += readButton('p7'); + box.innerHTML = html; renderMath(box); + + /* === IV1 — пошаговый «применитель» === */ + (function(){ + const TASKS = [ + { + q: '$\\log_2 24 - \\log_2 3$', + steps: [ + 'Используем свойство логарифма частного: $\\log_a b - \\log_a c = \\log_a \\dfrac{b}{c}$.', + 'Получаем $\\log_2 24 - \\log_2 3 = \\log_2 \\dfrac{24}{3} = \\log_2 8$.', + 'Так как $8 = 2^3$, имеем $\\log_2 8 = 3$.', + 'Ответ: $3$.' + ] + }, + { + q: '$\\log_3 5 + \\log_3 18$', + steps: [ + 'Используем свойство логарифма произведения: $\\log_a b + \\log_a c = \\log_a(bc)$.', + 'Получаем $\\log_3 5 + \\log_3 18 = \\log_3(5 \\cdot 18) = \\log_3 90$.', + 'Заметим, что $90 = 9 \\cdot 10$, поэтому $\\log_3 90 = \\log_3 9 + \\log_3 10 = 2 + \\log_3 10$.', + 'Ответ: $2 + \\log_3 10 \\approx 4{,}096$.' + ] + }, + { + q: '$2 \\log_5 3 + \\log_5 \\dfrac{25}{9}$', + steps: [ + 'По свойству степени: $2 \\log_5 3 = \\log_5 3^2 = \\log_5 9$.', + 'Получаем $\\log_5 9 + \\log_5 \\dfrac{25}{9} = \\log_5\\left(9 \\cdot \\dfrac{25}{9}\\right) = \\log_5 25$.', + 'Так как $25 = 5^2$, имеем $\\log_5 25 = 2$.', + 'Ответ: $2$.' + ] + }, + { + q: '$\\log_2 \\sqrt[3]{32}$', + steps: [ + 'Применяем свойство логарифма корня: $\\log_a \\sqrt[n]{b} = \\dfrac{1}{n} \\log_a b$.', + 'Получаем $\\log_2 \\sqrt[3]{32} = \\dfrac{1}{3} \\log_2 32$.', + 'Так как $32 = 2^5$, имеем $\\log_2 32 = 5$, значит $\\dfrac{1}{3} \\cdot 5 = \\dfrac{5}{3}$.', + 'Ответ: $\\dfrac{5}{3} \\approx 1{,}667$.' + ] + }, + { + q: '$\\log_{16} 8$', + steps: [ + 'Перепишем основание и аргумент по основанию $2$: $16 = 2^4$, $8 = 2^3$.', + 'Применяем формулу $\\log_{a^k} b^m = \\dfrac{m}{k} \\log_a b$: $\\log_{2^4} 2^3 = \\dfrac{3}{4} \\log_2 2 = \\dfrac{3}{4}$.', + 'Ответ: $\\dfrac{3}{4} = 0{,}75$.' + ] + } + ]; + const sn = document.getElementById('p7-iv1-sn'); + const nL = document.getElementById('p7-iv1-n'); + const qEl = document.getElementById('p7-iv1-q'); + const stepsEl = document.getElementById('p7-iv1-steps'); + const nextBtn = document.getElementById('p7-iv1-next'); + const allBtn = document.getElementById('p7-iv1-all'); + const resetBtn = document.getElementById('p7-iv1-reset'); + const seen = new Set(); + let _done = false; + let cur = 0, shown = 0; + + function render(){ + const t = TASKS[cur]; + nL.textContent = (cur + 1); + qEl.innerHTML = t.q; + stepsEl.innerHTML = t.steps.map((s, i) => { + const visible = i < shown; + const isLast = i === t.steps.length - 1 && visible; + const bg = isLast ? '#dcfce7' : 'var(--card)'; + const brd = isLast ? '2px solid #16a34a' : '1px solid var(--border)'; + return '
Шаг '+(i+1)+':'+s+'
'; + }).join(''); + renderMath(qEl); + renderMath(stepsEl); + if(shown >= t.steps.length){ + seen.add(cur); + if(!_done && seen.size >= 5){ _done = true; addXp(10, 'p7-iv1'); bumpProgress('p7', 15); } + } + } + function load(n){ cur = Math.max(0, Math.min(TASKS.length - 1, n)); shown = 1; render(); } + sn.addEventListener('input', () => load((+sn.value) - 1)); + nextBtn.addEventListener('click', () => { + if(shown < TASKS[cur].steps.length){ shown++; render(); } + }); + allBtn.addEventListener('click', () => { shown = TASKS[cur].steps.length; render(); }); + resetBtn.addEventListener('click', () => { shown = 1; render(); }); + load(0); + })(); + + /* === IV2 — калькулятор === */ + (function(){ + const modes = ['prod','quot','pow','base']; + const inputsEl = document.getElementById('p7-iv2-inputs'); + const outEl = document.getElementById('p7-iv2-out'); + const fbEl = document.getElementById('p7-iv2-fb'); + const goBtn = document.getElementById('p7-iv2-go'); + let mode = 'prod'; + let used = new Set(); + let _done = false; + + function setMode(m){ + mode = m; + modes.forEach(mm => { + const btn = document.getElementById('p7-iv2-m-'+mm); + if(btn) btn.classList.toggle('primary', mm === m); + }); + fbEl.style.display = 'none'; + outEl.innerHTML = 'Нажми «Вычислить», чтобы увидеть шаги.'; + if(m === 'prod'){ + inputsEl.innerHTML = + '$a$ ='+ + ''+ + '$b$ ='+ + ''+ + '$c$ ='+ + ''; + } else if(m === 'quot'){ + inputsEl.innerHTML = + '$a$ ='+ + ''+ + '$b$ ='+ + ''+ + '$c$ ='+ + ''; + } else if(m === 'pow'){ + inputsEl.innerHTML = + '$a$ ='+ + ''+ + '$b$ ='+ + ''+ + '$n$ ='+ + ''; + } else { // base + inputsEl.innerHTML = + '$a$ ='+ + ''+ + '$b$ ='+ + ''+ + '$c$ ='+ + ''; + } + renderMath(inputsEl); + } + modes.forEach(m => { + const btn = document.getElementById('p7-iv2-m-'+m); + if(btn) btn.addEventListener('click', () => setMode(m)); + }); + setMode('prod'); + + function num(id){ const el = document.getElementById(id); return el ? +el.value : NaN; } + function r(n){ return (Math.abs(n - Math.round(n)) < 1e-9) ? Math.round(n) : (+n.toFixed(4)); } + + goBtn.addEventListener('click', () => { + fbEl.style.display = 'none'; + const a = num('p7-iv2-a'), b = num('p7-iv2-b'), c = num('p7-iv2-c'), n = num('p7-iv2-n'); + // Базовая валидация + if(mode !== 'pow'){ + if(!isFinite(a) || a <= 0 || a === 1 || !isFinite(b) || b <= 0 || !isFinite(c) || c <= 0){ + feedback(fbEl, false, '✗ Требуется $a > 0$, $a \\ne 1$, $b > 0$, $c > 0$.'); return; + } + } else { + if(!isFinite(a) || a <= 0 || a === 1 || !isFinite(b) || b <= 0 || !isFinite(n)){ + feedback(fbEl, false, '✗ Требуется $a > 0$, $a \\ne 1$, $b > 0$.'); return; + } + } + let out = ''; + if(mode === 'prod'){ + const sum = Math.log(b*c) / Math.log(a); + const lb = Math.log(b) / Math.log(a); + const lc = Math.log(c) / Math.log(a); + out = + '
$\\log_{'+r(a)+'}('+r(b)+' \\cdot '+r(c)+') = \\log_{'+r(a)+'} '+r(b)+' + \\log_{'+r(a)+'} '+r(c)+'$
'+ + '
$= '+r(lb)+' + '+r(lc)+' \\approx '+r(sum)+'$
'+ + '
Проверка через Math.log: $\\log_{'+r(a)+'}('+r(b*c)+') \\approx '+r(sum)+'$.
'; + } else if(mode === 'quot'){ + const diff = Math.log(b/c) / Math.log(a); + const lb = Math.log(b) / Math.log(a); + const lc = Math.log(c) / Math.log(a); + out = + '
$\\log_{'+r(a)+'} \\dfrac{'+r(b)+'}{'+r(c)+'} = \\log_{'+r(a)+'} '+r(b)+' - \\log_{'+r(a)+'} '+r(c)+'$
'+ + '
$= '+r(lb)+' - '+r(lc)+' \\approx '+r(diff)+'$
'+ + '
Проверка: $\\log_{'+r(a)+'}('+r(b/c)+') \\approx '+r(diff)+'$.
'; + } else if(mode === 'pow'){ + const lb = Math.log(b) / Math.log(a); + const val = n * lb; + out = + '
$\\log_{'+r(a)+'} '+r(b)+'^{'+r(n)+'} = '+r(n)+' \\cdot \\log_{'+r(a)+'} '+r(b)+'$
'+ + '
$= '+r(n)+' \\cdot '+r(lb)+' \\approx '+r(val)+'$
'+ + '
Проверка: $\\log_{'+r(a)+'}('+r(Math.pow(b,n))+') \\approx '+r(val)+'$.
'; + } else { // base + if(c === 1){ feedback(fbEl, false, '✗ Новое основание $c$ не должно быть равно $1$.'); return; } + const lab = Math.log(b) / Math.log(a); + const lcb = Math.log(b) / Math.log(c); + const lca = Math.log(a) / Math.log(c); + out = + '
$\\log_{'+r(a)+'} '+r(b)+' = \\dfrac{\\log_{'+r(c)+'} '+r(b)+'}{\\log_{'+r(c)+'} '+r(a)+'}$
'+ + '
$\\approx \\dfrac{'+r(lcb)+'}{'+r(lca)+'} \\approx '+r(lab)+'$
'+ + '
Прямое значение: $\\log_{'+r(a)+'} '+r(b)+' \\approx '+r(lab)+'$.
'; + } + outEl.innerHTML = out; + renderMath(outEl); + used.add(mode); + if(!_done && used.size >= 3){ _done = true; addXp(10, 'p7-iv2'); bumpProgress('p7', 15); } + }); + })(); + + /* === IV3 — DnD-сортер === */ + (function(){ + const items = [ + {id:'s1', html:'$\\log_2 8 + \\log_2 4$', cat:'v5'}, + {id:'s2', html:'$\\log_3 81 - \\log_3 3$', cat:'v3'}, + {id:'s3', html:'$2 \\log_5 5$', cat:'v2'}, + {id:'s4', html:'$\\log_2 32 - \\log_2 8$', cat:'v2'}, + {id:'s5', html:'$\\log_3 9 + \\log_3 3$', cat:'v3'}, + {id:'s6', html:'$\\log_2 \\sqrt[3]{8}$', cat:'v1'}, + {id:'s7', html:'$\\log_5 125 + \\log_5 1$', cat:'v3'}, + {id:'s8', html:'$\\log_4 16 + \\log_4 4$', cat:'v3'} + ]; + const cats = ['v1','v2','v3','v5']; + const sorter = setupSorter({ + poolId: 'p7-iv3-pool', + scopeSelector: '#p7-iv3', + items, cats + }); + const checkBtn = document.getElementById('p7-iv3-check'); + const resetBtn = document.getElementById('p7-iv3-reset'); + const fbEl = document.getElementById('p7-iv3-fb'); + let _done = false; + + checkBtn.addEventListener('click', () => { + let ok = 0, total = items.length; + let missed = []; + items.forEach(it => { + if(sorter.placed[it.id] === it.cat) ok++; + else if(!sorter.placed[it.id]) missed.push(it.id); + }); + if(ok === total){ + feedback(fbEl, true, '✓ Отлично! Все 8 выражений на месте.'); + if(!_done){ _done = true; addXp(15, 'p7-iv3'); bumpProgress('p7', 25); } + } else { + const left = missed.length; + feedback(fbEl, false, '✗ Верно: '+ok+' / '+total+'. '+(left ? 'Не размещено: '+left+'.' : 'Проверь свойства логарифмов.')); + } + }); + resetBtn.addEventListener('click', () => { + sorter.reset(); + fbEl.style.display = 'none'; + }); + })(); + + /* === IV4 — тренажёр === */ + (function(){ + const TASKS = [ + { q: '$\\log_2 6 + \\log_2 \\dfrac{16}{3}$', a: 5, hint: '$\\log_2(6 \\cdot 16/3) = \\log_2 32 = 5$.' }, + { q: '$\\log_3 \\sqrt{27}$', a: 1.5, hint: '$\\dfrac{1}{2} \\log_3 27 = \\dfrac{3}{2}$.' }, + { q: '$\\log_5 100 - \\log_5 4$', a: 2, hint: '$\\log_5 (100/4) = \\log_5 25 = 2$.' }, + { q: '$\\log_4 64$', a: 3, hint: '$\\log_{2^2} 2^6 = 6/2 = 3$.' }, + { q: '$\\log_2 3 \\cdot \\log_3 8$', a: 3, hint: '$\\log_2 3 \\cdot \\dfrac{\\log_2 8}{\\log_2 3} = \\log_2 8 = 3$.' }, + { q: '$\\dfrac{\\lg 100}{\\lg 10}$', a: 2, hint: '$\\lg 100 / \\lg 10 = 2/1 = 2$.' } + ]; + const qEl = document.getElementById('p7-iv4-q'); + const ansEl = document.getElementById('p7-iv4-ans'); + const goBtn = document.getElementById('p7-iv4-go'); + const startBtn = document.getElementById('p7-iv4-start'); + const iEl = document.getElementById('p7-iv4-i'); + const sEl = document.getElementById('p7-iv4-s'); + const fbEl = document.getElementById('p7-iv4-fb'); + let idx = 0, score = 0; + let _done = false; + + function render(){ + iEl.textContent = (idx + 1); + sEl.textContent = score; + if(idx >= TASKS.length){ + qEl.innerHTML = 'Все задачи решены! Очков: '+score+' / '+TASKS.length+'.'; + ansEl.disabled = true; goBtn.disabled = true; + if(!_done){ _done = true; addXp(15, 'p7-iv4'); bumpProgress('p7', 25); } + renderMath(qEl); + return; + } + qEl.innerHTML = TASKS[idx].q; + ansEl.value = ''; + ansEl.disabled = false; goBtn.disabled = false; + fbEl.style.display = 'none'; + renderMath(qEl); + setTimeout(() => ansEl.focus(), 30); + } + goBtn.addEventListener('click', () => { + if(idx >= TASKS.length) return; + const v = +ansEl.value; + if(!isFinite(v) || ansEl.value === ''){ feedback(fbEl, false, '✗ Введи число.'); return; } + const t = TASKS[idx]; + if(Math.abs(v - t.a) < 0.05){ + score++; sEl.textContent = score; + feedback(fbEl, true, '✓ Верно! '+t.hint); + setTimeout(() => { idx++; render(); }, 950); + } else { + feedback(fbEl, false, '✗ Неверно. Подсказка: '+t.hint+' Правильный ответ: '+t.a+'.'); + setTimeout(() => { idx++; render(); }, 1900); + } + }); + ansEl.addEventListener('keydown', e => { if(e.key === 'Enter'){ e.preventDefault(); goBtn.click(); } }); + startBtn.addEventListener('click', () => { idx = 0; score = 0; _done = false; render(); }); + render(); + })(); + wireReadBtn('p7'); }