diff --git a/frontend/textbooks/algebra_8_ch3.html b/frontend/textbooks/algebra_8_ch3.html index 0f3c733..1adf3b1 100644 --- a/frontend/textbooks/algebra_8_ch3.html +++ b/frontend/textbooks/algebra_8_ch3.html @@ -1738,8 +1738,551 @@ function buildP16(){ refresh(); })(); } -function buildP17stub(){ document.getElementById('p17-body').innerHTML = `

§ 17 — Квадратные неравенства. Метод интервалов

Будет в Wave 3.

${secNav('p16','p18')}`; } -function buildP18stub(){ document.getElementById('p18-body').innerHTML = `

§ 18 — Дробно-рациональные неравенства

Будет в Wave 3.

${secNav('p17','final3')}`; } +function buildP17stub(){ buildP17(); } +function buildP18stub(){ buildP18(); } + +/* ============================================================ + § 17 — КВАДРАТНЫЕ НЕРАВЕНСТВА. МЕТОД ИНТЕРВАЛОВ + ============================================================ */ +function buildP17(){ + const box = document.getElementById('p17-body'); + let html = ''; + + html += makeCard('repeat','Повторение',null,` + `); + + html += makeCard('theory','Что такое квадратное неравенство','17.1',` +

Определение. Неравенство вида $ax^2 + bx + c \\gtrless 0$, $a \\neq 0$.

+

Геометрический смысл: найти, где парабола $y = ax^2 + bx + c$ выше или ниже оси OX (в зависимости от знака неравенства).

`); + + html += makeCard('algo','Метод интервалов','17.2',` +
    +
  1. Найти корни уравнения $ax^2 + bx + c = 0$.
  2. +
  3. Если $D < 0$ — знак выражения постоянен (совпадает со знаком $a$).
  4. +
  5. Если $D \\geq 0$ — отметить корни на числовой прямой.
  6. +
  7. Определить знак выражения на каждом интервале (можно подставить пробную точку).
  8. +
  9. Выбрать интервалы, удовлетворяющие неравенству.
  10. +
+

Правило знаков для квадратного: при $a > 0$ — между корнями знак минус, вне — плюс. При $a < 0$ — наоборот.

`); + + html += makeCard('example','Пример',null,` +

Решить $x^2 - 5x + 6 > 0$.

+

Корни: $x_1 = 2$, $x_2 = 3$. $a = 1 > 0$ — парабола вверх.

+

Знаки: $+$ при $x<2$, $-$ при $23$.

+

Ответ: $x \\in (-\\infty;\\,2) \\cup (3;\\,+\\infty)$.

`); + + /* INT 1 — Парабола + закраска */ + html += widget('Парабола и знак','INTERACT 1','Двигай $a, b, c$. Парабола показывает, где выражение положительно (зелёное) и отрицательно (красное).',` +
+ + + +
+ +
`); + + /* INT 2 — Шаговый решатель */ + html += widget('Метод интервалов: шаг за шагом','INTERACT 2','Введите $a, b, c$ для $ax^2 + bx + c \\geq 0$ и нажимайте «Дальше».',` +
+ + + + + + +
+
`); + + /* INT 3 — Тренажёр */ + html += widget('Тренажёр квадратных неравенств','INTERACT 3','Решите неравенство и выберите правильный промежуток-ответ.',` +
Задача 1 / 6Очки: 0
+
+
+ + `); + + /* INT 4 — Drag: парабола ↔ ответ */ + html += widget('Сопоставь параболу и неравенство','INTERACT 4','По описанию ситуации (направление ветвей + расположение корней) подбери решение неравенства $\\geq 0$.',` + ${DND_HINT_HTML} +
+
+
$(-\\infty; x_1] \\cup [x_2; +\\infty)$
+
$[x_1; x_2]$
+
$\\mathbb{R}$ (все числа)
+
$\\emptyset$ (нет решений)
+
+
+ `); + + /* INT 5 — Знак между / вне корней */ + html += widget('Где плюс, где минус?','INTERACT 5','Дана парабола. Кликни на интервал — раскрась знак.',` +

Пусть $y = x^2 - 4x + 3$. Корни: $x_1 = 1$, $x_2 = 3$. Кликни по каждому интервалу и поставь знак.

+
+ `); + + html += makeCard('class','Класс — решите',null,` +
    +
  1. $x^2 - 6x + 5 \\leq 0$
  2. +
  3. $-x^2 + 4x - 3 > 0$
  4. +
  5. $x^2 + 2x + 5 > 0$ (без корней — особый случай)
  6. +
  7. $4x^2 - 9 \\geq 0$
  8. +
`); + + html += makeCard('home','Домашка',null,` +
    +
  1. $x^2 - 7x + 10 < 0$
  2. +
  3. $3x^2 + 2x - 1 \\geq 0$
  4. +
  5. $x^2 + 1 \\leq 0$ (особый случай)
  6. +
  7. При каких $m$ неравенство $x^2 + 6x + m > 0$ верно для всех $x$?
  8. +
`); + + html += secNav('p16', 'p18'); + box.innerHTML = html; + if(window.renderMathInElement) setTimeout(()=>renderMath(box), 0); + + /* INIT 1 — Парабола */ + (function(){ + const aE = document.getElementById('p17p-a'), bE = document.getElementById('p17p-b'), cE = document.getElementById('p17p-c'); + const svg = document.getElementById('p17p-svg'), out = document.getElementById('p17p-out'); + let done = false; + const W = 220, H = 160, x0 = W/2, y0 = H/2, sx = 14, sy = 14; + function refresh(){ + const a = +aE.value, b = +bE.value, c = +cE.value; + document.getElementById('p17p-a-val').textContent = a; + document.getElementById('p17p-b-val').textContent = b; + document.getElementById('p17p-c-val').textContent = c; + let path = ''; + for(let i = 0; i <= 240; i++){ + const x = -W/(2*sx) + i * (W/sx) / 240; + const y = a*x*x + b*x + c; + const cx = x0 + x*sx, cy = y0 - y*sy; + path += (i === 0 ? 'M' : 'L') + cx.toFixed(2) + ',' + cy.toFixed(2) + ' '; + } + const D = b*b - 4*a*c; + const xs = D >= 0 ? [(-b - Math.sqrt(D))/(2*a), (-b + Math.sqrt(D))/(2*a)].sort((p,q)=>p-q) : []; + // фон зон + let s = ''; + if(a !== 0){ + if(D > 0){ + // полосы зелёный/красный на оси + const x1 = x0 + xs[0]*sx, x2 = x0 + xs[1]*sx; + if(a > 0){ + s += ''; + s += ''; + s += ''; + } else { + s += ''; + s += ''; + s += ''; + } + } else { + s += ''; + } + } + s += ''; + s += ''; + s += ''; + xs.forEach(r => { s += ''; }); + svg.innerHTML = s; + let info = '
$D$ = ' + D.toFixed(2) + '
'; + if(D > 0){ + info += '
Корни: $x_1 = ' + fmt(xs[0]) + ',\\ x_2 = ' + fmt(xs[1]) + '$
'; + if(a > 0) info += '
$ax^2+bx+c > 0$: $x < ' + fmt(xs[0]) + '$ или $x > ' + fmt(xs[1]) + '$
$ax^2+bx+c < 0$: $' + fmt(xs[0]) + ' < x < ' + fmt(xs[1]) + '$
'; + else info += '
$ax^2+bx+c > 0$: $' + fmt(xs[0]) + ' < x < ' + fmt(xs[1]) + '$
$ax^2+bx+c < 0$: $x < ' + fmt(xs[0]) + '$ или $x > ' + fmt(xs[1]) + '$
'; + } else if(D === 0){ + const r = -b/(2*a); + info += '
Один корень: $x = ' + fmt(r) + '$
'; + info += '
Парабола касается оси. Знак — везде ' + (a > 0 ? 'положителен (кроме точки $x = ' + fmt(r) + '$)' : 'отрицателен') + '.
'; + } else { + info += '
Корней нет. Парабола ' + (a > 0 ? 'выше' : 'ниже') + ' оси.
'; + info += '
Знак выражения везде ' + (a > 0 ? 'положителен' : 'отрицателен') + '.
'; + } + out.innerHTML = info; renderMath(out); + if(!done){ done = true; setTimeout(()=>{ achievement('p17_parab'); bumpProgress('p17', 14); }, 300); } + } + [aE,bE,cE].forEach(e => e.addEventListener('input', refresh)); + refresh(); + })(); + + /* INIT 2 — Метод интервалов шаговый */ + (function(){ + const stage = document.getElementById('p17s-stage'); + const goBtn = document.getElementById('p17s-go'), nextBtn = document.getElementById('p17s-next'), resetBtn = document.getElementById('p17s-reset'); + let steps = [], idx = 0, awarded = false; + function build(a, b, c){ + const arr = []; + arr.push('Дано: $' + a + 'x^2 ' + (b >= 0 ? '+ ' + b : '- ' + Math.abs(b)) + 'x ' + (c >= 0 ? '+ ' + c : '- ' + Math.abs(c)) + ' \\geq 0$'); + const D = b*b - 4*a*c; + arr.push('Шаг 1. $D = b^2 - 4ac = ' + (b*b) + ' - ' + (4*a*c) + ' = ' + D + '$'); + if(D < 0){ + arr.push('Шаг 2. $D < 0$ — корней нет. Знак совпадает со знаком $a = ' + a + '$.'); + if(a > 0) arr.push('Шаг 3. $a > 0$ — выражение всегда $> 0$, тем более $\\geq 0$. Ответ: $\\mathbb{R}$.'); + else arr.push('Шаг 3. $a < 0$ — выражение всегда $< 0$, ни одна точка не удовлетворяет $\\geq 0$. Ответ: $\\emptyset$.'); + } else if(D === 0){ + const r = -b/(2*a); + arr.push('Шаг 2. $D = 0$ — один корень: $x = ' + fmt(r) + '$.'); + if(a > 0) arr.push('Шаг 3. $a > 0$ — выражение всюду $\\geq 0$, равно нулю только в $x = ' + fmt(r) + '$. Ответ: $\\mathbb{R}$.'); + else arr.push('Шаг 3. $a < 0$ — выражение всюду $\\leq 0$, равно нулю только в $x = ' + fmt(r) + '$. Ответ: $\\{' + fmt(r) + '\\}$.'); + } else { + const x1 = (-b - Math.sqrt(D))/(2*a), x2 = (-b + Math.sqrt(D))/(2*a); + const lo = Math.min(x1, x2), hi = Math.max(x1, x2); + arr.push('Шаг 2. Корни: $x_1 = ' + fmt(lo) + ',\\ x_2 = ' + fmt(hi) + '$'); + if(a > 0){ + arr.push('Шаг 3. $a > 0$ — парабола вверх. Знак: $+$ при $x < x_1$, $-$ между корнями, $+$ при $x > x_2$.'); + arr.push('Ответ: $x \\in (-\\infty;\\,' + fmt(lo) + '] \\cup [' + fmt(hi) + ';\\,+\\infty)$'); + } else { + arr.push('Шаг 3. $a < 0$ — парабола вниз. Знак: $-$ вне, $+$ между корнями.'); + arr.push('Ответ: $x \\in [' + fmt(lo) + ';\\,' + fmt(hi) + ']$'); + } + } + return arr; + } + function render(){ + stage.innerHTML = steps.slice(0, idx + 1).map(s => `
${s}
`).join(''); + renderMath(stage); + if(idx >= steps.length - 1){ + nextBtn.disabled = true; nextBtn.textContent = 'Готово'; + if(!awarded){ awarded = true; achievement('p17_solver'); bumpProgress('p17', 16); confetti(); } + } else { nextBtn.disabled = false; nextBtn.textContent = 'Дальше (' + (idx + 1) + '/' + steps.length + ')'; } + } + goBtn.addEventListener('click', ()=>{ + const a = +document.getElementById('p17s-a').value, b = +document.getElementById('p17s-b').value, c = +document.getElementById('p17s-c').value; + if(!a){ stage.innerHTML = '

$a \\neq 0$

'; renderMath(stage); return; } + steps = build(a, b, c); idx = 0; awarded = false; + goBtn.style.display = 'none'; nextBtn.style.display = ''; resetBtn.style.display = ''; + render(); + }); + nextBtn.addEventListener('click', ()=>{ if(idx < steps.length - 1){ idx++; render(); } }); + resetBtn.addEventListener('click', ()=>{ idx = 0; stage.innerHTML = ''; goBtn.style.display = ''; nextBtn.style.display = 'none'; resetBtn.style.display = 'none'; }); + })(); + + /* INIT 3 — Тренажёр */ + (function(){ + const tasks = [ + { q:'$x^2 - 5x + 6 > 0$', opts:['$(-\\infty;\\,2) \\cup (3;\\,+\\infty)$','$[2;\\,3]$','$(2;\\,3)$','$\\mathbb{R}$'], ok:0 }, + { q:'$x^2 - 4x + 3 \\leq 0$', opts:['$[1;\\,3]$','$(-\\infty;\\,1] \\cup [3;\\,+\\infty)$','$(1;\\,3)$','$\\emptyset$'], ok:0 }, + { q:'$x^2 + 1 > 0$', opts:['$\\mathbb{R}$','$\\emptyset$','$x \\neq 0$','$x > 0$'], ok:0 }, + { q:'$x^2 + 2x + 5 < 0$', opts:['$\\emptyset$','$\\mathbb{R}$','$(-1;\\,1)$','$x < -1$'], ok:0 }, + { q:'$-x^2 + 4 \\geq 0$', opts:['$[-2;\\,2]$','$(-\\infty;\\,-2] \\cup [2;\\,+\\infty)$','$(-2;\\,2)$','$\\mathbb{R}$'], ok:0 }, + { q:'$x^2 - 9 < 0$', opts:['$(-3;\\,3)$','$[-3;\\,3]$','$(-\\infty;\\,-3) \\cup (3;\\,+\\infty)$','$\\emptyset$'], ok:0 }, + ]; + let cur = null, i = 1, score = 0, shuffled = []; + function show(){ + cur = shuffled[i-1]; + document.getElementById('p17t-i').textContent = i; + document.getElementById('p17t-task').innerHTML = '$' + cur.q + '$'; + renderMath(document.getElementById('p17t-task')); + const opts = document.getElementById('p17t-opts'); opts.innerHTML = ''; + cur.opts.forEach((o, k)=>{ + const b = document.createElement('button'); + b.className = 'btn'; b.innerHTML = o; b.style.cssText = 'text-align:left'; + b.addEventListener('click', ()=>{ + const fb = document.getElementById('p17t-fb'); fb.style.display = 'block'; + if(k === cur.ok){ score++; b.classList.add('ok'); feedback(fb, true, '✓'); } + else { b.classList.add('fail'); feedback(fb, false, 'Не то.'); } + document.getElementById('p17t-score').textContent = score; + if(i >= shuffled.length){ setTimeout(()=>{ feedback(fb, score >= 4, 'Итог: ' + score + '/' + shuffled.length); if(score >= 4){ achievement('p17_train'); bumpProgress('p17', 16); confetti(); } }, 700); } + else { i++; setTimeout(show, 900); } + }); + opts.appendChild(b); + }); + renderMath(opts); + document.getElementById('p17t-fb').style.display = 'none'; + } + document.getElementById('p17t-start').addEventListener('click', ()=>{ i=1; score=0; document.getElementById('p17t-score').textContent = 0; shuffled = [...tasks].sort(()=>Math.random()-0.5); show(); }); + })(); + + /* INIT 4 — Drag парабола ↔ ответ */ + (function(){ + const items = [ + { id:1, html:'$a > 0$, есть 2 корня, неравенство $\\geq 0$', cat:'out' }, + { id:2, html:'$a > 0$, есть 2 корня, неравенство $\\leq 0$', cat:'in' }, + { id:3, html:'$a < 0$, есть 2 корня, неравенство $\\geq 0$', cat:'in' }, + { id:4, html:'$a > 0$, $D < 0$, неравенство $\\geq 0$', cat:'all' }, + { id:5, html:'$a > 0$, $D < 0$, неравенство $\\leq 0$', cat:'none' }, + { id:6, html:'$a < 0$, $D < 0$, неравенство $\\geq 0$', cat:'none' }, + { id:7, html:'$a < 0$, $D < 0$, неравенство $\\leq 0$', cat:'all' }, + { id:8, html:'$a < 0$, есть 2 корня, неравенство $\\leq 0$', cat:'out' }, + ]; + const sorter = setupSorter({ poolId:'p17d-pool', cats:['out','in','all','none'], items, scopeSelector:'#p17-body', columnLayout:true }); + document.getElementById('p17d-check').addEventListener('click', ()=>{ + const fb = document.getElementById('p17d-fb'); fb.style.display = 'block'; + if(Object.keys(sorter.placed).length < items.length){ feedback(fb, false, '⚠ Разложите все.'); return; } + let ok = 0; items.forEach(it=>{ if(sorter.placed[it.id] === it.cat) ok++; }); + if(ok === items.length){ feedback(fb, true, '✓ Все верно!'); achievement('p17_drag'); bumpProgress('p17', 14); confetti(); } + else feedback(fb, false, 'Верно ' + ok + ' из ' + items.length); + }); + document.getElementById('p17d-reset').addEventListener('click', ()=>{ sorter.reset(); document.getElementById('p17d-fb').style.display='none'; }); + })(); + + /* INIT 5 — Знаки на интервалах (клик) */ + (function(){ + // 3 интервала: (-inf, 1), (1, 3), (3, inf). Должны быть: +, -, +. + const correct = ['+','-','+']; + const labels = ['$x < 1$', '$1 < x < 3$', '$x > 3$']; + const lineE = document.getElementById('p17z-line'); + function build(){ + let s = '
'; + s += '$-\\infty$'; + [0, 1, 2].forEach(i => { + s += ''; + if(i < 2) s += '●' + (i === 0 ? ' 1 ●' : ' 3 ●').replace('●','') + ''; + }); + s += '$+\\infty$
'; + lineE.innerHTML = s; + renderMath(lineE); + document.querySelectorAll('.p17z-int').forEach(btn => { + btn.addEventListener('click', ()=>{ + const cur = btn.dataset.sign; + const next = cur === '' ? '+' : cur === '+' ? '-' : ''; + btn.dataset.sign = next; + btn.querySelector('.sg').textContent = next || '?'; + btn.style.background = next === '+' ? 'rgba(16,185,129,.2)' : next === '-' ? 'rgba(239,68,68,.2)' : ''; + checkAll(); + }); + }); + } + function checkAll(){ + const all = [...document.querySelectorAll('.p17z-int')]; + const got = all.map(b => b.dataset.sign); + if(got.every((s, i) => s === correct[i])){ + const fb = document.getElementById('p17z-fb'); fb.style.display = 'block'; + feedback(fb, true, '✓ Точно! Между корнями знак минус (так как $a > 0$).'); + achievement('p17_intervals'); bumpProgress('p17', 14); confetti(); + } + } + build(); + })(); +} + +/* ============================================================ + § 18 — ДРОБНО-РАЦИОНАЛЬНЫЕ НЕРАВЕНСТВА + ============================================================ */ +function buildP18(){ + const box = document.getElementById('p18-body'); + let html = ''; + + html += makeCard('repeat','Повторение',null,` +
    +
  • Метод интервалов из § 17 — корни, знаки на интервалах.
  • +
  • ОДЗ (Глава 2 § 12): знаменатель $\\neq 0$.
  • +
  • $\\dfrac{a}{b}$ имеет тот же знак, что и $a \\cdot b$.
  • +
`); + + html += makeCard('theory','Что такое дробно-рациональное неравенство','18.1',` +

Дробно-рациональное — неравенство, в котором есть дроби с переменной в знаменателе:

+
$$\\dfrac{f(x)}{g(x)} \\gtrless 0$$
+

Ключевая идея: знак дроби определяется произведением знаков числителя и знаменателя. Метод интервалов работает, но точки, где знаменатель $= 0$, всегда выколотые (не входят в ОДЗ).

`); + + html += makeCard('algo','Алгоритм','18.2',` +
    +
  1. Привести к виду $\\dfrac{f(x)}{g(x)} \\gtrless 0$ (всё в одну часть, общий знаменатель).
  2. +
  3. Найти нули числителя $f(x) = 0$ и знаменателя $g(x) = 0$.
  4. +
  5. Отметить точки на прямой: нули $f$ — закрашены (если знак $\\geq, \\leq$), нули $g$ — всегда выколотые.
  6. +
  7. Определить знак выражения на каждом интервале.
  8. +
  9. Выбрать интервалы, удовлетворяющие неравенству.
  10. +
`); + + html += makeCard('example','Пример',null,` +

Решим: $\\dfrac{x - 1}{x + 2} \\geq 0$.

+

Нули: числитель $x = 1$ (входит), знаменатель $x = -2$ (выколот).

+

Знаки на интервалах: $(-\\infty;\\,-2)$ — $+$ (минус на минус), $(-2;\\,1)$ — $-$ (плюс на минус), $(1;\\,+\\infty)$ — $+$.

+

Ответ: $x \\in (-\\infty;\\,-2) \\cup [1;\\,+\\infty)$.

`); + + /* INT 1 — Пошаговый решатель */ + html += widget('Пошаговый решатель дроби','INTERACT 1','Решаем $\\dfrac{x-a}{x-b} \\geq 0$ пошагово.',` +
+ $\\dfrac{x -$$}{x -$$} \\geq 0$ + + + +
+
`); + + /* INT 2 — Тренажёр */ + html += widget('Тренажёр дробно-рациональных','INTERACT 2','Выбери правильный ответ.',` +
Задача 1 / 6Очки: 0
+
+
+ + `); + + /* INT 3 — Найди ОДЗ */ + html += widget('Найди ОДЗ','INTERACT 3','По выражению определи запрещённые точки (где знаменатель = 0).',` +
Раунд 1 / 5Очки: 0
+
+
+ + +
+ + `); + + /* INT 4 — Drag: закрашена/выколота */ + html += widget('Закрашена или выколота?','INTERACT 4','Отнеси каждую точку к нужной категории.',` + ${DND_HINT_HTML} +
+
+
Закрашена (входит)
+
Выколота (не входит)
+
+
+ `); + + html += makeCard('class','Класс — решите',null,` +
    +
  1. $\\dfrac{x - 2}{x + 3} > 0$
  2. +
  3. $\\dfrac{x + 1}{x - 4} \\leq 0$
  4. +
  5. $\\dfrac{x^2 - 4}{x - 1} \\geq 0$
  6. +
`); + + html += makeCard('home','Домашка',null,` +
    +
  1. $\\dfrac{x - 3}{x + 1} \\geq 0$
  2. +
  3. $\\dfrac{x + 5}{x^2 - 4} > 0$
  4. +
  5. $\\dfrac{x^2 - 9}{x^2 + 2x - 8} \\leq 0$
  6. +
`); + + html += secNav('p17', 'final3'); + box.innerHTML = html; + if(window.renderMathInElement) setTimeout(()=>renderMath(box), 0); + + /* INIT 1 — Пошаговый */ + (function(){ + const stage = document.getElementById('p18s-stage'); + const goBtn = document.getElementById('p18s-go'), nextBtn = document.getElementById('p18s-next'), resetBtn = document.getElementById('p18s-reset'); + let steps = [], idx = 0, awarded = false; + function build(a, b){ + const arr = []; + arr.push('Дано: $\\dfrac{x - (' + a + ')}{x - (' + b + ')} \\geq 0$'); + arr.push('Шаг 1. Нули числителя: $x = ' + a + '$ (входит, $\\geq$). Нули знаменателя: $x = ' + b + '$ (всегда выколот).'); + const lo = Math.min(a, b), hi = Math.max(a, b); + arr.push('Шаг 2. Отметим на прямой: ' + (a < b ? ('$' + a + '$ (закрашена), $' + b + '$ (выколота)') : ('$' + b + '$ (выколота), $' + a + '$ (закрашена)')) + '.'); + arr.push('Шаг 3. Знаки: подставим $x = ' + (hi + 1) + '$ — числитель $' + (hi + 1 - a) + ' > 0$, знаменатель $' + (hi + 1 - b) + ' > 0$, дробь $> 0$. Чередуем знаки от правого края: $+,\\ -,\\ +$.'); + if(a < b){ + arr.push('Шаг 4. $\\geq 0$ — берём $+$. Это $(-\\infty;\\,' + a + ']$ и $(' + b + ';\\,+\\infty)$.'); + arr.push('Ответ: $x \\in (-\\infty;\\,' + a + '] \\cup (' + b + ';\\,+\\infty)$'); + } else if(a > b){ + arr.push('Шаг 4. $\\geq 0$ — берём $+$. Это $(-\\infty;\\,' + b + ')$ и $[' + a + ';\\,+\\infty)$.'); + arr.push('Ответ: $x \\in (-\\infty;\\,' + b + ') \\cup [' + a + ';\\,+\\infty)$'); + } else { + arr.push('Шаг 4. $a = b$ — дробь равна 1 везде, кроме $x = ' + a + '$ (выколота). $1 \\geq 0$. Ответ: $x \\neq ' + a + '$.'); + } + return arr; + } + function render(){ + stage.innerHTML = steps.slice(0, idx + 1).map(s => `
${s}
`).join(''); + renderMath(stage); + if(idx >= steps.length - 1){ + nextBtn.disabled = true; nextBtn.textContent = 'Готово'; + if(!awarded){ awarded = true; achievement('p18_solver'); bumpProgress('p18', 16); confetti(); } + } else { nextBtn.disabled = false; nextBtn.textContent = 'Дальше (' + (idx + 1) + '/' + steps.length + ')'; } + } + goBtn.addEventListener('click', ()=>{ + const a = +document.getElementById('p18s-a').value, b = +document.getElementById('p18s-b').value; + steps = build(a, b); idx = 0; awarded = false; + goBtn.style.display = 'none'; nextBtn.style.display = ''; resetBtn.style.display = ''; + render(); + }); + nextBtn.addEventListener('click', ()=>{ if(idx < steps.length - 1){ idx++; render(); } }); + resetBtn.addEventListener('click', ()=>{ idx = 0; stage.innerHTML = ''; goBtn.style.display = ''; nextBtn.style.display = 'none'; resetBtn.style.display = 'none'; }); + })(); + + /* INIT 2 — Тренажёр */ + (function(){ + const tasks = [ + { q:'$\\dfrac{x - 3}{x + 1} > 0$', opts:['$(-\\infty;\\,-1) \\cup (3;\\,+\\infty)$','$(-1;\\,3)$','$[-1;\\,3]$','$\\emptyset$'], ok:0 }, + { q:'$\\dfrac{x + 2}{x - 5} \\leq 0$', opts:['$[-2;\\,5)$','$(-2;\\,5)$','$[-2;\\,5]$','$(-\\infty;\\,-2] \\cup (5;\\,+\\infty)$'], ok:0 }, + { q:'$\\dfrac{1}{x - 4} > 0$', opts:['$(4;\\,+\\infty)$','$(-\\infty;\\,4)$','$\\mathbb{R} \\setminus \\{4\\}$','$[4;\\,+\\infty)$'], ok:0 }, + { q:'$\\dfrac{x - 1}{x + 3} \\geq 0$', opts:['$(-\\infty;\\,-3) \\cup [1;\\,+\\infty)$','$[-3;\\,1]$','$(-3;\\,1)$','$\\mathbb{R}$'], ok:0 }, + { q:'$\\dfrac{x + 6}{x} < 0$', opts:['$(-6;\\,0)$','$[-6;\\,0)$','$(-\\infty;\\,-6)$','$(0;\\,+\\infty)$'], ok:0 }, + { q:'$\\dfrac{x - 2}{x - 2} > 0$', opts:['$\\mathbb{R} \\setminus \\{2\\}$','$\\mathbb{R}$','$\\{2\\}$','$\\emptyset$'], ok:0 }, + ]; + let cur = null, i = 1, score = 0, shuffled = []; + function show(){ + cur = shuffled[i-1]; + document.getElementById('p18t-i').textContent = i; + document.getElementById('p18t-task').innerHTML = '$' + cur.q.replace(/^\$|\$$/g,'') + '$'; + renderMath(document.getElementById('p18t-task')); + const opts = document.getElementById('p18t-opts'); opts.innerHTML = ''; + cur.opts.forEach((o, k)=>{ + const b = document.createElement('button'); + b.className = 'btn'; b.innerHTML = o; b.style.cssText = 'text-align:left'; + b.addEventListener('click', ()=>{ + const fb = document.getElementById('p18t-fb'); fb.style.display = 'block'; + if(k === cur.ok){ score++; b.classList.add('ok'); feedback(fb, true, '✓'); } + else { b.classList.add('fail'); feedback(fb, false, 'Не то.'); } + document.getElementById('p18t-score').textContent = score; + if(i >= shuffled.length){ setTimeout(()=>{ feedback(fb, score >= 4, 'Итог: ' + score + '/' + shuffled.length); if(score >= 4){ achievement('p18_intervals'); bumpProgress('p18', 16); confetti(); } }, 700); } + else { i++; setTimeout(show, 900); } + }); + opts.appendChild(b); + }); + renderMath(opts); + document.getElementById('p18t-fb').style.display = 'none'; + } + document.getElementById('p18t-start').addEventListener('click', ()=>{ i=1; score=0; document.getElementById('p18t-score').textContent = 0; shuffled = [...tasks].sort(()=>Math.random()-0.5); show(); }); + })(); + + /* INIT 3 — ОДЗ */ + (function(){ + const tasks = [ + { q:'$\\dfrac{x + 1}{x - 3}$', ans:[3] }, + { q:'$\\dfrac{x^2 + 1}{x(x - 5)}$', ans:[0, 5] }, + { q:'$\\dfrac{1}{x^2 - 4}$', ans:[-2, 2] }, + { q:'$\\dfrac{x + 3}{x^2 - 6x + 9}$', ans:[3] }, + { q:'$\\dfrac{1}{x} + \\dfrac{1}{x + 1}$', ans:[-1, 0] }, + ]; + let cur = null, i = 1, score = 0; + function show(){ + cur = tasks[i-1]; + document.getElementById('p18o-i').textContent = i; + document.getElementById('p18o-task').innerHTML = 'Запрещённые точки для ' + cur.q; + renderMath(document.getElementById('p18o-task')); + document.getElementById('p18o-inp').value = ''; + document.getElementById('p18o-fb').style.display = 'none'; + } + document.getElementById('p18o-go').addEventListener('click', ()=>{ + const fb = document.getElementById('p18o-fb'); fb.style.display = 'block'; + const u = document.getElementById('p18o-inp').value.replace(/[xX\s=]/g, '').split(/[;,]+/).filter(Boolean).map(Number).sort((a,b)=>a-b); + const a = [...cur.ans].sort((p,q)=>p-q); + const ok = u.length === a.length && a.every((v, k) => v === u[k]); + if(ok){ score++; feedback(fb, true, '✓'); } + else feedback(fb, false, 'Правильно: ' + a.join(', ')); + document.getElementById('p18o-score').textContent = score; + if(i >= tasks.length){ setTimeout(()=>{ feedback(fb, score >= 3, 'Итог: ' + score + '/' + tasks.length); if(score >= 3){ achievement('p18_odz'); bumpProgress('p18', 14); confetti(); } }, 700); } + else { i++; setTimeout(show, 900); } + }); + document.getElementById('p18o-start').addEventListener('click', ()=>{ i=1; score=0; document.getElementById('p18o-score').textContent = 0; show(); }); + })(); + + /* INIT 4 — Drag закрашена/выколота */ + (function(){ + const items = [ + { id:1, html:'нуль числителя при $\\geq 0$', cat:'full' }, + { id:2, html:'нуль знаменателя', cat:'open' }, + { id:3, html:'нуль числителя при $> 0$ строго', cat:'open' }, + { id:4, html:'нуль числителя при $\\leq 0$', cat:'full' }, + { id:5, html:'точка вне ОДЗ', cat:'open' }, + { id:6, html:'граница в системе с $\\leq$', cat:'full' }, + { id:7, html:'граница в системе с $<$', cat:'open' }, + { id:8, html:'нуль знаменателя — всегда', cat:'open' }, + ]; + const sorter = setupSorter({ poolId:'p18d-pool', cats:['full','open'], items, scopeSelector:'#p18-body', columnLayout:true }); + document.getElementById('p18d-check').addEventListener('click', ()=>{ + const fb = document.getElementById('p18d-fb'); fb.style.display = 'block'; + if(Object.keys(sorter.placed).length < items.length){ feedback(fb, false, '⚠ Разложите все.'); return; } + let ok = 0; items.forEach(it=>{ if(sorter.placed[it.id] === it.cat) ok++; }); + if(ok === items.length){ feedback(fb, true, '✓ Все верно!'); achievement('p18_odz'); bumpProgress('p18', 14); confetti(); } + else feedback(fb, false, 'Верно ' + ok + ' из ' + items.length); + }); + document.getElementById('p18d-reset').addEventListener('click', ()=>{ sorter.reset(); document.getElementById('p18d-fb').style.display='none'; }); + })(); +} function buildFinal3stub(){ document.getElementById('final3-body').innerHTML = `

Финал главы

Будет в Wave 4 — 7 боссов, увлекательная математика, практика.

${secNav('p18',null)}`; }