feat(geom11 ch4 wave3 + final): §11 «Построения» + Финал Раздела 4

This commit is contained in:
Maxim Dolgolyov
2026-05-29 15:06:29 +03:00
parent 46d373752c
commit 4423a72635
+528 -2
View File
@@ -314,7 +314,8 @@ const ACH_LABELS = {
p10_done:"Координаты и векторы освоено!",
p11_done:"Геометрические построения освоено!",
start:"Начало раздела 4!",
ch4_done:"Раздел 4 пройден!"
ch4_done:"Раздел 4 пройден!",
r4_done:"Магистр повторения"
};
function loadProgress(){
@@ -400,7 +401,7 @@ function buildParaSelector(){
}
const BUILT=new Set();
const BUILDERS = { p8:()=>buildP8(), p9:()=>buildP9(), p10:()=>buildP10(), p11:()=>buildStub('p11'), final4:()=>buildStub('final4') };
const BUILDERS = { p8:()=>buildP8(), p9:()=>buildP9(), p10:()=>buildP10(), p11:()=>buildP11(), final4:()=>buildFinal4() };
function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } }
function goTo(id){
STATE.current=id; ensureBuilt(id);
@@ -1607,6 +1608,531 @@ function buildP10(){
wireReadBtn('p10');
}
/* ===== §11 «Геометрические построения» (циркуль и линейка) ===== */
function buildP11(){
const box = document.getElementById('p11-body');
if(!box) return;
let html = '';
/* === ТЕОРИЯ === */
html += makeCard('theory', 'Основные построения циркулем и линейкой', '§ 11.1',
'<p><b>Инструменты:</b> циркуль (для окружностей и переноса длин) и линейка <i>без делений</i> (только проводить прямые через две точки). С их помощью решается удивительно широкий класс задач.</p>'
+ '<p><b>6 базовых построений</b> — основа всего курса:</p>'
+ '<ul style="margin:6px 0 10px 22px;line-height:1.75">'
+ '<li><b>Серединный перпендикуляр к отрезку $AB$.</b> Из $A$ и $B$ радиусом $r > |AB|/2$ проводим окружности — они пересекутся в двух точках $P_1$ (сверху) и $P_2$ (снизу). Прямая $P_1 P_2$ — искомая.</li>'
+ '<li><b>Биссектриса угла $\\angle AOB$.</b> Из вершины $O$ — окружность; точки пересечения с лучами назовём $X$ и $Y$. Из $X$ и $Y$ — окружности равного радиуса; их пересечение $Z$ — внутри угла. Луч $OZ$ — биссектриса.</li>'
+ '<li><b>Перпендикуляр к прямой через точку</b> (на прямой или вне неё) — вариация серединного перпендикуляра.</li>'
+ '<li><b>Параллельная прямая через точку</b> — через построение равных накрест лежащих углов.</li>'
+ '<li><b>Касательная к окружности</b> из внешней точки $P$ — строим окружность с диаметром $OP$; её пересечения с данной окружностью — точки касания.</li>'
+ '<li><b>Описанная окружность</b> треугольника — центр в пересечении серединных перпендикуляров сторон. <b>Вписанная</b> — центр в пересечении биссектрис углов.</li>'
+ '</ul>'
+ '<details class="spoiler"><summary>Почему серединный перпендикуляр работает?</summary><div class="spoiler-body">'
+ '<p>Точки $P_1$ и $P_2$ равноудалены от $A$ и $B$ (по построению $|P_i A| = |P_i B| = r$). Множество точек, равноудалённых от концов отрезка, — это серединный перпендикуляр (это его геометрическое определение). Значит, прямая $P_1 P_2$ и есть искомый перпендикуляр.</p>'
+ '</div></details>');
html += makeCard('rule', 'Алгоритм решения задач на построение', '§ 11.2',
'<p>Любая «настоящая» задача на построение решается по <b>4 этапам</b>:</p>'
+ '<ol style="margin:8px 0 10px 22px;line-height:1.75">'
+ '<li><b>Анализ.</b> Предположим, искомая фигура <i>уже построена</i>. Делаем «эскиз результата», ищем связи между известными и искомыми элементами. Часто отсюда «всплывает» вспомогательное построение (серединный $\\perp$, биссектриса, дуга...).</li>'
+ '<li><b>Построение.</b> Пошагово, циркулем и линейкой. Каждый шаг — точное действие: «Проведём окружность радиуса $r$ с центром в точке $M$», «Проведём прямую через точки $X$ и $Y$».</li>'
+ '<li><b>Доказательство.</b> Показываем, что построенная фигура удовлетворяет всем условиям задачи (опираясь на теоремы планиметрии).</li>'
+ '<li><b>Исследование.</b> При каких условиях задача имеет решение? Сколько решений? Единственно ли оно? Например: «построить треугольник по трём сторонам» возможно только при выполнении неравенства треугольника.</li>'
+ '</ol>'
+ '<p style="font-size:.88rem;color:var(--muted)">В школьной практике этапы 1 и 4 часто опускают, но именно они отличают задачу <i>на построение</i> от простого <i>описания</i> построения.</p>');
/* === ИНТЕРАКТИВ 1 — Анимация серединного перпендикуляра === */
html += '<div class="wg" id="p11-iv1">'
+ '<div class="wg-header"><span class="wg-badge">анимация · 5 шагов</span><div class="wg-title">Построение серединного $\\perp$ к отрезку $AB$</div></div>'
+ '<div class="wg-help">Двигай слайдер шагов — увидишь, как из двух дуг рождается серединный перпендикуляр. Дойди до шага 5 — +10 XP.</div>'
+ '<div style="background:var(--card);border-radius:9px;padding:10px;text-align:center"><svg id="p11-iv1-svg" viewBox="0 0 480 280" width="100%" style="max-width:480px;height:auto;display:block;margin:0 auto"></svg></div>'
+ '<div style="margin-top:10px">'
+ '<label style="display:block;font-size:.9rem;margin-bottom:6px">Шаг построения: <b id="p11-iv1-step-v">1</b> / 5</label>'
+ '<input type="range" id="p11-iv1-step" min="1" max="5" step="1" value="1" style="width:100%">'
+ '</div>'
+ '<div id="p11-iv1-desc" style="margin-top:10px;padding:10px 14px;background:var(--card);border:1px solid var(--border);border-radius:9px;font-size:.92rem;line-height:1.6;min-height:38px"></div>'
+ '</div>';
/* === ИНТЕРАКТИВ 2 — Анимация биссектрисы === */
html += '<div class="wg" id="p11-iv2">'
+ '<div class="wg-header"><span class="wg-badge">анимация · 4 шага</span><div class="wg-title">Построение биссектрисы угла $\\angle AOB$</div></div>'
+ '<div class="wg-help">Двигай слайдер шагов — биссектриса появится из пересечения двух дуг. Дойди до шага 4 — +10 XP.</div>'
+ '<div style="background:var(--card);border-radius:9px;padding:10px;text-align:center"><svg id="p11-iv2-svg" viewBox="0 0 480 280" width="100%" style="max-width:480px;height:auto;display:block;margin:0 auto"></svg></div>'
+ '<div style="margin-top:10px">'
+ '<label style="display:block;font-size:.9rem;margin-bottom:6px">Шаг построения: <b id="p11-iv2-step-v">1</b> / 4</label>'
+ '<input type="range" id="p11-iv2-step" min="1" max="4" step="1" value="1" style="width:100%">'
+ '</div>'
+ '<div id="p11-iv2-desc" style="margin-top:10px;padding:10px 14px;background:var(--card);border:1px solid var(--border);border-radius:9px;font-size:.92rem;line-height:1.6;min-height:38px"></div>'
+ '</div>';
/* === ИНТЕРАКТИВ 3 — Квикфайр «Какое построение?» === */
html += '<div class="wg" id="p11-iv3">'
+ '<div class="wg-header"><span class="wg-badge">квикфайр · 6 вопросов</span><div class="wg-title">Какое базовое построение нужно?</div></div>'
+ '<div class="wg-help">Для каждой ситуации выбери базовое построение. После 6 верных — +15 XP.</div>'
+ '<div id="p11-iv3-area"></div>'
+ '<div class="score-display" style="margin-top:10px">Верно: <b id="p11-iv3-score">0</b> / 6</div>'
+ '</div>';
html += secNavFor('p11');
html += readButton('p11');
box.innerHTML = html;
renderMath(box);
/* === IV1 — анимация серединного перпендикуляра === */
(function(){
const svg = document.getElementById('p11-iv1-svg');
const sl = document.getElementById('p11-iv1-step');
const sv = document.getElementById('p11-iv1-step-v');
const desc = document.getElementById('p11-iv1-desc');
let xpGiven = false, maxStep = 1;
/* координаты в SVG */
const A = {x:130, y:160}, B = {x:350, y:160};
const Mx = (A.x + B.x) / 2;
const r = 130; /* радиус > |AB|/2 = 110 */
/* пересечения двух окружностей */
const dx = (B.x - A.x);
const half = dx / 2;
const hY = Math.sqrt(Math.max(0, r*r - half*half));
const P1 = {x:Mx, y:160 - hY};
const P2 = {x:Mx, y:160 + hY};
const M = {x:Mx, y:160};
const TXT = [
'Шаг 1. Дан отрезок $AB$. Его нужно разделить пополам перпендикуляром.',
'Шаг 2. Проводим окружность радиуса $r > |AB|/2$ с центром в точке $A$.',
'Шаг 3. Тем же радиусом — окружность с центром в точке $B$.',
'Шаг 4. Окружности пересеклись в точках $P_1$ (сверху) и $P_2$ (снизу).',
'Шаг 5. Прямая $P_1 P_2$ — серединный перпендикуляр; она пересекает $AB$ в середине $M$.'
];
function draw(step){
let g = '';
/* фон-сетка */
g += '<rect x="0" y="0" width="480" height="280" fill="#fafafa"/>';
/* отрезок AB всегда */
g += '<line x1="'+A.x+'" y1="'+A.y+'" x2="'+B.x+'" y2="'+B.y+'" stroke="#0f172a" stroke-width="2.2"/>';
g += '<circle cx="'+A.x+'" cy="'+A.y+'" r="4.5" fill="#0f172a"/>';
g += '<circle cx="'+B.x+'" cy="'+B.y+'" r="4.5" fill="#0f172a"/>';
g += '<text x="'+(A.x-14)+'" y="'+(A.y+5)+'" font-family="Inter" font-size="14" font-weight="700" fill="#0f172a">A</text>';
g += '<text x="'+(B.x+8)+'" y="'+(B.y+5)+'" font-family="Inter" font-size="14" font-weight="700" fill="#0f172a">B</text>';
if(step >= 2){
g += '<circle cx="'+A.x+'" cy="'+A.y+'" r="'+r+'" fill="none" stroke="#2563eb" stroke-width="1.6" opacity="0.75"/>';
}
if(step >= 3){
g += '<circle cx="'+B.x+'" cy="'+B.y+'" r="'+r+'" fill="none" stroke="#dc2626" stroke-width="1.6" opacity="0.75"/>';
}
if(step >= 4){
g += '<circle cx="'+P1.x+'" cy="'+P1.y+'" r="5" fill="#10b981" stroke="#fff" stroke-width="2"/>';
g += '<circle cx="'+P2.x+'" cy="'+P2.y+'" r="5" fill="#10b981" stroke="#fff" stroke-width="2"/>';
g += '<text x="'+(P1.x+8)+'" y="'+(P1.y-4)+'" font-family="Inter" font-size="13" font-weight="700" fill="#10b981">P1</text>';
g += '<text x="'+(P2.x+8)+'" y="'+(P2.y+14)+'" font-family="Inter" font-size="13" font-weight="700" fill="#10b981">P2</text>';
}
if(step >= 5){
g += '<line x1="'+P1.x+'" y1="'+(P1.y-12)+'" x2="'+P2.x+'" y2="'+(P2.y+12)+'" stroke="#7c3aed" stroke-width="2.4"/>';
g += '<circle cx="'+M.x+'" cy="'+M.y+'" r="4.5" fill="#7c3aed" stroke="#fff" stroke-width="2"/>';
g += '<text x="'+(M.x+7)+'" y="'+(M.y+20)+'" font-family="Inter" font-size="13" font-weight="700" fill="#7c3aed">M</text>';
/* пометка прямого угла */
g += '<polyline points="'+(M.x+10)+','+M.y+' '+(M.x+10)+','+(M.y-10)+' '+M.x+','+(M.y-10)+'" fill="none" stroke="#7c3aed" stroke-width="1.5"/>';
}
svg.innerHTML = g;
}
function update(){
const s = +sl.value;
sv.textContent = s;
desc.innerHTML = TXT[s-1];
try{ renderMath(desc); }catch(e){}
draw(s);
if(s > maxStep) maxStep = s;
if(maxStep >= 5 && !xpGiven){
xpGiven = true;
addXp(10, 'p11-iv1');
bumpProgress('p11', 25);
}
}
sl.addEventListener('input', update);
update();
})();
/* === IV2 — анимация биссектрисы === */
(function(){
const svg = document.getElementById('p11-iv2-svg');
const sl = document.getElementById('p11-iv2-step');
const sv = document.getElementById('p11-iv2-step-v');
const desc = document.getElementById('p11-iv2-desc');
let xpGiven = false, maxStep = 1;
/* угол с вершиной в O */
const O = {x:120, y:220};
const angA = -10 * Math.PI/180; /* луч OA — почти горизонтальный */
const angB = -55 * Math.PI/180; /* луч OB — выше */
const angBis = (angA + angB) / 2;
const Ld = 320; /* длина лучей */
const Aend = {x:O.x + Ld*Math.cos(angA), y:O.y + Ld*Math.sin(angA)};
const Bend = {x:O.x + Ld*Math.cos(angB), y:O.y + Ld*Math.sin(angB)};
const r1 = 110;
const X = {x:O.x + r1*Math.cos(angA), y:O.y + r1*Math.sin(angA)};
const Y = {x:O.x + r1*Math.cos(angB), y:O.y + r1*Math.sin(angB)};
/* пересечение Z двух одинаковых дуг из X и Y радиуса r2 — на биссектрисе */
const r2 = 90;
/* Z на луче биссектрисы от O */
const Zd = r1*Math.cos((angB-angA)/2) + Math.sqrt(Math.max(0, r2*r2 - (r1*Math.sin((angB-angA)/2))*(r1*Math.sin((angB-angA)/2))));
const Z = {x:O.x + Zd*Math.cos(angBis), y:O.y + Zd*Math.sin(angBis)};
const TXT = [
'Шаг 1. Дан угол $\\angle AOB$ с вершиной в $O$. Нужно построить его биссектрису.',
'Шаг 2. Окружность с центром $O$ — пересекает лучи в точках $X$ (на $OA$) и $Y$ (на $OB$).',
'Шаг 3. Из $X$ и $Y$ — окружности равного радиуса. Они пересекаются в точке $Z$ внутри угла.',
'Шаг 4. Луч $OZ$ — биссектриса; он делит угол $\\angle AOB$ ровно пополам.'
];
function draw(step){
let g = '';
g += '<rect x="0" y="0" width="480" height="280" fill="#fafafa"/>';
/* лучи угла */
g += '<line x1="'+O.x+'" y1="'+O.y+'" x2="'+Aend.x+'" y2="'+Aend.y+'" stroke="#0f172a" stroke-width="2.2"/>';
g += '<line x1="'+O.x+'" y1="'+O.y+'" x2="'+Bend.x+'" y2="'+Bend.y+'" stroke="#0f172a" stroke-width="2.2"/>';
g += '<circle cx="'+O.x+'" cy="'+O.y+'" r="4.5" fill="#0f172a"/>';
g += '<text x="'+(O.x-16)+'" y="'+(O.y+6)+'" font-family="Inter" font-size="14" font-weight="700" fill="#0f172a">O</text>';
g += '<text x="'+(Aend.x-20)+'" y="'+(Aend.y+18)+'" font-family="Inter" font-size="13" font-weight="700" fill="#0f172a">A</text>';
g += '<text x="'+(Bend.x-12)+'" y="'+(Bend.y-6)+'" font-family="Inter" font-size="13" font-weight="700" fill="#0f172a">B</text>';
if(step >= 2){
g += '<circle cx="'+O.x+'" cy="'+O.y+'" r="'+r1+'" fill="none" stroke="#2563eb" stroke-width="1.6" opacity="0.75"/>';
g += '<circle cx="'+X.x+'" cy="'+X.y+'" r="4.5" fill="#2563eb" stroke="#fff" stroke-width="2"/>';
g += '<circle cx="'+Y.x+'" cy="'+Y.y+'" r="4.5" fill="#2563eb" stroke="#fff" stroke-width="2"/>';
g += '<text x="'+(X.x+6)+'" y="'+(X.y+16)+'" font-family="Inter" font-size="12" font-weight="700" fill="#2563eb">X</text>';
g += '<text x="'+(Y.x+6)+'" y="'+(Y.y-6)+'" font-family="Inter" font-size="12" font-weight="700" fill="#2563eb">Y</text>';
}
if(step >= 3){
g += '<circle cx="'+X.x+'" cy="'+X.y+'" r="'+r2+'" fill="none" stroke="#dc2626" stroke-width="1.4" opacity="0.65"/>';
g += '<circle cx="'+Y.x+'" cy="'+Y.y+'" r="'+r2+'" fill="none" stroke="#dc2626" stroke-width="1.4" opacity="0.65"/>';
g += '<circle cx="'+Z.x+'" cy="'+Z.y+'" r="5" fill="#10b981" stroke="#fff" stroke-width="2"/>';
g += '<text x="'+(Z.x+7)+'" y="'+(Z.y-4)+'" font-family="Inter" font-size="13" font-weight="700" fill="#10b981">Z</text>';
}
if(step >= 4){
const Bend2 = {x:O.x + Ld*Math.cos(angBis), y:O.y + Ld*Math.sin(angBis)};
g += '<line x1="'+O.x+'" y1="'+O.y+'" x2="'+Bend2.x+'" y2="'+Bend2.y+'" stroke="#7c3aed" stroke-width="2.6"/>';
/* стрелка */
const tipx = Bend2.x, tipy = Bend2.y;
const back = 14;
const bx = O.x + (Ld-back)*Math.cos(angBis), by = O.y + (Ld-back)*Math.sin(angBis);
const nx = -Math.sin(angBis), ny = Math.cos(angBis);
g += '<polygon points="'+tipx+','+tipy+' '+(bx + 6*nx)+','+(by + 6*ny)+' '+(bx - 6*nx)+','+(by - 6*ny)+'" fill="#7c3aed"/>';
}
svg.innerHTML = g;
}
function update(){
const s = +sl.value;
sv.textContent = s;
desc.innerHTML = TXT[s-1];
try{ renderMath(desc); }catch(e){}
draw(s);
if(s > maxStep) maxStep = s;
if(maxStep >= 4 && !xpGiven){
xpGiven = true;
addXp(10, 'p11-iv2');
bumpProgress('p11', 25);
}
}
sl.addEventListener('input', update);
update();
})();
/* === IV3 — квикфайр === */
(function(){
const tasks = [
{ q:'Нужно разделить отрезок $AB$ пополам.', a:'midperp' },
{ q:'Нужно разделить угол $\\angle AOB$ пополам.', a:'bisect' },
{ q:'Из внешней точки $P$ провести прямую, касающуюся данной окружности.', a:'tangent' },
{ q:'Через точку $P$ провести прямую, параллельную данной прямой $l$.', a:'parallel' },
{ q:'Найти центр окружности, <b>описанной</b> около треугольника $ABC$.', a:'midperp' },
{ q:'Найти центр окружности, <b>вписанной</b> в треугольник $ABC$.', a:'bisect' }
];
const LABELS = {midperp:'Серединный $\\perp$', bisect:'Биссектриса', tangent:'Касательная', parallel:'Параллельная'};
const HINTS = {
midperp:'центр описанной окружности лежит в точке пересечения серединных перпендикуляров сторон',
bisect:'центр вписанной окружности — в точке пересечения биссектрис углов',
tangent:'строится через окружность с диаметром на отрезке от центра до внешней точки',
parallel:'строится через копирование угла или равное расстояние'
};
const area = document.getElementById('p11-iv3-area');
const scoreEl = document.getElementById('p11-iv3-score');
const solved = new Set();
let xpGiven = false;
area.innerHTML = tasks.map(function(t, i){
return '<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:10px 12px;margin-bottom:8px" id="p11-iv3-q-'+i+'">'
+ '<div style="margin-bottom:6px"><b>Вопрос '+(i+1)+'.</b> '+t.q+'</div>'
+ '<div style="display:flex;gap:6px;flex-wrap:wrap">'
+ '<button class="btn" data-i="'+i+'" data-v="midperp">Серединный $\\perp$</button>'
+ '<button class="btn" data-i="'+i+'" data-v="bisect">Биссектриса</button>'
+ '<button class="btn" data-i="'+i+'" data-v="tangent">Касательная</button>'
+ '<button class="btn" data-i="'+i+'" data-v="parallel">Параллельная</button>'
+ '</div>'
+ '<div class="feedback" id="p11-iv3-fb-'+i+'"></div>'
+ '</div>';
}).join('');
renderMath(area);
area.querySelectorAll('button[data-i]').forEach(function(b){
b.addEventListener('click', function(){
const i = +b.dataset.i, v = b.dataset.v, t = tasks[i];
const fb = document.getElementById('p11-iv3-fb-'+i);
if(v === t.a){
feedback(fb, true, '&#10003; Верно — '+LABELS[t.a]+'!');
if(!solved.has(i)){
solved.add(i);
scoreEl.textContent = solved.size;
if(solved.size === tasks.length && !xpGiven){
xpGiven = true;
addXp(15, 'p11-iv3');
bumpProgress('p11', 30);
setTimeout(function(){ achievement('p11_done'); }, 400);
}
}
} else {
feedback(fb, false, '&#10007; Не то. Подумай: '+HINTS[t.a]+'.');
}
});
});
})();
wireReadBtn('p11');
}
/* ===== Финал Раздела 4 «Повторение» ===== */
function buildFinal4(){
const box = document.getElementById('final4-body');
if(!box) return;
let html = '';
/* === Часть А — Шпаргалка раздела 4 (4 mini-карточки) === */
html += '<div class="card">'
+ '<div class="card-header">'
+ '<div class="card-icon theory">' + ICONS.theory + '</div>'
+ '<div class="card-title">Шпаргалка раздела 4</div>'
+ '<div class="card-num">Итог</div>'
+ '</div>'
+ '<div class="card-body">'
+ '<p>Ключевые формулы и идеи четырёх параграфов раздела — пробеги глазами перед битвой с боссами.</p>'
+ '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin-top:10px">'
/* §8 Планиметрия */
+ '<div style="padding:12px 14px;background:var(--sec-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="#2563eb" stroke-width="2" style="width:18px;height:18px"><polygon points="12,3 22,21 2,21"/></svg>'
+ '<div style="font-family:\'Unbounded\',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 8 · Планиметрия</div>'
+ '</div>'
+ '<div style="font-size:.94rem;line-height:1.55">Пифагор $a^2+b^2=c^2$. Синусов $\\dfrac{a}{\\sin A}=2R$. Косинусов $a^2=b^2+c^2-2bc\\cos A$. Замечательные точки: $G$ (медианы), $H$ (высоты), $O$ (серединные $\\perp$), $I$ (биссектрисы). Параллелограмм, ромб, трапеция.</div>'
+ '</div>'
/* §9 Площади и объёмы */
+ '<div style="padding:12px 14px;background:var(--sec-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="#dc2626" stroke-width="2" style="width:18px;height:18px"><rect x="3" y="9" width="18" height="12" rx="1"/><polyline points="3,9 12,3 21,9"/></svg>'
+ '<div style="font-family:\'Unbounded\',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 9 · Площади и объёмы</div>'
+ '</div>'
+ '<div style="font-size:.94rem;line-height:1.55">$S_\\triangle=\\tfrac{1}{2}ab\\sin C$, Герон $S=\\sqrt{p(p-a)(p-b)(p-c)}$. Круг $S=\\pi R^2$. Призма $V=S_{осн}h$, цилиндр $V=\\pi R^2 h$. Пирамида $V=\\tfrac{1}{3}S_{осн}h$. Шар $V=\\tfrac{4}{3}\\pi R^3$, $S=4\\pi R^2$.</div>'
+ '</div>'
/* §10 Векторы 3D */
+ '<div style="padding:12px 14px;background:var(--sec-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="#7c3aed" stroke-width="2" style="width:18px;height:18px"><line x1="3" y1="21" x2="21" y2="3"/><polyline points="14,3 21,3 21,10"/></svg>'
+ '<div style="font-family:\'Unbounded\',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 10 · Векторы 3D</div>'
+ '</div>'
+ '<div style="font-size:.94rem;line-height:1.55">$|\\vec{v}|=\\sqrt{x^2+y^2+z^2}$. $\\vec{a}\\cdot\\vec{b}=a_1 b_1 + a_2 b_2 + a_3 b_3$. $\\cos\\alpha=\\dfrac{\\vec{a}\\cdot\\vec{b}}{|\\vec{a}||\\vec{b}|}$. Перпендикулярность: $\\vec{a}\\cdot\\vec{b}=0$.</div>'
+ '</div>'
/* §11 Построения */
+ '<div style="padding:12px 14px;background:var(--sec-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="#0891b2" stroke-width="2" style="width:18px;height:18px"><circle cx="12" cy="12" r="9"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="12" y1="3" x2="12" y2="21"/></svg>'
+ '<div style="font-family:\'Unbounded\',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 11 · Построения</div>'
+ '</div>'
+ '<div style="font-size:.94rem;line-height:1.55">6 базовых: серединный $\\perp$, биссектриса, перпендикуляр, параллельная, касательная, описанная / вписанная окружность. Алгоритм: <b>анализ → построение → доказательство → исследование</b>.</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">Боссы раздела 4</div>'
+ '<div class="card-num">5</div>'
+ '</div>'
+ '<div class="card-body">'
+ '<p>5 интегрированных задач — каждая опирается на темы § 8, § 9, § 10 или их синтез. За каждого побеждённого босса: <b>+10 XP, +18% к прогрессу</b>. Победишь всех — ачивка <b>«Магистр повторения»</b> и <b>+50 XP бонус</b>.</p>'
+ '<p style="font-size:.88rem;color:var(--muted);margin-top:6px">Для расчётов с $\\pi$ используй $\\pi\\approx 3{,}14$. Допуск — $\\pm 0{,}05$ (для больших чисел — $\\pm 0{,}5$).</p>'
+ '</div>'
+ '</div>';
html += '<div id="r4-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="r4-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="r4-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="r4-boss-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,#e11d48,#fb7185)"></div>'
+ '</div>'
+ '<div id="r4-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.1rem;margin-bottom:6px">Геометрия 11 пройдена!</div>'
+ '<div style="font-size:.92rem;margin-bottom:10px">Все 4 раздела изучены. Готовы к финалу курса! +50 XP бонус, ачивка «Магистр повторения».</div>'
+ '<a class="btn primary" href="/textbook/geometry-11" style="text-decoration:none">К хабу Геометрии 11 <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></a>'
+ '</div>'
+ '</div>';
html += secNav('p11', null);
box.innerHTML = html;
renderMath(box);
/* === Боссы === */
const BOSSES = [
{
n:1, color:'#2563eb',
title:'Циклоп Планиметрии',
tag:'§ 8',
q:'В треугольнике стороны равны 6, 8 и 10. Найдите наибольший угол (в градусах).',
ans:90, tol:0.05,
hint:'$6^2+8^2=36+64=100=10^2$ — треугольник прямоугольный (теорема, обратная Пифагору). Наибольший угол — против гипотенузы $= 90°$.'
},
{
n:2, color:'#dc2626',
title:'Минотавр Объёмов',
tag:'§ 9',
q:'Шар с радиусом $R=3$. Найдите объём $V$ (используй $\\pi\\approx 3{,}14$).',
ans:113.04, tol:0.5,
hint:'$V=\\tfrac{4}{3}\\pi R^3=\\tfrac{4}{3}\\cdot 3{,}14\\cdot 27 \\approx 113{,}04$.'
},
{
n:3, color:'#7c3aed',
title:'Гарпия Векторов',
tag:'§ 10',
q:'Найдите скалярное произведение $\\vec{a}=(2;\\, -1;\\, 3)$ и $\\vec{b}=(1;\\, 4;\\, 2)$.',
ans:4, tol:0.05,
hint:'$\\vec{a}\\cdot\\vec{b}=2\\cdot 1 + (-1)\\cdot 4 + 3\\cdot 2 = 2-4+6 = 4$.'
},
{
n:4, color:'#0891b2',
title:'Дракон Угла',
tag:'§ 10',
q:'Векторы $\\vec{a}=(1;\\, 0;\\, 1)$ и $\\vec{b}=(0;\\, 1;\\, 0)$. Найдите угол между ними (в градусах).',
ans:90, tol:0.05,
hint:'$\\vec{a}\\cdot\\vec{b}=1\\cdot 0+0\\cdot 1+1\\cdot 0 = 0$, значит $\\vec{a}\\perp\\vec{b}$ — угол $= 90°$.'
},
{
n:5, color:'#f59e0b',
title:'Мастер Геометрии',
tag:'синтез § 9 + § 10',
q:'Прямоугольный параллелепипед со сторонами $3$, $4$, $12$. Найдите длину его главной диагонали.',
ans:13, tol:0.05,
hint:'$d=\\sqrt{3^2+4^2+12^2}=\\sqrt{9+16+144}=\\sqrt{169}=13$.'
}
];
const cont = document.getElementById('r4-bosses-container');
const STATE_KEY = 'geometry11_ch4_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=>{
return '<div class="boss-card" id="boss4-'+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"/><circle cx="12" cy="14" r="3"/></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 class="boss-q" id="boss4-'+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="boss4-'+b.n+'-ans" class="tinp" style="width:140px;text-align:center" step="0.01" placeholder="число">'
+ '<button class="btn primary" id="boss4-'+b.n+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атаковать</button>'
+ '<button class="btn" id="boss4-'+b.n+'-hint">Подсказка</button>'
+ '</div>'
+ '<div class="feedback" id="boss4-'+b.n+'-fb"></div>'
+ '</div>';
}).join('');
renderMath(cont);
function refreshOverall(){
const won = BOSS_STATE.filter(s => s.defeated).length;
const txt = document.getElementById('r4-boss-overall');
const fill = document.getElementById('r4-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('r4-final-reward');
if(reward && reward.style.display === 'none'){
reward.style.display = 'block';
if(!STATE.achievements.has('r4_done')){
achievement('r4_done','Магистр повторения');
addXp(50, 'r4-bonus');
bumpProgress('final4', 30);
if(window.confetti){ try{ confetti(); }catch(e){} }
}
}
}
}
BOSSES.forEach((b, idx)=>{
const card = document.getElementById('boss4-'+b.n+'-card');
const goBtn = document.getElementById('boss4-'+b.n+'-go');
const hintBtn= document.getElementById('boss4-'+b.n+'-hint');
const ansInp = document.getElementById('boss4-'+b.n+'-ans');
if(BOSS_STATE[idx].defeated){
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
card.classList.add('glow');
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.innerHTML = '&#10003; Повержен';
ansInp.disabled = true;
}
goBtn.addEventListener('click', ()=>{
if(BOSS_STATE[idx].defeated) return;
const fb = document.getElementById('boss4-'+b.n+'-fb');
const raw = (ansInp.value||'').replace(',', '.').trim();
const val = parseFloat(raw);
if(!isFinite(val)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(val - b.ans) <= b.tol){
BOSS_STATE[idx].defeated = true; saveBosses();
feedback(fb, true, '&#10003; Босс '+b.n+' повержен! +10 XP. '+b.hint);
addXp(10, 'boss-r4-'+b.n);
bumpProgress('final4', 18);
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.innerHTML = '&#10003; Повержен';
ansInp.disabled = true;
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
card.classList.add('glow','pulse');
setTimeout(()=>card.classList.remove('pulse'), 900);
refreshOverall();
} else {
feedback(fb, false, '&#10007; Промах. Попробуй ещё. Подсказка доступна.');
}
});
hintBtn.addEventListener('click', ()=>{
const fb = document.getElementById('boss4-'+b.n+'-fb');
fb.className = 'feedback ok';
fb.innerHTML = '<b>Подсказка:</b> '+b.hint;
fb.style.display = 'block';
fb.style.background = 'var(--warn-bg,#fef3c7)';
fb.style.color = '#92400e';
fb.style.borderLeftColor = 'var(--warn,#f59e0b)';
try{ renderMath(fb); }catch(e){}
});
ansInp.addEventListener('keydown', e=>{ if(e.key === 'Enter') goBtn.click(); });
});
refreshOverall();
}
/* ===== Search ===== */
const SEARCH_INDEX = (function(){
const arr=[];