feat(alg9 ch2 final): Финал Главы 2 «Функции» (5 боссов + ачивка)
This commit is contained in:
@@ -241,7 +241,7 @@ a{color:inherit;text-decoration:none}
|
||||
<section id="sec-p7" class="sec" data-watermark="↗"><div class="sec-header"><span class="sec-num">§ 7</span><h2 class="sec-h">Свойства функции</h2></div><div id="p7-body"></div></section>
|
||||
<section id="sec-p8" class="sec" data-watermark="±"><div class="sec-header"><span class="sec-num">§ 8</span><h2 class="sec-h">Чётные и нечётные функции</h2></div><div id="p8-body"></div></section>
|
||||
<section id="sec-p9" class="sec" data-watermark="→"><div class="sec-header"><span class="sec-num">§ 9</span><h2 class="sec-h">Сдвиги графиков</h2></div><div id="p9-body"></div></section>
|
||||
<section id="sec-final2" class="sec" data-watermark="★"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#059669,#10b981)">Финал главы</span><h2 class="sec-h">Итоги. 4 боссов главы 2</h2></div><div id="final2-body"></div></section>
|
||||
<section id="sec-final2" class="sec" data-watermark="★"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#059669,#10b981)">Финал главы</span><h2 class="sec-h">Итоги. 5 боссов главы 2</h2></div><div id="final2-body"></div></section>
|
||||
|
||||
</div>
|
||||
<aside class="col-side" id="col-side"><div id="sidebar-content"></div></aside>
|
||||
@@ -353,7 +353,7 @@ const PARAS = [
|
||||
{ id:'p7', num:'§ 7', name:'Свойства функции', sub:'нули, монотонность, экстр.' },
|
||||
{ id:'p8', num:'§ 8', name:'Чётные и нечётные функции', sub:'симметрия графика' },
|
||||
{ id:'p9', num:'§ 9', name:'Сдвиги графиков', sub:'$y=f(x)+b$, $y=f(x \pm a)$' },
|
||||
{ id:'final2', num:'★', name:'Финал главы', sub:'Итоги · 4 боссов', final:true }
|
||||
{ id:'final2', num:'★', name:'Финал главы', sub:'Итоги · 5 боссов', final:true }
|
||||
];
|
||||
|
||||
function buildParaSelector(){
|
||||
@@ -388,7 +388,7 @@ const SIDEBARS = {
|
||||
p7:{title:'Шпаргалка \xA77',rows:[['Нуль','$f(x_0) = 0$'],['Возрастает','при бо́льшем $x$ — бо́льшее $f(x)$'],['Убывает','при бо́льшем $x$ — меньшее $f(x)$'],['$y_{max}$','наиб. значение на промежутке']]},
|
||||
p8:{title:'Шпаргалка \xA78',rows:[['Чётная','$f(-x) = f(x)$ — симм. отн. $Oy$'],['Нечётная','$f(-x) = -f(x)$ — симм. отн. $O$'],['Ни та, ни др.','общий случай']]},
|
||||
p9:{title:'Шпаргалка \xA79',rows:[['$f(x) + b$','сдвиг вверх на $b$'],['$f(x) - b$','сдвиг вниз на $b$'],['$f(x - a)$','сдвиг вправо на $a$'],['$f(x + a)$','сдвиг влево на $a$']]},
|
||||
final2:{title:'Финал главы',rows:[['§§6–9','теория главы 2'],['Боссов','4'],['Награда','+100 XP']]}
|
||||
final2:{title:'Финал главы',rows:[['§§6–9','теория главы 2'],['Боссов','5'],['Награда','+100 XP']]}
|
||||
};
|
||||
|
||||
const TIPS=[
|
||||
@@ -396,7 +396,7 @@ const TIPS=[
|
||||
{sec:'p7',html:'<b>Нули</b> функции — это решения уравнения $f(x) = 0$.'},
|
||||
{sec:'p8',html:'Чётная функция: $f(-x) = f(x)$. Нечётная: $f(-x) = -f(x)$.'},
|
||||
{sec:'p9',html:'$y = f(x) + b$ — сдвиг по $Oy$. $y = f(x - a)$ — сдвиг по $Ox$ вправо на $a$.'},
|
||||
{sec:'final2',html:'4 босса главы 2.'}
|
||||
{sec:'final2',html:'5 боссов главы 2.'}
|
||||
];
|
||||
|
||||
function buildSidebar(id){
|
||||
@@ -1802,21 +1802,204 @@ function buildP9(){
|
||||
}
|
||||
|
||||
function buildFinal2(){
|
||||
const root = document.getElementById('final2-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('final2-body');
|
||||
let html = '';
|
||||
|
||||
/* Часть А — Шпаргалка главы (4 mini-карточки) */
|
||||
html += `<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon theory">${ICONS.theory}</span>
|
||||
<span class="card-title">Шпаргалка главы 2</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">§ 6 · Функция</div>
|
||||
<div style="font-size:.95rem">$y = f(x)$. $D(f)$ — область определения, $E(f)$ — область значений. 3 способа: формула, таблица, график.</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">§ 7 · Свойства</div>
|
||||
<div style="font-size:.95rem">Возрастает / убывает; нули — $f(x) = 0$; знакопостоянство; экстремумы ($y_{max}$, $y_{min}$).</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">§ 8 · Чётность</div>
|
||||
<div style="font-size:.95rem">Чётная: $f(-x) = f(x)$ — симм. отн. $Oy$. Нечётная: $f(-x) = -f(x)$ — симм. отн. $O$.</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">§ 9 · Сдвиги</div>
|
||||
<div style="font-size:.95rem">$y = f(x - a) + b$ — вправо на $a$, вверх на $b$. Минус в скобке — вправо!</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('p9', null) + readButton('final2');
|
||||
renderMath(root);
|
||||
wireReadBtn('final2');
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
/* Часть Б — 5 боссов */
|
||||
html += `<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon rule">${ICONS.rule}</span>
|
||||
<span class="card-title">Боссы главы 2</span>
|
||||
<span class="card-num">5</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>5 интегрированных задач. Каждая комбинирует несколько тем главы 2. За каждого побеждённого босса — <b>+10 XP</b>. Победишь всех — <b>+50 XP бонус</b> и ачивка «Магистр функций»!</p>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
html += '<div id="ch2-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="ch2-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="ch2-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="ch2-boss-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,#059669,#10b981);transition:width .35s"></div>
|
||||
</div>
|
||||
<div id="ch2-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">Глава 2 пройдена! Все 5 боссов повержены. +50 XP бонус.</div>
|
||||
<a class="btn primary" href="/textbook/algebra-9-ch3" style="text-decoration:none">Дальше: Глава 3 <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></a>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
html += secNav('p9', null);
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* Боссы */
|
||||
const BOSSES = [
|
||||
{
|
||||
n:1, color:'#10b981',
|
||||
title:'Грифон Областей',
|
||||
tag:'§ 6 + § 7',
|
||||
q:'Дана функция $f(x) = \\dfrac{1}{x - 4}$. Сколько <b>целых</b> значений $x$ из промежутка $[1;\\ 7]$ принадлежат $D(f)$?',
|
||||
ans:6,
|
||||
hint:'$D(f):\\ x \\ne 4$. На $[1;7]$ целые: $1, 2, 3, 4, 5, 6, 7$ — исключаем $4$. Остаётся <b>6</b> значений.'
|
||||
},
|
||||
{
|
||||
n:2, color:'#0891b2',
|
||||
title:'Феникс Симметрии',
|
||||
tag:'§ 7 + § 8',
|
||||
q:'Функция $f(x) = x^3 - 3x$. Найди $f(2)$, затем $f(-2)$. Введи значение $f(-2)$.',
|
||||
ans:-2,
|
||||
hint:'$f(2) = 8 - 6 = 2$. Функция нечётная: $f(-x) = -f(x)$. Значит $f(-2) = -f(2) = -2$.'
|
||||
},
|
||||
{
|
||||
n:3, color:'#7c3aed',
|
||||
title:'Кракен Сдвигов',
|
||||
tag:'§ 9 + § 6',
|
||||
q:'График $y = (x - 3)^2 - 5$ — парабола. Найди её <b>вершину</b> $(x_0;\\ y_0)$ и введи сумму $x_0 + y_0$.',
|
||||
ans:-2,
|
||||
hint:'$y = (x - 3)^2 - 5$ — сдвиг параболы $y=x^2$ вправо на $3$ и вниз на $5$. Вершина $(3;\\ -5)$. Сумма: $3 + (-5) = -2$.'
|
||||
},
|
||||
{
|
||||
n:4, color:'#dc2626',
|
||||
title:'Минотавр Свойств',
|
||||
tag:'§ 7 + § 9',
|
||||
q:'Сколько нулей у функции $y = (x + 2)^2 - 1$?',
|
||||
ans:2,
|
||||
hint:'$(x+2)^2 - 1 = 0 \\Rightarrow (x+2)^2 = 1 \\Rightarrow x + 2 = \\pm 1 \\Rightarrow x = -1$ или $x = -3$. <b>2 нуля</b>.'
|
||||
},
|
||||
{
|
||||
n:5, color:'#f59e0b',
|
||||
title:'Мастер Функций',
|
||||
tag:'§§ 6–9 — синтез',
|
||||
q:'Дана $f(x) = (x + 1)^2 - 4$. Найди: а) $f(0)$; б) оба нуля функции. Введи сумму всех трёх чисел: $f(0) + x_1 + x_2$.',
|
||||
ans:-5,
|
||||
hint:'$f(0) = 1 - 4 = -3$. Нули: $(x+1)^2 = 4 \\Rightarrow x = 1$ или $x = -3$. Сумма: $-3 + 1 + (-3) = -5$.'
|
||||
},
|
||||
];
|
||||
|
||||
const cont = document.getElementById('ch2-bosses-container');
|
||||
const STATE_KEY = 'algebra9_ch2_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="boss2-'+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="boss2-'+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="boss2-'+b.n+'-ans" class="tinp" style="width:120px;text-align:center" placeholder="число">'
|
||||
+'<button class="btn primary" id="boss2-'+b.n+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атаковать</button>'
|
||||
+'<button class="btn" id="boss2-'+b.n+'-hint">Подсказка</button>'
|
||||
+'</div>'
|
||||
+'<div class="feedback" id="boss2-'+b.n+'-fb"></div>'
|
||||
+'</div>';
|
||||
}).join('');
|
||||
renderMath(cont);
|
||||
|
||||
function refreshOverall(){
|
||||
const won = BOSS_STATE.filter(s => s.defeated).length;
|
||||
const txt = document.getElementById('ch2-boss-overall');
|
||||
const fill = document.getElementById('ch2-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('ch2-final-reward');
|
||||
if(reward && reward.style.display === 'none'){
|
||||
reward.style.display = 'block';
|
||||
if(!STATE.achievements.has('ch2_done')){
|
||||
achievement('ch2_done','Магистр функций');
|
||||
addXp(50, 'ch2-bonus');
|
||||
bumpProgress('final2', 30);
|
||||
if(window.confetti){ try{ confetti(); }catch(e){} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOSSES.forEach((b, idx)=>{
|
||||
const card = document.getElementById('boss2-'+b.n+'-card');
|
||||
const goBtn = document.getElementById('boss2-'+b.n+'-go');
|
||||
const hintBtn = document.getElementById('boss2-'+b.n+'-hint');
|
||||
const ansInp = document.getElementById('boss2-'+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('boss2-'+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-ch2-'+b.n);
|
||||
bumpProgress('final2', 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('boss2-'+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