feat(geom9 ch3 wave2 + final): §12 «Герон» + Финал Главы 3
This commit is contained in:
@@ -1052,19 +1052,515 @@ function buildP11(){
|
||||
|
||||
wireReadBtn('p11');
|
||||
}
|
||||
function buildP12(){ _stubBuilder('p12', '§12', 'Формула Герона. Решение треугольников', 'p11', 'final3'); }
|
||||
function buildP12(){
|
||||
const box = document.getElementById('p12-body');
|
||||
let html = '';
|
||||
|
||||
html += makeCard('theory', 'Формула Герона', '12.1', `
|
||||
<p>Площадь треугольника по трём сторонам $a, b, c$ находится по <b>формуле Герона</b>:</p>
|
||||
$$S = \\sqrt{p(p-a)(p-b)(p-c)}$$
|
||||
<p>где $p = \\dfrac{a+b+c}{2}$ — <b>полупериметр</b> треугольника.</p>
|
||||
<p><b>В чём ценность?</b> Не нужны ни высоты, ни углы — достаточно знать только длины трёх сторон.</p>
|
||||
<p><b>Пример.</b> Треугольник со сторонами $5, 7, 8$.</p>
|
||||
$$p = \\dfrac{5 + 7 + 8}{2} = 10$$
|
||||
$$S = \\sqrt{10 \\cdot (10-5) \\cdot (10-7) \\cdot (10-8)} = \\sqrt{10 \\cdot 5 \\cdot 3 \\cdot 2} = \\sqrt{300} = 10\\sqrt{3} \\approx 17{,}32$$
|
||||
<details class="spoiler"><summary>Откуда берётся формула?</summary><div class="spoiler-body">
|
||||
Идея: $S = \\tfrac{1}{2}ab\\sin C$, $\\cos C = \\tfrac{a^2+b^2-c^2}{2ab}$ (теорема косинусов), $\\sin^2 C = 1 - \\cos^2 C$. После алгебраических преобразований через разность квадратов выражение упрощается до формулы Герона.
|
||||
</div></details>`);
|
||||
|
||||
html += makeCard('rule', 'Медиана треугольника', '12.2', `
|
||||
<p>Длина медианы $m_c$, проведённой к стороне $c$:</p>
|
||||
$$m_c = \\dfrac{1}{2}\\sqrt{2a^2 + 2b^2 - c^2}$$
|
||||
<p>Аналогично для остальных медиан:</p>
|
||||
$$m_a = \\dfrac{1}{2}\\sqrt{2b^2 + 2c^2 - a^2}, \\qquad m_b = \\dfrac{1}{2}\\sqrt{2a^2 + 2c^2 - b^2}$$
|
||||
<p><b>Удобно:</b> три медианы пересекаются в одной точке — <b>центроиде</b>, делящей каждую медиану в отношении $2:1$ от вершины.</p>`);
|
||||
|
||||
html += makeCard('example', 'Решение произвольного треугольника', '12.3', `
|
||||
<p><b>«Решить треугольник»</b> — значит найти все его стороны и углы. Какие случаи бывают:</p>
|
||||
<ul style="padding-left:22px;line-height:1.95">
|
||||
<li><b>3 стороны $(a,b,c)$:</b> углы — по теореме косинусов; площадь — по Герону.</li>
|
||||
<li><b>2 стороны и угол между ними $(a, b, C)$:</b> 3-я сторона — по теореме косинусов; остальные углы — по теореме синусов; площадь $S = \\tfrac{1}{2}ab\\sin C$.</li>
|
||||
<li><b>1 сторона и 2 угла $(a, A, B)$:</b> 3-й угол $C = 180° - A - B$; остальные стороны — по теореме синусов.</li>
|
||||
<li><b>2 стороны и угол не между ними:</b> может быть 0, 1 или 2 решения (<i>неоднозначный случай</i>).</li>
|
||||
</ul>
|
||||
<p><b>Пример.</b> $a = 5$, $b = 7$, угол между ними $C = 60°$.</p>
|
||||
$$c^2 = 25 + 49 - 2 \\cdot 5 \\cdot 7 \\cdot \\cos 60° = 74 - 35 = 39 \\Rightarrow c \\approx 6{,}24$$
|
||||
$$S = \\dfrac{1}{2} \\cdot 5 \\cdot 7 \\cdot \\sin 60° = \\dfrac{35\\sqrt{3}}{4} \\approx 15{,}16$$`);
|
||||
|
||||
/* IV1 — Slider 3-х сторон */
|
||||
html += `<div class="wg" id="p12-iv1">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Слайдер трёх сторон</div></div>
|
||||
<div class="wg-help">Задай стороны $a, b, c$ ползунками. Если они удовлетворяют неравенству треугольника, увидишь треугольник; площадь, углы и радиусы вычисляются автоматически.</div>
|
||||
<div class="sliders">
|
||||
<label>$a$<b id="p12-iv1-aval">5</b><input type="range" id="p12-iv1-a" min="1" max="15" step="1" value="5"></label>
|
||||
<label>$b$<b id="p12-iv1-bval">7</b><input type="range" id="p12-iv1-b" min="1" max="15" step="1" value="7"></label>
|
||||
<label>$c$<b id="p12-iv1-cval">8</b><input type="range" id="p12-iv1-c" min="1" max="15" step="1" value="8"></label>
|
||||
</div>
|
||||
<div style="background:var(--card);border-radius:10px;padding:10px;overflow-x:auto">
|
||||
<svg id="p12-iv1-svg" viewBox="0 0 420 320" style="width:100%;min-width:340px;height:auto;display:block"></svg>
|
||||
</div>
|
||||
<div id="p12-iv1-out" style="margin-top:10px;padding:10px 14px;background:var(--pri-soft);border-radius:9px;font-size:.95rem;line-height:1.9"></div>
|
||||
</div>`;
|
||||
|
||||
/* IV2 — Калькулятор Герона */
|
||||
html += `<div class="wg" id="p12-iv2">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор Герона</div></div>
|
||||
<div class="wg-help">Введи длины трёх сторон — программа найдёт площадь по формуле Герона.</div>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px;margin-bottom:10px">
|
||||
<label style="display:block;font-size:.85rem;color:var(--muted)">сторона $a$<input type="number" id="p12-iv2-a" class="tinp" style="width:100%;margin-top:4px" value="5" step="0.1" min="0.1"></label>
|
||||
<label style="display:block;font-size:.85rem;color:var(--muted)">сторона $b$<input type="number" id="p12-iv2-b" class="tinp" style="width:100%;margin-top:4px" value="7" step="0.1" min="0.1"></label>
|
||||
<label style="display:block;font-size:.85rem;color:var(--muted)">сторона $c$<input type="number" id="p12-iv2-c" class="tinp" style="width:100%;margin-top:4px" value="8" step="0.1" min="0.1"></label>
|
||||
</div>
|
||||
<div style="text-align:center;margin-bottom:10px"><button class="btn primary" id="p12-iv2-go">Найти площадь</button></div>
|
||||
<div id="p12-iv2-out" style="padding:12px 14px;background:var(--card);border-radius:9px;font-size:.95rem;min-height:50px;line-height:1.9"></div>
|
||||
<div class="feedback" id="p12-iv2-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* IV3 — Какой случай / какой метод? */
|
||||
html += `<div class="wg" id="p12-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="p12-iv3-i">1</b> / 6</span><span>Очки: <b id="p12-iv3-s">0</b> / 6</span></div>
|
||||
<div id="p12-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(180px,1fr));gap:8px">
|
||||
<button class="btn primary" data-ans="heron" id="p12-iv3-heron">Формула Герона<br><span style="font-size:.78rem;opacity:.85">$\\sqrt{p(p-a)(p-b)(p-c)}$</span></button>
|
||||
<button class="btn primary" data-ans="cos" id="p12-iv3-cos">Теорема косинусов<br><span style="font-size:.78rem;opacity:.85">$a^2 = b^2+c^2-2bc\\cos A$</span></button>
|
||||
<button class="btn primary" data-ans="sin" id="p12-iv3-sin">Теорема синусов<br><span style="font-size:.78rem;opacity:.85">$\\dfrac{a}{\\sin A} = 2R$</span></button>
|
||||
<button class="btn primary" data-ans="sab" id="p12-iv3-sab">$S = \\tfrac{1}{2}ab\\sin C$<br><span style="font-size:.78rem;opacity:.85">площадь через 2 стороны и угол</span></button>
|
||||
</div>
|
||||
<div class="feedback" id="p12-iv3-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* IV4 — Тренажёр */
|
||||
html += `<div class="wg" id="p12-iv4">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр Герона</div></div>
|
||||
<div class="wg-help">Реши задачу и введи площадь (округляй до 2 знаков после запятой).</div>
|
||||
<div class="score-display"><span>Задача <b id="p12-iv4-i">1</b> / 6</span><span>Очки: <b id="p12-iv4-s">0</b> / 6</span></div>
|
||||
<div id="p12-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">$S =$</span>
|
||||
<input type="number" id="p12-iv4-ans" class="tinp" style="width:120px;text-align:center" step="0.01">
|
||||
<button class="btn primary" id="p12-iv4-go">Проверить</button>
|
||||
<button class="btn" id="p12-iv4-start">Заново</button>
|
||||
</div>
|
||||
<div class="feedback" id="p12-iv4-fb"></div>
|
||||
</div>`;
|
||||
|
||||
html += readButton('p12');
|
||||
html += secNav('p11', 'final3');
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* IV1 — Slider 3-х сторон */
|
||||
(function(){
|
||||
const sA=document.getElementById('p12-iv1-a');
|
||||
const sB=document.getElementById('p12-iv1-b');
|
||||
const sC=document.getElementById('p12-iv1-c');
|
||||
const lA=document.getElementById('p12-iv1-aval');
|
||||
const lB=document.getElementById('p12-iv1-bval');
|
||||
const lC=document.getElementById('p12-iv1-cval');
|
||||
const svg=document.getElementById('p12-iv1-svg');
|
||||
const out=document.getElementById('p12-iv1-out');
|
||||
const seen=new Set();
|
||||
function draw(){
|
||||
const a=+sA.value, b=+sB.value, c=+sC.value;
|
||||
lA.textContent=a; lB.textContent=b; lC.textContent=c;
|
||||
// Неравенство треугольника
|
||||
if(a+b<=c || a+c<=b || b+c<=a){
|
||||
svg.innerHTML='<rect x="0" y="0" width="420" height="320" fill="none"/>'
|
||||
+'<text x="210" y="160" text-anchor="middle" font-family="Inter,sans-serif" font-size="18" font-weight="700" fill="#dc2626">Невозможный треугольник</text>'
|
||||
+'<text x="210" y="186" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" fill="#7f1d1d">Сумма двух сторон должна быть больше третьей</text>';
|
||||
out.innerHTML='<b style="color:#dc2626">Неравенство треугольника не выполнено:</b> $a + b > c$, $a + c > b$, $b + c > a$.';
|
||||
renderMath(out);
|
||||
return;
|
||||
}
|
||||
// Построение треугольника: A в начале, B = (c, 0); C находим по теореме косинусов через cos A
|
||||
const cosA=(b*b+c*c-a*a)/(2*b*c);
|
||||
const sinA=Math.sqrt(Math.max(0,1-cosA*cosA));
|
||||
// в локальных координатах: A=(0,0), B=(c,0), C=(b*cosA, b*sinA)
|
||||
const Ax=0, Ay=0, Bx=c, By=0, Cx=b*cosA, Cy=b*sinA;
|
||||
// масштабируем в SVG 420×320 с полем 40
|
||||
const minX=Math.min(Ax,Bx,Cx), maxX=Math.max(Ax,Bx,Cx);
|
||||
const minY=Math.min(Ay,Cy), maxY=Math.max(By,Cy);
|
||||
const W=420, H=320, pad=46;
|
||||
const sx=(W-2*pad)/(maxX-minX||1), sy=(H-2*pad)/(maxY-minY||1);
|
||||
const s_=Math.min(sx,sy);
|
||||
function T(X,Y){ return {x: pad+(X-minX)*s_, y: H-pad-(Y-minY)*s_}; }
|
||||
const Av=T(Ax,Ay), Bv=T(Bx,By), Cv=T(Cx,Cy);
|
||||
// углы
|
||||
const cosB_=(a*a+c*c-b*b)/(2*a*c); const cosC_=(a*a+b*b-c*c)/(2*a*b);
|
||||
const Aang=Math.acos(Math.max(-1,Math.min(1,cosA)))*180/Math.PI;
|
||||
const Bang=Math.acos(Math.max(-1,Math.min(1,cosB_)))*180/Math.PI;
|
||||
const Cang=Math.acos(Math.max(-1,Math.min(1,cosC_)))*180/Math.PI;
|
||||
const p=(a+b+c)/2;
|
||||
const S=Math.sqrt(Math.max(0,p*(p-a)*(p-b)*(p-c)));
|
||||
const R=(a*b*c)/(4*S||1e-9);
|
||||
const r=S/(p||1e-9);
|
||||
const mA=0.5*Math.sqrt(2*b*b+2*c*c-a*a);
|
||||
const mB=0.5*Math.sqrt(2*a*a+2*c*c-b*b);
|
||||
const mC=0.5*Math.sqrt(2*a*a+2*b*b-c*c);
|
||||
let s='';
|
||||
s += '<rect x="0" y="0" width="420" height="320" fill="none"/>';
|
||||
s += '<polygon points="'+Av.x.toFixed(2)+','+Av.y.toFixed(2)+' '+Bv.x.toFixed(2)+','+Bv.y.toFixed(2)+' '+Cv.x.toFixed(2)+','+Cv.y.toFixed(2)+'" fill="rgba(124,58,237,.12)" stroke="#7c3aed" stroke-width="2.2" stroke-linejoin="round"/>';
|
||||
// подписи сторон
|
||||
const midAB={x:(Av.x+Bv.x)/2, y:(Av.y+Bv.y)/2};
|
||||
const midAC={x:(Av.x+Cv.x)/2, y:(Av.y+Cv.y)/2};
|
||||
const midBC={x:(Bv.x+Cv.x)/2, y:(Bv.y+Cv.y)/2};
|
||||
s += '<text x="'+midAB.x.toFixed(2)+'" y="'+(midAB.y+18).toFixed(2)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#5b21b6">c = '+c+'</text>';
|
||||
s += '<text x="'+(midAC.x-16).toFixed(2)+'" y="'+midAC.y.toFixed(2)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#5b21b6">b = '+b+'</text>';
|
||||
s += '<text x="'+(midBC.x+16).toFixed(2)+'" y="'+midBC.y.toFixed(2)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="13" font-weight="700" fill="#dc2626">a = '+a+'</text>';
|
||||
// вершины с подписями
|
||||
[['A',Av,-14,18],['B',Bv,14,18],['C',Cv,0,-10]].forEach(([n,P,dx,dy])=>{
|
||||
s += '<circle cx="'+P.x.toFixed(2)+'" cy="'+P.y.toFixed(2)+'" r="4" fill="#0f172a"/>';
|
||||
s += '<text x="'+(P.x+dx).toFixed(2)+'" y="'+(P.y+dy).toFixed(2)+'" text-anchor="middle" font-family="Inter,sans-serif" font-size="14" font-weight="800" fill="#0f172a">'+n+'</text>';
|
||||
});
|
||||
svg.innerHTML=s;
|
||||
out.innerHTML = '<b>Полупериметр:</b> $p = '+p.toFixed(2)+'$ · <b>Площадь (Герон):</b> $S \\approx '+S.toFixed(2)+'$<br>'
|
||||
+ '<b>Углы:</b> $A \\approx '+Aang.toFixed(1)+'°$, $B \\approx '+Bang.toFixed(1)+'°$, $C \\approx '+Cang.toFixed(1)+'°$<br>'
|
||||
+ '<b>Радиусы:</b> $R \\approx '+R.toFixed(2)+'$, $r \\approx '+r.toFixed(2)+'$<br>'
|
||||
+ '<b>Медианы:</b> $m_a \\approx '+mA.toFixed(2)+'$, $m_b \\approx '+mB.toFixed(2)+'$, $m_c \\approx '+mC.toFixed(2)+'$';
|
||||
renderMath(out);
|
||||
seen.add(a+'|'+b+'|'+c);
|
||||
if(seen.size>=4 && !seen.has('done')){ addXp(10,'p12-iv1'); bumpProgress('p12',15); seen.add('done'); }
|
||||
}
|
||||
[sA,sB,sC].forEach(s=>s.addEventListener('input', draw));
|
||||
draw();
|
||||
})();
|
||||
|
||||
/* IV2 — Калькулятор Герона */
|
||||
(function(){
|
||||
const aI=document.getElementById('p12-iv2-a');
|
||||
const bI=document.getElementById('p12-iv2-b');
|
||||
const cI=document.getElementById('p12-iv2-c');
|
||||
const go=document.getElementById('p12-iv2-go');
|
||||
const out=document.getElementById('p12-iv2-out');
|
||||
const fb=document.getElementById('p12-iv2-fb');
|
||||
let solved=0;
|
||||
go.addEventListener('click', ()=>{
|
||||
const a=parseFloat(aI.value), b=parseFloat(bI.value), c=parseFloat(cI.value);
|
||||
if(!isFinite(a)||!isFinite(b)||!isFinite(c)){ feedback(fb,false,'✗ Введи все значения.'); return; }
|
||||
if(a<=0||b<=0||c<=0){ feedback(fb,false,'✗ Стороны должны быть положительными.'); return; }
|
||||
if(a+b<=c||a+c<=b||b+c<=a){ feedback(fb,false,'✗ Неравенство треугольника не выполняется.'); return; }
|
||||
const p=(a+b+c)/2;
|
||||
const inner=p*(p-a)*(p-b)*(p-c);
|
||||
const S=Math.sqrt(Math.max(0,inner));
|
||||
out.innerHTML = '$p = \\dfrac{a+b+c}{2} = \\dfrac{'+a+'+'+b+'+'+c+'}{2} = '+p.toFixed(2)+'$<br>'
|
||||
+ '$S = \\sqrt{p(p-a)(p-b)(p-c)} = \\sqrt{'+p.toFixed(2)+' \\cdot '+(p-a).toFixed(2)+' \\cdot '+(p-b).toFixed(2)+' \\cdot '+(p-c).toFixed(2)+'}$<br>'
|
||||
+ '$S = \\sqrt{'+inner.toFixed(3)+'} \\approx '+S.toFixed(3)+'$';
|
||||
renderMath(out);
|
||||
feedback(fb,true,'✓ Площадь найдена.');
|
||||
solved++;
|
||||
if(solved===1){ addXp(10,'p12-iv2'); bumpProgress('p12',10); }
|
||||
});
|
||||
})();
|
||||
|
||||
/* IV3 — Какой метод? */
|
||||
(function(){
|
||||
const Q=[
|
||||
{t:'Известны три стороны треугольника. Найти его площадь.', a:'heron'},
|
||||
{t:'Известны две стороны и угол между ними. Найти площадь.', a:'sab'},
|
||||
{t:'Известны три стороны. Найти один из углов.', a:'cos'},
|
||||
{t:'Известны сторона и противолежащий ей угол. Найти радиус описанной окружности.', a:'sin'},
|
||||
{t:'Известны два угла и одна сторона. Найти другую сторону.', a:'sin'},
|
||||
{t:'Известны две стороны и угол между ними. Найти третью сторону.', a:'cos'}
|
||||
];
|
||||
const explain={
|
||||
heron:'$S = \\sqrt{p(p-a)(p-b)(p-c)}$ — площадь без углов и высот.',
|
||||
cos:'$a^2 = b^2+c^2-2bc\\cos A$ — связывает 3 стороны и угол.',
|
||||
sin:'$\\dfrac{a}{\\sin A} = 2R$ — пара (сторона; противолежащий угол).',
|
||||
sab:'$S = \\tfrac{1}{2}ab\\sin C$ — площадь через две стороны и угол между ними.'
|
||||
};
|
||||
const labels={heron:'формула Герона', cos:'теорема косинусов', sin:'теорема синусов', sab:'$S = \\tfrac{1}{2}ab\\sin C$'};
|
||||
const qBox=document.getElementById('p12-iv3-q');
|
||||
const iEl=document.getElementById('p12-iv3-i');
|
||||
const sEl=document.getElementById('p12-iv3-s');
|
||||
const fb=document.getElementById('p12-iv3-fb');
|
||||
const btns=document.querySelectorAll('#p12-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,'p12-iv3'); bumpProgress('p12',25); if(score===Q.length) achievement('p12_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?('✓ Верно! '+explain[Q[i].a]):('✗ Правильно: '+labels[Q[i].a]+'. '+explain[Q[i].a]));
|
||||
i++;
|
||||
setTimeout(show, 1100);
|
||||
}));
|
||||
show();
|
||||
})();
|
||||
|
||||
/* IV4 — Тренажёр Герона */
|
||||
(function(){
|
||||
const Q=[
|
||||
{t:'Треугольник со сторонами $3, 4, 5$. Найди площадь $S$.', a:6, tol:0.02},
|
||||
{t:'Треугольник со сторонами $5, 5, 6$. Найди площадь $S$.', a:12, tol:0.02},
|
||||
{t:'Треугольник со сторонами $7, 8, 9$. Найди площадь $S$.', a:26.83, tol:0.05},
|
||||
{t:'Треугольник со сторонами $6, 8, 10$. Найди площадь $S$.', a:24, tol:0.02},
|
||||
{t:'Треугольник со сторонами $13, 14, 15$. Найди площадь $S$.', a:84, tol:0.05},
|
||||
{t:'Стороны $a = 4$, $b = 6$, угол между ними $C = 90°$. Найди $S$.', a:12, tol:0.02}
|
||||
];
|
||||
const qBox=document.getElementById('p12-iv4-q');
|
||||
const ans=document.getElementById('p12-iv4-ans');
|
||||
const go=document.getElementById('p12-iv4-go');
|
||||
const reset=document.getElementById('p12-iv4-start');
|
||||
const iEl=document.getElementById('p12-iv4-i');
|
||||
const sEl=document.getElementById('p12-iv4-s');
|
||||
const fb=document.getElementById('p12-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,'p12-iv4'); bumpProgress('p12',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,'✗ Введи число.'); return; }
|
||||
const tol=Q[i].tol||0.05;
|
||||
const ok=Math.abs(v-Q[i].a)<=tol;
|
||||
if(ok) score++;
|
||||
feedback(fb, ok, ok?'✓ Верно!':'✗ Правильный ответ: $'+Q[i].a+'$');
|
||||
i++;
|
||||
setTimeout(show, 1000);
|
||||
});
|
||||
reset.addEventListener('click', ()=>{ i=0; score=0; done=false; show(); });
|
||||
show();
|
||||
})();
|
||||
|
||||
wireReadBtn('p12');
|
||||
}
|
||||
|
||||
function buildFinal3(){
|
||||
const body = document.getElementById('final3-body');
|
||||
const box = document.getElementById('final3-body');
|
||||
let html = '';
|
||||
html += makeCard('theory', 'Финал главы 3', '★', `
|
||||
<p>Итоговый раздел главы <b>«Теоремы синусов и косинусов»</b> будет добавлен в следующих обновлениях.</p>
|
||||
<p style="color:var(--muted);font-size:.9rem">Раздел Phase 7.</p>`);
|
||||
html += readButton('final3');
|
||||
|
||||
/* Часть А — Шпаргалка главы (3 mini-карточки) */
|
||||
html += `<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-icon theory">${ICONS.theory}</div>
|
||||
<div class="card-title">Шпаргалка главы 3</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="6 7 17 7 12 18"/></svg>
|
||||
<span style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 10 · Теорема синусов</span>
|
||||
</div>
|
||||
<div style="font-size:.92rem">$\\dfrac{a}{\\sin A} = \\dfrac{b}{\\sin B} = \\dfrac{c}{\\sin C} = 2R$. Пара «сторона + противолежащий угол» $\\Rightarrow$ радиус $R$ и любая сторона.</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="4 20 20 20 12 4"/><line x1="4" y1="20" x2="20" y2="20"/></svg>
|
||||
<span style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 11 · Теорема косинусов</span>
|
||||
</div>
|
||||
<div style="font-size:.92rem">$a^2 = b^2 + c^2 - 2bc\\cos A$ — обобщение Пифагора. По 3 сторонам — любой угол; по 2 сторонам и углу — 3-я сторона.</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"><path d="M4 17 L12 5 L20 17 Z"/><line x1="8" y1="17" x2="16" y2="17"/></svg>
|
||||
<span style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 12 · Формула Герона</span>
|
||||
</div>
|
||||
<div style="font-size:.92rem">$S = \\sqrt{p(p-a)(p-b)(p-c)}$, где $p = \\dfrac{a+b+c}{2}$ — полупериметр. Площадь по трём сторонам.</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">Боссы главы 3</div>
|
||||
<div class="card-num">5</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>5 интегрированных задач — каждая комбинирует темы §§10–12. За каждого побеждённого босса — <b>+10 XP</b> и +18% к прогрессу. Победишь всех — <b>+50 XP бонус</b> и ачивка «Магистр треугольников»!</p>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
html += '<div id="ch3G-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="ch3G-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="ch3G-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="ch3G-boss-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,var(--pri),var(--acc));transition:width .35s"></div>
|
||||
</div>
|
||||
<div id="ch3G-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">Глава 3 пройдена! Все 5 боссов повержены. +50 XP бонус.</div>
|
||||
<a class="btn primary" href="/textbook/geometry-9-ch4" style="text-decoration:none">Дальше: Глава 4 <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></a>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
html += secNav('p12', null);
|
||||
body.innerHTML = html;
|
||||
wireReadBtn('final3');
|
||||
if(window.renderMathInElement) renderMath(body);
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* Боссы */
|
||||
const BOSSES = [
|
||||
{
|
||||
n:1, color:'#10b981',
|
||||
title:'Циклоп Синусов',
|
||||
tag:'§ 10',
|
||||
q:'В треугольнике сторона $a = 12$ и противолежащий угол $A = 30°$. Найди радиус описанной окружности $R$.',
|
||||
ans:12, decimal:false,
|
||||
hint:'$R = \\dfrac{a}{2\\sin A} = \\dfrac{12}{2 \\cdot 0{,}5} = \\dfrac{12}{1} = 12$.'
|
||||
},
|
||||
{
|
||||
n:2, color:'#0891b2',
|
||||
title:'Минотавр Косинусов',
|
||||
tag:'§ 11',
|
||||
q:'В треугольнике стороны $b = 5$ и $c = 8$, угол между ними $A = 60°$. Найди сторону $a$.',
|
||||
ans:7, decimal:false,
|
||||
hint:'$a^2 = 25 + 64 - 2 \\cdot 5 \\cdot 8 \\cdot \\cos 60° = 89 - 40 = 49 \\Rightarrow a = 7$.'
|
||||
},
|
||||
{
|
||||
n:3, color:'#7c3aed',
|
||||
title:'Гарпия Герона',
|
||||
tag:'§ 12',
|
||||
q:'Треугольник со сторонами $5, 12, 13$. Найди его площадь $S$.',
|
||||
ans:30, decimal:false,
|
||||
hint:'$p = 15$. $S = \\sqrt{15 \\cdot 10 \\cdot 3 \\cdot 2} = \\sqrt{900} = 30$. Заметь: это прямоугольный треугольник, и $S = \\tfrac{1}{2}\\cdot 5 \\cdot 12 = 30$.'
|
||||
},
|
||||
{
|
||||
n:4, color:'#dc2626',
|
||||
title:'Дракон Решения',
|
||||
tag:'§§ 11 + 12',
|
||||
q:'В треугольнике стороны $7, 8, 9$. Найди наибольший угол (в градусах, округли до целого).',
|
||||
ans:73, decimal:false,
|
||||
hint:'Наибольший угол лежит против стороны $9$. $\\cos C = \\dfrac{49+64-81}{2\\cdot 7\\cdot 8} = \\dfrac{32}{112} \\approx 0{,}2857$. $C = \\arccos(0{,}2857) \\approx 73°$.'
|
||||
},
|
||||
{
|
||||
n:5, color:'#f59e0b',
|
||||
title:'Мастер Треугольников',
|
||||
tag:'§§ 10–12 — синтез',
|
||||
q:'В треугольнике стороны $a = 10$, $b = 6$, угол между ними $C = 60°$. Найди площадь $S$ (округли до 2 знаков).',
|
||||
ans:25.98, decimal:true,
|
||||
hint:'$S = \\tfrac{1}{2}ab\\sin C = \\tfrac{1}{2}\\cdot 10 \\cdot 6 \\cdot \\sin 60° = 30 \\cdot \\dfrac{\\sqrt{3}}{2} = 15\\sqrt{3} \\approx 25{,}98$.'
|
||||
}
|
||||
];
|
||||
|
||||
const cont = document.getElementById('ch3G-bosses-container');
|
||||
const STATE_KEY = 'geometry9_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)=>{
|
||||
const stepAttr = b.decimal ? 'step="0.01"' : 'step="1"';
|
||||
const ph = b.decimal ? 'число (можно с запятой)' : 'целое число';
|
||||
return '<div class="boss-card" id="bossG3-'+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"><polygon points="12 4 20 18 4 18"/><circle cx="12" cy="13" r="2.4"/></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="bossG3-'+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="bossG3-'+b.n+'-ans" class="tinp" style="width:150px;text-align:center" placeholder="'+ph+'">'
|
||||
+'<button class="btn primary" id="bossG3-'+b.n+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атаковать</button>'
|
||||
+'<button class="btn" id="bossG3-'+b.n+'-hint">Подсказка</button>'
|
||||
+'</div>'
|
||||
+'<div class="feedback" id="bossG3-'+b.n+'-fb"></div>'
|
||||
+'</div>';
|
||||
}).join('');
|
||||
renderMath(cont);
|
||||
|
||||
function markDefeatedUI(b){
|
||||
const card = document.getElementById('bossG3-'+b.n+'-card');
|
||||
const goBtn = document.getElementById('bossG3-'+b.n+'-go');
|
||||
const ansInp = document.getElementById('bossG3-'+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('ch3G-boss-overall');
|
||||
const fill = document.getElementById('ch3G-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('ch3G-final-reward');
|
||||
if(reward && reward.style.display === 'none'){
|
||||
reward.style.display = 'block';
|
||||
if(!STATE.achievements.has('ch3_done')){
|
||||
achievement('ch3_done','Магистр треугольников');
|
||||
addXp(50, 'ch3-bonus');
|
||||
bumpProgress('final3', 10);
|
||||
if(window.confetti){ try{ window.confetti(); }catch(e){} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOSSES.forEach((b, idx)=>{
|
||||
const goBtn = document.getElementById('bossG3-'+b.n+'-go');
|
||||
const hintBtn = document.getElementById('bossG3-'+b.n+'-hint');
|
||||
const ansInp = document.getElementById('bossG3-'+b.n+'-ans');
|
||||
if(BOSS_STATE[idx].defeated) markDefeatedUI(b);
|
||||
goBtn.addEventListener('click', ()=>{
|
||||
if(BOSS_STATE[idx].defeated) return;
|
||||
const fb = document.getElementById('bossG3-'+b.n+'-fb');
|
||||
const raw = (ansInp.value||'').replace(',', '.').trim();
|
||||
const val = parseFloat(raw);
|
||||
if(isNaN(val) || raw === ''){ feedback(fb, false, '✗ Введи число.'); return; }
|
||||
const ok = Math.abs(val - b.ans) < 0.05;
|
||||
if(ok){
|
||||
BOSS_STATE[idx].defeated = true; saveBosses();
|
||||
feedback(fb, true, '✓ Босс '+b.n+' повержен! +10 XP. '+b.hint);
|
||||
addXp(10, 'boss-ch3-'+b.n);
|
||||
bumpProgress('final3', 18);
|
||||
markDefeatedUI(b);
|
||||
refreshOverall();
|
||||
} else {
|
||||
feedback(fb, false, '✗ Промах. Попробуй ещё. Подсказка доступна.');
|
||||
}
|
||||
});
|
||||
hintBtn.addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('bossG3-'+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