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:
@@ -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.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}
|
||||
.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)}
|
||||
@@ -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+ ===== */
|
||||
|
||||
function buildP14(){
|
||||
const root = document.getElementById('p14-body');
|
||||
root.innerHTML = `
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon theory">${ICONS.theory}</span>
|
||||
<span class="card-title">В разработке</span>
|
||||
<span class="card-num">§ 14</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Содержание параграфа <b>«Числовая последовательность»</b> будет добавлено в следующих обновлениях.</p>
|
||||
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 1.</p>
|
||||
</div>
|
||||
</div>` + secNav(null, 'p15') + readButton('p14');
|
||||
renderMath(root);
|
||||
const box = document.getElementById('p14-body');
|
||||
let html = '';
|
||||
|
||||
html += makeCard('theory', 'Определение', '14.1', `
|
||||
<p><b>Числовая последовательность</b> — это функция, заданная на множестве натуральных чисел $\\mathbb{N}$.</p>
|
||||
<p>Записывают: $a_1,\\ a_2,\\ a_3,\\ \\ldots,\\ a_n,\\ \\ldots$ — это <b>члены последовательности</b>.</p>
|
||||
<ul style="padding-left:22px;line-height:1.9">
|
||||
<li>$a_n$ — <b>общий член</b> (член с номером $n$);</li>
|
||||
<li>$n$ — <b>номер</b> члена ($n \\in \\mathbb{N}$);</li>
|
||||
<li>$a_1$ — первый член, $a_2$ — второй, и т. д.</li>
|
||||
</ul>
|
||||
<p>Кратко последовательность обозначают $(a_n)$. Это значит: $a \\colon \\mathbb{N} \\to \\mathbb{R}$.</p>`);
|
||||
|
||||
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 · Очки: <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 · Очки: <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 ? '✓ Верно. $a_n = ' + it.ans + '$' : '✗ Неверно. Правильный ответ: $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?'✓ Верно. ':'✗ Неверно. ') + 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 ? '✓ Все верно! ' + correct + ' / ' + total : '✗ Правильно: ' + 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');
|
||||
}
|
||||
|
||||
function buildP15(){
|
||||
const root = document.getElementById('p15-body');
|
||||
root.innerHTML = `
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon theory">${ICONS.theory}</span>
|
||||
<span class="card-title">В разработке</span>
|
||||
<span class="card-num">§ 15</span>
|
||||
const box = document.getElementById('p15-body');
|
||||
let html = '';
|
||||
|
||||
html += makeCard('theory', 'Определение и формула $n$-го члена', '15.1', `
|
||||
<p><b>Арифметическая прогрессия</b> — числовая последовательность, в которой каждый следующий член отличается от предыдущего на одно и то же число $d$ — <b>разность прогрессии</b>.</p>
|
||||
<p>$$a_{n+1} - a_n = d \\quad \\text{для всех } n \\in \\mathbb{N}.$$</p>
|
||||
<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 class="card-body">
|
||||
<p>Содержание параграфа <b>«Арифметическая прогрессия»</b> будет добавлено в следующих обновлениях.</p>
|
||||
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 1.</p>
|
||||
<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">Найти $d$</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-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>` + secNav('p14', 'p16') + readButton('p15');
|
||||
renderMath(root);
|
||||
</div>
|
||||
</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 · Очки: <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 · Очки: <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?'✓ Верно. ':'✗ Неверно. ') + 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 ? '✓ Верно: $'+it.ans+'$' : '✗ Неверно. Правильный ответ: $'+it.ans+'$');
|
||||
i++;
|
||||
setTimeout(render, 1000);
|
||||
});
|
||||
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ e.preventDefault(); btn.click(); } });
|
||||
render();
|
||||
})();
|
||||
|
||||
wireReadBtn('p15');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user