diff --git a/frontend/textbooks/algebra_9_ch2.html b/frontend/textbooks/algebra_9_ch2.html
index 05592e5..335fbec 100644
--- a/frontend/textbooks/algebra_9_ch2.html
+++ b/frontend/textbooks/algebra_9_ch2.html
@@ -1118,38 +1118,686 @@ function buildP7(){
}
function buildP8(){
- const root = document.getElementById('p8-body');
- root.innerHTML = `
-
-
-
-
Содержание параграфа «Чётные и нечётные функции» будет добавлено в следующих обновлениях.
-
Раздел Phase 1.
-
-
` + secNav('p7', 'p9') + readButton('p8');
- renderMath(root);
+ const box = document.getElementById('p8-body');
+ let html = '';
+
+ html += makeCard('theory', 'Определения', '8.1', `
+ Функция $y = f(x)$ называется чётной , если её область определения $D(f)$ симметрична относительно нуля и для всех $x \\in D(f)$ выполняется равенство:
+ $f(-x) = f(x)$
+ Функция $y = f(x)$ называется нечётной , если её область определения $D(f)$ симметрична относительно нуля и для всех $x \\in D(f)$ выполняется равенство:
+ $f(-x) = -f(x)$
+ Если ни одно из этих условий не выполняется — функцию называют функцией общего вида .
+ Что значит «$D(f)$ симметрична относительно нуля»?
+ Это значит: если $x \\in D(f)$, то и $-x \\in D(f)$. Например, $D(f) = [-3; 3]$ — симметрична, а $D(f) = [0; +\\infty)$ — нет.
+
`);
+
+ html += makeCard('rule', 'Графическая симметрия', '8.2', `
+ Свойство чётности или нечётности имеет наглядный геометрический смысл:
+
+ График чётной функции симметричен относительно оси $Oy$ (зеркальное отражение).
+ График нечётной функции симметричен относительно начала координат (центральная симметрия — точка $O$).
+
+ Поэтому достаточно построить график на $[0; +\\infty)$ — а на $(-\\infty; 0]$ его можно восстановить отражением.
`);
+
+ html += makeCard('example', 'Примеры функций', '8.3', `
+ Чётные: $y = x^2$, $y = x^4$, $y = |x|$, $y = \\cos x$, $y = x^2 + 1$.
+ Нечётные: $y = x$, $y = x^3$, $y = \\dfrac{1}{x}$, $y = \\sin x$, $y = x^5 - x$.
+ Общего вида:
+
+ $y = x + 1$ — линейная без чётности;
+ $y = x^2 + x$ — смесь чётной и нечётной части;
+ $y = \\sqrt{x}$ — $D(f) = [0; +\\infty)$ не симметрична относительно нуля.
+ `);
+
+ /* INTERACTIVE 1 — симметрия графика */
+ html += `
+
+
Выбери функцию ползунком. На графике появятся линии симметрии: для чётной — относительно оси $Oy$, для нечётной — относительно точки $O$.
+
+ Функция №1 / 6
+
+
+
+
+
+
+
`;
+
+ /* INTERACTIVE 2 — квикфайр */
+ html += `
+
+
Определи тип функции, выбрав один из трёх вариантов.
+
Задача: 1 / 8 · Очки: 0
+
+
+ Чётная
+ Нечётная
+ Общая
+
+
+
`;
+
+ /* INTERACTIVE 3 — подставь -x */
+ html += `
+
+
Подставь в формулу $-x$ вместо $x$. Сравни с исходной: получилась $f(x)$? — чётная. Получилась $-f(x)$? — нечётная. Что-то другое? — общая.
+
Задача: 1 / 6 · Очки: 0
+
+
+ $f(-x) = f(x)$
+ $f(-x) = -f(x)$
+ Другое
+
+
+
`;
+
+ /* INTERACTIVE 4 — DnD сортер */
+ html += `
+
+
Перетащи каждую функцию в подходящий ящик.
+
+
+
Проверить Сбросить
+
+
`;
+
+ box.innerHTML = html + secNav('p7', 'p9') + readButton('p8');
+ renderMath(box);
+
+ /* ===== IV1 wiring — симметрия графика ===== */
+ (function(){
+ const fns = [
+ { tex:'y = x^2', f:x=>x*x, type:'even', xmin:-4, xmax:4 },
+ { tex:'y = x^3', f:x=>x*x*x, type:'odd', xmin:-4, xmax:4 },
+ { tex:'y = |x|', f:x=>Math.abs(x),type:'even', xmin:-4, xmax:4 },
+ { tex:'y = x', f:x=>x, type:'odd', xmin:-4, xmax:4 },
+ { tex:'y = x^2 + x', f:x=>x*x+x, type:'gen', xmin:-4, xmax:4 },
+ { tex:'y = \\dfrac{1}{x}', f:x=>1/x, type:'odd', xmin:-4, xmax:4 }
+ ];
+ const sl = document.getElementById('p8-iv1-fn');
+ const fi = document.getElementById('p8-iv1-fi');
+ const svg = document.getElementById('p8-iv1-svg');
+ const formula = document.getElementById('p8-iv1-formula');
+ const out = document.getElementById('p8-iv1-out');
+ let bumped = false;
+
+ function redraw(){
+ const idx = (+sl.value)-1;
+ const fobj = fns[idx];
+ fi.textContent = (idx+1);
+ const ax = axes2D(380, 280, 28, -4, 4, -4, 4);
+ let g = ax.content;
+
+ // линии симметрии под графиком
+ if (fobj.type === 'even'){
+ // подсветим ось Oy
+ const x0 = ax.toX(0);
+ g += ' ';
+ } else if (fobj.type === 'odd'){
+ // выделим точку O маркером
+ const cx = ax.toX(0), cy = ax.toY(0);
+ g += ' ';
+ // диагонали через O
+ g += ' ';
+ g += ' ';
+ }
+
+ // график
+ if (fobj.type === 'odd' && fobj.tex.indexOf('1') !== -1 && fobj.tex.indexOf('x') !== -1 && fobj.tex.indexOf('dfrac') !== -1){
+ // гипербола: рисуем две ветви
+ g += plotFunc(fobj.f, -4, -0.05, ax.toX, ax.toY, '#059669', 200);
+ g += plotFunc(fobj.f, 0.05, 4, ax.toX, ax.toY, '#059669', 200);
+ } else {
+ g += plotFunc(fobj.f, fobj.xmin, fobj.xmax, ax.toX, ax.toY, '#059669', 300);
+ }
+
+ svg.innerHTML = g;
+ formula.innerHTML = '$' + fobj.tex + '$';
+
+ // подсчёт f(-x)
+ const xprobe = 1.5;
+ let fnegX_tex = '';
+ if (idx === 0) fnegX_tex = '(-x)^2 = x^2 = f(x)';
+ else if (idx === 1) fnegX_tex = '(-x)^3 = -x^3 = -f(x)';
+ else if (idx === 2) fnegX_tex = '|-x| = |x| = f(x)';
+ else if (idx === 3) fnegX_tex = '-x = -f(x)';
+ else if (idx === 4) fnegX_tex = '(-x)^2 + (-x) = x^2 - x \\ne \\pm f(x)';
+ else if (idx === 5) fnegX_tex = '\\dfrac{1}{-x} = -\\dfrac{1}{x} = -f(x)';
+
+ const typeLabel = (fobj.type==='even')?'чётная ':(fobj.type==='odd')?'нечётная ':'общего вида ';
+ const symLabel = (fobj.type==='even')?'ось $Oy$':(fobj.type==='odd')?'точка $O$ (начало координат)':'нет симметрии';
+
+ out.innerHTML =
+ '$f(-x) = ' + fnegX_tex + '$
' +
+ 'Тип: ' + typeLabel + '
' +
+ 'Симметрия: ' + symLabel + '
';
+ renderMath(formula); renderMath(out);
+ if (!bumped){ bumped = true; bumpProgress('p8', 15); addXp(10,'p8-iv1'); }
+ }
+ sl.addEventListener('input', redraw);
+ redraw();
+ })();
+
+ /* ===== IV2 wiring — квикфайр 8 ===== */
+ (function(){
+ const items = [
+ { q:'$f(x) = x^4$', ans:'even', hint:'$(-x)^4 = x^4$ — чётная.' },
+ { q:'$f(x) = x^5$', ans:'odd', hint:'$(-x)^5 = -x^5$ — нечётная.' },
+ { q:'$f(x) = x^2 + 3$', ans:'even', hint:'$(-x)^2 + 3 = x^2 + 3 = f(x)$ — чётная.' },
+ { q:'$f(x) = x^3 + x$', ans:'odd', hint:'$(-x)^3 + (-x) = -x^3 - x = -(x^3+x) = -f(x)$ — нечётная.' },
+ { q:'$f(x) = x^3 + 1$', ans:'gen', hint:'$(-x)^3 + 1 = -x^3 + 1$ — не равно ни $f(x)$, ни $-f(x)$. Общая.' },
+ { q:'$f(x) = |x| - 5$', ans:'even', hint:'$|-x| - 5 = |x| - 5 = f(x)$ — чётная.' },
+ { q:'$f(x) = \\dfrac{1}{x^2}$', ans:'even', hint:'$\\dfrac{1}{(-x)^2} = \\dfrac{1}{x^2} = f(x)$ — чётная.' },
+ { q:'$f(x) = \\dfrac{1}{x^3}$', ans:'odd', hint:'$\\dfrac{1}{(-x)^3} = -\\dfrac{1}{x^3} = -f(x)$ — нечётная.' }
+ ];
+ let i = 0, sc = 0;
+ const idxEl = document.getElementById('p8-iv2-idx');
+ const scEl = document.getElementById('p8-iv2-sc');
+ const qEl = document.getElementById('p8-iv2-q');
+ const fb = document.getElementById('p8-iv2-fb');
+ const eBtn = document.getElementById('p8-iv2-e');
+ const oBtn = document.getElementById('p8-iv2-o');
+ const gBtn = document.getElementById('p8-iv2-g');
+ let bumped = false;
+ function render(){
+ idxEl.textContent = Math.min(i+1, items.length);
+ scEl.textContent = sc;
+ if (i >= items.length){
+ qEl.innerHTML = 'Готово! Результат: ' + sc + ' / ' + items.length;
+ eBtn.disabled = true; oBtn.disabled = true; gBtn.disabled = true;
+ eBtn.style.opacity = .5; oBtn.style.opacity = .5; gBtn.style.opacity = .5;
+ if (!bumped){ bumped = true; bumpProgress('p8', 15); addXp(10,'p8-iv2'); }
+ return;
+ }
+ qEl.innerHTML = items[i].q;
+ fb.style.display = 'none';
+ renderMath(qEl);
+ }
+ function answer(v){
+ if (i >= items.length) return;
+ const it = items[i];
+ const ok = (v === it.ans);
+ if (ok) sc++;
+ feedback(fb, ok, (ok?'✓ Верно. ':'✗ Неверно. ') + it.hint);
+ i++;
+ setTimeout(render, 1000);
+ }
+ eBtn.addEventListener('click', ()=>answer('even'));
+ oBtn.addEventListener('click', ()=>answer('odd'));
+ gBtn.addEventListener('click', ()=>answer('gen'));
+ render();
+ })();
+
+ /* ===== IV3 wiring — подставь -x ===== */
+ (function(){
+ const items = [
+ { q:'$f(x) = 2x^2 - 7$', ans:'a', hint:'$f(-x) = 2(-x)^2 - 7 = 2x^2 - 7 = f(x)$. Чётная.' },
+ { q:'$f(x) = x^5 - 2x$', ans:'b', hint:'$f(-x) = -x^5 + 2x = -(x^5 - 2x) = -f(x)$. Нечётная.' },
+ { q:'$f(x) = x + 1$', ans:'c', hint:'$f(-x) = -x + 1$ — не равно ни $f(x)$, ни $-f(x)$. Общая.' },
+ { q:'$f(x) = -3x^2 + |x|$', ans:'a', hint:'$f(-x) = -3x^2 + |x| = f(x)$. Чётная.' },
+ { q:'$f(x) = x^3 - 3x$', ans:'b', hint:'$f(-x) = -x^3 + 3x = -(x^3 - 3x) = -f(x)$. Нечётная.' },
+ { q:'$f(x) = (x + 2)^2$', ans:'c', hint:'$f(-x) = (-x + 2)^2 = (x - 2)^2$ — не равно ни $f(x)$, ни $-f(x)$. Общая.' }
+ ];
+ let i = 0, sc = 0;
+ const idxEl = document.getElementById('p8-iv3-idx');
+ const scEl = document.getElementById('p8-iv3-sc');
+ const qEl = document.getElementById('p8-iv3-q');
+ const fb = document.getElementById('p8-iv3-fb');
+ const aBtn = document.getElementById('p8-iv3-a');
+ const bBtn = document.getElementById('p8-iv3-b');
+ const cBtn = document.getElementById('p8-iv3-c');
+ let bumped = false;
+ function render(){
+ idxEl.textContent = Math.min(i+1, items.length);
+ scEl.textContent = sc;
+ if (i >= items.length){
+ qEl.innerHTML = 'Готово! Результат: ' + sc + ' / ' + items.length;
+ aBtn.disabled = true; bBtn.disabled = true; cBtn.disabled = true;
+ aBtn.style.opacity = .5; bBtn.style.opacity = .5; cBtn.style.opacity = .5;
+ if (!bumped){ bumped = true; bumpProgress('p8', 25); addXp(15,'p8-iv3'); }
+ return;
+ }
+ qEl.innerHTML = items[i].q;
+ fb.style.display = 'none';
+ renderMath(qEl);
+ }
+ function answer(v){
+ if (i >= items.length) return;
+ const it = items[i];
+ const ok = (v === it.ans);
+ if (ok) sc++;
+ feedback(fb, ok, (ok?'✓ Верно. ':'✗ Неверно. ') + it.hint);
+ i++;
+ setTimeout(render, 1100);
+ }
+ aBtn.addEventListener('click', ()=>answer('a'));
+ bBtn.addEventListener('click', ()=>answer('b'));
+ cBtn.addEventListener('click', ()=>answer('c'));
+ render();
+ })();
+
+ /* ===== IV4 wiring — DnD сортер ===== */
+ (function(){
+ const items = [
+ { id:'a', html:'$y = 5x^2$', cat:'even' },
+ { id:'b', html:'$y = 7x^3$', cat:'odd' },
+ { id:'c', html:'$y = 4x + 1$', cat:'gen' },
+ { id:'d', html:'$y = x^2 - 9$', cat:'even' },
+ { id:'e', html:'$y = x^3 - 2x$', cat:'odd' },
+ { id:'f', html:'$y = \\sqrt{x}$', cat:'gen' }
+ ];
+ const sorter = setupSorter({
+ poolId: 'p8-iv4-pool',
+ scopeSelector: '#p8-iv4',
+ cats: ['even','odd','gen'],
+ items: items
+ });
+ let bumped = false;
+ document.getElementById('p8-iv4-check').addEventListener('click', ()=>{
+ const fb = document.getElementById('p8-iv4-fb');
+ const total = items.length;
+ let correct = 0, placed = 0;
+ items.forEach(it=>{ if(sorter.placed[it.id]){ placed++; if(sorter.placed[it.id]===it.cat) correct++; } });
+ if (placed < total){ feedback(fb, false, 'Размещены не все: ' + placed + ' / ' + total + '.'); return; }
+ const ok = (correct === total);
+ feedback(fb, ok, ok ? '✓ Все верно! ' + correct + ' / ' + total : '✗ Правильно: ' + correct + ' / ' + total);
+ if (ok && !bumped){ bumped = true; bumpProgress('p8', 25); addXp(15,'p8-iv4'); }
+ });
+ document.getElementById('p8-iv4-reset').addEventListener('click', ()=>{
+ sorter.reset();
+ const fb = document.getElementById('p8-iv4-fb'); fb.style.display='none';
+ });
+ })();
+
wireReadBtn('p8');
}
function buildP9(){
- const root = document.getElementById('p9-body');
- root.innerHTML = `
-
-
-
-
Содержание параграфа «Сдвиги графиков» будет добавлено в следующих обновлениях.
-
Раздел Phase 1.
-
-
` + secNav('p8', 'final2') + readButton('p9');
- renderMath(root);
+ const box = document.getElementById('p9-body');
+ let html = '';
+
+ html += makeCard('theory', 'Вертикальный сдвиг $y = f(x) + b$', '9.1', `
+ График функции $y = f(x) + b$ получается из графика $y = f(x)$ параллельным переносом вдоль оси $Oy$:
+
+ если $b > 0$ — вверх на $b$ единиц;
+ если $b < 0$ — вниз на $|b|$ единиц.
+
+ Пример. График $y = x^2 + 3$ — это парабола $y = x^2$, сдвинутая вверх на 3 единицы. Её вершина: $(0;\\ 3)$.
+ Почему так?
+ При том же $x$ значение $y$ увеличивается на $b$ — то есть каждая точка поднимается на $b$ вверх. Это и есть вертикальный перенос.
+
`);
+
+ html += makeCard('rule', 'Горизонтальный сдвиг $y = f(x \\pm a)$', '9.2', `
+ График функции $y = f(x - a)$ получается из графика $y = f(x)$ параллельным переносом вдоль оси $Ox$:
+
+ если $a > 0$ — вправо на $a$ единиц;
+ если $a < 0$ — влево на $|a|$ единиц.
+
+ Внимание! Знак минус внутри аргумента — сдвиг вправо . Знак плюс (то есть $f(x + a)$) — сдвиг влево . Это самая частая ошибка!
+ Пример. График $y = (x - 2)^2$ — парабола $y = x^2$, сдвинутая вправо на 2. Её вершина: $(2;\\ 0)$.
+ А $y = (x + 5)^2$ — сдвиг влево на 5. Вершина: $(-5;\\ 0)$.
`);
+
+ html += makeCard('example', 'Комбинированный сдвиг', '9.3', `
+ График $y = f(x - a) + b$ получается комбинацией двух переносов: сдвиг на $a$ по оси $Ox$ и на $b$ по оси $Oy$.
+
+ $y = (x - 1)^2 + 3$ — парабола с вершиной в $(1;\\ 3)$.
+ $y = (x + 2)^2 - 4$ — парабола с вершиной в $(-2;\\ -4)$.
+ $y = \\sqrt{x - 3} + 1$ — график $y = \\sqrt{x}$, сдвинутый вправо на 3 и вверх на 1.
+ $y = |x + 1| - 2$ — график $y = |x|$, сдвинутый влево на 1 и вниз на 2.
+
+ Лайфхак. Для параболы $y = (x - a)^2 + b$ вершина всегда в точке $(a;\\ b)$.
`);
+
+ /* INTERACTIVE 1 — слайдер сдвигов */
+ html += `
+
+
Выбери базовую функцию и крути ползунки $a$ и $b$. Синяя кривая — сдвинутый график $y = f(x - a) + b$, серая — исходный.
+
+ Функция №1 / 4
+ $a$ =0
+ $b$ =0
+
+
+
+
+
+
+
`;
+
+ /* INTERACTIVE 2 — вершина параболы */
+ html += `
+
+
Для параболы $y = (x - a)^2 + b$ вершина — в точке $(a;\\ b)$. Введи сумму координат вершины: $a + b$.
+
Задача: 1 / 6 · Очки: 0
+
+
+ $a + b$ =
+
+ Проверить
+
+
+
`;
+
+ /* INTERACTIVE 3 — куда сдвиг */
+ html += `
+
+
Определи направление сдвига графика относительно исходной функции.
+
Задача: 1 / 8 · Очки: 0
+
+
+ Вверх
+ Вниз
+ Вправо
+ Влево
+
+
+
`;
+
+ /* INTERACTIVE 4 — конструктор формулы */
+ html += `
+
+
Для каждого описания выбери верную формулу из выпадающего списка. Нажми «Проверить» в конце.
+
+
Проверить Сбросить
+
+
`;
+
+ box.innerHTML = html + secNav('p8', 'final2') + readButton('p9');
+ renderMath(box);
+
+ /* ===== IV1 wiring — слайдер сдвигов ===== */
+ (function(){
+ const fns = [
+ { tex:'x^2', f:x=>x*x, dom:[-6,6] },
+ { tex:'|x|', f:x=>Math.abs(x), dom:[-6,6] },
+ { tex:'\\sqrt{x}', f:x=>Math.sqrt(x), dom:[0,6], shiftDom:true },
+ { tex:'x^3', f:x=>x*x*x, dom:[-6,6] }
+ ];
+ const fnSl = document.getElementById('p9-iv1-fn');
+ const aSl = document.getElementById('p9-iv1-a');
+ const bSl = document.getElementById('p9-iv1-b');
+ const fi = document.getElementById('p9-iv1-fi');
+ const av = document.getElementById('p9-iv1-av');
+ const bv = document.getElementById('p9-iv1-bv');
+ const svg = document.getElementById('p9-iv1-svg');
+ const formula = document.getElementById('p9-iv1-formula');
+ const out = document.getElementById('p9-iv1-out');
+ let bumped = false;
+
+ function redraw(){
+ const idx = (+fnSl.value)-1;
+ const a = +aSl.value, b = +bSl.value;
+ const fobj = fns[idx];
+ fi.textContent = (idx+1);
+ av.textContent = a;
+ bv.textContent = b;
+
+ const ax = axes2D(420, 320, 30, -6, 6, -6, 6);
+ let g = ax.content;
+
+ // исходная (серая)
+ g += plotFunc(fobj.f, fobj.dom[0], fobj.dom[1], ax.toX, ax.toY, '#94a3b8', 250);
+ // сдвинутая (синяя) — y = f(x - a) + b
+ const shifted = x => fobj.f(x - a) + b;
+ const xmin2 = fobj.dom[0] + a, xmax2 = fobj.dom[1] + a;
+ g += plotFunc(shifted, Math.max(xmin2,-6), Math.min(xmax2,6), ax.toX, ax.toY, '#2563eb', 280);
+
+ // маркеры начальных точек
+ // исходная: для x^2, |x|, x^3 — это (0;0); для sqrt — тоже (0;0)
+ const ox = 0, oy = fobj.f(0);
+ if (oy>=-6 && oy<=6){
+ const cx = ax.toX(ox), cy = ax.toY(oy);
+ g += ' ';
+ }
+ // сдвинутая
+ const sx = ox + a, sy = oy + b;
+ if (sx>=-6 && sx<=6 && sy>=-6 && sy<=6){
+ const cx = ax.toX(sx), cy = ax.toY(sy);
+ g += ' ';
+ // стрелка от (0;0) до (a;b)
+ if (a !== 0 || b !== 0){
+ const sx0 = ax.toX(ox), sy0 = ax.toY(oy);
+ g += ' ';
+ }
+ }
+
+ svg.innerHTML = g;
+
+ // формула
+ const aStr = (a >= 0) ? ('- ' + a) : ('+ ' + (-a));
+ const bStr = (b >= 0) ? ('+ ' + b) : ('- ' + (-b));
+ let inner;
+ if (a === 0) inner = fobj.tex;
+ else if (fobj.tex === '\\sqrt{x}') inner = '\\sqrt{x ' + aStr + '}';
+ else if (fobj.tex === '|x|') inner = '|x ' + aStr + '|';
+ else if (fobj.tex === 'x^2') inner = '(x ' + aStr + ')^2';
+ else if (fobj.tex === 'x^3') inner = '(x ' + aStr + ')^3';
+ else inner = fobj.tex;
+ let full = 'y = ' + inner;
+ if (b !== 0) full += ' ' + bStr;
+ formula.innerHTML = '$' + full + '$';
+
+ // направление
+ let dir = [];
+ if (a > 0) dir.push('вправо на ' + a);
+ else if (a < 0) dir.push('влево на ' + (-a));
+ if (b > 0) dir.push('вверх на ' + b);
+ else if (b < 0) dir.push('вниз на ' + (-b));
+ const dirText = dir.length ? 'Сдвиг: ' + dir.join(', ') + '.' : 'Сдвига нет — графики совпадают.';
+ out.innerHTML = dirText;
+
+ renderMath(formula); renderMath(out);
+ if (!bumped){ bumped = true; bumpProgress('p9', 15); addXp(10,'p9-iv1'); }
+ }
+ fnSl.addEventListener('input', redraw);
+ aSl.addEventListener('input', redraw);
+ bSl.addEventListener('input', redraw);
+ redraw();
+ })();
+
+ /* ===== IV2 wiring — вершина параболы ===== */
+ (function(){
+ const items = [
+ { q:'$y = (x - 2)^2 + 3$', ans:5, hint:'Вершина $(2;\\ 3)$, $a + b = 5$.' },
+ { q:'$y = (x + 1)^2 - 4$', ans:-5, hint:'Вершина $(-1;\\ -4)$, $a + b = -5$.' },
+ { q:'$y = (x - 5)^2$', ans:5, hint:'Вершина $(5;\\ 0)$, $a + b = 5$.' },
+ { q:'$y = x^2 + 7$', ans:7, hint:'Вершина $(0;\\ 7)$, $a + b = 7$.' },
+ { q:'$y = (x + 3)^2 + 2$', ans:-1, hint:'Вершина $(-3;\\ 2)$, $a + b = -1$.' },
+ { q:'$y = (x - 4)^2 - 6$', ans:-2, hint:'Вершина $(4;\\ -6)$, $a + b = -2$.' }
+ ];
+ let i = 0, sc = 0;
+ const idxEl = document.getElementById('p9-iv2-idx');
+ const scEl = document.getElementById('p9-iv2-sc');
+ const qEl = document.getElementById('p9-iv2-q');
+ const inEl = document.getElementById('p9-iv2-in');
+ const fb = document.getElementById('p9-iv2-fb');
+ const goBtn = document.getElementById('p9-iv2-go');
+ let bumped = false;
+ function render(){
+ idxEl.textContent = Math.min(i+1, items.length);
+ scEl.textContent = sc;
+ if (i >= items.length){
+ qEl.innerHTML = 'Готово! Результат: ' + sc + ' / ' + items.length;
+ inEl.disabled = true; goBtn.disabled = true; goBtn.style.opacity = .5;
+ if (!bumped){ bumped = true; bumpProgress('p9', 15); addXp(10,'p9-iv2'); }
+ return;
+ }
+ qEl.innerHTML = items[i].q;
+ inEl.value = '';
+ fb.style.display = 'none';
+ renderMath(qEl);
+ inEl.focus();
+ }
+ function check(){
+ if (i >= items.length) return;
+ const v = +inEl.value;
+ if (!Number.isFinite(v) || inEl.value === ''){ feedback(fb, false, 'Введи число.'); return; }
+ const it = items[i];
+ const ok = (v === it.ans);
+ if (ok) sc++;
+ feedback(fb, ok, (ok?'✓ Верно. ':'✗ Неверно. ') + it.hint);
+ i++;
+ setTimeout(render, 1100);
+ }
+ goBtn.addEventListener('click', check);
+ inEl.addEventListener('keydown', e=>{ if (e.key === 'Enter') check(); });
+ render();
+ })();
+
+ /* ===== IV3 wiring — направление сдвига ===== */
+ (function(){
+ const items = [
+ { q:'$y = x^2 + 5$', ans:'u', hint:'$b = 5 > 0$ — вверх.' },
+ { q:'$y = x^2 - 3$', ans:'d', hint:'$b = -3 < 0$ — вниз.' },
+ { q:'$y = (x - 2)^2$', ans:'r', hint:'$x - 2$ → сдвиг вправо на 2.' },
+ { q:'$y = (x + 4)^2$', ans:'l', hint:'$x + 4$ → сдвиг влево на 4.' },
+ { q:'$y = |x| + 1$', ans:'u', hint:'$b = 1 > 0$ — вверх.' },
+ { q:'$y = |x - 7|$', ans:'r', hint:'$x - 7$ → сдвиг вправо на 7.' },
+ { q:'$y = \\sqrt{x + 2}$', ans:'l', hint:'$x + 2$ → сдвиг влево на 2.' },
+ { q:'$y = x^3 - 1$', ans:'d', hint:'$b = -1 < 0$ — вниз.' }
+ ];
+ let i = 0, sc = 0;
+ const idxEl = document.getElementById('p9-iv3-idx');
+ const scEl = document.getElementById('p9-iv3-sc');
+ const qEl = document.getElementById('p9-iv3-q');
+ const fb = document.getElementById('p9-iv3-fb');
+ const uBtn = document.getElementById('p9-iv3-u');
+ const dBtn = document.getElementById('p9-iv3-d');
+ const rBtn = document.getElementById('p9-iv3-r');
+ const lBtn = document.getElementById('p9-iv3-l');
+ let bumped = false;
+ function render(){
+ idxEl.textContent = Math.min(i+1, items.length);
+ scEl.textContent = sc;
+ if (i >= items.length){
+ qEl.innerHTML = 'Готово! Результат: ' + sc + ' / ' + items.length;
+ [uBtn,dBtn,rBtn,lBtn].forEach(b=>{ b.disabled=true; b.style.opacity=.5; });
+ if (!bumped){ bumped = true; bumpProgress('p9', 25); addXp(15,'p9-iv3'); }
+ return;
+ }
+ qEl.innerHTML = items[i].q;
+ fb.style.display = 'none';
+ renderMath(qEl);
+ }
+ function answer(v){
+ if (i >= items.length) return;
+ const it = items[i];
+ const ok = (v === it.ans);
+ if (ok) sc++;
+ feedback(fb, ok, (ok?'✓ Верно. ':'✗ Неверно. ') + it.hint);
+ i++;
+ setTimeout(render, 900);
+ }
+ uBtn.addEventListener('click', ()=>answer('u'));
+ dBtn.addEventListener('click', ()=>answer('d'));
+ rBtn.addEventListener('click', ()=>answer('r'));
+ lBtn.addEventListener('click', ()=>answer('l'));
+ render();
+ })();
+
+ /* ===== IV4 wiring — конструктор формулы ===== */
+ (function(){
+ const tasks = [
+ {
+ desc: 'Парабола $y = x^2$ сдвинута на 2 влево ',
+ opts: [
+ { v:'a', tex:'y = (x - 2)^2' },
+ { v:'b', tex:'y = (x + 2)^2' },
+ { v:'c', tex:'y = x^2 + 2' },
+ { v:'d', tex:'y = x^2 - 2' }
+ ],
+ ans: 'b'
+ },
+ {
+ desc: '$y = |x|$ сдвинута на 4 вверх ',
+ opts: [
+ { v:'a', tex:'y = |x| + 4' },
+ { v:'b', tex:'y = |x| - 4' },
+ { v:'c', tex:'y = |x - 4|' },
+ { v:'d', tex:'y = |x + 4|' }
+ ],
+ ans: 'a'
+ },
+ {
+ desc: '$y = \\sqrt{x}$ сдвинута на 1 вниз ',
+ opts: [
+ { v:'a', tex:'y = \\sqrt{x} + 1' },
+ { v:'b', tex:'y = \\sqrt{x} - 1' },
+ { v:'c', tex:'y = \\sqrt{x - 1}' },
+ { v:'d', tex:'y = \\sqrt{x + 1}' }
+ ],
+ ans: 'b'
+ },
+ {
+ desc: '$y = x^2$ сдвинута на 3 вправо и 2 вверх ',
+ opts: [
+ { v:'a', tex:'y = (x + 3)^2 + 2' },
+ { v:'b', tex:'y = (x - 3)^2 - 2' },
+ { v:'c', tex:'y = (x - 3)^2 + 2' },
+ { v:'d', tex:'y = (x + 3)^2 - 2' }
+ ],
+ ans: 'c'
+ },
+ {
+ desc: '$y = x^3$ сдвинута на 5 влево ',
+ opts: [
+ { v:'a', tex:'y = (x - 5)^3' },
+ { v:'b', tex:'y = (x + 5)^3' },
+ { v:'c', tex:'y = x^3 + 5' },
+ { v:'d', tex:'y = x^3 - 5' }
+ ],
+ ans: 'b'
+ },
+ {
+ desc: '$y = |x|$ сдвинута на 1 вправо и 4 вниз ',
+ opts: [
+ { v:'a', tex:'y = |x - 1| - 4' },
+ { v:'b', tex:'y = |x + 1| - 4' },
+ { v:'c', tex:'y = |x - 1| + 4' },
+ { v:'d', tex:'y = |x + 1| + 4' }
+ ],
+ ans: 'a'
+ }
+ ];
+ const rowsBox = document.getElementById('p9-iv4-rows');
+ let rowsHtml = '';
+ tasks.forEach((t, i)=>{
+ let optsHtml = '— выбери — ';
+ t.opts.forEach(o=>{ optsHtml += ''+o.tex+' '; });
+ rowsHtml +=
+ '' +
+ '
'+(i+1)+'. '+t.desc+'
' +
+ '
'+optsHtml+' ' +
+ '
';
+ });
+ rowsBox.innerHTML = rowsHtml;
+ renderMath(rowsBox);
+ let bumped = false;
+ document.getElementById('p9-iv4-check').addEventListener('click', ()=>{
+ const fb = document.getElementById('p9-iv4-fb');
+ let correct = 0, answered = 0;
+ tasks.forEach((t,i)=>{
+ const v = document.getElementById('p9-iv4-s'+i).value;
+ if (v) answered++;
+ if (v === t.ans) correct++;
+ });
+ if (answered < tasks.length){
+ feedback(fb, false, 'Отвечены не все: ' + answered + ' / ' + tasks.length + '.');
+ return;
+ }
+ const ok = (correct === tasks.length);
+ feedback(fb, ok, ok ? '✓ Все верно! ' + correct + ' / ' + tasks.length : '✗ Правильно: ' + correct + ' / ' + tasks.length);
+ if (ok && !bumped){ bumped = true; bumpProgress('p9', 25); addXp(15,'p9-iv4'); }
+ });
+ document.getElementById('p9-iv4-reset').addEventListener('click', ()=>{
+ tasks.forEach((t,i)=>{ document.getElementById('p9-iv4-s'+i).value = ''; });
+ const fb = document.getElementById('p9-iv4-fb'); fb.style.display='none';
+ });
+ })();
+
wireReadBtn('p9');
}