feat(alg9 ch1 wave3 + final): §5 «Преобразование» + Финал главы 1 (5 боссов + ачивка)
This commit is contained in:
@@ -1503,39 +1503,523 @@ function buildP4(){
|
||||
}
|
||||
|
||||
function buildP5(){
|
||||
const root = document.getElementById('p5-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">§ 5</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Содержание параграфа <b>«Преобразование выражений»</b> будет добавлено в следующих обновлениях.</p>
|
||||
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 1.</p>
|
||||
</div>
|
||||
</div>` + secNav('p4', 'final1') + readButton('p5');
|
||||
renderMath(root);
|
||||
const box = document.getElementById('p5-body');
|
||||
let html = '';
|
||||
|
||||
html += makeCard('theory', 'Алгоритм многошагового упрощения', '5.1', `
|
||||
<p>Сложное рациональное выражение упрощается по чёткому порядку действий:</p>
|
||||
<ol style="padding-left:22px;line-height:2">
|
||||
<li><b>Разложи</b> все числители и знаменатели на множители.</li>
|
||||
<li>Выполни действия <b>в скобках</b> (сначала $+$ и $-$).</li>
|
||||
<li>Выполни <b>умножение и деление</b> в порядке слева направо.</li>
|
||||
<li><b>Сократи</b> результат.</li>
|
||||
</ol>
|
||||
<p style="background:var(--warn-bg);padding:8px 12px;border-radius:8px;border-left:3px solid var(--warn)"><b>Главное:</b> на каждом шаге следи за <b>ОДЗ</b> — никакой знаменатель не должен обращаться в ноль.</p>
|
||||
<p>Помни также правила приоритета: сначала всё, что в скобках; затем умножение/деление; затем сложение/вычитание.</p>`);
|
||||
|
||||
html += makeCard('example', 'Пример: вся цепочка от начала до конца', '5.2', `
|
||||
<p>Упростим выражение:</p>
|
||||
\\[\\left( \\dfrac{1}{x-1} + \\dfrac{1}{x+1} \\right) \\cdot \\dfrac{x^2-1}{2x}.\\]
|
||||
<p><b>Шаг 1 (скобка).</b> Общий знаменатель — $(x-1)(x+1)$:</p>
|
||||
\\[\\dfrac{1}{x-1} + \\dfrac{1}{x+1} = \\dfrac{(x+1)+(x-1)}{(x-1)(x+1)} = \\dfrac{2x}{(x-1)(x+1)}.\\]
|
||||
<p><b>Шаг 2 (умножение).</b> Разложим $x^2-1 = (x-1)(x+1)$:</p>
|
||||
\\[\\dfrac{2x}{(x-1)(x+1)} \\cdot \\dfrac{(x-1)(x+1)}{2x}.\\]
|
||||
<p><b>Шаг 3 (сокращение).</b> Сокращаем $2x$ и $(x-1)(x+1)$:</p>
|
||||
\\[= 1.\\]
|
||||
<p><b>ОДЗ:</b> $x \\ne 0,\\ x \\ne 1,\\ x \\ne -1$. На ОДЗ результат равен $1$.</p>`);
|
||||
|
||||
html += makeCard('rule', 'Полезные тождества для сокращений', '5.3', `
|
||||
<p>Без этих формул многие дроби не упростить:</p>
|
||||
<ul style="padding-left:22px;line-height:2">
|
||||
<li><b>Разность квадратов:</b> $a^2 - b^2 = (a-b)(a+b)$</li>
|
||||
<li><b>Квадрат суммы/разности:</b> $(a \\pm b)^2 = a^2 \\pm 2ab + b^2$</li>
|
||||
<li><b>Сумма/разность кубов:</b> $a^3 \\pm b^3 = (a \\pm b)(a^2 \\mp ab + b^2)$</li>
|
||||
<li><b>Вынесение общего множителя:</b> $ax \\pm ay = a(x \\pm y)$</li>
|
||||
</ul>
|
||||
<p>Перед сокращением всегда попробуй применить одно из этих тождеств — часто это единственный путь.</p>
|
||||
<details class="spoiler"><summary>Подсказка: алгоритм поиска множителя</summary><div class="spoiler-body">
|
||||
Если в числителе или знаменателе видишь два слагаемых — проверь: это разность квадратов? Если три слагаемых — может, полный квадрат? Если есть общий буквенный множитель — вынеси его. И только после этого сокращай.
|
||||
</div></details>`);
|
||||
|
||||
/* INTERACTIVE 1 — Конвейер пошагового упрощения */
|
||||
html += `<div class="wg" id="p5-iv1">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Конвейер упрощения</div></div>
|
||||
<div class="wg-help">Выбери задачу и раскрывай шаги один за другим. После последнего шага — «Готово!». 5 задач.</div>
|
||||
<div class="sliders">
|
||||
<label>Задача №<b id="p5-iv1-idx">1</b> / 5<input type="range" id="p5-iv1-sl" min="1" max="5" step="1" value="1"></label>
|
||||
</div>
|
||||
<div id="p5-iv1-before" style="text-align:center;font-size:1.18rem;padding:14px;background:var(--card);border-radius:9px;margin-bottom:10px;min-height:60px"></div>
|
||||
<div id="p5-iv1-steps" style="padding:0;background:transparent;margin-bottom:10px"></div>
|
||||
<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-bottom:10px">
|
||||
<button class="btn primary" id="p5-iv1-next">Следующий шаг ▶</button>
|
||||
<button class="btn" id="p5-iv1-reset">Сначала</button>
|
||||
</div>
|
||||
<div class="feedback" id="p5-iv1-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 2 — DnD: разложи числитель и знаменатель */
|
||||
html += `<div class="wg" id="p5-iv2">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Сборщик: разложи и сократи</div></div>
|
||||
<div class="wg-help">Дана дробь $\\dfrac{x^2-4}{x^2+4x+4}$. Перетащи правильные разложения в ящики «Числитель» и «Знаменатель». Лишние карточки оставь в пуле.</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> Подсказка: $a^2-b^2$ и $(a+b)^2$</div>
|
||||
<div id="p5-iv2-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="num">Числитель: $x^2-4$</h5><div class="drop-items" data-cat="num"></div></div>
|
||||
<div class="drop-box"><h5 data-cat="den">Знаменатель: $x^2+4x+4$</h5><div class="drop-items" data-cat="den"></div></div>
|
||||
</div>
|
||||
<div class="actions"><button class="btn primary" id="p5-iv2-check">Проверить</button><button class="btn" id="p5-iv2-reset">Сначала</button></div>
|
||||
<div id="p5-iv2-result" style="margin-top:10px;text-align:center;font-size:1.05rem;display:none;padding:10px;background:var(--sec-acc-soft);border-radius:9px"></div>
|
||||
<div class="feedback" id="p5-iv2-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 3 — Найди ошибку (квикфайр) */
|
||||
html += `<div class="wg" id="p5-iv3">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Найди ошибку в упрощении</div></div>
|
||||
<div class="wg-help">6 цепочек преобразований. Реши: верно или ошибка?</div>
|
||||
<div class="score-display"><span>Задача <b id="p5-iv3-i">1</b> / 6</span><span>Очки: <b id="p5-iv3-s">0</b> / 6</span></div>
|
||||
<div id="p5-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.1rem;text-align:center;margin-bottom:10px;min-height:54px"></div>
|
||||
<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap">
|
||||
<button class="btn primary" id="p5-iv3-ok" style="background:#10b981;border-color:#10b981">Верно</button>
|
||||
<button class="btn primary" id="p5-iv3-err" style="background:#dc2626;border-color:#dc2626">Ошибка</button>
|
||||
</div>
|
||||
<div class="feedback" id="p5-iv3-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 4 — Тренажёр многошагового упрощения */
|
||||
html += `<div class="wg" id="p5-iv4">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр многошагового упрощения</div></div>
|
||||
<div class="wg-help">Упрости каждое выражение и введи число, которое спрашивают в подсказке.</div>
|
||||
<div class="score-display"><span>Задача <b id="p5-iv4-i">1</b> / 5</span><span>Очки: <b id="p5-iv4-s">0</b> / 5</span></div>
|
||||
<div id="p5-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;margin-bottom:10px;text-align:center;min-height:60px"></div>
|
||||
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
|
||||
<span id="p5-iv4-prompt" style="font-family:'JetBrains Mono',monospace;font-size:.95rem">ответ =</span>
|
||||
<input type="number" id="p5-iv4-ans" class="tinp" style="width:110px;text-align:center">
|
||||
<button class="btn primary" id="p5-iv4-go">Проверить</button>
|
||||
<button class="btn" id="p5-iv4-start">Заново</button>
|
||||
</div>
|
||||
<div class="feedback" id="p5-iv4-fb"></div>
|
||||
</div>`;
|
||||
|
||||
html += secNav('p4', 'final1');
|
||||
html += readButton('p5');
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* IV1 — Конвейер */
|
||||
(function(){
|
||||
const T = [
|
||||
{
|
||||
title:'\\dfrac{x^2-9}{x+3} : \\dfrac{x-3}{2}',
|
||||
steps:[
|
||||
'\\dfrac{x^2-9}{x+3} : \\dfrac{x-3}{2} = \\dfrac{(x-3)(x+3)}{x+3} \\cdot \\dfrac{2}{x-3}',
|
||||
'= \\dfrac{(x-3)(x+3) \\cdot 2}{(x+3)(x-3)}',
|
||||
'= 2'
|
||||
],
|
||||
notes:['разность квадратов и деление = умножение на обратную','перемножили','сократили $(x-3)(x+3)$']
|
||||
},
|
||||
{
|
||||
title:'\\dfrac{1}{a} + \\dfrac{1}{a^2}',
|
||||
steps:[
|
||||
'\\dfrac{1}{a} + \\dfrac{1}{a^2} = \\dfrac{a}{a^2} + \\dfrac{1}{a^2}',
|
||||
'= \\dfrac{a+1}{a^2}'
|
||||
],
|
||||
notes:['общий знаменатель $a^2$','сложили числители']
|
||||
},
|
||||
{
|
||||
title:'\\dfrac{x^2-4}{x} \\cdot \\dfrac{1}{x+2}',
|
||||
steps:[
|
||||
'\\dfrac{x^2-4}{x} \\cdot \\dfrac{1}{x+2} = \\dfrac{(x-2)(x+2)}{x} \\cdot \\dfrac{1}{x+2}',
|
||||
'= \\dfrac{(x-2)(x+2)}{x(x+2)}',
|
||||
'= \\dfrac{x-2}{x}'
|
||||
],
|
||||
notes:['разложили $x^2-4$','перемножили','сократили $(x+2)$']
|
||||
},
|
||||
{
|
||||
title:'\\dfrac{a}{a-b} - \\dfrac{b}{a-b}',
|
||||
steps:[
|
||||
'\\dfrac{a}{a-b} - \\dfrac{b}{a-b} = \\dfrac{a-b}{a-b}',
|
||||
'= 1'
|
||||
],
|
||||
notes:['одинаковые знаменатели — вычли числители','сократили']
|
||||
},
|
||||
{
|
||||
title:'\\dfrac{x+1}{x-1} : \\dfrac{x^2-1}{x-1}',
|
||||
steps:[
|
||||
'\\dfrac{x+1}{x-1} : \\dfrac{x^2-1}{x-1} = \\dfrac{x+1}{x-1} \\cdot \\dfrac{x-1}{(x-1)(x+1)}',
|
||||
'= \\dfrac{(x+1)(x-1)}{(x-1)(x-1)(x+1)}',
|
||||
'= \\dfrac{1}{x-1}'
|
||||
],
|
||||
notes:['обратная дробь + разложение $x^2-1$','перемножили','сократили $(x+1)(x-1)$']
|
||||
},
|
||||
];
|
||||
const sl = document.getElementById('p5-iv1-sl');
|
||||
const idx = document.getElementById('p5-iv1-idx');
|
||||
const bEl = document.getElementById('p5-iv1-before');
|
||||
const stEl = document.getElementById('p5-iv1-steps');
|
||||
const next = document.getElementById('p5-iv1-next');
|
||||
const rst = document.getElementById('p5-iv1-reset');
|
||||
const fb = document.getElementById('p5-iv1-fb');
|
||||
let step = 0;
|
||||
const seen = new Set();
|
||||
function show(){
|
||||
const k = +sl.value; idx.textContent = k;
|
||||
const t = T[k-1];
|
||||
bEl.innerHTML = '$' + t.title + '$';
|
||||
let out = '';
|
||||
for(let i=0;i<step && i<t.steps.length;i++){
|
||||
out += '<div style="padding:10px 14px;background:var(--card);border-radius:9px;margin-bottom:8px;border-left:3px solid var(--pri)"><div style="font-size:.85rem;color:var(--muted);margin-bottom:4px">Шаг '+(i+1)+': '+t.notes[i]+'</div><div style="text-align:center;font-size:1.05rem">$'+t.steps[i]+'$</div></div>';
|
||||
}
|
||||
stEl.innerHTML = out;
|
||||
renderMath(bEl); renderMath(stEl);
|
||||
if(step >= t.steps.length){
|
||||
feedback(fb, true, '✓ Готово! Все шаги пройдены.');
|
||||
seen.add(k);
|
||||
if(seen.size === T.length && !seen.has('done')){ addXp(10,'p5-iv1'); bumpProgress('p5', 15); seen.add('done'); }
|
||||
} else {
|
||||
fb.style.display = 'none';
|
||||
}
|
||||
}
|
||||
sl.addEventListener('input', ()=>{ step = 0; show(); });
|
||||
next.addEventListener('click', ()=>{ const t = T[(+sl.value)-1]; if(step < t.steps.length){ step++; show(); } });
|
||||
rst.addEventListener('click', ()=>{ step = 0; show(); });
|
||||
show();
|
||||
})();
|
||||
|
||||
/* IV2 — DnD: разложи числитель и знаменатель */
|
||||
(function(){
|
||||
const items = [
|
||||
{ id:'s1', cat:'num', html:'$(x-2)(x+2)$' },
|
||||
{ id:'s2', cat:'den', html:'$(x+2)^2$' },
|
||||
{ id:'s3', cat:'trash', html:'$(x-2)^2$' },
|
||||
{ id:'s4', cat:'trash', html:'$x^2-2$' },
|
||||
{ id:'s5', cat:'num', html:'$(x+2)(x-2)$' },
|
||||
{ id:'s6', cat:'trash', html:'$(x-4)(x+1)$' },
|
||||
];
|
||||
const sorter = setupSorter({
|
||||
poolId:'p5-iv2-pool',
|
||||
scopeSelector:'#p5-iv2',
|
||||
items: items,
|
||||
cats:['num','den'],
|
||||
columnLayout:true,
|
||||
});
|
||||
let solved = false;
|
||||
document.getElementById('p5-iv2-check').addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('p5-iv2-fb');
|
||||
const result = document.getElementById('p5-iv2-result');
|
||||
const placed = sorter.placed;
|
||||
const inNum = items.filter(it => placed[it.id] === 'num');
|
||||
const inDen = items.filter(it => placed[it.id] === 'den');
|
||||
const trashPlaced = items.filter(it => it.cat === 'trash' && placed[it.id]);
|
||||
if(inNum.length === 0 || inDen.length === 0){ feedback(fb, false, '✗ В каждый ящик нужно что-то положить.'); return; }
|
||||
const numOk = inNum.every(it => it.cat === 'num');
|
||||
const denOk = inDen.every(it => it.cat === 'den') && inDen.some(it => it.id === 's2');
|
||||
if(numOk && denOk && trashPlaced.length === 0){
|
||||
feedback(fb, true, '✓ Верно! Разложили правильно. +10 XP');
|
||||
result.style.display = 'block';
|
||||
result.innerHTML = 'После сокращения: $\\dfrac{x^2-4}{x^2+4x+4} = \\dfrac{(x-2)(x+2)}{(x+2)^2} = \\dfrac{x-2}{x+2}$';
|
||||
renderMath(result);
|
||||
if(!solved){ addXp(10,'p5-iv2'); bumpProgress('p5', 15); solved = true; }
|
||||
} else if(trashPlaced.length > 0){
|
||||
feedback(fb, false, '✗ Лишние карточки в ящиках — убери их в пул.');
|
||||
} else {
|
||||
feedback(fb, false, '✗ Не все разложения подходят. Подсказка: $x^2-4$ — разность квадратов, $x^2+4x+4 = (x+2)^2$.');
|
||||
}
|
||||
});
|
||||
document.getElementById('p5-iv2-reset').addEventListener('click', ()=>{
|
||||
sorter.reset();
|
||||
document.getElementById('p5-iv2-fb').style.display = 'none';
|
||||
document.getElementById('p5-iv2-result').style.display = 'none';
|
||||
});
|
||||
})();
|
||||
|
||||
/* IV3 — Найди ошибку */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ expr:'$\\dfrac{x+2}{x+3} = \\dfrac{2}{3}$', ok:false, why:'нельзя сокращать <b>слагаемые</b> — только общий <b>множитель</b>' },
|
||||
{ expr:'$\\dfrac{(x+2)(x-1)}{(x+2)(x+3)} = \\dfrac{x-1}{x+3}$', ok:true, why:'сократили общий множитель $(x+2)$' },
|
||||
{ expr:'$\\dfrac{x^2-9}{x-3} = x+3$', ok:true, why:'$\\dfrac{(x-3)(x+3)}{x-3} = x+3$' },
|
||||
{ expr:'$\\dfrac{a^2+a}{a^2-a} = -1$', ok:false, why:'правильно $\\dfrac{a(a+1)}{a(a-1)} = \\dfrac{a+1}{a-1}$' },
|
||||
{ expr:'$\\dfrac{2x-2}{x-1} = 2$', ok:true, why:'$\\dfrac{2(x-1)}{x-1} = 2$' },
|
||||
{ expr:'$\\dfrac{x}{x+y} = \\dfrac{1}{1+y}$', ok:false, why:'нельзя сокращать $x$ со слагаемым $x$ в сумме $x+y$' },
|
||||
];
|
||||
let i = 0, score = 0;
|
||||
function show(){
|
||||
if(i >= Q.length){
|
||||
document.getElementById('p5-iv3-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
if(score === Q.length){ addXp(15,'p5-iv3'); bumpProgress('p5', 25); }
|
||||
else if(score >= Q.length - 1){ addXp(8,'p5-iv3'); bumpProgress('p5', 15); }
|
||||
return;
|
||||
}
|
||||
document.getElementById('p5-iv3-i').textContent = (i+1);
|
||||
document.getElementById('p5-iv3-s').textContent = score;
|
||||
document.getElementById('p5-iv3-q').innerHTML = Q[i].expr;
|
||||
renderMath(document.getElementById('p5-iv3-q'));
|
||||
document.getElementById('p5-iv3-fb').style.display = 'none';
|
||||
}
|
||||
function answer(isOk){
|
||||
if(i >= Q.length) return;
|
||||
const fb = document.getElementById('p5-iv3-fb');
|
||||
if(isOk === Q[i].ok){ score++; feedback(fb, true, '✓ Верно! '+Q[i].why+'. Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. '+Q[i].why+'. Дальше ▶');
|
||||
document.getElementById('p5-iv3-s').textContent = score;
|
||||
i++;
|
||||
setTimeout(show, 1500);
|
||||
}
|
||||
document.getElementById('p5-iv3-ok').addEventListener('click', ()=>answer(true));
|
||||
document.getElementById('p5-iv3-err').addEventListener('click', ()=>answer(false));
|
||||
show();
|
||||
})();
|
||||
|
||||
/* IV4 — Тренажёр многошагового упрощения */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ q:'$\\dfrac{a-b}{a+b} + \\dfrac{2b}{a+b}$', ans:1, prompt:'значение =', hint:'$=\\dfrac{a-b+2b}{a+b} = \\dfrac{a+b}{a+b} = 1$', res:'1' },
|
||||
{ q:'$\\dfrac{x^2-25}{x+5}$', ans:-5, prompt:'свободный член результата =', hint:'$=\\dfrac{(x-5)(x+5)}{x+5} = x-5$ — свободный член $-5$', res:'x-5' },
|
||||
{ q:'$\\dfrac{2}{x-3} - \\dfrac{2}{x+3}$', ans:12, prompt:'числитель результата =', hint:'$=\\dfrac{2(x+3)-2(x-3)}{(x-3)(x+3)} = \\dfrac{12}{x^2-9}$', res:'\\dfrac{12}{x^2-9}' },
|
||||
{ q:'$\\dfrac{a^2-1}{a+1} \\cdot \\dfrac{1}{a-1}$', ans:1, prompt:'значение =', hint:'$=\\dfrac{(a-1)(a+1)}{a+1} \\cdot \\dfrac{1}{a-1} = 1$', res:'1' },
|
||||
{ q:'$\\dfrac{x}{x^2-1} : \\dfrac{x^2}{x-1}$', ans:-1, prompt:'сумма корней знаменателя $x(x+1)$ =', hint:'$=\\dfrac{x}{(x-1)(x+1)} \\cdot \\dfrac{x-1}{x^2} = \\dfrac{1}{x(x+1)}$. Корни $0$ и $-1$, сумма $-1$.', res:'\\dfrac{1}{x(x+1)}' },
|
||||
];
|
||||
let i = 0, score = 0;
|
||||
function show(){
|
||||
if(i >= Q.length){
|
||||
document.getElementById('p5-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
document.getElementById('p5-iv4-prompt').textContent = 'ответ =';
|
||||
if(score === Q.length){ addXp(15,'p5-iv4'); bumpProgress('p5', 25); }
|
||||
else if(score >= 4){ addXp(8,'p5-iv4'); bumpProgress('p5', 15); }
|
||||
return;
|
||||
}
|
||||
document.getElementById('p5-iv4-i').textContent = (i+1);
|
||||
document.getElementById('p5-iv4-s').textContent = score;
|
||||
document.getElementById('p5-iv4-q').innerHTML = 'Упрости: ' + Q[i].q;
|
||||
document.getElementById('p5-iv4-prompt').textContent = Q[i].prompt;
|
||||
document.getElementById('p5-iv4-ans').value = '';
|
||||
renderMath(document.getElementById('p5-iv4-q'));
|
||||
document.getElementById('p5-iv4-fb').style.display = 'none';
|
||||
}
|
||||
function go(){
|
||||
if(i >= Q.length) return;
|
||||
const fb = document.getElementById('p5-iv4-fb');
|
||||
const ans = parseInt(document.getElementById('p5-iv4-ans').value, 10);
|
||||
if(isNaN(ans)){ feedback(fb, false, '✗ Введи целое число.'); return; }
|
||||
if(ans === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! Ответ: $' + Q[i].res + '$ ('+Q[i].hint+'). Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. Должно быть $' + Q[i].ans + '$. Ответ: $' + Q[i].res + '$ ('+Q[i].hint+'). Дальше ▶');
|
||||
document.getElementById('p5-iv4-s').textContent = score;
|
||||
i++;
|
||||
setTimeout(show, 1600);
|
||||
}
|
||||
document.getElementById('p5-iv4-go').addEventListener('click', go);
|
||||
document.getElementById('p5-iv4-ans').addEventListener('keydown', e=>{ if(e.key === 'Enter') go(); });
|
||||
document.getElementById('p5-iv4-start').addEventListener('click', ()=>{ i=0; score=0; show(); });
|
||||
show();
|
||||
})();
|
||||
|
||||
wireReadBtn('p5');
|
||||
}
|
||||
|
||||
function buildFinal1(){
|
||||
const root = document.getElementById('final1-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">★</span>
|
||||
const box = document.getElementById('final1-body');
|
||||
let html = '';
|
||||
|
||||
/* Часть А — Шпаргалка главы (5 mini-карточек) */
|
||||
html += `<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon theory">${ICONS.theory}</span>
|
||||
<span class="card-title">Шпаргалка главы 1</span>
|
||||
<span class="card-num">Итог</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Все ключевые правила главы — в одном месте. Просмотри перед боссами!</p>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;margin-top:10px">
|
||||
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
|
||||
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);margin-bottom:6px;font-size:.92rem">§ 1 · ОДЗ</div>
|
||||
<div style="font-size:.95rem">$\\dfrac{P(x)}{Q(x)},\\ Q(x) \\ne 0$. Решаем $Q(x)=0$ и исключаем корни.</div>
|
||||
</div>
|
||||
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
|
||||
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);margin-bottom:6px;font-size:.92rem">§ 2 · Сокращение</div>
|
||||
<div style="font-size:.95rem">$\\dfrac{AC}{BC} = \\dfrac{A}{B}$. Только общий <b>множитель</b>, не слагаемое!</div>
|
||||
</div>
|
||||
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
|
||||
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);margin-bottom:6px;font-size:.92rem">§ 3 · Сложение</div>
|
||||
<div style="font-size:.95rem">Одинаковые знам. — числители $\\pm$. Разные — приводим к НОЗ.</div>
|
||||
</div>
|
||||
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
|
||||
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);margin-bottom:6px;font-size:.92rem">§ 4 · Умножение и деление</div>
|
||||
<div style="font-size:.95rem">$\\dfrac{A}{B} \\cdot \\dfrac{C}{D} = \\dfrac{AC}{BD}$. Деление — умножение на обратную.</div>
|
||||
</div>
|
||||
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
|
||||
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);margin-bottom:6px;font-size:.92rem">§ 5 · Преобразование</div>
|
||||
<div style="font-size:.95rem">Разложи → скобки → $\\cdot$ и $:$ слева направо → сократи.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Содержание финала главы <b>Финал главы — в разработке</b> будет добавлено в следующих обновлениях.</p>
|
||||
<p style="color:var(--muted);font-size:.9rem">Боссы и итоговые задания будут добавлены в Phase 1.</p>
|
||||
</div>
|
||||
</div>` + secNav('p5', null) + readButton('final1');
|
||||
renderMath(root);
|
||||
wireReadBtn('final1');
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
/* Часть Б — 5 боссов */
|
||||
html += `<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon rule">${ICONS.rule}</span>
|
||||
<span class="card-title">Боссы главы 1</span>
|
||||
<span class="card-num">5</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>5 интегрированных задач. Каждая комбинирует несколько тем. За каждого побеждённого босса — <b>+10 XP</b>. Победишь всех — <b>+50 XP бонус</b> и ачивка «Магистр рациональных дробей»!</p>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
html += '<div id="ch1-bosses-container"></div>';
|
||||
|
||||
html += `<div style="margin-top:18px;padding:18px 20px;background:linear-gradient(135deg,var(--pri-soft),var(--sec-acc-soft));border-radius:14px;border:1.5px solid var(--pri);text-align:center" id="ch1-final-summary">
|
||||
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:var(--pri2);font-size:1.1rem;margin-bottom:6px">Прогресс по боссам</div>
|
||||
<div id="ch1-boss-overall" style="font-size:.95rem;color:var(--text);margin-bottom:10px">0 / 5 боссов побеждено</div>
|
||||
<div style="height:12px;background:var(--card);border-radius:8px;overflow:hidden;border:1px solid var(--border)">
|
||||
<div id="ch1-boss-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,#0891b2,#22d3ee);transition:width .35s"></div>
|
||||
</div>
|
||||
<div id="ch1-final-reward" style="margin-top:14px;display:none;padding:14px;background:var(--card);border-radius:11px;border:2px solid #f59e0b">
|
||||
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:#92400e;font-size:1.05rem;margin-bottom:6px">Магистр рациональных дробей</div>
|
||||
<div style="font-size:.92rem;margin-bottom:10px">Глава 1 пройдена! Все 5 боссов повержены. +50 XP бонус.</div>
|
||||
<a class="btn primary" href="/textbook/algebra-9-ch2" style="text-decoration:none">Дальше: Глава 2 <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></a>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
html += secNav('p5', null);
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* Боссы */
|
||||
const BOSSES = [
|
||||
{
|
||||
n:1, color:'#10b981',
|
||||
title:'Огр Сокращения',
|
||||
tag:'§ 1 + § 2',
|
||||
q:'Найди ОДЗ и сократи: $\\dfrac{x^2-16}{x^2-x-12}$. После сокращения получится $\\dfrac{x+4}{x+?}$. Введи число в знаменателе.',
|
||||
ans:3,
|
||||
hint:'$x^2-16 = (x-4)(x+4)$; $x^2-x-12 = (x-4)(x+3)$. Сократи $(x-4)$ — останется $\\dfrac{x+4}{x+3}$.'
|
||||
},
|
||||
{
|
||||
n:2, color:'#0891b2',
|
||||
title:'Дракон Сложения',
|
||||
tag:'§ 3 + § 2',
|
||||
q:'Упрости: $\\dfrac{1}{x-2} + \\dfrac{1}{x+2} - \\dfrac{4}{x^2-4}$. Результат имеет вид $\\dfrac{?}{x+2}$. Введи числитель.',
|
||||
ans:2,
|
||||
hint:'Общий знаменатель $x^2-4 = (x-2)(x+2)$. Числитель: $(x+2)+(x-2)-4 = 2x-4 = 2(x-2)$. После сокращения $(x-2)$: $\\dfrac{2}{x+2}$.'
|
||||
},
|
||||
{
|
||||
n:3, color:'#7c3aed',
|
||||
title:'Гидра Умножения',
|
||||
tag:'§ 4 + § 5',
|
||||
q:'Упрости: $\\dfrac{x^2-9}{x+5} \\cdot \\dfrac{x^2+10x+25}{x-3}$. Результат — квадратный трёхчлен $x^2+\\Box x+15$. Введи коэффициент при $x$.',
|
||||
ans:8,
|
||||
hint:'$(x^2-9)(x+5)^2 / ((x+5)(x-3)) = (x-3)(x+3) \\cdot (x+5)/(x-3) = (x+3)(x+5) = x^2+8x+15$.'
|
||||
},
|
||||
{
|
||||
n:4, color:'#dc2626',
|
||||
title:'Тёмный Лорд Деления',
|
||||
tag:'§ 4 + § 1',
|
||||
q:'Реши: $\\dfrac{x+1}{x^2-9} : \\dfrac{x+1}{x-3}$. Результат имеет вид $\\dfrac{1}{x+?}$. Введи число.',
|
||||
ans:3,
|
||||
hint:'ОДЗ: $x \\ne \\pm 3,\\ x \\ne -1$. $\\dfrac{x+1}{(x-3)(x+3)} \\cdot \\dfrac{x-3}{x+1} = \\dfrac{1}{x+3}$.'
|
||||
},
|
||||
{
|
||||
n:5, color:'#f59e0b',
|
||||
title:'Мастер Синтеза',
|
||||
tag:'§ 5 — итог',
|
||||
q:'Найди значение: $\\left(\\dfrac{1}{a-b} - \\dfrac{1}{a+b}\\right) : \\dfrac{2b}{a^2-b^2}$ при $a=5,\\ b=3$. Введи число.',
|
||||
ans:1,
|
||||
hint:'$\\dfrac{(a+b)-(a-b)}{(a-b)(a+b)} : \\dfrac{2b}{a^2-b^2} = \\dfrac{2b}{a^2-b^2} \\cdot \\dfrac{a^2-b^2}{2b} = 1$ — независимо от $a, b$.'
|
||||
},
|
||||
];
|
||||
|
||||
const cont = document.getElementById('ch1-bosses-container');
|
||||
const STATE_KEY = 'algebra9_ch1_bosses';
|
||||
const BOSS_STATE = (function(){
|
||||
try{ const s = localStorage.getItem(STATE_KEY); if(s) return JSON.parse(s); }catch(e){}
|
||||
return BOSSES.map(()=>({defeated:false}));
|
||||
})();
|
||||
function saveBosses(){ try{ localStorage.setItem(STATE_KEY, JSON.stringify(BOSS_STATE)); }catch(e){} }
|
||||
|
||||
cont.innerHTML = BOSSES.map((b, idx)=>{
|
||||
return '<div class="boss-card" id="boss-'+b.n+'-card" style="padding:16px;background:var(--card);border-radius:12px;border:2px solid '+b.color+';margin-bottom:14px">'
|
||||
+'<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap">'
|
||||
+'<svg viewBox="0 0 24 24" fill="none" stroke="'+b.color+'" stroke-width="2.2" style="width:28px;height:28px;flex-shrink:0"><polygon points="12,2 22,20 2,20"/></svg>'
|
||||
+'<div style="font-family:\'Unbounded\',sans-serif;font-weight:800;color:'+b.color+';font-size:1.05rem">Босс '+b.n+': '+b.title+'</div>'
|
||||
+'<div style="margin-left:auto;font-size:.78rem;color:var(--muted);padding:3px 8px;background:var(--sec-acc-soft);border-radius:6px">'+b.tag+'</div>'
|
||||
+'</div>'
|
||||
+'<div id="boss-'+b.n+'-q" style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:1rem;line-height:1.5;margin-bottom:10px">'+b.q+'</div>'
|
||||
+'<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">'
|
||||
+'<span style="font-family:\'JetBrains Mono\',monospace;font-size:.92rem">ответ =</span>'
|
||||
+'<input type="number" id="boss-'+b.n+'-ans" class="tinp" style="width:120px;text-align:center" placeholder="число">'
|
||||
+'<button class="btn primary" id="boss-'+b.n+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атаковать</button>'
|
||||
+'<button class="btn" id="boss-'+b.n+'-hint">Подсказка</button>'
|
||||
+'</div>'
|
||||
+'<div class="feedback" id="boss-'+b.n+'-fb"></div>'
|
||||
+'</div>';
|
||||
}).join('');
|
||||
renderMath(cont);
|
||||
|
||||
function refreshOverall(){
|
||||
const won = BOSS_STATE.filter(s => s.defeated).length;
|
||||
const txt = document.getElementById('ch1-boss-overall');
|
||||
const fill = document.getElementById('ch1-boss-overall-fill');
|
||||
if(txt) txt.textContent = won + ' / ' + BOSSES.length + ' боссов побеждено';
|
||||
if(fill) fill.style.width = (won * 100 / BOSSES.length) + '%';
|
||||
if(won >= BOSSES.length){
|
||||
const reward = document.getElementById('ch1-final-reward');
|
||||
if(reward && reward.style.display === 'none'){
|
||||
reward.style.display = 'block';
|
||||
if(!STATE.achievements.has('ch1_done')){
|
||||
achievement('ch1_done','Магистр рациональных дробей');
|
||||
addXp(50, 'ch1-bonus');
|
||||
bumpProgress('final1', 30);
|
||||
if(window.confetti){ try{ confetti(); }catch(e){} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOSSES.forEach((b, idx)=>{
|
||||
const card = document.getElementById('boss-'+b.n+'-card');
|
||||
const goBtn = document.getElementById('boss-'+b.n+'-go');
|
||||
const hintBtn = document.getElementById('boss-'+b.n+'-hint');
|
||||
const ansInp = document.getElementById('boss-'+b.n+'-ans');
|
||||
if(BOSS_STATE[idx].defeated){
|
||||
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
|
||||
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.textContent = '✓ Повержен';
|
||||
ansInp.disabled = true;
|
||||
}
|
||||
goBtn.addEventListener('click', ()=>{
|
||||
if(BOSS_STATE[idx].defeated) return;
|
||||
const fb = document.getElementById('boss-'+b.n+'-fb');
|
||||
const val = parseInt(ansInp.value, 10);
|
||||
if(isNaN(val)){ feedback(fb, false, '✗ Введи целое число.'); return; }
|
||||
if(val === b.ans){
|
||||
BOSS_STATE[idx].defeated = true; saveBosses();
|
||||
feedback(fb, true, '✓ Босс '+b.n+' повержен! +10 XP. '+b.hint);
|
||||
addXp(10, 'boss-ch1-'+b.n);
|
||||
bumpProgress('final1', 18);
|
||||
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.textContent = '✓ Повержен';
|
||||
ansInp.disabled = true;
|
||||
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
|
||||
refreshOverall();
|
||||
} else {
|
||||
feedback(fb, false, '✗ Промах. Попробуй ещё. Подсказка доступна.');
|
||||
}
|
||||
});
|
||||
hintBtn.addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('boss-'+b.n+'-fb');
|
||||
fb.className = 'feedback ok';
|
||||
fb.innerHTML = '<b>Подсказка:</b> '+b.hint;
|
||||
fb.style.display = 'block';
|
||||
fb.style.background = 'var(--warn-bg)';
|
||||
fb.style.color = '#92400e';
|
||||
fb.style.borderLeftColor = 'var(--warn)';
|
||||
renderMath(fb);
|
||||
});
|
||||
ansInp.addEventListener('keydown', e=>{ if(e.key === 'Enter') goBtn.click(); });
|
||||
});
|
||||
|
||||
refreshOverall();
|
||||
}
|
||||
|
||||
/* ===== Search ===== */
|
||||
|
||||
Reference in New Issue
Block a user