From 70c5641452bc9570498c06e1849324703d46fc98 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 29 May 2026 08:26:11 +0300 Subject: [PATCH] =?UTF-8?q?feat(alg9=20ch2=20wave2):=20=C2=A78=20=C2=AB?= =?UTF-8?q?=D0=A7=D1=91=D1=82=D0=BD=D1=8B=D0=B5/=D0=BD=D0=B5=D1=87=D1=91?= =?UTF-8?q?=D1=82=D0=BD=D1=8B=D0=B5=C2=BB=20+=20=C2=A79=20=C2=AB=D0=A1?= =?UTF-8?q?=D0=B4=D0=B2=D0=B8=D0=B3=D0=B8=20=D0=B3=D1=80=D0=B0=D1=84=D0=B8?= =?UTF-8?q?=D0=BA=D0=BE=D0=B2=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/textbooks/algebra_9_ch2.html | 704 +++++++++++++++++++++++++- 1 file changed, 676 insertions(+), 28 deletions(-) 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 = ` -
-
- ${ICONS.theory} - В разработке - § 8 -
-
-

Содержание параграфа «Чётные и нечётные функции» будет добавлено в следующих обновлениях.

-

Раздел 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', ` +

Свойство чётности или нечётности имеет наглядный геометрический смысл:

+ +

Поэтому достаточно построить график на $[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$.

+

Общего вида:

+ `); + + /* INTERACTIVE 1 — симметрия графика */ + html += `
+
ИНТЕРАКТИВ 1
Симметрия графика
+
Выбери функцию ползунком. На графике появятся линии симметрии: для чётной — относительно оси $Oy$, для нечётной — относительно точки $O$.
+
+ +
+
+
+ +
+
+
`; + + /* INTERACTIVE 2 — квикфайр */ + html += `
+
ИНТЕРАКТИВ 2
Чётная, нечётная или общая?
+
Определи тип функции, выбрав один из трёх вариантов.
+
Задача: 1 / 8 · Очки: 0
+
+
+ + + +
+ +
`; + + /* INTERACTIVE 3 — подставь -x */ + html += `
+
ИНТЕРАКТИВ 3
Подставь $-x$
+
Подставь в формулу $-x$ вместо $x$. Сравни с исходной: получилась $f(x)$? — чётная. Получилась $-f(x)$? — нечётная. Что-то другое? — общая.
+
Задача: 1 / 6 · Очки: 0
+
+
+ + + +
+ +
`; + + /* INTERACTIVE 4 — DnD сортер */ + html += `
+
ИНТЕРАКТИВ 4
Сортер: какой тип?
+
Перетащи каждую функцию в подходящий ящик.
+
+
+
Чётная
+
Нечётная
+
Общая
+
+
+ +
`; + + 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 = ` -
-
- ${ICONS.theory} - В разработке - § 9 -
-
-

Содержание параграфа «Сдвиги графиков» будет добавлено в следующих обновлениях.

-

Раздел 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 += `
+
ИНТЕРАКТИВ 1
Слайдер сдвигов
+
Выбери базовую функцию и крути ползунки $a$ и $b$. Синяя кривая — сдвинутый график $y = f(x - a) + b$, серая — исходный.
+
+ + + +
+
+
+ +
+
+
`; + + /* INTERACTIVE 2 — вершина параболы */ + html += `
+
ИНТЕРАКТИВ 2
Сдвиг и вершина параболы
+
Для параболы $y = (x - a)^2 + b$ вершина — в точке $(a;\\ b)$. Введи сумму координат вершины: $a + b$.
+
Задача: 1 / 6 · Очки: 0
+
+
+ $a + b$ = + + +
+ +
`; + + /* INTERACTIVE 3 — куда сдвиг */ + html += `
+
ИНТЕРАКТИВ 3
В какую сторону сдвиг?
+
Определи направление сдвига графика относительно исходной функции.
+
Задача: 1 / 8 · Очки: 0
+
+
+ + + + +
+ +
`; + + /* INTERACTIVE 4 — конструктор формулы */ + html += `
+
ИНТЕРАКТИВ 4
Конструктор формулы по графику
+
Для каждого описания выбери верную формулу из выпадающего списка. Нажми «Проверить» в конце.
+
+
+ +
`; + + 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 += ''; }); + rowsHtml += + '
' + + '
'+(i+1)+'. '+t.desc+'
' + + '' + + '
'; + }); + 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'); }