feat(alg11 ch2 wave1): §4 «Показательная функция» + двухпанельный визуализатор

This commit is contained in:
Maxim Dolgolyov
2026-05-29 11:55:03 +03:00
parent d0e249613c
commit 3483223f42
+435 -5
View File
@@ -598,15 +598,445 @@ function wireReadBtn(paraId){
function buildP4(){
const box = document.getElementById('p4-body');
let html = '';
html += makeCard('theory', 'В разработке', '4.0', `
<p>Содержание параграфа <b>Показательная функция</b> будет добавлено в Phase 1+.</p>
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 0 — skeleton. Здесь появятся теория, примеры и интерактивы.</p>
<p style="color:var(--muted);font-size:.9rem">Ключевая формула: $y = a^x$</p>
`);
/* === ТЕОРИЯ === */
html += makeCard('theory', 'Определение и графики', '4.1', `
<p>Функция вида $y = a^x$, где $a > 0$, $a \\ne 1$, называется <b>показательной</b>.</p>
<p>Условия $a > 0$ и $a \\ne 1$ — обязательны:</p>
<ul style="margin:8px 0 8px 22px;line-height:1.75">
<li>при $a \\le 0$ степень $a^x$ не определена для большинства $x$ (например, $(-2)^{1/2} = \\sqrt{-2}$ не существует в $\\mathbb{R}$);</li>
<li>при $a = 1$ получаем $y = 1^x = 1$ — постоянная функция, лишённая показательного характера.</li>
</ul>
<p>Примеры показательных функций:</p>
<ul style="margin:8px 0 8px 22px;line-height:1.75">
<li>$y = 2^x$, $y = 3^x$, $y = 10^x$ — основания больше единицы;</li>
<li>$y = \\left(\\dfrac{1}{2}\\right)^x$, $y = (0{,}3)^x$ — основания между нулём и единицей;</li>
<li>$y = e^x$, где $e \\approx 2{,}718$ — <b>натуральная экспонента</b> (играет центральную роль в анализе).</li>
</ul>
<p>График показательной функции:</p>
<ul style="margin:8px 0 8px 22px;line-height:1.75">
<li>при $a > 1$ — <b>возрастающая</b> кривая, круто стремящаяся вверх при $x \\to +\\infty$;</li>
<li>при $0 < a < 1$ — <b>убывающая</b> кривая, круто стремящаяся вверх при $x \\to -\\infty$.</li>
</ul>
<p>Все графики $y = a^x$ проходят через точку $(0; 1)$ — ведь $a^0 = 1$ при любом $a > 0$.</p>`);
html += makeCard('rule', 'Свойства показательной функции', '4.2', `
<p>Сведём все ключевые свойства функции $y = a^x$ в одну таблицу — отдельно для двух случаев $a > 1$ и $0 < a < 1$:</p>
<div style="overflow-x:auto;margin:10px 0">
<table style="width:100%;border-collapse:collapse;font-size:.88rem">
<thead>
<tr style="background:var(--sec-acc-soft)">
<th style="padding:8px;border:1px solid var(--border);text-align:left">Свойство</th>
<th style="padding:8px;border:1px solid var(--border);text-align:left;color:#1d4ed8">$a > 1$</th>
<th style="padding:8px;border:1px solid var(--border);text-align:left;color:#c2410c">$0 < a < 1$</th>
</tr>
</thead>
<tbody>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>$D(y)$ — область определения</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$\\mathbb{R}$ — вся числовая прямая</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>$E(y)$ — область значений</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$(0; +\\infty)$ — только положительные значения</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Нули</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">нет — график не пересекает ось $Ox$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Знакопостоянство</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$y > 0$ при всех $x \\in \\mathbb{R}$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Монотонность</b></td><td style="padding:7px;border:1px solid var(--border)"><b>возрастает</b> на $\\mathbb{R}$</td><td style="padding:7px;border:1px solid var(--border)"><b>убывает</b> на $\\mathbb{R}$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Точка пересечения с $Oy$</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$(0; 1)$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Асимптота</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$y = 0$ (ось абсцисс)</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>При $x \\to +\\infty$</b></td><td style="padding:7px;border:1px solid var(--border)">$y \\to +\\infty$</td><td style="padding:7px;border:1px solid var(--border)">$y \\to 0$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>При $x \\to -\\infty$</b></td><td style="padding:7px;border:1px solid var(--border)">$y \\to 0$</td><td style="padding:7px;border:1px solid var(--border)">$y \\to +\\infty$</td></tr>
</tbody>
</table>
</div>
<p><b>Ключевая идея:</b> два графика $y = a^x$ и $y = (1/a)^x$ симметричны относительно оси $Oy$, потому что $(1/a)^x = a^{-x}$.</p>`);
html += makeCard('example', 'Показательная функция в реальной жизни', '4.3', `
<p>Показательная функция описывает <b>огромное число</b> процессов в природе и технике — везде, где скорость изменения пропорциональна текущему значению величины.</p>
<ul style="margin:8px 0 8px 22px;line-height:1.85">
<li><b>Радиоактивный распад:</b> $N(t) = N_0 \\cdot 2^{-t/T}$, где $T$ — период полураспада (для $^{14}\\text{C}$ это $\\approx 5730$ лет — основа радиоуглеродного датирования).</li>
<li><b>Сложные проценты (банковский вклад):</b> $S = S_0 (1 + r)^n$, где $r$ — ставка, $n$ — число лет.</li>
<li><b>Рост популяции:</b> $P(t) = P_0 \\cdot e^{kt}$ (модель Мальтуса), описывает рост числа бактерий, клеток в чашке Петри.</li>
<li><b>Барометрическая формула:</b> $p(h) = p_0 \\cdot e^{-h/H}$ — атмосферное давление убывает с высотой по показательному закону.</li>
<li><b>Закон охлаждения Ньютона:</b> $T(t) = T_{ср} + (T_0 - T_{ср}) \\cdot e^{-kt}$ — кофе в чашке остывает по показательному закону.</li>
<li><b>Разряд конденсатора:</b> $U(t) = U_0 \\cdot e^{-t/(RC)}$ — основа всей радиотехники.</li>
</ul>
<p>Поэтому изучение функции $y = a^x$ — это <b>фундамент</b> для физики, химии, биологии, экономики и информатики.</p>`);
/* === ИНТЕРАКТИВЫ === */
/* IV1 — двухпанельный сравнительный визуализатор */
html += `<div class="wg" id="p4-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Двухпанельный визуализатор $y = a^x$</div></div>
<div class="wg-help">Меняй основание $a$ ползунком — наблюдай, как меняется характер функции. <b>Левая панель</b> для $a > 1$ (возрастающая, синий), <b>правая</b> — для $0 < a < 1$ (убывающая, оранжевый). Snap-точки: $a \\in \\{0{,}1;\\ 0{,}25;\\ 0{,}5;\\ 1;\\ 2;\\ e;\\ 3;\\ 10\\}$. Точка $(0; 1)$ — всегда на графике.</div>
<div class="sliders">
<label>Основание $a$ = <b id="p4-iv1-a">2</b><input type="range" id="p4-iv1-sa" min="0.1" max="10" step="0.05" value="2"></label>
</div>
<div style="background:var(--card);border-radius:9px;padding:10px;text-align:center"><svg id="p4-iv1-svg" viewBox="0 0 720 360" width="100%" style="max-width:720px;height:auto"></svg></div>
<div id="p4-iv1-desc" style="margin-top:10px;padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;line-height:1.6;min-height:80px"></div>
</div>`;
/* IV2 — калькулятор y = a^x */
html += `<div class="wg" id="p4-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор $y = a^x$</div></div>
<div class="wg-help">Введи основание $a$ ($a > 0$, $a \\ne 1$) и показатель $x$. Получи $y = a^x$ и характер функции. Под результатом — мини-график с отметкой точки $(x; y)$.</div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;margin-bottom:8px">
<span style="font-family:'JetBrains Mono',monospace">$a$ =</span>
<input type="number" id="p4-iv2-a" class="tinp" style="width:90px;text-align:center" value="2" step="0.1">
<span style="font-family:'JetBrains Mono',monospace">$x$ =</span>
<input type="number" id="p4-iv2-x" class="tinp" style="width:90px;text-align:center" value="3" step="0.1">
<button class="btn primary" id="p4-iv2-go">Вычислить $y = a^x$</button>
</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:center;margin-bottom:10px;font-size:.82rem">
<span style="color:var(--muted)">Быстро для $a$:</span>
<button class="btn" data-qa="2" style="padding:4px 10px;font-size:.82rem">$a = 2$</button>
<button class="btn" data-qa="3" style="padding:4px 10px;font-size:.82rem">$a = 3$</button>
<button class="btn" data-qa="10" style="padding:4px 10px;font-size:.82rem">$a = 10$</button>
<button class="btn" data-qa="2.71828" style="padding:4px 10px;font-size:.82rem">$a = e$</button>
</div>
<div id="p4-iv2-out" style="padding:12px 14px;background:var(--card);border-radius:9px;font-size:.96rem;min-height:60px;line-height:1.7;text-align:center"></div>
<div style="text-align:center;margin-top:8px"><svg id="p4-iv2-mini" viewBox="0 0 240 140" width="240" height="140" style="background:var(--card-soft);border-radius:8px"></svg></div>
<div class="feedback" id="p4-iv2-fb"></div>
</div>`;
/* IV3 — квикфайр «Возрастает или убывает?» */
html += `<div class="wg" id="p4-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Возрастает или убывает?</div></div>
<div class="wg-help">Для каждой показательной функции определи характер монотонности. 8 заданий.</div>
<div class="score-display"><span>Задача <b id="p4-iv3-i">1</b> / 8</span><span>Очки: <b id="p4-iv3-s">0</b> / 8</span></div>
<div id="p4-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.1rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p4-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:10px;max-width:420px;margin:0 auto"></div>
<div class="feedback" id="p4-iv3-fb"></div>
<div class="actions" style="justify-content:center"><button class="btn" id="p4-iv3-restart">Начать заново</button></div>
</div>`;
/* IV4 — тренажёр значений */
html += `<div class="wg" id="p4-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр значений $a^x$</div></div>
<div class="wg-help">Вычисли значение $a^x$ (десятичное до 4 знаков, допуск $\\pm 0{,}05$). 6 задач.</div>
<div class="score-display"><span>Задача <b id="p4-iv4-i">1</b> / 6</span><span>Очки: <b id="p4-iv4-s">0</b> / 6</span></div>
<div id="p4-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.15rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p4-iv4-ans" class="tinp" style="width:120px;text-align:center" step="0.0001">
<button class="btn primary" id="p4-iv4-go">Проверить</button>
<button class="btn" id="p4-iv4-start">Заново</button>
</div>
<div class="feedback" id="p4-iv4-fb"></div>
</div>`;
html += secNavFor('p4');
html += readButton('p4');
box.innerHTML = html;
renderMath(box);
/* === IV1 — двухпанельный визуализатор === */
(function(){
const sa = document.getElementById('p4-iv1-sa');
const aL = document.getElementById('p4-iv1-a');
const svg = document.getElementById('p4-iv1-svg');
const desc = document.getElementById('p4-iv1-desc');
const E = Math.E;
const SNAP = [0.1, 0.25, 0.5, 1, 2, E, 3, 10];
const seen = new Set();
let _done = false;
const COL_GROW = '#2563eb';
const COL_DECAY = '#ea580c';
const COL_NEUTRAL = '#94a3b8';
const COL_POINT01 = '#10b981';
const COL_POINT1A = '#dc2626';
function aLabel(a){
if(Math.abs(a - E) < 0.03) return 'e \\approx 2{,}72';
if(Math.abs(a - Math.round(a)) < 0.005) return String(Math.round(a));
if(Math.abs(a - 0.5) < 0.005) return '\\tfrac{1}{2}';
if(Math.abs(a - 0.25) < 0.005) return '\\tfrac{1}{4}';
if(Math.abs(a - 0.1) < 0.005) return '\\tfrac{1}{10}';
return (+a.toFixed(2)).toString();
}
function aPlain(a){
if(Math.abs(a - E) < 0.03) return 'e';
return (+a.toFixed(2)).toString();
}
function drawPanel(active, isGrow, a, ox){
const W = 360, H = 360, pad = 28;
const xmin = -3, xmax = 3, ymin = -0.5, ymax = 8;
const ax = axes2D(W, H, pad, xmin, xmax, ymin, ymax);
// сдвинуть всё содержимое axes2D на ox по X через обёртку <g transform>
let g = '<g transform="translate('+ox+',0)">';
g += ax.content;
const col = isGrow ? COL_GROW : COL_DECAY;
const title = isGrow ? 'a > 1 \\u2014 возрастающая' : '0 < a < 1 \\u2014 убывающая';
// заголовок панели
g += '<text x="'+(W/2)+'" y="16" font-family="Inter,sans-serif" font-size="13" font-weight="700" text-anchor="middle" fill="'+(active?col:COL_NEUTRAL)+'">'
+ (isGrow ? 'a &gt; 1 — возрастающая' : '0 &lt; a &lt; 1 — убывающая')
+ '</text>';
if(!active){
// серая заглушка
g += '<rect x="'+pad+'" y="'+(pad+8)+'" width="'+(W-2*pad)+'" height="'+(H-2*pad-8)+'" fill="#f1f5f9" opacity=".55"/>';
g += '<text x="'+(W/2)+'" y="'+(H/2)+'" font-family="Inter,sans-serif" font-size="12" font-weight="600" text-anchor="middle" fill="#94a3b8">';
g += isGrow ? 'выбери a &gt; 1' : 'выбери 0 &lt; a &lt; 1';
g += '</text>';
g += '</g>';
return g;
}
// асимптота y = 0
g += asymptote('h', 0, ax.toX, ax.toY, xmin, xmax, ymin, ymax, '#94a3b8');
// график y = a^x
g += plotFunc(x => Math.pow(a, x), xmin, xmax, ax.toX, ax.toY, col, 240);
// точка (0; 1) — зелёная
g += pointWithDrop(0, 1, ax.toX, ax.toY, COL_POINT01, '(0; 1)');
// точка (1; a) — красная (если a в пределах ymax)
if(a > 0 && a <= ymax){
g += pointWithDrop(1, a, ax.toX, ax.toY, COL_POINT1A, '(1; '+aPlain(a)+')');
}
g += '</g>';
return g;
}
function drawConstPanel(a){
// обе панели показывают серую линию y = 1
const W = 720, H = 360, pad = 28;
const xmin = -3, xmax = 3, ymin = -0.5, ymax = 8;
let out = '';
// левая
let leftAx;
{
const ax = axes2D(360, H, pad, xmin, xmax, ymin, ymax);
leftAx = ax;
let g = '<g transform="translate(0,0)">' + ax.content;
g += '<text x="180" y="16" font-family="Inter,sans-serif" font-size="13" font-weight="700" text-anchor="middle" fill="'+COL_NEUTRAL+'">a = 1: y = 1 константа</text>';
g += plotFunc(x => 1, xmin, xmax, ax.toX, ax.toY, COL_NEUTRAL, 60);
g += pointWithDrop(0, 1, ax.toX, ax.toY, COL_POINT01, '(0; 1)');
g += '</g>';
out += g;
}
// правая
{
const ax = axes2D(360, H, pad, xmin, xmax, ymin, ymax);
let g = '<g transform="translate(360,0)">' + ax.content;
g += '<text x="180" y="16" font-family="Inter,sans-serif" font-size="13" font-weight="700" text-anchor="middle" fill="'+COL_NEUTRAL+'">a = 1 — не показательная</text>';
g += plotFunc(x => 1, xmin, xmax, ax.toX, ax.toY, COL_NEUTRAL, 60);
g += pointWithDrop(0, 1, ax.toX, ax.toY, COL_POINT01, '(0; 1)');
g += '</g>';
out += g;
}
return out;
}
function describe(a){
if(Math.abs(a - 1) < 0.01){
return '<b>$a = 1$:</b> функция $y = 1^x = 1$ — постоянная, <b>не является</b> показательной (нарушено условие $a \\ne 1$).';
}
if(a > 1){
const aTxt = aLabel(a);
return '<b>$a = '+aTxt+' > 1$ — функция возрастает.</b><br>'
+ '$D = \\mathbb{R}$, $E = (0; +\\infty)$. Проходит через $(0; 1)$ и $(1; '+aPlain(a)+')$.<br>'
+ 'При $x \\to +\\infty$: $y \\to +\\infty$. При $x \\to -\\infty$: $y \\to 0$ (асимптота $y = 0$).';
}
// 0 < a < 1
const aTxt = aLabel(a);
return '<b>$a = '+aTxt+'$, $0 < a < 1$ — функция убывает.</b><br>'
+ '$D = \\mathbb{R}$, $E = (0; +\\infty)$. Проходит через $(0; 1)$ и $(1; '+aPlain(a)+')$.<br>'
+ 'При $x \\to +\\infty$: $y \\to 0$ (асимптота $y = 0$). При $x \\to -\\infty$: $y \\to +\\infty$.';
}
function draw(){
let a = +sa.value;
a = snapToValue(a, SNAP, 0.06);
aL.textContent = aLabel(a);
let svgContent = '';
if(Math.abs(a - 1) < 0.01){
svgContent = drawConstPanel(a);
} else if(a > 1){
svgContent += drawPanel(true, true, a, 0);
svgContent += drawPanel(false, false, a, 360);
} else {
svgContent += drawPanel(false, true, a, 0);
svgContent += drawPanel(true, false, a, 360);
}
// разделительная линия между панелями
svgContent += '<line x1="360" y1="6" x2="360" y2="354" stroke="#e2e8f0" stroke-width="1.5" stroke-dasharray="4 4"/>';
svg.innerHTML = svgContent;
desc.innerHTML = describe(a);
renderMath(desc);
const key = (+a.toFixed(2)).toString();
seen.add(key);
if(!_done && seen.size >= 6){ _done = true; addXp(10, 'p4-iv1'); bumpProgress('p4', 15); }
}
sa.addEventListener('input', draw);
draw();
})();
/* === IV2 — калькулятор y = a^x === */
(function(){
const aI = document.getElementById('p4-iv2-a');
const xI = document.getElementById('p4-iv2-x');
const go = document.getElementById('p4-iv2-go');
const out = document.getElementById('p4-iv2-out');
const fb = document.getElementById('p4-iv2-fb');
const mini = document.getElementById('p4-iv2-mini');
const used = new Set();
let _done = false;
document.querySelectorAll('#p4-iv2 [data-qa]').forEach(b => {
b.addEventListener('click', () => { aI.value = b.dataset.qa; calc(); });
});
function drawMini(a, xPt){
const W = 240, H = 140, pad = 16;
const xmin = -3, xmax = 3;
// подобрать ymax
let ymax = Math.max(1.5, Math.pow(a, 2), Math.pow(a, -2));
ymax = Math.min(ymax, 10);
const ymin = -0.3;
const ax = axes2D(W, H, pad, xmin, xmax, ymin, ymax);
let g = ax.content;
const col = a > 1 ? '#2563eb' : '#ea580c';
g += asymptote('h', 0, ax.toX, ax.toY, xmin, xmax, ymin, ymax, '#94a3b8');
g += plotFunc(x => Math.pow(a, x), xmin, xmax, ax.toX, ax.toY, col, 160);
g += pointWithDrop(0, 1, ax.toX, ax.toY, '#10b981', '');
// точка (x; a^x), если в пределах
const yPt = Math.pow(a, xPt);
if(xPt >= xmin && xPt <= xmax && yPt >= ymin && yPt <= ymax){
g += pointWithDrop(xPt, yPt, ax.toX, ax.toY, '#dc2626', '');
}
mini.innerHTML = g;
}
function calc(){
const a = parseFloat(aI.value);
const x = parseFloat(xI.value);
if(!isFinite(a) || !isFinite(x)){ feedback(fb, false, '&#10007; Введи числа $a$ и $x$.'); return; }
if(a <= 0){ feedback(fb, false, '&#10007; Основание должно быть положительным: $a > 0$.'); return; }
if(Math.abs(a - 1) < 1e-9){ feedback(fb, false, '&#10007; При $a = 1$ функция не является показательной.'); return; }
const y = Math.pow(a, x);
const sign = a > 1 ? 'возрастающая' : 'убывающая';
const signCol = a > 1 ? '#1d4ed8' : '#c2410c';
out.innerHTML = '<div style="font-size:1.05rem;margin-bottom:6px">$y = '+aI.value+'^{'+xI.value+'} = <b style="color:'+signCol+'">'+(+y.toFixed(4))+'</b>$</div>'
+ '<div style="color:'+signCol+';font-weight:700">Функция $y = '+aI.value+'^x$ — '+sign+'</div>';
renderMath(out);
feedback(fb, true, '&#10003; Готово.');
drawMini(a, x);
const key = (+a.toFixed(3))+'_'+(+x.toFixed(3));
used.add(key);
if(!_done && used.size >= 4){ _done = true; addXp(10, 'p4-iv2'); bumpProgress('p4', 15); }
}
go.addEventListener('click', calc);
aI.addEventListener('keydown', e => { if(e.key === 'Enter') calc(); });
xI.addEventListener('keydown', e => { if(e.key === 'Enter') calc(); });
calc();
})();
/* === IV3 — квикфайр «Возрастает или убывает?» === */
(function(){
const Q = [
{ q:'$y = 2^x$', ans:0 },
{ q:'$y = (0{,}5)^x$', ans:1 },
{ q:'$y = \\left(\\dfrac{1}{3}\\right)^x$', ans:1 },
{ q:'$y = 10^x$', ans:0 },
{ q:'$y = e^x$', ans:0 },
{ q:'$y = \\left(\\dfrac{1}{2}\\right)^x$', ans:1 },
{ q:'$y = 5^x$', ans:0 },
{ q:'$y = (0{,}1)^x$', ans:1 },
];
const OPTS = ['Возрастает', 'Убывает'];
let i = 0, score = 0;
const qEl = document.getElementById('p4-iv3-q');
const oEl = document.getElementById('p4-iv3-opts');
const fb = document.getElementById('p4-iv3-fb');
const iEl = document.getElementById('p4-iv3-i');
const sEl = document.getElementById('p4-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p4-iv3'); bumpProgress('p4', 25); }
else if(score >= 5){ addXp(8, 'p4-iv3'); bumpProgress('p4', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = 'Какая монотонность у функции ' + item.q + ' ?';
oEl.innerHTML = OPTS.map((o, k) => '<button class="btn primary" data-k="'+k+'">'+o+'</button>').join('');
fb.style.display = 'none';
renderMath(qEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const k = +b.dataset.k;
if(k === item.ans){ score++; feedback(fb, true, '&#10003; Верно! Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Правильно: <b>'+OPTS[item.ans]+'</b>. Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1100);
});
});
}
document.getElementById('p4-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* === IV4 — тренажёр значений === */
(function(){
const Q = [
{ q:'$2^3 = \\,?$', ans:8, hint:'$2 \\cdot 2 \\cdot 2 = 8$' },
{ q:'$3^{-2} = \\,?$ (десятичная)', ans:1/9, hint:'$3^{-2} = \\dfrac{1}{9} \\approx 0{,}1111$' },
{ q:'$\\left(\\dfrac{1}{2}\\right)^4 = \\,?$ (десятичная)', ans:0.0625, hint:'$\\dfrac{1}{16} = 0{,}0625$' },
{ q:'$10^{1{,}5} = \\,?$', ans:31.6228, hint:'$10^{3/2} = 10\\sqrt{10} \\approx 31{,}62$' },
{ q:'$4^{0{,}5} = \\,?$', ans:2, hint:'$4^{1/2} = \\sqrt{4} = 2$' },
{ q:'$9^{1{,}5} = \\,?$', ans:27, hint:'$9^{3/2} = (\\sqrt{9})^3 = 3^3 = 27$' },
];
let i = 0, score = 0;
function show(){
const qEl = document.getElementById('p4-iv4-q');
const iEl = document.getElementById('p4-iv4-i');
const sEl = document.getElementById('p4-iv4-s');
const fb = document.getElementById('p4-iv4-fb');
const ansI = document.getElementById('p4-iv4-ans');
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
if(score === Q.length){ addXp(15, 'p4-iv4'); bumpProgress('p4', 25); }
else if(score >= 4){ addXp(8, 'p4-iv4'); bumpProgress('p4', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
qEl.innerHTML = Q[i].q;
ansI.value = '';
renderMath(qEl);
fb.style.display = 'none';
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p4-iv4-fb');
const raw = document.getElementById('p4-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
// допуск: либо абсолютный 0.05, либо относительный 1% для крупных значений
const tol = Math.max(0.05, Math.abs(Q[i].ans) * 0.01);
if(Math.abs(ans - Q[i].ans) < tol){
score++;
feedback(fb, true, '&#10003; Верно! '+Q[i].hint+'. Дальше ▶');
} else {
feedback(fb, false, '&#10007; Неверно. Ответ $\\approx '+(+Q[i].ans.toFixed(4))+'$ ('+Q[i].hint+'). Дальше ▶');
}
document.getElementById('p4-iv4-s').textContent = score;
i++;
setTimeout(show, 1400);
}
document.getElementById('p4-iv4-go').addEventListener('click', go);
document.getElementById('p4-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p4-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p4');
}