From bf794f76a6949f9a60128eb705d317733f11eb42 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 29 May 2026 14:49:25 +0300 Subject: [PATCH] =?UTF-8?q?feat(geom11=20ch3=20final):=20=D0=A4=D0=B8?= =?UTF-8?q?=D0=BD=D0=B0=D0=BB=20=D0=A0=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=D0=B0?= =?UTF-8?q?=203=20(5=20=D0=B1=D0=BE=D1=81=D1=81=D0=BE=D0=B2=20+=20=D0=B0?= =?UTF-8?q?=D1=87=D0=B8=D0=B2=D0=BA=D0=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/textbooks/geometry_11_ch3.html | 217 +++++++++++++++++++++++- 1 file changed, 216 insertions(+), 1 deletion(-) diff --git a/frontend/textbooks/geometry_11_ch3.html b/frontend/textbooks/geometry_11_ch3.html index 47818e7..0292315 100644 --- a/frontend/textbooks/geometry_11_ch3.html +++ b/frontend/textbooks/geometry_11_ch3.html @@ -396,7 +396,7 @@ function buildParaSelector(){ } const BUILT=new Set(); -const BUILDERS = { p5:buildP5, p6:buildP6, p7:buildP7, final3:()=>buildStub('final3') }; +const BUILDERS = { p5:buildP5, p6:buildP6, p7:buildP7, final3:buildFinal3 }; 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); @@ -2083,6 +2083,221 @@ function buildP7(){ wireReadBtn('p7'); } +/* ===== ФИНАЛ РАЗДЕЛА 3 — Сфера, шар, правильные многогранники ===== */ + +function buildFinal3(){ + const box = document.getElementById('final3-body'); + if(!box) return; + let html = ''; + + /* Часть А — Шпаргалка раздела 3 (3 mini-карточки по числу § в разделе) */ + html += '
' + + '
' + + '
' + ICONS.theory + '
' + + '
Шпаргалка раздела 3
' + + '
Итог
' + + '
' + + '
' + + '

Ключевые формулы трёх параграфов раздела в одном месте — пробеги глазами перед битвой с боссами.

' + + '
' + + '
' + + '
' + + '' + + '
§ 5 · Сфера
' + + '
' + + '
Уравнение: $(x-a)^2+(y-b)^2+(z-c)^2=R^2$. Касательная плоскость $\\perp$ радиусу в точке касания. Сечение плоскостью — окружность радиуса $r=\\sqrt{R^2-d^2}$, где $d$ — расстояние от центра до плоскости.
' + + '
' + + '
' + + '
' + + '' + + '
§ 6 · Шар
' + + '
' + + '
$S=4\\pi R^2$, $V=\\tfrac{4}{3}\\pi R^3$. Сегмент: $V_{сег}=\\tfrac{\\pi h^2(3R-h)}{3}$, $S_{сферич}=2\\pi R h$. Куб со стороной $a$: вписанный шар $r=a/2$, описанный $R=\\tfrac{a\\sqrt{3}}{2}$.
' + + '
' + + '
' + + '
' + + '' + + '
§ 7 · Многогранники
' + + '
' + + '
$5$ платоновых тел: тетраэдр, куб, октаэдр, додекаэдр, икосаэдр. Формула Эйлера: $V-E+F=2$. Двойственные пары: тетраэдр$\\leftrightarrow$тетраэдр, куб$\\leftrightarrow$октаэдр, додекаэдр$\\leftrightarrow$икосаэдр.
' + + '
' + + '
' + + '
' + + '
'; + + /* Часть Б — анонс 5 боссов */ + html += '
' + + '
' + + '
' + ICONS.rule + '
' + + '
Боссы раздела 3
' + + '
5
' + + '
' + + '
' + + '

5 интегрированных задач — каждая опирается на темы § 5, § 6 или § 7. За каждого побеждённого босса: +10 XP, +18% к прогрессу. Победишь всех — ачивка «Мастер сферической геометрии» и +50 XP бонус.

' + + '

Для расчётов с $\\pi$ используй $\\pi\\approx 3{,}14$. Допуск ответа — $\\pm 0{,}05$ (для больших чисел — $\\pm 0{,}5$).

' + + '
' + + '
'; + + html += '
'; + + /* Прогресс-итог + ачивка */ + html += '
' + + '
Прогресс по боссам
' + + '
0 / 5 боссов побеждено
' + + '
' + + '
' + + '
' + + '' + + '
'; + + html += secNav('p7', null); + + box.innerHTML = html; + renderMath(box); + + /* === Боссы === */ + const BOSSES = [ + { + n:1, color:'#2563eb', + title:'Циклоп Сферы', + tag:'§ 5', + q:'Сфера задана уравнением $x^2+y^2+z^2=49$. Найдите радиус $R$.', + ans:7, tol:0.05, + hint:'$R=\\sqrt{49}=7$.' + }, + { + n:2, color:'#dc2626', + title:'Минотавр Шара', + tag:'§ 6', + q:'Шар с $R=6$. Найдите площадь поверхности (используй $\\pi\\approx 3{,}14$).', + ans:452.16, tol:0.5, + hint:'$S=4\\pi R^2=4\\cdot 3{,}14\\cdot 36=452{,}16$.' + }, + { + n:3, color:'#0891b2', + title:'Гарпия Куба и Шара', + tag:'§ 6 + куб', + q:'Шар вписан в куб со стороной $a=8$. Найдите объём шара (используй $\\pi\\approx 3{,}14$).', + ans:267.95, tol:0.5, + hint:'Радиус вписанного шара: $r=a/2=4$. $V=\\tfrac{4}{3}\\pi r^3=\\tfrac{4}{3}\\cdot 3{,}14\\cdot 64\\approx 267{,}95$.' + }, + { + n:4, color:'#7c3aed', + title:'Дракон Многогранников', + tag:'§ 7', + q:'Сколько вершин у икосаэдра?', + ans:12, tol:0.05, + hint:'У икосаэдра $V=12$, $E=30$, $F=20$. Проверка Эйлера: $V-E+F=12-30+20=2$.' + }, + { + n:5, color:'#f59e0b', + title:'Мастер Сферической Геометрии', + tag:'синтез § 6 + § 7', + q:'Шар описан около куба со стороной $a=4$. Найдите радиус шара (округлите до сотых).', + ans:3.46, tol:0.05, + hint:'Диагональ куба $d=a\\sqrt{3}$. Радиус описанного шара $R=\\tfrac{a\\sqrt{3}}{2}=\\tfrac{4\\sqrt{3}}{2}=2\\sqrt{3}\\approx 3{,}46$.' + } + ]; + + const cont = document.getElementById('r3-bosses-container'); + const STATE_KEY = 'geometry11_ch3_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 '
' + + '
' + + '' + + '
Босс '+b.n+': '+b.title+'
' + + '
'+b.tag+'
' + + '
' + + '
'+b.q+'
' + + '
' + + 'ответ =' + + '' + + '' + + '' + + '
' + + '' + + '
'; + }).join(''); + renderMath(cont); + + function refreshOverall(){ + const won = BOSS_STATE.filter(s => s.defeated).length; + const txt = document.getElementById('r3-boss-overall'); + const fill = document.getElementById('r3-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('r3-final-reward'); + if(reward && reward.style.display === 'none'){ + reward.style.display = 'block'; + if(!STATE.achievements.has('r3_done')){ + achievement('r3_done','Мастер сферической геометрии'); + addXp(50, 'r3-bonus'); + bumpProgress('final3', 30); + if(window.confetti){ try{ confetti(); }catch(e){} } + } + } + } + } + + BOSSES.forEach((b, idx)=>{ + const card = document.getElementById('boss3-'+b.n+'-card'); + const goBtn = document.getElementById('boss3-'+b.n+'-go'); + const hintBtn= document.getElementById('boss3-'+b.n+'-hint'); + const ansInp = document.getElementById('boss3-'+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 = '✓ Повержен'; + ansInp.disabled = true; + } + goBtn.addEventListener('click', ()=>{ + if(BOSS_STATE[idx].defeated) return; + const fb = document.getElementById('boss3-'+b.n+'-fb'); + const raw = (ansInp.value||'').replace(',', '.').trim(); + const val = parseFloat(raw); + if(!isFinite(val)){ feedback(fb, false, '✗ Введи число.'); return; } + if(Math.abs(val - b.ans) <= b.tol){ + BOSS_STATE[idx].defeated = true; saveBosses(); + feedback(fb, true, '✓ Босс '+b.n+' повержен! +10 XP. '+b.hint); + addXp(10, 'boss-r3-'+b.n); + bumpProgress('final3', 18); + goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.innerHTML = '✓ Повержен'; + 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, '✗ Промах. Попробуй ещё. Подсказка доступна.'); + } + }); + hintBtn.addEventListener('click', ()=>{ + const fb = document.getElementById('boss3-'+b.n+'-fb'); + fb.className = 'feedback ok'; + fb.innerHTML = 'Подсказка: '+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(); +} + /* ===== STUB BUILDER — единый для всех параграфов раздела (Phase 0) ===== */ function buildStub(id){