feat(geom9 ch2 wave2 + final): §9 «Четырёхугольники» + Финал Главы 2

This commit is contained in:
Maxim Dolgolyov
2026-05-29 10:02:33 +03:00
parent cd11b2aec6
commit b76315573c
+508 -11
View File
@@ -1049,19 +1049,516 @@ function buildP8(){
wireReadBtn('p8');
}
function buildP9(){ _stubBuilder('p9', '§9', 'Вписанные и описанные четырёхугольники', 'p8', 'final2'); }
function buildFinal2(){
const body = document.getElementById('final2-body');
/* ===== §9 Вписанные и описанные четырёхугольники ===== */
function buildP9(){
const box = document.getElementById('p9-body');
let html = '';
html += makeCard('theory', 'Финал главы 2', '★', `
<p>Итоговый раздел главы <b>«Окружности»</b> будет добавлен в следующих обновлениях.</p>
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 7.</p>`);
html += readButton('final2');
html += makeCard('theory', 'Вписанный четырёхугольник', '9.1', `
<p>Четырёхугольник <b>вписан</b> в окружность, если все его четыре вершины лежат на этой окружности.</p>
<p><b>Критерий вписанности:</b> четырёхугольник вписан в окружность тогда и только тогда, когда сумма его противоположных углов равна $180°$:</p>
$$\\alpha + \\gamma = 180°, \\qquad \\beta + \\delta = 180°$$
<p>где $\\alpha, \\beta, \\gamma, \\delta$ — углы при последовательных вершинах $A, B, C, D$.</p>
<details class="spoiler"><summary>Какие фигуры всегда вписаны?</summary><div class="spoiler-body">
<b>Прямоугольник</b> всегда можно вписать в окружность: его диагональ — диаметр (углы по 90°, $90+90=180$). <b>Произвольный параллелограмм</b> — нельзя: его противоположные углы равны, и сумма $180°$ возможна только если все углы прямые — то есть только прямоугольник.
</div></details>`);
html += makeCard('rule', 'Описанный четырёхугольник', '9.2', `
<p>Четырёхугольник <b>описан</b> около окружности, если все его четыре стороны касаются окружности.</p>
<p><b>Критерий описанности:</b> четырёхугольник описан около окружности тогда и только тогда, когда суммы его противоположных сторон равны:</p>
$$a + c = b + d$$
<p>где $a, b, c, d$ — последовательные стороны $AB, BC, CD, DA$.</p>
<p><b>Квадрат и ромб</b> всегда описаны около окружности (центр — точка пересечения диагоналей). Произвольный прямоугольник — нет (только квадрат).</p>`);
html += makeCard('example', 'Свойства и примеры', '9.3', `
<ul style="padding-left:22px;line-height:1.95">
<li><b>Квадрат:</b> и вписан, и описан (правильный четырёхугольник).</li>
<li><b>Прямоугольник:</b> вписан (диагональ = диаметр), описан только если квадрат.</li>
<li><b>Ромб:</b> описан (диагонали — биссектрисы), вписан только если квадрат.</li>
<li>В произвольном вписанном четырёхугольнике: если $\\alpha = 80°$, то $\\gamma = 180° - 80° = 100°$.</li>
<li>В описанном со сторонами $3, 5, 7, d$: $3 + 7 = 5 + d \\Rightarrow d = 5$.</li>
</ul>`);
/* IV1 — Вписанный четырёхугольник (SVG) */
html += `<div class="wg" id="p9-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Вписанный четырёхугольник</div></div>
<div class="wg-help">Перемещай вершины $A, B, C, D$ по окружности — они всегда останутся на ней. Проверь, что $\\alpha + \\gamma = 180°$ и $\\beta + \\delta = 180°$.</div>
<div class="sliders">
<label>$A$ (°)<b id="p9-iv1-aval">20</b><input type="range" id="p9-iv1-a" min="0" max="89" step="1" value="20"></label>
<label>$B$ (°)<b id="p9-iv1-bval">100</b><input type="range" id="p9-iv1-b" min="91" max="179" step="1" value="100"></label>
<label>$C$ (°)<b id="p9-iv1-cval">200</b><input type="range" id="p9-iv1-c" min="181" max="269" step="1" value="200"></label>
<label>$D$ (°)<b id="p9-iv1-dval">290</b><input type="range" id="p9-iv1-d" min="271" max="359" step="1" value="290"></label>
</div>
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
<svg id="p9-iv1-svg" viewBox="0 0 380 380" style="width:100%;min-width:320px;height:auto;display:block"></svg>
</div>
<div id="p9-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--pri-soft);border-radius:9px;font-size:.95rem;text-align:center;line-height:1.9"></div>
</div>`;
/* IV2 — Калькулятор сторон описанного */
html += `<div class="wg" id="p9-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор 4-й стороны</div></div>
<div class="wg-help">Введи три последовательные стороны описанного четырёхугольника $a, b, c$ — программа найдёт $d$ из условия $a + c = b + d$.</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="p9-iv2-a" class="tinp" style="width:80px;text-align:center" value="3" min="0.1" step="0.1">
<span style="font-family:'JetBrains Mono',monospace">$b$ =</span>
<input type="number" id="p9-iv2-b" class="tinp" style="width:80px;text-align:center" value="5" min="0.1" step="0.1">
<span style="font-family:'JetBrains Mono',monospace">$c$ =</span>
<input type="number" id="p9-iv2-c" class="tinp" style="width:80px;text-align:center" value="7" min="0.1" step="0.1">
<button class="btn primary" id="p9-iv2-go">Найти $d$</button>
</div>
<div id="p9-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="p9-iv2-fb"></div>
</div>`;
/* IV3 — «Вписан или описан?» */
html += `<div class="wg" id="p9-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Вписан или описан?</div></div>
<div class="wg-help">Дана фигура — определи, можно ли её вписать в окружность, описать около окружности — или и то, и то.</div>
<div class="score-display"><span>Задача <b id="p9-iv3-i">1</b> / 6</span><span>Очки: <b id="p9-iv3-s">0</b> / 6</span></div>
<div id="p9-iv3-q" style="padding:14px;background:var(--pri-soft);border-radius:10px;font-size:1.05rem;text-align:center;margin-bottom:10px"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:8px">
<button class="btn primary" data-ans="in" id="p9-iv3-in">Вписан</button>
<button class="btn primary" data-ans="out" id="p9-iv3-out">Описан</button>
<button class="btn primary" data-ans="both" id="p9-iv3-both">И то, и то</button>
</div>
<div class="feedback" id="p9-iv3-fb"></div>
</div>`;
/* IV4 — Тренажёр */
html += `<div class="wg" id="p9-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="p9-iv4-i">1</b> / 6</span><span>Очки: <b id="p9-iv4-s">0</b> / 6</span></div>
<div id="p9-iv4-q" style="padding:14px;background:var(--pri-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="p9-iv4-ans" class="tinp" style="width:110px;text-align:center" step="0.01">
<button class="btn primary" id="p9-iv4-go">Проверить</button>
<button class="btn" id="p9-iv4-start">Заново</button>
</div>
<div class="feedback" id="p9-iv4-fb"></div>
</div>`;
html += secNav('p8', 'final2');
html += readButton('p9');
box.innerHTML = html;
renderMath(box);
/* IV1 — Вписанный четырёхугольник */
(function(){
const sA=document.getElementById('p9-iv1-a');
const sB=document.getElementById('p9-iv1-b');
const sC=document.getElementById('p9-iv1-c');
const sD=document.getElementById('p9-iv1-d');
const lA=document.getElementById('p9-iv1-aval');
const lB=document.getElementById('p9-iv1-bval');
const lC=document.getElementById('p9-iv1-cval');
const lD=document.getElementById('p9-iv1-dval');
const svg=document.getElementById('p9-iv1-svg');
const out=document.getElementById('p9-iv1-out');
const seen=new Set();
const cx=190, cy=190, R=140;
function pt(angDeg){
const a = deg2rad(angDeg);
return { x: cx + R*Math.cos(a), y: cy - R*Math.sin(a) };
}
function interiorAngle(prev, cur, next){
// Угол при cur между сторонами cur->prev и cur->next
const v1 = unitVec(cur, prev);
const v2 = unitVec(cur, next);
let cos = v1.x*v2.x + v1.y*v2.y;
if(cos>1) cos=1; if(cos<-1) cos=-1;
return Math.acos(cos) * 180 / Math.PI;
}
function draw(){
const aA=+sA.value, aB=+sB.value, aC=+sC.value, aD=+sD.value;
lA.textContent=aA; lB.textContent=aB; lC.textContent=aC; lD.textContent=aD;
const A=pt(aA), B=pt(aB), C=pt(aC), D=pt(aD);
const sides={
AB:Math.hypot(A.x-B.x,A.y-B.y),
BC:Math.hypot(B.x-C.x,B.y-C.y),
CD:Math.hypot(C.x-D.x,C.y-D.y),
DA:Math.hypot(D.x-A.x,D.y-A.y)
};
const angA=interiorAngle(D,A,B);
const angB=interiorAngle(A,B,C);
const angC=interiorAngle(B,C,D);
const angD=interiorAngle(C,D,A);
let s='';
s += '<rect x="0" y="0" width="380" height="380" fill="none"/>';
// Окружность
s += '<circle cx="'+cx+'" cy="'+cy+'" r="'+R+'" fill="rgba(14,165,233,.06)" stroke="#0ea5e9" stroke-width="2" stroke-dasharray="6 4"/>';
// Центр
s += '<circle cx="'+cx+'" cy="'+cy+'" r="3" fill="#0ea5e9"/>';
s += '<text x="'+(cx+8)+'" y="'+(cy-6)+'" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#0369a1">O</text>';
// Четырёхугольник
s += '<polygon points="'+A.x.toFixed(2)+','+A.y.toFixed(2)+' '+B.x.toFixed(2)+','+B.y.toFixed(2)+' '+C.x.toFixed(2)+','+C.y.toFixed(2)+' '+D.x.toFixed(2)+','+D.y.toFixed(2)+'" fill="rgba(5,150,105,.10)" stroke="#059669" stroke-width="2.2" stroke-linejoin="round"/>';
// Вершины
const labs=[{p:A,n:'A',ang:aA},{p:B,n:'B',ang:aB},{p:C,n:'C',ang:aC},{p:D,n:'D',ang:aD}];
labs.forEach(L=>{
s += '<circle cx="'+L.p.x.toFixed(2)+'" cy="'+L.p.y.toFixed(2)+'" r="4" fill="#0f172a"/>';
// подпись наружу
const ra=deg2rad(L.ang);
const lx=cx + (R+18)*Math.cos(ra);
const ly=cy - (R+18)*Math.sin(ra);
s += '<text x="'+lx.toFixed(2)+'" y="'+(ly+4).toFixed(2)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="15" font-weight="700" fill="#0f172a">'+L.n+'</text>';
});
svg.innerHTML=s;
const sumAC=angA+angC, sumBD=angB+angD;
const okAC=Math.abs(sumAC-180)<0.5;
const okBD=Math.abs(sumBD-180)<0.5;
let info = 'Углы: $\\alpha = '+angA.toFixed(1)+'°$, $\\beta = '+angB.toFixed(1)+'°$, $\\gamma = '+angC.toFixed(1)+'°$, $\\delta = '+angD.toFixed(1)+'°$<br>'
+ '$\\alpha + \\gamma = '+sumAC.toFixed(1)+'°$ &nbsp;·&nbsp; $\\beta + \\delta = '+sumBD.toFixed(1)+'°$';
if(okAC && okBD) info += '<br><b style="color:#059669">&#10003; Это вписанный четырёхугольник!</b>';
out.innerHTML=info;
renderMath(out);
seen.add(aA+'|'+aB+'|'+aC+'|'+aD);
if(seen.size>=4 && !seen.has('done')){ addXp(10,'p9-iv1'); bumpProgress('p9',15); seen.add('done'); }
}
[sA,sB,sC,sD].forEach(s=>s.addEventListener('input', draw));
draw();
})();
/* IV2 — Калькулятор 4-й стороны */
(function(){
const aI=document.getElementById('p9-iv2-a');
const bI=document.getElementById('p9-iv2-b');
const cI=document.getElementById('p9-iv2-c');
const go=document.getElementById('p9-iv2-go');
const out=document.getElementById('p9-iv2-out');
const fb=document.getElementById('p9-iv2-fb');
let solved=0;
function calc(){
const a=parseFloat(aI.value), b=parseFloat(bI.value), c=parseFloat(cI.value);
if(!isFinite(a)||!isFinite(b)||!isFinite(c)){ feedback(fb,false,'&#10007; Введи три числа.'); return; }
if(a<=0||b<=0||c<=0){ feedback(fb,false,'&#10007; Стороны должны быть положительными.'); return; }
const d = a + c - b;
if(d<=0){
out.innerHTML = '$a + c = b + d \\Rightarrow d = a + c - b = '+a+' + '+c+' - '+b+' = '+fmt(d)+'$';
renderMath(out);
feedback(fb,false,'&#10007; Не существует описанного четырёхугольника с такими сторонами: $d \\le 0$.');
return;
}
out.innerHTML = '$a + c = b + d$<br>'
+ '$d = a + c - b = '+a+' + '+c+' - '+b+' = '+fmt(d)+'$<br>'
+ '<b>4-я сторона: $d = '+fmt(d)+'$</b>';
renderMath(out);
feedback(fb,true,'&#10003; Готово!');
solved++;
if(solved===1){ addXp(10,'p9-iv2'); bumpProgress('p9',15); }
}
go.addEventListener('click', calc);
})();
/* IV3 — Quickfire «Вписан или описан?» */
(function(){
const Q=[
{t:'Квадрат.', a:'both'},
{t:'Прямоугольник (не квадрат).', a:'in'},
{t:'Ромб (не квадрат).', a:'out'},
{t:'Трапеция, у которой суммы противоположных углов равны $180°$.', a:'in'},
{t:'Четырёхугольник со сторонами $3, 5, 7, 5$ (по порядку).', a:'out'},
{t:'Четырёхугольник с углами $90°, 80°, 90°, 100°$ (по порядку).', a:'in'}
];
const explain={
in:'Вписан: $\\alpha + \\gamma = 180°$.',
out:'Описан: $a + c = b + d$.',
both:'И вписан, и описан (правильный четырёхугольник).'
};
const qBox=document.getElementById('p9-iv3-q');
const iEl=document.getElementById('p9-iv3-i');
const sEl=document.getElementById('p9-iv3-s');
const fb=document.getElementById('p9-iv3-fb');
const btns=document.querySelectorAll('#p9-iv3 button[data-ans]');
let i=0, score=0, done=false;
function show(){
if(i>=Q.length){
qBox.innerHTML='Завершено! Очки: <b>'+score+'</b> / '+Q.length;
if(!done){ done=true; addXp(15,'p9-iv3'); bumpProgress('p9',25); if(score===Q.length) achievement('p9_done'); }
btns.forEach(b=>b.disabled=true);
return;
}
qBox.innerHTML=Q[i].t; renderMath(qBox);
iEl.textContent=(i+1); sEl.textContent=score;
fb.style.display='none';
}
btns.forEach(b=>b.addEventListener('click', ()=>{
const ok = b.dataset.ans===Q[i].a;
if(ok) score++;
feedback(fb, ok, ok?('&#10003; Верно! '+explain[Q[i].a]):('&#10007; Правильный ответ: '+({in:'Вписан',out:'Описан',both:'И то, и то'}[Q[i].a])+'. '+explain[Q[i].a]));
i++;
setTimeout(show, 900);
}));
show();
})();
/* IV4 — Тренажёр */
(function(){
const Q=[
{t:'Во вписанном 4-уг. углы $\\angle A = 75°$, $\\angle B = 110°$. Найди $\\angle C$.', a:105},
{t:'Во вписанном 4-уг. углы $\\angle A = 90°$, $\\angle B = 95°$. Найди $\\angle D$.', a:85},
{t:'В описанном 4-уг. стороны $a=4$, $b=6$, $c=8$. Найди $d$.', a:6},
{t:'В описанном 4-уг. стороны $a=5$, $b=7$, $c=9$. Найди $d$.', a:7},
{t:'Сумма противоположных углов вписанного четырёхугольника равна …', a:180},
{t:'Параллелограмм вписан в окружность. Чему равны его углы (в градусах)?', a:90}
];
const qBox=document.getElementById('p9-iv4-q');
const ans=document.getElementById('p9-iv4-ans');
const go=document.getElementById('p9-iv4-go');
const reset=document.getElementById('p9-iv4-start');
const iEl=document.getElementById('p9-iv4-i');
const sEl=document.getElementById('p9-iv4-s');
const fb=document.getElementById('p9-iv4-fb');
let i=0, score=0, done=false;
function show(){
if(i>=Q.length){
qBox.innerHTML='Финиш! Очки: <b>'+score+'</b> / '+Q.length;
if(!done){ done=true; addXp(15,'p9-iv4'); bumpProgress('p9',25); }
go.disabled=true; ans.disabled=true;
return;
}
qBox.innerHTML=Q[i].t; renderMath(qBox);
iEl.textContent=(i+1); sEl.textContent=score;
ans.value=''; fb.style.display='none'; go.disabled=false; ans.disabled=false;
}
go.addEventListener('click', ()=>{
const v=parseFloat((ans.value||'').replace(',', '.'));
if(!isFinite(v)){ feedback(fb,false,'&#10007; Введи число.'); return; }
const ok=Math.abs(v-Q[i].a)<0.05;
if(ok) score++;
feedback(fb, ok, ok?'&#10003; Верно!':'&#10007; Правильный ответ: $'+Q[i].a+'$');
i++;
setTimeout(show, 900);
});
reset.addEventListener('click', ()=>{ i=0; score=0; done=false; show(); });
show();
})();
wireReadBtn('p9');
}
/* ===== Финал главы 2 ===== */
function buildFinal2(){
const box = document.getElementById('final2-body');
let html = '';
/* Часть А — Шпаргалка главы (3 mini-карточки) */
html += `<div class="card">
<div class="card-header">
<div class="card-icon theory">${ICONS.theory}</div>
<div class="card-title">Шпаргалка главы 2</div>
<div class="card-num">Итог</div>
</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(--acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--pri2)" stroke-width="2" style="width:18px;height:18px"><circle cx="12" cy="12" r="9"/><polygon points="4 8 12 18 20 8"/></svg>
<span style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 7 · Окружности треугольника</span>
</div>
<div style="font-size:.92rem">Описанная — пересечение серединных $\\perp$, $R = \\dfrac{abc}{4S}$. Вписанная — пересечение биссектрис, $r = \\dfrac{S}{p}$.</div>
</div>
<div style="padding:12px 14px;background:var(--acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--pri2)" stroke-width="2" style="width:18px;height:18px"><polygon points="3 21 21 21 3 3"/><circle cx="12" cy="12" r="9"/></svg>
<span style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 8 · Прямоугольный $\\triangle$</span>
</div>
<div style="font-size:.92rem">$R = \\dfrac{c}{2}$ — центр на середине гипотенузы. $r = \\dfrac{a + b - c}{2}$.</div>
</div>
<div style="padding:12px 14px;background:var(--acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--pri2)" stroke-width="2" style="width:18px;height:18px"><circle cx="12" cy="12" r="9"/><polygon points="6 7 18 7 17 17 7 17"/></svg>
<span style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 9 · Четырёхугольники</span>
</div>
<div style="font-size:.92rem">Вписан $\\Leftrightarrow$ $\\alpha + \\gamma = 180°$. Описан $\\Leftrightarrow$ $a + c = b + d$.</div>
</div>
</div>
</div>
</div>`;
/* Часть Б — 5 боссов */
html += `<div class="card">
<div class="card-header">
<div class="card-icon rule">${ICONS.rule}</div>
<div class="card-title">Боссы главы 2</div>
<div class="card-num">5</div>
</div>
<div class="card-body">
<p>5 интегрированных задач — каждая комбинирует темы §§7–9. За каждого побеждённого босса — <b>+10 XP</b> и +18% к прогрессу. Победишь всех — <b>+50 XP бонус</b> и ачивка «Магистр окружностей»!</p>
</div>
</div>`;
html += '<div id="ch2G-bosses-container"></div>';
html += `<div style="margin-top:18px;padding:18px 20px;background:linear-gradient(135deg,var(--pri-soft),var(--acc-soft));border-radius:14px;border:1.5px solid var(--pri);text-align:center" id="ch2G-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="ch2G-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="ch2G-boss-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,var(--pri),var(--acc));transition:width .35s"></div>
</div>
<div id="ch2G-final-reward" style="margin-top:14px;display:none;padding:14px;background:var(--card);border-radius:11px;border:2px solid var(--warn)">
<div style="display:flex;align-items:center;justify-content:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="var(--warn)" stroke-width="2.2" style="width:22px;height:22px"><polygon points="12 2 15 9 22 9 17 14 19 22 12 18 5 22 7 14 2 9 9 9"/></svg>
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:var(--pri2);font-size:1.05rem">Магистр окружностей</div>
</div>
<div style="font-size:.92rem;margin-bottom:10px">Глава 2 пройдена! Все 5 боссов повержены. +50 XP бонус.</div>
<a class="btn primary" href="/textbook/geometry-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);
body.innerHTML = html;
wireReadBtn('final2');
if(window.renderMathInElement) renderMath(body);
box.innerHTML = html;
renderMath(box);
/* Боссы */
const BOSSES = [
{
n:1, color:'#10b981',
title:'Циклоп Окружностей',
tag:'§ 8 + § 7',
q:'В прямоугольном треугольнике катеты $a = 6$ и $b = 8$. Найди радиус <b>описанной</b> окружности.',
ans:5, decimal:false,
hint:'Гипотенуза $c = \\sqrt{6^2 + 8^2} = 10$. $R = \\dfrac{c}{2} = 5$.'
},
{
n:2, color:'#0891b2',
title:'Минотавр Вписанной',
tag:'§ 8 + § 7',
q:'В прямоугольном треугольнике катеты $a = 9$ и $b = 12$. Найди радиус <b>вписанной</b> окружности.',
ans:3, decimal:false,
hint:'Гипотенуза $c = \\sqrt{81+144} = 15$. $r = \\dfrac{a + b - c}{2} = \\dfrac{9 + 12 - 15}{2} = 3$.'
},
{
n:3, color:'#7c3aed',
title:'Гарпия 4-угольников',
tag:'§ 9',
q:'Во вписанном четырёхугольнике углы $\\angle A = 65°$ и $\\angle B = 95°$. Найди сумму $\\angle C + \\angle D$ в градусах.',
ans:200, decimal:false,
hint:'$\\angle C = 180° - \\angle A = 115°$, $\\angle D = 180° - \\angle B = 85°$. Сумма $= 115 + 85 = 200°$.'
},
{
n:4, color:'#dc2626',
title:'Дракон Окружности',
tag:'§ 7 + § 9',
q:'В равностороннем треугольнике со стороной $a = 6$ проведена описанная окружность. Найди её радиус $R$ (округли до 2 знаков).',
ans:3.46, decimal:true,
hint:'Для равностороннего $R = \\dfrac{a}{\\sqrt{3}} = \\dfrac{6}{\\sqrt{3}} = 2\\sqrt{3} \\approx 3{,}46$.'
},
{
n:5, color:'#f59e0b',
title:'Мастер Окружностей',
tag:'§§ 7-9 — синтез',
q:'В описанном четырёхугольнике стороны последовательно равны $5, 7, x, 4$. Найди $x$.',
ans:6, decimal:false,
hint:'$a + c = b + d \\Rightarrow 5 + x = 7 + 4 \\Rightarrow x = 6$.'
}
];
const cont = document.getElementById('ch2G-bosses-container');
const STATE_KEY = 'geometry9_ch2_bosses';
const BOSS_STATE = (function(){
try{ const s = localStorage.getItem(STATE_KEY); if(s){ const p = JSON.parse(s); if(Array.isArray(p) && p.length === BOSSES.length) return p; } }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)=>{
const stepAttr = b.decimal ? 'step="0.01"' : 'step="1"';
const ph = b.decimal ? 'число (можно с запятой)' : 'целое число';
return '<div class="boss-card" id="bossG2-'+b.n+'-card" style="padding:16px;background:var(--card);border-radius:12px;border:2px solid '+b.color+';margin-bottom:14px;transition:background .3s,box-shadow .3s">'
+'<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"><circle cx="12" cy="12" r="9"/><polygon points="12 4 20 18 4 18"/></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(--acc-soft);border-radius:6px">'+b.tag+'</div>'
+'</div>'
+'<div id="bossG2-'+b.n+'-q" class="boss-q" style="padding:12px 14px;background:var(--acc-soft);border-radius:9px;font-size:1rem;line-height:1.55;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" '+stepAttr+' id="bossG2-'+b.n+'-ans" class="tinp" style="width:150px;text-align:center" placeholder="'+ph+'">'
+'<button class="btn primary" id="bossG2-'+b.n+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атаковать</button>'
+'<button class="btn" id="bossG2-'+b.n+'-hint">Подсказка</button>'
+'</div>'
+'<div class="feedback" id="bossG2-'+b.n+'-fb"></div>'
+'</div>';
}).join('');
renderMath(cont);
function markDefeatedUI(b){
const card = document.getElementById('bossG2-'+b.n+'-card');
const goBtn = document.getElementById('bossG2-'+b.n+'-go');
const ansInp = document.getElementById('bossG2-'+b.n+'-ans');
if(!card || !goBtn || !ansInp) return;
card.style.background = 'linear-gradient(135deg,var(--acc-soft),var(--pri-soft))';
card.style.boxShadow = '0 0 0 2px '+b.color+'33, 0 8px 24px rgba(16,185,129,.12)';
goBtn.disabled = true; goBtn.style.opacity = .55;
goBtn.innerHTML = '<svg class="ic" viewBox="0 0 24 24" style="margin-right:4px"><polyline points="20 6 9 17 4 12"/></svg> Повержен';
ansInp.disabled = true;
}
function refreshOverall(){
const won = BOSS_STATE.filter(s => s.defeated).length;
const txt = document.getElementById('ch2G-boss-overall');
const fill = document.getElementById('ch2G-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('ch2G-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', 10);
if(window.confetti){ try{ window.confetti(); }catch(e){} }
}
}
}
}
BOSSES.forEach((b, idx)=>{
const goBtn = document.getElementById('bossG2-'+b.n+'-go');
const hintBtn = document.getElementById('bossG2-'+b.n+'-hint');
const ansInp = document.getElementById('bossG2-'+b.n+'-ans');
if(BOSS_STATE[idx].defeated) markDefeatedUI(b);
goBtn.addEventListener('click', ()=>{
if(BOSS_STATE[idx].defeated) return;
const fb = document.getElementById('bossG2-'+b.n+'-fb');
const raw = (ansInp.value||'').replace(',', '.').trim();
const val = parseFloat(raw);
if(isNaN(val) || raw === ''){ feedback(fb, false, '&#10007; Введи число.'); return; }
const ok = Math.abs(val - b.ans) < 0.05;
if(ok){
BOSS_STATE[idx].defeated = true; saveBosses();
feedback(fb, true, '&#10003; Босс '+b.n+' повержен! +10 XP. '+b.hint);
addXp(10, 'boss-ch2-'+b.n);
bumpProgress('final2', 18);
markDefeatedUI(b);
refreshOverall();
} else {
feedback(fb, false, '&#10007; Промах. Попробуй ещё. Подсказка доступна.');
}
});
hintBtn.addEventListener('click', ()=>{
const fb = document.getElementById('bossG2-'+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 ===== */