feat(alg9 ch1 wave1): §1 «Рациональная дробь» + §2 «Основное свойство, сокращение»
This commit is contained in:
@@ -110,6 +110,46 @@ 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}
|
||||
.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
|
||||
.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px}
|
||||
.spoiler summary::-webkit-details-marker{display:none}
|
||||
.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px}
|
||||
.spoiler[open] summary::before{content:'\2212'}
|
||||
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
|
||||
.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)}
|
||||
@@ -403,6 +443,25 @@ function initTheme(){
|
||||
function renderMath(root){ if(window.renderMathInElement){ try{ renderMathInElement(root, {delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false}); }catch(e){} } }
|
||||
function feedback(elm, ok, text){ if(!elm) return; elm.className='feedback '+(ok?'ok':'fail'); elm.innerHTML=text||(ok?'✓ Верно!':'✗ Неверно'); elm.style.display='block'; try{renderMath(elm);}catch(e){} }
|
||||
function fmt(n){ if(!isFinite(n)) return '?'; if(Number.isInteger(n)) return String(n); return Math.abs(n-Math.round(n))<1e-9?String(Math.round(n)):(+n.toFixed(6)).toString(); }
|
||||
function ipow(base, exp){ let r=1; for(let i=0;i<Math.abs(exp);i++) r*=base; return exp<0 ? 1/r : r; }
|
||||
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(); }};
|
||||
}
|
||||
|
||||
const ICONS = {
|
||||
repeat:'<svg class="ic" viewBox="0 0 24 24"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>',
|
||||
@@ -431,8 +490,11 @@ function readButton(paraId){
|
||||
function wireReadBtn(paraId){
|
||||
const btn = document.getElementById(paraId+'-read-btn'); if(!btn) return;
|
||||
btn.addEventListener('click', ()=>{
|
||||
addXp(10, paraId+'-read'); bumpProgress(paraId, 100);
|
||||
addXp(10, paraId+'-read'); bumpProgress(paraId, 30);
|
||||
btn.textContent='Прочитано! +10 XP'; btn.disabled=true; btn.style.opacity=.6;
|
||||
if(paraId==='p2') achievement('p2_done');
|
||||
if(paraId==='p4') achievement('p4_done');
|
||||
if(paraId==='p5') achievement('p5_done');
|
||||
if(paraId==='final1') achievement('ch1_done');
|
||||
});
|
||||
}
|
||||
@@ -440,38 +502,493 @@ function wireReadBtn(paraId){
|
||||
/* ===== STUB BUILDERS — наполнение в Phase 1+ ===== */
|
||||
|
||||
function buildP1(){
|
||||
const root = document.getElementById('p1-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">§ 1</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, 'p2') + readButton('p1');
|
||||
renderMath(root);
|
||||
const box = document.getElementById('p1-body');
|
||||
let html = '';
|
||||
|
||||
html += makeCard('theory', 'Определение рациональной дроби', '1.1', `
|
||||
<p><b>Рациональной дробью</b> называется выражение вида $\\dfrac{P(x)}{Q(x)}$, где $P(x)$ и $Q(x)$ — многочлены, причём $Q(x) \\ne 0$.</p>
|
||||
<p>Числитель $P(x)$ может быть любым многочленом (даже нулевым), а знаменатель $Q(x)$ — <b>не равен нулю</b>.</p>
|
||||
<p>Примеры рациональных дробей:</p>
|
||||
<ul style="padding-left:22px;line-height:1.9">
|
||||
<li>$\\dfrac{x+1}{x-2}$ — простейшая дробь с линейными многочленами</li>
|
||||
<li>$\\dfrac{3}{x^2+1}$ — числитель константа, знаменатель квадратный</li>
|
||||
<li>$\\dfrac{2a^2-1}{a^2-9}$ — обе части квадратные многочлены</li>
|
||||
</ul>
|
||||
<details class="spoiler"><summary>А целые выражения — это дроби?</summary><div class="spoiler-body">
|
||||
Да! Любой многочлен $P(x)$ — это частный случай рациональной дроби: $P(x) = \\dfrac{P(x)}{1}$. Поэтому рациональные дроби обобщают и целые выражения.
|
||||
</div></details>`);
|
||||
|
||||
html += makeCard('rule', 'Область допустимых значений (ОДЗ)', '1.2', `
|
||||
<p><b>ОДЗ</b> рациональной дроби $\\dfrac{P(x)}{Q(x)}$ — это все значения переменной, при которых <b>знаменатель не обращается в ноль</b>.</p>
|
||||
<p>Алгоритм нахождения ОДЗ:</p>
|
||||
<ol style="padding-left:22px;line-height:2">
|
||||
<li>Записать уравнение $Q(x) = 0$.</li>
|
||||
<li>Найти все его корни.</li>
|
||||
<li>Исключить эти корни из множества действительных чисел.</li>
|
||||
</ol>
|
||||
<p>Запись: «$x \\ne$ ...» или «$x \\in \\mathbb{R}, x \\ne $...».</p>
|
||||
<p>Если уравнение $Q(x) = 0$ <b>не имеет корней</b> (например, $x^2 + 1 = 0$), то ОДЗ — <b>все действительные числа</b>.</p>`);
|
||||
|
||||
html += makeCard('example', 'Примеры нахождения ОДЗ', '1.3', `
|
||||
<p><b>а)</b> $\\dfrac{1}{x-3}$. Знаменатель: $x - 3 = 0 \\Rightarrow x = 3$. <b>ОДЗ: $x \\ne 3$.</b></p>
|
||||
<p><b>б)</b> $\\dfrac{x+5}{x^2-4}$. Знаменатель: $x^2 - 4 = 0 \\Rightarrow (x-2)(x+2) = 0 \\Rightarrow x = \\pm 2$. <b>ОДЗ: $x \\ne 2, x \\ne -2$.</b></p>
|
||||
<p><b>в)</b> $\\dfrac{2x}{(x-1)(x+7)}$. Знаменатель: $(x-1)(x+7) = 0 \\Rightarrow x = 1$ или $x = -7$. <b>ОДЗ: $x \\ne 1, x \\ne -7$.</b></p>`);
|
||||
|
||||
/* INTERACTIVE 1 — slider + SVG number line */
|
||||
html += `<div class="wg" id="p1-iv1">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">ОДЗ на числовой прямой</div></div>
|
||||
<div class="wg-help">Выбери дробь ползунком — увидишь её ОДЗ на числовой прямой и в текстовой форме.</div>
|
||||
<div class="sliders">
|
||||
<label>Дробь №<b id="p1-iv1-idx">1</b> / 5<input type="range" id="p1-iv1-sl" min="1" max="5" step="1" value="1"></label>
|
||||
</div>
|
||||
<div id="p1-iv1-formula" style="text-align:center;font-size:1.15rem;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="p1-iv1-svg" viewBox="0 0 600 100" style="width:100%;min-width:520px;height:auto;display:block"></svg>
|
||||
</div>
|
||||
<div id="p1-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.95rem;text-align:center"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 2 — calc ОДЗ */
|
||||
html += `<div class="wg" id="p1-iv2">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор ОДЗ дроби $\\dfrac{1}{ax+b}$</div></div>
|
||||
<div class="wg-help">Введи целые коэффициенты $a$ и $b$, нажми «Найти ОДЗ» — посчитаем точку, исключённую из ОДЗ.</div>
|
||||
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;margin-bottom:10px">
|
||||
<span style="font-family:'JetBrains Mono',monospace">$a$ =</span>
|
||||
<input type="number" id="p1-iv2-a" class="tinp" style="width:80px;text-align:center" value="2">
|
||||
<span style="font-family:'JetBrains Mono',monospace">$b$ =</span>
|
||||
<input type="number" id="p1-iv2-b" class="tinp" style="width:80px;text-align:center" value="-6">
|
||||
<button class="btn primary" id="p1-iv2-go">Найти ОДЗ</button>
|
||||
</div>
|
||||
<div id="p1-iv2-out" style="padding:12px 14px;background:var(--card);border-radius:9px;text-align:center;font-size:1rem;min-height:48px"></div>
|
||||
<div class="feedback" id="p1-iv2-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 3 — quickfire quiz */
|
||||
html += `<div class="wg" id="p1-iv3">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Входит ли в ОДЗ?</div></div>
|
||||
<div class="wg-help">Дробь и значение $x_0$. Решай: входит ли $x_0$ в ОДЗ (т.е. не обращает ли знаменатель в 0)?</div>
|
||||
<div class="score-display"><span>Задача <b id="p1-iv3-i">1</b> / 8</span><span>Очки: <b id="p1-iv3-s">0</b> / 8</span></div>
|
||||
<div id="p1-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.15rem;text-align:center;margin-bottom:10px"></div>
|
||||
<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap">
|
||||
<button class="btn primary" id="p1-iv3-yes" style="background:#10b981;border-color:#10b981">Да, входит</button>
|
||||
<button class="btn primary" id="p1-iv3-no" style="background:#dc2626;border-color:#dc2626">Нет, не входит</button>
|
||||
</div>
|
||||
<div class="feedback" id="p1-iv3-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 4 — trainer */
|
||||
html += `<div class="wg" id="p1-iv4">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр: сумма корней знаменателя</div></div>
|
||||
<div class="wg-help">Для каждой дроби введи <b>сумму всех значений</b>, при которых знаменатель $= 0$ (т.е. сумму точек, исключённых из ОДЗ).</div>
|
||||
<div class="score-display"><span>Задача <b id="p1-iv4-i">1</b> / 6</span><span>Очки: <b id="p1-iv4-s">0</b> / 6</span></div>
|
||||
<div id="p1-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;margin-bottom:10px;text-align:center"></div>
|
||||
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
|
||||
<span style="font-family:'JetBrains Mono',monospace;font-size:1rem">сумма корней =</span>
|
||||
<input type="number" id="p1-iv4-ans" class="tinp" style="width:100px;text-align:center" step="1">
|
||||
<button class="btn primary" id="p1-iv4-go">Проверить</button>
|
||||
<button class="btn" id="p1-iv4-start">Заново</button>
|
||||
</div>
|
||||
<div class="feedback" id="p1-iv4-fb"></div>
|
||||
</div>`;
|
||||
|
||||
html += secNav(null, 'p2');
|
||||
html += readButton('p1');
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* IV1 — slider + SVG */
|
||||
(function(){
|
||||
const FRACS = [
|
||||
{ fr:'\\dfrac{1}{x-2}', roots:[2], text:'$x \\ne 2$' },
|
||||
{ fr:'\\dfrac{x}{x^2-9}', roots:[-3,3], text:'$x \\ne -3,\\ x \\ne 3$' },
|
||||
{ fr:'\\dfrac{3}{(x+1)(x-4)}', roots:[-1,4], text:'$x \\ne -1,\\ x \\ne 4$' },
|
||||
{ fr:'\\dfrac{2x+1}{x^2-25}', roots:[-5,5], text:'$x \\ne -5,\\ x \\ne 5$' },
|
||||
{ fr:'\\dfrac{1}{x^2+1}', roots:[], text:'$x \\in \\mathbb{R}$ — все действительные' },
|
||||
];
|
||||
const sl = document.getElementById('p1-iv1-sl');
|
||||
const idx = document.getElementById('p1-iv1-idx');
|
||||
const fEl = document.getElementById('p1-iv1-formula');
|
||||
const svg = document.getElementById('p1-iv1-svg');
|
||||
const out = document.getElementById('p1-iv1-out');
|
||||
const seen = new Set();
|
||||
function draw(){
|
||||
const k = +sl.value; idx.textContent = k;
|
||||
const cur = FRACS[k-1];
|
||||
fEl.innerHTML = '$' + cur.fr + '$';
|
||||
let s = '';
|
||||
// grid
|
||||
s += '<rect x="0" y="0" width="600" height="100" fill="none"/>';
|
||||
for(let v=-10; v<=10; v++){
|
||||
const x = 30 + (v+10) * 27;
|
||||
s += '<line x1="'+x+'" y1="42" x2="'+x+'" y2="58" stroke="#cbd5e1" stroke-width="1"/>';
|
||||
if(v%2===0){
|
||||
s += '<text x="'+x+'" y="76" text-anchor="middle" font-family="JetBrains Mono,monospace" font-size="12" fill="#64748b">'+v+'</text>';
|
||||
}
|
||||
}
|
||||
// axis
|
||||
s += '<line x1="20" y1="50" x2="580" y2="50" stroke="#0f172a" stroke-width="2"/>';
|
||||
s += '<polygon points="580,50 570,45 570,55" fill="#0f172a"/>';
|
||||
s += '<text x="585" y="46" font-family="JetBrains Mono,monospace" font-size="13" fill="#0f172a">x</text>';
|
||||
// excluded points
|
||||
cur.roots.forEach(r=>{
|
||||
const x = 30 + (r+10) * 27;
|
||||
s += '<circle cx="'+x+'" cy="50" r="6" fill="#fff" stroke="#dc2626" stroke-width="2.5"/>';
|
||||
s += '<text x="'+x+'" y="32" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" fill="#dc2626" font-weight="700">'+r+'</text>';
|
||||
});
|
||||
svg.innerHTML = s;
|
||||
out.innerHTML = 'ОДЗ: ' + cur.text;
|
||||
renderMath(fEl); renderMath(out);
|
||||
seen.add(k);
|
||||
if(seen.size === FRACS.length){ addXp(10,'p1-iv1'); bumpProgress('p1', 15); seen.clear(); seen.add('done'); }
|
||||
}
|
||||
sl.addEventListener('input', draw);
|
||||
draw();
|
||||
})();
|
||||
|
||||
/* IV2 — ОДЗ калькулятор */
|
||||
(function(){
|
||||
const go = document.getElementById('p1-iv2-go');
|
||||
const aI = document.getElementById('p1-iv2-a');
|
||||
const bI = document.getElementById('p1-iv2-b');
|
||||
const out = document.getElementById('p1-iv2-out');
|
||||
const fb = document.getElementById('p1-iv2-fb');
|
||||
let solved = 0;
|
||||
function showFormula(){ out.innerHTML = 'Дробь: $\\dfrac{1}{('+(aI.value||'a')+')x + ('+(bI.value||'b')+')}$'; renderMath(out); }
|
||||
function calc(){
|
||||
const a = parseInt(aI.value, 10), b = parseInt(bI.value, 10);
|
||||
if(isNaN(a) || isNaN(b)){ feedback(fb, false, '✗ Введи целые числа $a$ и $b$.'); return; }
|
||||
if(a === 0){
|
||||
out.innerHTML = '<b>$a = 0$</b> — выражение не является рациональной дробью с переменной (знаменатель — константа $'+b+'$). Если ещё и $b = 0$, дробь не определена.';
|
||||
feedback(fb, true, '✓ При $a = 0$ переменная в знаменателе исчезает.');
|
||||
return;
|
||||
}
|
||||
// root: ax + b = 0 → x = -b/a
|
||||
const num = -b, den = a;
|
||||
const d = gcd(Math.abs(num), Math.abs(den));
|
||||
let nn = num/d, dd = den/d;
|
||||
if(dd < 0){ nn = -nn; dd = -dd; }
|
||||
let xstr;
|
||||
if(dd === 1) xstr = String(nn);
|
||||
else xstr = '\\dfrac{'+nn+'}{'+dd+'}';
|
||||
out.innerHTML = 'Знаменатель: $'+a+'x + ('+b+') = 0 \\Rightarrow x = '+xstr+'$.<br><b>ОДЗ: $x \\ne '+xstr+'$.</b>';
|
||||
renderMath(out);
|
||||
feedback(fb, true, '✓ ОДЗ найдена! +10 XP');
|
||||
solved++;
|
||||
if(solved === 1){ addXp(10,'p1-iv2'); bumpProgress('p1', 15); }
|
||||
}
|
||||
aI.addEventListener('input', showFormula); bI.addEventListener('input', showFormula);
|
||||
go.addEventListener('click', calc);
|
||||
showFormula();
|
||||
})();
|
||||
|
||||
/* IV3 — quickfire */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ expr:'$\\dfrac{1}{x-3}, \\ x_0 = 5$', yes:true, why:'$5 - 3 = 2 \\ne 0$' },
|
||||
{ expr:'$\\dfrac{1}{x-3}, \\ x_0 = 3$', yes:false, why:'$3 - 3 = 0$ — знаменатель обнуляется' },
|
||||
{ expr:'$\\dfrac{x}{x^2-4}, \\ x_0 = 2$', yes:false, why:'$2^2 - 4 = 0$' },
|
||||
{ expr:'$\\dfrac{x}{x^2-4}, \\ x_0 = 0$', yes:true, why:'$0^2 - 4 = -4 \\ne 0$' },
|
||||
{ expr:'$\\dfrac{1}{(x+1)(x-5)}, \\ x_0 = -1$', yes:false, why:'$(-1+1)(-1-5) = 0$' },
|
||||
{ expr:'$\\dfrac{2}{x^2+1}, \\ x_0 = -7$', yes:true, why:'$(-7)^2 + 1 = 50 \\ne 0$ (знаменатель всегда $>0$)' },
|
||||
{ expr:'$\\dfrac{x+1}{x-x}, \\ x_0 = 2$', yes:false, why:'$x - x = 0$ всегда! Дробь не определена ни при каком $x$' },
|
||||
{ expr:'$\\dfrac{3}{x^2-9}, \\ x_0 = 3$', yes:false, why:'$3^2 - 9 = 0$' },
|
||||
];
|
||||
let i = 0, score = 0;
|
||||
function show(){
|
||||
if(i >= Q.length){
|
||||
document.getElementById('p1-iv3-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
if(score === Q.length){ addXp(15,'p1-iv3'); bumpProgress('p1', 25); }
|
||||
else if(score >= Q.length - 2){ addXp(8,'p1-iv3'); bumpProgress('p1', 15); }
|
||||
return;
|
||||
}
|
||||
document.getElementById('p1-iv3-i').textContent = (i+1);
|
||||
document.getElementById('p1-iv3-s').textContent = score;
|
||||
document.getElementById('p1-iv3-q').innerHTML = Q[i].expr;
|
||||
renderMath(document.getElementById('p1-iv3-q'));
|
||||
document.getElementById('p1-iv3-fb').style.display = 'none';
|
||||
}
|
||||
function answer(isYes){
|
||||
if(i >= Q.length) return;
|
||||
const fb = document.getElementById('p1-iv3-fb');
|
||||
if(isYes === Q[i].yes){ score++; feedback(fb, true, '✓ Верно! '+Q[i].why+'. Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. '+Q[i].why+'. Дальше ▶');
|
||||
document.getElementById('p1-iv3-s').textContent = score;
|
||||
i++;
|
||||
setTimeout(show, 1000);
|
||||
}
|
||||
document.getElementById('p1-iv3-yes').addEventListener('click', ()=>answer(true));
|
||||
document.getElementById('p1-iv3-no').addEventListener('click', ()=>answer(false));
|
||||
show();
|
||||
})();
|
||||
|
||||
/* IV4 — trainer */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ q:'$\\dfrac{1}{x-7}$', sum:7, hint:'корень $x = 7$' },
|
||||
{ q:'$\\dfrac{x}{x^2-16}$', sum:0, hint:'корни $x = \\pm 4$, сумма $= 0$' },
|
||||
{ q:'$\\dfrac{1}{(x-1)(x+9)}$', sum:-8, hint:'корни $x = 1, -9$, сумма $= -8$' },
|
||||
{ q:'$\\dfrac{x^2}{x^2-25}$', sum:0, hint:'корни $x = \\pm 5$, сумма $= 0$' },
|
||||
{ q:'$\\dfrac{2}{(x+3)^2}$', sum:-3, hint:'один корень $x = -3$' },
|
||||
{ q:'$\\dfrac{1}{x^2-2x-15}$', sum:2, hint:'корни $x = 5, -3$, сумма $= 2$' },
|
||||
];
|
||||
let i = 0, score = 0;
|
||||
function show(){
|
||||
if(i >= Q.length){
|
||||
document.getElementById('p1-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
if(score === Q.length){ addXp(15,'p1-iv4'); bumpProgress('p1', 25); }
|
||||
else if(score >= 4){ addXp(8,'p1-iv4'); bumpProgress('p1', 15); }
|
||||
return;
|
||||
}
|
||||
document.getElementById('p1-iv4-i').textContent = (i+1);
|
||||
document.getElementById('p1-iv4-s').textContent = score;
|
||||
document.getElementById('p1-iv4-q').innerHTML = 'Дробь: ' + Q[i].q;
|
||||
document.getElementById('p1-iv4-ans').value = '';
|
||||
renderMath(document.getElementById('p1-iv4-q'));
|
||||
document.getElementById('p1-iv4-fb').style.display = 'none';
|
||||
}
|
||||
function go(){
|
||||
if(i >= Q.length) return;
|
||||
const fb = document.getElementById('p1-iv4-fb');
|
||||
const ans = parseInt(document.getElementById('p1-iv4-ans').value, 10);
|
||||
if(isNaN(ans)){ feedback(fb, false, '✗ Введи целое число.'); return; }
|
||||
if(ans === Q[i].sum){ score++; feedback(fb, true, '✓ Верно! '+Q[i].hint+'. Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. Сумма $= '+Q[i].sum+'$ ('+Q[i].hint+'). Дальше ▶');
|
||||
document.getElementById('p1-iv4-s').textContent = score;
|
||||
i++;
|
||||
setTimeout(show, 1200);
|
||||
}
|
||||
document.getElementById('p1-iv4-go').addEventListener('click', go);
|
||||
document.getElementById('p1-iv4-ans').addEventListener('keydown', e=>{ if(e.key === 'Enter') go(); });
|
||||
document.getElementById('p1-iv4-start').addEventListener('click', ()=>{ i=0; score=0; show(); });
|
||||
show();
|
||||
})();
|
||||
|
||||
wireReadBtn('p1');
|
||||
}
|
||||
|
||||
function buildP2(){
|
||||
const root = document.getElementById('p2-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">§ 2</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Содержание параграфа <b>«Основное свойство дроби»</b> будет добавлено в следующих обновлениях.</p>
|
||||
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 1.</p>
|
||||
</div>
|
||||
</div>` + secNav('p1', 'p3') + readButton('p2');
|
||||
renderMath(root);
|
||||
const box = document.getElementById('p2-body');
|
||||
let html = '';
|
||||
|
||||
html += makeCard('theory', 'Основное свойство дроби', '2.1', `
|
||||
<p><b>Основное свойство рациональной дроби:</b> если числитель и знаменатель дроби умножить или разделить на одно и то же отличное от нуля выражение, то значение дроби не изменится:</p>
|
||||
\\[\\dfrac{A}{B} = \\dfrac{A \\cdot C}{B \\cdot C}, \\qquad \\dfrac{A \\cdot C}{B \\cdot C} = \\dfrac{A}{B}, \\quad B \\ne 0,\\ C \\ne 0.\\]
|
||||
<p>Слева направо — это <b>умножение</b> на $C$ (приведение к общему знаменателю), справа налево — это <b>сокращение</b>.</p>
|
||||
<details class="spoiler"><summary>Правило знаков</summary><div class="spoiler-body">
|
||||
$\\dfrac{-a}{-b} = \\dfrac{a}{b}$ (минусы сокращаются), $\\dfrac{-a}{b} = -\\dfrac{a}{b} = \\dfrac{a}{-b}$ — минус можно «перекидывать».
|
||||
</div></details>`);
|
||||
|
||||
html += makeCard('rule', 'Как сокращать дробь', '2.2', `
|
||||
<p><b>Алгоритм сокращения:</b></p>
|
||||
<ol style="padding-left:22px;line-height:2">
|
||||
<li><b>Разложить</b> числитель и знаменатель на множители (вынести общий множитель, применить формулы сокращённого умножения).</li>
|
||||
<li><b>Найти</b> общий множитель числителя и знаменателя.</li>
|
||||
<li><b>Разделить</b> на него (вычеркнуть).</li>
|
||||
</ol>
|
||||
<p>Пример: $\\dfrac{12x^2}{18x} = \\dfrac{6x \\cdot 2x}{6x \\cdot 3} = \\dfrac{2x}{3}$ — общий множитель $6x$.</p>
|
||||
<p style="background:var(--warn-bg);padding:8px 12px;border-radius:8px;border-left:3px solid var(--warn)"><b>Внимание:</b> сокращать можно только <b>множители</b>, а не слагаемые! $\\dfrac{a+b}{a} \\ne 1 + b$ — это ошибка.</p>`);
|
||||
|
||||
html += makeCard('example', 'Примеры сокращения', '2.3', `
|
||||
<p><b>а)</b> $\\dfrac{a^2-b^2}{a+b} = \\dfrac{(a-b)(a+b)}{a+b} = a - b$ — применили формулу разности квадратов.</p>
|
||||
<p><b>б)</b> $\\dfrac{x^2-9}{x^2+3x} = \\dfrac{(x-3)(x+3)}{x(x+3)} = \\dfrac{x-3}{x}$ — общий множитель $(x+3)$.</p>
|
||||
<p><b>в)</b> $\\dfrac{6a^2b}{15ab^2} = \\dfrac{3ab \\cdot 2a}{3ab \\cdot 5b} = \\dfrac{2a}{5b}$ — общий множитель $3ab$.</p>`);
|
||||
|
||||
/* INTERACTIVE 1 — slider visualizer */
|
||||
html += `<div class="wg" id="p2-iv1">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Сократить с подсветкой</div></div>
|
||||
<div class="wg-help">Выбери задачу — увидишь дробь с подсвеченным общим множителем. Нажми «Показать сокращение», чтобы увидеть результат.</div>
|
||||
<div class="sliders">
|
||||
<label>Задача №<b id="p2-iv1-idx">1</b> / 5<input type="range" id="p2-iv1-sl" min="1" max="5" step="1" value="1"></label>
|
||||
</div>
|
||||
<div id="p2-iv1-before" style="text-align:center;font-size:1.25rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:10px;min-height:60px"></div>
|
||||
<div style="display:flex;gap:10px;justify-content:center;margin-bottom:10px">
|
||||
<button class="btn primary" id="p2-iv1-go">Показать сокращение</button>
|
||||
<button class="btn" id="p2-iv1-hide">Скрыть</button>
|
||||
</div>
|
||||
<div id="p2-iv1-after" style="text-align:center;font-size:1.25rem;padding:14px;background:var(--sec-acc-soft);border-radius:9px;min-height:50px;display:none"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 2 — number GCD calc */
|
||||
html += `<div class="wg" id="p2-iv2">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор сокращения числовой дроби</div></div>
|
||||
<div class="wg-help">Введи целые числитель и знаменатель — посчитаем НОД и покажем сокращённую дробь.</div>
|
||||
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;margin-bottom:10px">
|
||||
<span style="font-family:'JetBrains Mono',monospace">числитель =</span>
|
||||
<input type="number" id="p2-iv2-num" class="tinp" style="width:90px;text-align:center" value="24">
|
||||
<span style="font-family:'JetBrains Mono',monospace">знаменатель =</span>
|
||||
<input type="number" id="p2-iv2-den" class="tinp" style="width:90px;text-align:center" value="36">
|
||||
<button class="btn primary" id="p2-iv2-go">Сократить</button>
|
||||
</div>
|
||||
<div id="p2-iv2-out" style="padding:12px 14px;background:var(--card);border-radius:9px;text-align:center;font-size:1rem;min-height:50px"></div>
|
||||
<div class="feedback" id="p2-iv2-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 3 — DnD sorter "Можно сократить / Уже не сокращается" */
|
||||
html += `<div class="wg" id="p2-iv3">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Сократимо или нет?</div></div>
|
||||
<div class="wg-help">Перетащи каждую дробь в нужный ящик. Шесть дробей — два варианта.</div>
|
||||
<div class="dnd-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11V6a3 3 0 0 1 6 0v5"/><path d="M9 11h6v8a4 4 0 0 1-8 0z"/></svg> 6 дробей — 2 ящика</div>
|
||||
<div id="p2-iv3-pool"></div>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;margin-top:8px">
|
||||
<div class="drop-box"><h5 data-cat="yes">Можно сократить</h5><div class="drop-items" data-cat="yes"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="no">Уже не сокращается</h5><div class="drop-items" data-cat="no"></div></div>
|
||||
</div>
|
||||
<div class="actions"><button class="btn primary" id="p2-iv3-check">Проверить</button><button class="btn" id="p2-iv3-reset">Сначала</button></div>
|
||||
<div class="feedback" id="p2-iv3-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 4 — trainer */
|
||||
html += `<div class="wg" id="p2-iv4">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр сокращения</div></div>
|
||||
<div class="wg-help">Сократи дробь и введи <b>числитель</b> результата (число или числовой коэффициент).</div>
|
||||
<div class="score-display"><span>Задача <b id="p2-iv4-i">1</b> / 6</span><span>Очки: <b id="p2-iv4-s">0</b> / 6</span></div>
|
||||
<div id="p2-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;margin-bottom:10px;text-align:center"></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="p2-iv4-ans" class="tinp" style="width:90px;text-align:center">
|
||||
<button class="btn primary" id="p2-iv4-go">Проверить</button>
|
||||
<button class="btn" id="p2-iv4-start">Заново</button>
|
||||
</div>
|
||||
<div class="feedback" id="p2-iv4-fb"></div>
|
||||
</div>`;
|
||||
|
||||
html += secNav('p1', 'p3');
|
||||
html += readButton('p2');
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* IV1 */
|
||||
(function(){
|
||||
const TASKS = [
|
||||
{ before:'\\dfrac{6x}{9}', after:'\\dfrac{2x}{3}', common:'общий множитель $3$' },
|
||||
{ before:'\\dfrac{a^2-1}{a+1}', after:'a - 1', common:'разность квадратов: $a^2 - 1 = (a-1)(a+1)$' },
|
||||
{ before:'\\dfrac{x^2-4}{x-2}', after:'x + 2', common:'разность квадратов: $x^2 - 4 = (x-2)(x+2)$' },
|
||||
{ before:'\\dfrac{4ab^2}{6a^2 b}', after:'\\dfrac{2b}{3a}', common:'общий множитель $2ab$' },
|
||||
{ before:'\\dfrac{x^2+5x}{x^2-25}', after:'\\dfrac{x}{x-5}', common:'$x^2+5x = x(x+5)$, $x^2-25 = (x-5)(x+5)$, сокращаем $(x+5)$' },
|
||||
];
|
||||
const sl = document.getElementById('p2-iv1-sl');
|
||||
const idx = document.getElementById('p2-iv1-idx');
|
||||
const bEl = document.getElementById('p2-iv1-before');
|
||||
const aEl = document.getElementById('p2-iv1-after');
|
||||
const go = document.getElementById('p2-iv1-go');
|
||||
const hide = document.getElementById('p2-iv1-hide');
|
||||
const seen = new Set();
|
||||
function show(){
|
||||
const k = +sl.value; idx.textContent = k;
|
||||
const t = TASKS[k-1];
|
||||
bEl.innerHTML = '$' + t.before + '$';
|
||||
aEl.innerHTML = '<div style="font-size:.85rem;color:var(--muted);margin-bottom:6px">' + t.common + '</div>$' + t.before + ' \\;=\\; ' + t.after + '$';
|
||||
aEl.style.display = 'none';
|
||||
renderMath(bEl);
|
||||
seen.add(k);
|
||||
if(seen.size === TASKS.length && !seen.has('done')){ addXp(10,'p2-iv1'); bumpProgress('p2', 15); seen.add('done'); }
|
||||
}
|
||||
sl.addEventListener('input', show);
|
||||
go.addEventListener('click', ()=>{ aEl.style.display = 'block'; renderMath(aEl); });
|
||||
hide.addEventListener('click', ()=>{ aEl.style.display = 'none'; });
|
||||
show();
|
||||
})();
|
||||
|
||||
/* IV2 */
|
||||
(function(){
|
||||
const go = document.getElementById('p2-iv2-go');
|
||||
const nI = document.getElementById('p2-iv2-num');
|
||||
const dI = document.getElementById('p2-iv2-den');
|
||||
const out = document.getElementById('p2-iv2-out');
|
||||
const fb = document.getElementById('p2-iv2-fb');
|
||||
let solved = 0;
|
||||
function calc(){
|
||||
const n = parseInt(nI.value, 10), d = parseInt(dI.value, 10);
|
||||
if(isNaN(n) || isNaN(d)){ feedback(fb, false, '✗ Введи целые числа.'); return; }
|
||||
if(d === 0){ feedback(fb, false, '✗ Знаменатель не может быть 0.'); out.innerHTML = ''; return; }
|
||||
const g = gcd(n, d);
|
||||
const n2 = n/g, d2 = d/g;
|
||||
if(g === 1){
|
||||
out.innerHTML = '$\\dfrac{'+n+'}{'+d+'}$ — НОД $= 1$, дробь уже несократима.';
|
||||
} else {
|
||||
out.innerHTML = '$\\dfrac{'+n+'}{'+d+'} = \\dfrac{'+n+':'+g+'}{'+d+':'+g+'} = \\dfrac{'+n2+'}{'+d2+'}$, где НОД $= '+g+'$.';
|
||||
}
|
||||
renderMath(out);
|
||||
feedback(fb, true, '✓ Готово! +10 XP');
|
||||
solved++;
|
||||
if(solved === 1){ addXp(10,'p2-iv2'); bumpProgress('p2', 15); }
|
||||
}
|
||||
go.addEventListener('click', calc);
|
||||
calc();
|
||||
})();
|
||||
|
||||
/* IV3 — DnD sorter */
|
||||
(function(){
|
||||
const items = [
|
||||
{ id:'i1', cat:'yes', html:'$\\dfrac{12}{18}$' },
|
||||
{ id:'i2', cat:'no', html:'$\\dfrac{5}{7}$' },
|
||||
{ id:'i3', cat:'yes', html:'$\\dfrac{a^2-b^2}{a-b}$' },
|
||||
{ id:'i4', cat:'no', html:'$\\dfrac{x+1}{x-1}$' },
|
||||
{ id:'i5', cat:'yes', html:'$\\dfrac{4xy}{6xz}$' },
|
||||
{ id:'i6', cat:'no', html:'$\\dfrac{1}{x^2+1}$' },
|
||||
];
|
||||
const sorter = setupSorter({
|
||||
poolId:'p2-iv3-pool',
|
||||
scopeSelector:'#p2-iv3',
|
||||
items: items,
|
||||
cats:['yes','no'],
|
||||
columnLayout:true,
|
||||
});
|
||||
document.getElementById('p2-iv3-check').addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('p2-iv3-fb');
|
||||
const placedCount = items.filter(it => sorter.placed[it.id]).length;
|
||||
const correct = items.filter(it => sorter.placed[it.id] === it.cat).length;
|
||||
if(placedCount < items.length){ feedback(fb, false, '✗ Размести все 6 дробей.'); return; }
|
||||
if(correct === items.length){ feedback(fb, true, '✓ Все 6 на месте! +15 XP'); addXp(15,'p2-iv3'); bumpProgress('p2', 25); }
|
||||
else feedback(fb, false, '✗ Правильно ' + correct + ' из 6. Попробуй ещё.');
|
||||
});
|
||||
document.getElementById('p2-iv3-reset').addEventListener('click', ()=>{ sorter.reset(); document.getElementById('p2-iv3-fb').style.display = 'none'; });
|
||||
})();
|
||||
|
||||
/* IV4 — trainer */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ q:'$\\dfrac{8a}{12}$', n:2, res:'\\dfrac{2a}{3}', hint:'НОД(8,12) $= 4$' },
|
||||
{ q:'$\\dfrac{15x^2}{25x}$', n:3, res:'\\dfrac{3x}{5}', hint:'НОД(15,25) $= 5$, сокращаем $x$' },
|
||||
{ q:'$\\dfrac{x^2-25}{x-5}$', n:1, res:'x+5', hint:'$(x-5)(x+5) / (x-5) = x+5$' },
|
||||
{ q:'$\\dfrac{a^2-9}{a+3}$', n:1, res:'a-3', hint:'$(a-3)(a+3) / (a+3) = a-3$' },
|
||||
{ q:'$\\dfrac{14m^2 n}{21mn^2}$', n:2, res:'\\dfrac{2m}{3n}', hint:'НОД(14,21) $= 7$, сокращаем $mn$' },
|
||||
{ q:'$\\dfrac{6(x-1)}{8(x-1)^2}$', n:3, res:'\\dfrac{3}{4(x-1)}', hint:'НОД(6,8) $= 2$, сокращаем $(x-1)$' },
|
||||
];
|
||||
let i = 0, score = 0;
|
||||
function show(){
|
||||
if(i >= Q.length){
|
||||
document.getElementById('p2-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
if(score === Q.length){ addXp(15,'p2-iv4'); bumpProgress('p2', 25); }
|
||||
else if(score >= 4){ addXp(8,'p2-iv4'); bumpProgress('p2', 15); }
|
||||
return;
|
||||
}
|
||||
document.getElementById('p2-iv4-i').textContent = (i+1);
|
||||
document.getElementById('p2-iv4-s').textContent = score;
|
||||
document.getElementById('p2-iv4-q').innerHTML = 'Сократи: ' + Q[i].q;
|
||||
document.getElementById('p2-iv4-ans').value = '';
|
||||
renderMath(document.getElementById('p2-iv4-q'));
|
||||
document.getElementById('p2-iv4-fb').style.display = 'none';
|
||||
}
|
||||
function go(){
|
||||
if(i >= Q.length) return;
|
||||
const fb = document.getElementById('p2-iv4-fb');
|
||||
const ans = parseInt(document.getElementById('p2-iv4-ans').value, 10);
|
||||
if(isNaN(ans)){ feedback(fb, false, '✗ Введи число.'); return; }
|
||||
if(ans === Q[i].n){ score++; feedback(fb, true, '✓ Верно! $' + Q[i].res + '$ ('+Q[i].hint+'). Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. Должно быть $' + Q[i].n + '$, ответ $' + Q[i].res + '$ ('+Q[i].hint+'). Дальше ▶');
|
||||
document.getElementById('p2-iv4-s').textContent = score;
|
||||
i++;
|
||||
setTimeout(show, 1300);
|
||||
}
|
||||
document.getElementById('p2-iv4-go').addEventListener('click', go);
|
||||
document.getElementById('p2-iv4-ans').addEventListener('keydown', e=>{ if(e.key === 'Enter') go(); });
|
||||
document.getElementById('p2-iv4-start').addEventListener('click', ()=>{ i=0; score=0; show(); });
|
||||
show();
|
||||
})();
|
||||
|
||||
wireReadBtn('p2');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user