feat(alg9 ch4 wave1): §14 «Числовая последовательность» + §15 «Арифм. прогрессия»

§14: 3 теор. карточки (определение, способы задания, монотонность) +
4 интерактива — конструктор последовательности (5 типов + точечная
диаграмма (n, a_n)), тренажёр вычисления a_n (6 задач), квикфайр
монотонности (3 кнопки), DnD-сортер «рекуррентно vs формула».

§15: 3 теор. карточки (определение и a_n = a_1 + (n-1)d, характеристическое
свойство, примеры) + 4 интерактива — конструктор арифм. прогрессии
(слайдеры a_1 и d + точки на прямой), двойной калькулятор a_n и d,
квикфайр «арифм. или нет» (6 заданий), тренажёр прогрессии (6 задач).

Добавлены CSS .wg/.tinp/.sliders/.score-display/.dnd-*/.drop-* и хелперы
makeCard, setupSorter, gcd, axes2D, plotFunc (по образцу ch1/ch2).
This commit is contained in:
Maxim Dolgolyov
2026-05-29 08:51:52 +03:00
parent 5c3ca4c1b6
commit eb565081f6
+665 -26
View File
@@ -111,6 +111,40 @@ a{color:inherit;text-decoration:none}
.feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)} .feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)}
.feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)} .feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)}
.wg{background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)));border:1.5px solid var(--sec-acc,var(--pri));border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1}
.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px}
.wg-badge{padding:4px 9px;background:var(--sec-acc,var(--pri));color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));flex:1}
.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--sec-acc-soft,var(--pri-soft)));border-left:4px solid var(--warn,#f59e0b);padding:9px 14px;border-radius:9px}
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s;font-family:'JetBrains Mono',monospace}
.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))}
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px}
.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5}
.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--sec-acc-d,var(--pri2));margin-left:4px}
.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--sec-acc,var(--pri))}
.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px}
.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem}
.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s}
.dnd-pool.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid}
.dnd-pool.col{flex-direction:column;align-items:stretch}
.dnd-pool.col .dnd-chip{width:auto}
.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%}
.dnd-chip:hover{transform:translateY(-1px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)}
.dnd-chip:active{cursor:grabbing}
.dnd-chip.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(217,119,6,.22);transform:translateY(-1px)}
.dnd-chip.dragging{opacity:.28}
.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer}
.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)}
.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s}
.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))}
.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)}
.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px}
.dnd-hint{font-size:.78rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px}
.dnd-hint svg{width:14px;height:14px;flex-shrink:0}
.col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto} .col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto}
.sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)} .sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)}
.sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)} .sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}
@@ -442,41 +476,646 @@ function wireReadBtn(paraId){
}); });
} }
function gcd(a,b){ a=Math.abs(a|0); b=Math.abs(b|0); while(b){ const t=b; b=a%b; a=t; } return a||1; }
function makeCard(kind, title, num, body){
const labels = {repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно'};
return '<div class="card"><div class="card-header"><div class="card-icon '+kind+'">'+ICONS[kind]+'</div><div class="card-title">'+(labels[kind]||'')+(title&&title!==labels[kind]?' \xb7 '+title:'')+'</div>'+(num?'<div class="card-num">'+num+'</div>':'')+'</div><div class="card-body">'+body+'</div></div>';
}
function setupSorter(cfg){
const placed = {}; const pool = document.getElementById(cfg.poolId); const scope = document.querySelector(cfg.scopeSelector);
if(!pool||!scope) return {placed,render:()=>{},reset:()=>{}};
pool.classList.add('dnd-pool'); if(cfg.columnLayout) pool.classList.add('col');
let armed = null;
function buildChip(it,isPlaced){ const e=document.createElement('div'); e.className='dnd-chip'+(isPlaced?' placed':''); e.dataset.id=it.id; e.innerHTML='<span class="dnd-txt">'+it.html+'</span><span class="dnd-x" title="Убрать">\xd7</span>'; attach(e,it.id); return e; }
function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault(); if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); }
function attachBoxTaps(){ scope.querySelectorAll('.drop-box').forEach(box=>{ box.addEventListener('click',ev=>{ if(!armed)return; if(ev.target.closest('.dnd-chip'))return; const di=box.querySelector('[data-cat]'); if(di){placed[armed]=di.dataset.cat;armed=null;render();} }); }); }
function render(){ pool.innerHTML=''; cfg.items.forEach(it=>{if(placed[it.id])return;const c=buildChip(it,false);if(armed===it.id)c.classList.add('armed');pool.appendChild(c);}); cfg.cats.forEach(cat=>{const box=scope.querySelector('.drop-items[data-cat="'+cat+'"]');if(!box)return;box.innerHTML='';cfg.items.forEach(it=>{if(placed[it.id]!==cat)return;box.appendChild(buildChip(it,true));});}); if(window.renderMathInElement)try{renderMath(scope);}catch(_){} }
attachBoxTaps(); render();
return {placed,render,reset(){ for(const k in placed)delete placed[k];armed=null;render(); }};
}
/* Координатная плоскость в SVG */
function axes2D(W, H, pad, xmin, xmax, ymin, ymax){
const ux = (W - 2*pad) / (xmax - xmin);
const uy = (H - 2*pad) / (ymax - ymin);
const toX = v => pad + (v - xmin) * ux;
const toY = v => H - pad - (v - ymin) * uy;
let g = '';
g += '<g stroke="#e5e7eb" stroke-width="1">';
for (let x = Math.ceil(xmin); x <= xmax; x++){
g += '<line x1="'+toX(x)+'" y1="'+pad+'" x2="'+toX(x)+'" y2="'+(H-pad)+'"/>';
}
for (let y = Math.ceil(ymin); y <= ymax; y++){
g += '<line x1="'+pad+'" y1="'+toY(y)+'" x2="'+(W-pad)+'" y2="'+toY(y)+'"/>';
}
g += '</g>';
const y0 = toY(0), x0 = toX(0);
g += '<line x1="'+pad+'" y1="'+y0+'" x2="'+(W-pad)+'" y2="'+y0+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<line x1="'+x0+'" y1="'+pad+'" x2="'+x0+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<text x="'+(W-pad+2)+'" y="'+(y0-4)+'" font-size="11" fill="#0f172a">x</text>';
g += '<text x="'+(x0+4)+'" y="'+(pad-2)+'" font-size="11" fill="#0f172a">y</text>';
g += '<g font-size="10" fill="#64748b">';
for (let x = Math.ceil(xmin); x <= xmax; x++){
if (x !== 0) g += '<text x="'+(toX(x)-3)+'" y="'+(y0+12)+'">'+x+'</text>';
}
for (let y = Math.ceil(ymin); y <= ymax; y++){
if (y !== 0) g += '<text x="'+(x0+4)+'" y="'+(toY(y)+3)+'">'+y+'</text>';
}
g += '<text x="'+(x0+4)+'" y="'+(y0+12)+'">0</text>';
g += '</g>';
return { content: g, toX, toY, ux, uy };
}
/* График функции y=f(x) — возвращает строку <path ...> */
function plotFunc(f, xmin, xmax, toX, toY, color, N){
N = N || 200;
let d = '';
let prevValid = false;
for (let i = 0; i <= N; i++){
const x = xmin + (xmax - xmin) * i / N;
let y;
try { y = f(x); } catch(e){ y = NaN; }
if (!isFinite(y) || isNaN(y) || y < -1e4 || y > 1e4){ prevValid = false; continue; }
d += (prevValid ? ' L' : ' M') + toX(x).toFixed(2) + ',' + toY(y).toFixed(2);
prevValid = true;
}
return '<path d="'+d+'" stroke="'+color+'" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>';
}
/* ===== STUB BUILDERS — наполнение в Phase 1+ ===== */ /* ===== STUB BUILDERS — наполнение в Phase 1+ ===== */
function buildP14(){ function buildP14(){
const root = document.getElementById('p14-body'); const box = document.getElementById('p14-body');
root.innerHTML = ` let html = '';
<div class="card">
<div class="card-header"> html += makeCard('theory', 'Определение', '14.1', `
<span class="card-icon theory">${ICONS.theory}</span> <p><b>Числовая последовательность</b> — это функция, заданная на множестве натуральных чисел $\\mathbb{N}$.</p>
<span class="card-title">В разработке</span> <p>Записывают: $a_1,\\ a_2,\\ a_3,\\ \\ldots,\\ a_n,\\ \\ldots$ — это <b>члены последовательности</b>.</p>
<span class="card-num">§ 14</span> <ul style="padding-left:22px;line-height:1.9">
</div> <li>$a_n$ — <b>общий член</b> (член с номером $n$);</li>
<div class="card-body"> <li>$n$ — <b>номер</b> члена ($n \\in \\mathbb{N}$);</li>
<p>Содержание параграфа <b>«Числовая последовательность»</b> будет добавлено в следующих обновлениях.</p> <li>$a_1$ — первый член, $a_2$ — второй, и т. д.</li>
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 1.</p> </ul>
</div> <p>Кратко последовательность обозначают $(a_n)$. Это значит: $a \\colon \\mathbb{N} \\to \\mathbb{R}$.</p>`);
</div>` + secNav(null, 'p15') + readButton('p14');
renderMath(root); html += makeCard('rule', 'Три способа задания', '14.2', `
<p>Последовательность можно задать одним из трёх способов:</p>
<ol style="padding-left:22px;line-height:2">
<li><b>Формулой $n$-го члена:</b> $a_n = 2n + 1$ → последовательность $3, 5, 7, 9, \\ldots$ Чтобы найти любой член, подставь его номер $n$.</li>
<li><b>Рекуррентно:</b> задают $a_1$ и формулу, выражающую следующий член через предыдущий. Например: $a_1 = 1,\\ a_{n+1} = a_n + 3$$1, 4, 7, 10, \\ldots$</li>
<li><b>Перечислением (словесно):</b> $2, 4, 8, 16, \\ldots$ — если из записи понятен алгоритм (удвоение).</li>
</ol>`);
html += makeCard('example', 'Конечные/бесконечные · возрастающие/убывающие', '14.3', `
<p><b>Конечная</b> последовательность: $1, 2, 3, \\ldots, 100$ — заканчивается на $a_{100}$.</p>
<p><b>Бесконечная</b>: $1, 2, 4, 8, \\ldots$ — продолжается без конца.</p>
<p>Последовательность $(a_n)$ — <b>возрастающая</b>, если $a_{n+1} > a_n$ для всех $n$.<br>
<b>Убывающая</b> — если $a_{n+1} < a_n$ для всех $n$.</p>
<p><b>Примеры:</b></p>
<ul style="padding-left:22px;line-height:1.9">
<li>$a_n = -n$$-1, -2, -3, \\ldots$ — убывающая;</li>
<li>$a_n = \\dfrac{1}{n}$$1, \\dfrac{1}{2}, \\dfrac{1}{3}, \\ldots$ — убывающая, но положительная;</li>
<li>$a_n = n^2$$1, 4, 9, 16, \\ldots$ — возрастающая;</li>
<li>$a_n = (-1)^n$$-1, 1, -1, 1, \\ldots$ — ни возрастает, ни убывает.</li>
</ul>`);
/* INTERACTIVE 1 — конструктор последовательности */
html += `<div class="wg" id="p14-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор последовательности</div></div>
<div class="wg-help">Выбери одну из пяти классических последовательностей и посмотри её точечную диаграмму $(n, a_n)$ на координатной плоскости.</div>
<div class="sliders">
<label>Тип №<b id="p14-iv1-ti">1</b> / 5<input type="range" id="p14-iv1-fn" min="1" max="5" step="1" value="1"></label>
</div>
<div id="p14-iv1-formula" style="text-align:center;font-size:1.1rem;padding:10px;background:var(--card);border-radius:9px;margin-bottom:10px"></div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
<svg id="p14-iv1-svg" viewBox="0 0 420 280" style="width:100%;max-width:520px;height:auto;display:block;margin:0 auto"></svg>
</div>
<div id="p14-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;text-align:center"></div>
<div id="p14-iv1-mono" style="margin-top:8px;padding:8px 12px;background:var(--card);border-radius:8px;font-size:.88rem;text-align:center;color:var(--sec-acc-d,var(--pri2));font-weight:600"></div>
</div>`;
/* INTERACTIVE 2 — вычисли a_n */
html += `<div class="wg" id="p14-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Вычисли $a_n$</div></div>
<div class="wg-help">Подставь номер $n$ в формулу и вычисли значение $a_n$. Ответ — целое число.</div>
<div class="score-display">Задача: <b id="p14-iv2-idx">1</b> / 6 &middot; Очки: <b id="p14-iv2-sc">0</b></div>
<div id="p14-iv2-q" style="text-align:center;font-size:1.05rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center;align-items:center">
<span style="font-family:'JetBrains Mono',monospace">$a_n$ =</span>
<input type="number" id="p14-iv2-ans" class="tinp" style="width:120px;text-align:center" step="1">
<button class="btn primary" id="p14-iv2-go">Проверить</button>
</div>
<div class="feedback" id="p14-iv2-fb"></div>
</div>`;
/* INTERACTIVE 3 — возрастает/убывает/нет */
html += `<div class="wg" id="p14-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Возрастает или убывает?</div></div>
<div class="wg-help">Дана формула $a_n$. Определи: возрастает, убывает или ни то ни другое.</div>
<div class="score-display">Задача: <b id="p14-iv3-idx">1</b> / 6 &middot; Очки: <b id="p14-iv3-sc">0</b></div>
<div id="p14-iv3-q" style="text-align:center;font-size:1.05rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p14-iv3-up">Возрастает</button>
<button class="btn" id="p14-iv3-dn">Убывает</button>
<button class="btn" id="p14-iv3-no">Ни то ни другое</button>
</div>
<div class="feedback" id="p14-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — сортер: рекуррентно или формулой */
html += `<div class="wg" id="p14-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Рекуррентно или формулой?</div></div>
<div class="wg-help">Перетащи каждый способ задания в нужный ящик.</div>
<div id="p14-iv4-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px">
<div class="drop-box"><h5>Рекуррентное правило</h5><div class="drop-items" data-cat="rec"></div></div>
<div class="drop-box"><h5>Формула $n$-го члена</h5><div class="drop-items" data-cat="fml"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p14-iv4-check">Проверить</button><button class="btn" id="p14-iv4-reset">Сбросить</button></div>
<div class="feedback" id="p14-iv4-fb"></div>
</div>`;
box.innerHTML = html + secNav(null, 'p15') + readButton('p14');
renderMath(box);
/* ===== IV1 wiring ===== */
(function(){
const types = [
{ tex:'a_n = 2n - 1', f:n=>2*n-1, seq:'1, 3, 5, 7, 9, \\ldots', mono:'возрастает' },
{ tex:'a_n = n^2', f:n=>n*n, seq:'1, 4, 9, 16, 25, \\ldots', mono:'возрастает' },
{ tex:'a_n = (-1)^n', f:n=>Math.pow(-1,n), seq:'-1, 1, -1, 1, \\ldots', mono:'ни возрастает, ни убывает' },
{ tex:'a_n = \\dfrac{1}{n}', f:n=>1/n, seq:'1, \\dfrac{1}{2}, \\dfrac{1}{3}, \\dfrac{1}{4}, \\ldots', mono:'убывает' },
{ tex:'a_n = n(n+1)', f:n=>n*(n+1), seq:'2, 6, 12, 20, 30, \\ldots', mono:'возрастает' }
];
const svg = document.getElementById('p14-iv1-svg');
const fnSl = document.getElementById('p14-iv1-fn');
const ti = document.getElementById('p14-iv1-ti');
const formula = document.getElementById('p14-iv1-formula');
const out = document.getElementById('p14-iv1-out');
const mono = document.getElementById('p14-iv1-mono');
let bumped = false;
function redraw(){
const idx = (+fnSl.value)-1;
const t = types[idx];
ti.textContent = (idx+1);
// вычисляем 10 точек
const pts = [];
for (let n=1;n<=10;n++){ const y=t.f(n); if (isFinite(y)) pts.push([n,y]); }
let ymin = Math.min(...pts.map(p=>p[1]), 0);
let ymax = Math.max(...pts.map(p=>p[1]), 0);
// ограничим, чтобы график не «улетал»
if (ymax > 30) ymax = 30;
if (ymin < -5) ymin = -5;
const pad = Math.max(1, (ymax-ymin)*0.1);
ymin = Math.floor(ymin - pad);
ymax = Math.ceil(ymax + pad);
const ax = axes2D(420, 280, 32, 0, 11, ymin, ymax);
let g = ax.content;
// точки + соединяющие отрезки (пунктир)
let path = '';
pts.forEach((p,i)=>{
const x = ax.toX(p[0]); const y = ax.toY(Math.min(ymax,Math.max(ymin,p[1])));
path += (i===0?'M':' L')+x.toFixed(1)+','+y.toFixed(1);
});
g += '<path d="'+path+'" stroke="#0891b2" stroke-width="1.2" fill="none" stroke-dasharray="3 3" opacity=".55"/>';
pts.forEach(p=>{
const x = ax.toX(p[0]); const y = ax.toY(Math.min(ymax,Math.max(ymin,p[1])));
g += '<circle cx="'+x.toFixed(1)+'" cy="'+y.toFixed(1)+'" r="5" fill="#0891b2" stroke="#fff" stroke-width="2"/>';
});
svg.innerHTML = g;
formula.innerHTML = '$' + t.tex + '$';
out.innerHTML = '<b>Первые 10 членов:</b> $' + t.seq + '$';
mono.innerHTML = 'Эта последовательность <b>' + t.mono + '</b>';
renderMath(formula); renderMath(out); renderMath(mono);
if (!bumped){ bumped = true; bumpProgress('p14', 15); addXp(10,'p14-iv1'); }
}
fnSl.addEventListener('input', redraw);
redraw();
})();
/* ===== IV2 wiring ===== */
(function(){
const items = [
{ q:'$a_n = 3n + 1$, найти $a_5$', ans: 16 },
{ q:'$a_n = n^2 - 2$, найти $a_4$', ans: 14 },
{ q:'$a_n = 2^n$, найти $a_6$', ans: 64 },
{ q:'$a_n = \\dfrac{6}{n}$, найти $a_3$', ans: 2 },
{ q:'$a_n = (-1)^n \\cdot n$, найти $a_7$', ans: -7 },
{ q:'$a_n = n^3 - n$, найти $a_3$', ans: 24 }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p14-iv2-idx');
const scEl = document.getElementById('p14-iv2-sc');
const qEl = document.getElementById('p14-iv2-q');
const inp = document.getElementById('p14-iv2-ans');
const btn = document.getElementById('p14-iv2-go');
const fb = document.getElementById('p14-iv2-fb');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
inp.disabled = true; btn.disabled = true;
inp.style.opacity = .5; btn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p14', 15); addXp(10,'p14-iv2'); }
return;
}
qEl.innerHTML = items[i].q;
inp.value = ''; fb.style.display = 'none';
renderMath(qEl);
}
btn.addEventListener('click', ()=>{
if (i >= items.length) return;
const v = +inp.value;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, ok ? '&#10003; Верно. $a_n = ' + it.ans + '$' : '&#10007; Неверно. Правильный ответ: $a_n = ' + it.ans + '$');
i++;
setTimeout(render, 1000);
});
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } });
render();
})();
/* ===== IV3 wiring ===== */
(function(){
const items = [
{ q:'$a_n = 2n$', ans:'up', hint:'$a_{n+1} - a_n = 2 > 0$.' },
{ q:'$a_n = 10 - n$', ans:'dn', hint:'$a_{n+1} - a_n = -1 < 0$.' },
{ q:'$a_n = (-1)^n$', ans:'no', hint:'Чередуется $-1, 1, -1, 1, \\ldots$' },
{ q:'$a_n = n^2$', ans:'up', hint:'$1, 4, 9, 16, \\ldots$ — растёт.' },
{ q:'$a_n = \\dfrac{1}{n}$', ans:'dn', hint:'$1, \\tfrac{1}{2}, \\tfrac{1}{3}, \\ldots$ — убывает.' },
{ q:'$a_n = n - n^2$', ans:'dn', hint:'$0, -2, -6, -12, \\ldots$ — убывает с $n=1$.' }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p14-iv3-idx');
const scEl = document.getElementById('p14-iv3-sc');
const qEl = document.getElementById('p14-iv3-q');
const fb = document.getElementById('p14-iv3-fb');
const upB = document.getElementById('p14-iv3-up');
const dnB = document.getElementById('p14-iv3-dn');
const noB = document.getElementById('p14-iv3-no');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
[upB,dnB,noB].forEach(b=>{ b.disabled=true; b.style.opacity=.5; });
if (!bumped){ bumped = true; bumpProgress('p14', 25); addXp(15,'p14-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?'&#10003; Верно. ':'&#10007; Неверно. ') + it.hint);
i++;
setTimeout(render, 1000);
}
upB.addEventListener('click', ()=>answer('up'));
dnB.addEventListener('click', ()=>answer('dn'));
noB.addEventListener('click', ()=>answer('no'));
render();
})();
/* ===== IV4 wiring — DnD ===== */
(function(){
const items = [
{ id:'a', html:'$a_n = 5n - 3$', cat:'fml' },
{ id:'b', html:'$a_1 = 2,\\ a_{n+1} = a_n + 5$', cat:'rec' },
{ id:'c', html:'$a_n = n^2$', cat:'fml' },
{ id:'d', html:'$a_1 = 1,\\ a_{n+1} = 2 a_n$', cat:'rec' },
{ id:'e', html:'$a_n = \\dfrac{n}{n+1}$', cat:'fml' },
{ id:'f', html:'$a_1 = 3,\\ a_{n+1} = a_n - 2$', cat:'rec' }
];
const sorter = setupSorter({
poolId: 'p14-iv4-pool',
scopeSelector: '#p14-iv4',
cats: ['rec','fml'],
items: items
});
let bumped = false;
document.getElementById('p14-iv4-check').addEventListener('click', ()=>{
const fb = document.getElementById('p14-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 ? '&#10003; Все верно! ' + correct + ' / ' + total : '&#10007; Правильно: ' + correct + ' / ' + total);
if (ok && !bumped){ bumped = true; bumpProgress('p14', 25); addXp(15,'p14-iv4'); }
});
document.getElementById('p14-iv4-reset').addEventListener('click', ()=>{
sorter.reset();
const fb = document.getElementById('p14-iv4-fb'); fb.style.display='none';
});
})();
wireReadBtn('p14'); wireReadBtn('p14');
} }
function buildP15(){ function buildP15(){
const root = document.getElementById('p15-body'); const box = document.getElementById('p15-body');
root.innerHTML = ` let html = '';
<div class="card">
<div class="card-header"> html += makeCard('theory', 'Определение и формула $n$-го члена', '15.1', `
<span class="card-icon theory">${ICONS.theory}</span> <p><b>Арифметическая прогрессия</b> — числовая последовательность, в которой каждый следующий член отличается от предыдущего на одно и то же число $d$ — <b>разность прогрессии</b>.</p>
<span class="card-title">В разработке</span> <p>$$a_{n+1} - a_n = d \\quad \\text{для всех } n \\in \\mathbb{N}.$$</p>
<span class="card-num">§ 15</span> <p><b>Формула $n$-го члена:</b></p>
<p>$$a_n = a_1 + (n - 1)\\, d.$$</p>
<p><b>Пример.</b> $5, 8, 11, 14, \\ldots$ — арифметическая прогрессия с $a_1 = 5$, $d = 3$. Тогда
$a_{10} = 5 + 9 \\cdot 3 = 32$.</p>`);
html += makeCard('rule', 'Характеристическое свойство', '15.2', `
<p>В арифметической прогрессии любой <b>средний</b> член — это <b>среднее арифметическое</b> своих соседей:</p>
<p>$$a_n = \\dfrac{a_{n-1} + a_{n+1}}{2}\\quad (n \\ge 2).$$</p>
<p>Это свойство <b>полностью характеризует</b> арифметическую прогрессию: если оно выполняется для всех $n \\ge 2$, то $(a_n)$ — арифметическая прогрессия.</p>
<p><b>Пример.</b> Между $4$ и $16$ в арифметической прогрессии стоит число $\\dfrac{4 + 16}{2} = 10$.</p>`);
html += makeCard('example', 'Применение формулы', '15.3', `
<p><b>а)</b> Найти $a_{20}$, если $a_1 = 3$, $d = 4$:<br>
$a_{20} = a_1 + 19 d = 3 + 19 \\cdot 4 = 3 + 76 = \\mathbf{79}.$</p>
<p><b>б)</b> Найти $d$, если $a_1 = 2$, $a_{10} = 29$:<br>
$29 = 2 + 9 d \\Rightarrow 9 d = 27 \\Rightarrow \\mathbf{d = 3}.$</p>
<p><b>в)</b> Найти $a_1$, если $d = -2$, $a_5 = 10$:<br>
$10 = a_1 + 4 \\cdot (-2) = a_1 - 8 \\Rightarrow \\mathbf{a_1 = 18}.$</p>`);
/* INTERACTIVE 1 — конструктор арифм. прогрессии */
html += `<div class="wg" id="p15-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конструктор арифм. прогрессии</div></div>
<div class="wg-help">Двигай ползунки $a_1$ и $d$. Точки $(n; a_n)$ ложатся на прямую $y = a_1 + (x - 1) d$ — пунктирная линия.</div>
<div class="sliders">
<label>$a_1$ =<b id="p15-iv1-av">0</b><input type="range" id="p15-iv1-a1" min="-10" max="10" step="1" value="2"></label>
<label>$d$ =<b id="p15-iv1-dv">0</b><input type="range" id="p15-iv1-d" min="-5" max="5" step="1" value="2"></label>
</div>
<div id="p15-iv1-formula" style="text-align:center;font-size:1.05rem;padding:10px;background:var(--card);border-radius:9px;margin-bottom:10px"></div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
<svg id="p15-iv1-svg" viewBox="0 0 460 300" style="width:100%;max-width:560px;height:auto;display:block;margin:0 auto"></svg>
</div>
<div id="p15-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;text-align:center"></div>
</div>`;
/* INTERACTIVE 2 — калькулятор a_n и d */
html += `<div class="wg" id="p15-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор $a_n$ и $d$</div></div>
<div class="wg-help">Две формы. <b>Слева</b> — найди $a_n$ по $a_1$, $d$, $n$. <b>Справа</b> — найди $d$ по $a_1$, $a_n$, $n$.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px;margin-bottom:8px">
<div style="background:var(--card);border-radius:11px;padding:14px">
<div style="font-weight:700;color:var(--sec-acc-d,var(--pri2));margin-bottom:10px">Найти $a_n$</div>
<div style="display:grid;grid-template-columns:auto 1fr;gap:8px 10px;align-items:center;margin-bottom:10px">
<span>$a_1$ =</span><input type="number" id="p15-iv2a-a1" class="tinp" value="3" step="1">
<span>$d$ =</span><input type="number" id="p15-iv2a-d" class="tinp" value="4" step="1">
<span>$n$ =</span><input type="number" id="p15-iv2a-n" class="tinp" value="10" step="1" min="1">
</div>
<button class="btn primary" id="p15-iv2a-go" style="width:100%">Найти $a_n$</button>
<div id="p15-iv2a-out" style="margin-top:10px;padding:8px 12px;background:var(--sec-acc-soft);border-radius:8px;font-size:.92rem;min-height:36px"></div>
</div> </div>
<div class="card-body"> <div style="background:var(--card);border-radius:11px;padding:14px">
<p>Содержание параграфа <b>«Арифметическая прогрессия»</b> будет добавлено в следующих обновлениях.</p> <div style="font-weight:700;color:var(--sec-acc-d,var(--pri2));margin-bottom:10px">Найти $d$</div>
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 1.</p> <div style="display:grid;grid-template-columns:auto 1fr;gap:8px 10px;align-items:center;margin-bottom:10px">
<span>$a_1$ =</span><input type="number" id="p15-iv2b-a1" class="tinp" value="2" step="1">
<span>$a_n$ =</span><input type="number" id="p15-iv2b-an" class="tinp" value="29" step="1">
<span>$n$ =</span><input type="number" id="p15-iv2b-n" class="tinp" value="10" step="1" min="2">
</div>
<button class="btn primary" id="p15-iv2b-go" style="width:100%">Найти $d$</button>
<div id="p15-iv2b-out" style="margin-top:10px;padding:8px 12px;background:var(--sec-acc-soft);border-radius:8px;font-size:.92rem;min-height:36px"></div>
</div> </div>
</div>` + secNav('p14', 'p16') + readButton('p15'); </div>
renderMath(root); </div>`;
/* INTERACTIVE 3 — арифм. или нет */
html += `<div class="wg" id="p15-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Является ли арифметической?</div></div>
<div class="wg-help">Даны первые члены последовательности. Если разность $a_{n+1} - a_n$ постоянна — это арифметическая прогрессия.</div>
<div class="score-display">Задача: <b id="p15-iv3-idx">1</b> / 6 &middot; Очки: <b id="p15-iv3-sc">0</b></div>
<div id="p15-iv3-q" style="text-align:center;font-size:1.05rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p15-iv3-y">Да, арифм.</button>
<button class="btn" id="p15-iv3-n">Нет</button>
</div>
<div class="feedback" id="p15-iv3-fb"></div>
</div>`;
/* INTERACTIVE 4 — тренажёр */
html += `<div class="wg" id="p15-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр прогрессии</div></div>
<div class="wg-help">Используй формулы $a_n = a_1 + (n - 1) d$ и характеристическое свойство $a_n = \\dfrac{a_{n-1} + a_{n+1}}{2}$. Ответ — целое число.</div>
<div class="score-display">Задача: <b id="p15-iv4-idx">1</b> / 6 &middot; Очки: <b id="p15-iv4-sc">0</b></div>
<div id="p15-iv4-q" style="text-align:center;font-size:1.05rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:12px;min-height:60px"></div>
<div class="actions" style="justify-content:center;align-items:center">
<span style="font-family:'JetBrains Mono',monospace">Ответ:</span>
<input type="number" id="p15-iv4-ans" class="tinp" style="width:120px;text-align:center" step="1">
<button class="btn primary" id="p15-iv4-go">Проверить</button>
</div>
<div class="feedback" id="p15-iv4-fb"></div>
</div>`;
box.innerHTML = html + secNav('p14', 'p16') + readButton('p15');
renderMath(box);
/* ===== IV1 wiring ===== */
(function(){
const svg = document.getElementById('p15-iv1-svg');
const a1Sl = document.getElementById('p15-iv1-a1');
const dSl = document.getElementById('p15-iv1-d');
const av = document.getElementById('p15-iv1-av');
const dv = document.getElementById('p15-iv1-dv');
const formula = document.getElementById('p15-iv1-formula');
const out = document.getElementById('p15-iv1-out');
let bumped = false;
function redraw(){
const a1 = +a1Sl.value, d = +dSl.value;
av.textContent = a1; dv.textContent = d;
// вычисляем 15 точек
const N = 15;
const pts = [];
for (let n=1;n<=N;n++) pts.push([n, a1 + (n-1)*d]);
const ys = pts.map(p=>p[1]);
let ymin = Math.min(...ys, 0), ymax = Math.max(...ys, 0);
const pad = Math.max(2, (ymax-ymin)*0.12);
ymin = Math.floor(ymin - pad);
ymax = Math.ceil(ymax + pad);
const ax = axes2D(460, 300, 34, 0, N+1, ymin, ymax);
let g = ax.content;
// пунктирная прямая y = a1 + (x-1) d
const xL = 0, xR = N + 1;
const yL = a1 + (xL - 1)*d;
const yR = a1 + (xR - 1)*d;
// зажмём в пределах ymin/ymax
function clip(x, y){
if (y > ymax){ return [ x + (ymax - y) / (d || 1e-9) * 1, ymax ]; }
if (y < ymin){ return [ x + (ymin - y) / (d || 1e-9) * 1, ymin ]; }
return [x, y];
}
const cL = clip(xL, yL), cR = clip(xR, yR);
g += '<line x1="'+ax.toX(cL[0])+'" y1="'+ax.toY(cL[1])+'" x2="'+ax.toX(cR[0])+'" y2="'+ax.toY(cR[1])+'" stroke="#06b6d4" stroke-width="1.5" stroke-dasharray="5 4" opacity=".7"/>';
// точки
pts.forEach(p=>{
if (p[1] < ymin || p[1] > ymax) return;
const x = ax.toX(p[0]); const y = ax.toY(p[1]);
g += '<circle cx="'+x.toFixed(1)+'" cy="'+y.toFixed(1)+'" r="5" fill="#0891b2" stroke="#fff" stroke-width="2"/>';
});
svg.innerHTML = g;
// формула
const dPart = (d>=0 ? '+ ' + d : '- ' + (-d));
formula.innerHTML = '$a_n = ' + a1 + ' ' + dPart + ' \\cdot (n - 1)$';
// первые 8 членов
const first8 = pts.slice(0,8).map(p=>p[1]).join(',\\ ');
out.innerHTML = '<b>Первые 8 членов:</b> $' + first8 + ',\\ \\ldots$';
renderMath(formula); renderMath(out);
if (!bumped){ bumped = true; bumpProgress('p15', 15); addXp(10,'p15-iv1'); }
}
a1Sl.addEventListener('input', redraw);
dSl.addEventListener('input', redraw);
redraw();
})();
/* ===== IV2 wiring ===== */
(function(){
// Найти a_n
const outA = document.getElementById('p15-iv2a-out');
let bumpedA = false;
document.getElementById('p15-iv2a-go').addEventListener('click', ()=>{
const a1 = +document.getElementById('p15-iv2a-a1').value;
const d = +document.getElementById('p15-iv2a-d').value;
const n = +document.getElementById('p15-iv2a-n').value;
if (!Number.isInteger(n) || n < 1){ outA.innerHTML = 'Номер $n$ должен быть натуральным.'; return; }
const an = a1 + (n - 1) * d;
const dPart = (d>=0 ? '+ ' + d : '- ' + (-d));
outA.innerHTML = '$a_{'+n+'} = '+a1+' '+dPart+' \\cdot ('+n+' - 1) = \\mathbf{'+fmt(an)+'}$';
renderMath(outA);
if (!bumpedA){ bumpedA = true; bumpProgress('p15', 8); addXp(5,'p15-iv2a'); }
});
// Найти d
const outB = document.getElementById('p15-iv2b-out');
let bumpedB = false;
document.getElementById('p15-iv2b-go').addEventListener('click', ()=>{
const a1 = +document.getElementById('p15-iv2b-a1').value;
const an = +document.getElementById('p15-iv2b-an').value;
const n = +document.getElementById('p15-iv2b-n').value;
if (!Number.isInteger(n) || n < 2){ outB.innerHTML = '$n$ должно быть целым и $\\ge 2$.'; renderMath(outB); return; }
const d = (an - a1) / (n - 1);
outB.innerHTML = '$d = \\dfrac{a_n - a_1}{n - 1} = \\dfrac{'+an+' - '+a1+'}{'+n+' - 1} = \\mathbf{'+fmt(d)+'}$';
renderMath(outB);
if (!bumpedB){ bumpedB = true; bumpProgress('p15', 7); addXp(5,'p15-iv2b'); }
});
})();
/* ===== IV3 wiring ===== */
(function(){
const items = [
{ q:'$1,\\ 4,\\ 7,\\ 10,\\ \\ldots$', ans:true, hint:'Разность $d = 3$ постоянна.' },
{ q:'$2,\\ 4,\\ 8,\\ 16,\\ \\ldots$', ans:false, hint:'Это геометрическая ($\\cdot 2$), не арифметическая.' },
{ q:'$5,\\ 5,\\ 5,\\ 5,\\ \\ldots$', ans:true, hint:'Это арифм. прогрессия с $d = 0$.' },
{ q:'$10,\\ 7,\\ 4,\\ 1,\\ \\ldots$', ans:true, hint:'$d = -3$ постоянна.' },
{ q:'$1,\\ 3,\\ 6,\\ 10,\\ \\ldots$', ans:false, hint:'Разности $2, 3, 4, \\ldots$ — не равны.' },
{ q:'$-2,\\ 1,\\ 4,\\ 7,\\ \\ldots$', ans:true, hint:'$d = 3$ постоянна.' }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p15-iv3-idx');
const scEl = document.getElementById('p15-iv3-sc');
const qEl = document.getElementById('p15-iv3-q');
const fb = document.getElementById('p15-iv3-fb');
const yBtn = document.getElementById('p15-iv3-y');
const nBtn = document.getElementById('p15-iv3-n');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
yBtn.disabled = true; nBtn.disabled = true;
yBtn.style.opacity = .5; nBtn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p15', 25); addXp(15,'p15-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?'&#10003; Верно. ':'&#10007; Неверно. ') + it.hint);
i++;
setTimeout(render, 1000);
}
yBtn.addEventListener('click', ()=>answer(true));
nBtn.addEventListener('click', ()=>answer(false));
render();
})();
/* ===== IV4 wiring — тренажёр ===== */
(function(){
const items = [
{ q:'$a_1 = 2,\\ d = 3$. Найти $a_5$.', ans: 14 },
{ q:'$a_1 = 10,\\ d = -2$. Найти $a_{10}$.', ans: -8 },
{ q:'$a_1 = 5,\\ a_4 = 11$. Найти $d$.', ans: 2 },
{ q:'$a_3 = 7,\\ d = 3$. Найти $a_1$.', ans: 1 },
{ q:'$a_n = 3 + 2(n - 1)$. Найти $a_{50}$.', ans: 101 },
{ q:'Найти число, стоящее между $4$ и $16$ в арифм. прогрессии (т.е. среднее арифметическое).', ans: 10 }
];
let i = 0, sc = 0;
const idxEl = document.getElementById('p15-iv4-idx');
const scEl = document.getElementById('p15-iv4-sc');
const qEl = document.getElementById('p15-iv4-q');
const inp = document.getElementById('p15-iv4-ans');
const btn = document.getElementById('p15-iv4-go');
const fb = document.getElementById('p15-iv4-fb');
let bumped = false;
function render(){
idxEl.textContent = Math.min(i+1, items.length);
scEl.textContent = sc;
if (i >= items.length){
qEl.innerHTML = '<b>Готово!</b> Результат: ' + sc + ' / ' + items.length;
inp.disabled = true; btn.disabled = true;
inp.style.opacity = .5; btn.style.opacity = .5;
if (!bumped){ bumped = true; bumpProgress('p15', 25); addXp(15,'p15-iv4'); achievement('p15_done'); }
return;
}
qEl.innerHTML = items[i].q;
inp.value = ''; fb.style.display = 'none';
renderMath(qEl);
}
btn.addEventListener('click', ()=>{
if (i >= items.length) return;
const v = +inp.value;
const it = items[i];
const ok = (v === it.ans);
if (ok) sc++;
feedback(fb, ok, ok ? '&#10003; Верно: $'+it.ans+'$' : '&#10007; Неверно. Правильный ответ: $'+it.ans+'$');
i++;
setTimeout(render, 1000);
});
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } });
render();
})();
wireReadBtn('p15'); wireReadBtn('p15');
} }