feat(phys10 ch2 wave3 + final): §15 «Цикл Карно» + Финал Главы 2 (5 боссов)
This commit is contained in:
@@ -2156,35 +2156,653 @@ function build_p14(){
|
||||
function build_p15(){
|
||||
const box = document.getElementById('p15-body');
|
||||
let html = '';
|
||||
html += makeCard('theory', "Тепловые двигатели. Цикл Карно", "§15", `
|
||||
<p><b>Тепловые двигатели. Цикл Карно</b> — этот параграф в разработке (Phase 1+).</p>
|
||||
<p>Здесь появятся: теория, формулы, разобранные примеры и 3–4 интерактива в стиле «алгебры 11» — таблицы, симуляции, ползунки, drag-and-drop и автопроверяемые тренажёры.</p>
|
||||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem">
|
||||
<b>Phase 0:</b> создан скелет учебника. <b>Phase 2+:</b> наполнение этого § содержанием по учебнику «Физика 10» (Беларусь, 2019).
|
||||
</p>
|
||||
|
||||
/* THEORY 1 — Устройство и принцип теплового двигателя */
|
||||
html += makeCard('theory', "Устройство и принцип теплового двигателя", "§15", `
|
||||
<p><b>Тепловой двигатель</b> — устройство, преобразующее часть теплоты в механическую работу.</p>
|
||||
<p>Три обязательных элемента:</p>
|
||||
<ul style="margin:6px 0 8px 22px;line-height:1.75">
|
||||
<li><b>Нагреватель</b> — источник теплоты, температура $T_1$.</li>
|
||||
<li><b>Рабочее тело</b> — газ или пар; циклически расширяется и сжимается.</li>
|
||||
<li><b>Холодильник</b> — приёмник избыточной теплоты, температура $T_2 < T_1$.</li>
|
||||
</ul>
|
||||
<p>За один полный цикл рабочее тело:</p>
|
||||
<ol style="margin:6px 0 8px 22px;line-height:1.75">
|
||||
<li>получает $Q_1$ от нагревателя;</li>
|
||||
<li>совершает полезную работу $A$;</li>
|
||||
<li>отдаёт $Q_2$ холодильнику.</li>
|
||||
</ol>
|
||||
<p>По закону сохранения энергии $Q_1 = A + Q_2$, откуда:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$A = Q_1 - Q_2$$</p>
|
||||
<p style="padding:10px 14px;background:var(--warn-bg,#fef3c7);border-left:4px solid var(--warn,#f59e0b);border-radius:9px"><b>Без холодильника двигатель работать не может</b> — без отвода $Q_2$ цикл не замкнётся.</p>
|
||||
`);
|
||||
|
||||
/* THEORY 2 — КПД и цикл Карно */
|
||||
html += makeCard('rule', "КПД и цикл Карно", "§15", `
|
||||
<p><b>Коэффициент полезного действия</b> (КПД) теплового двигателя — отношение совершённой работы к полученной теплоте:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$\\eta = \\dfrac{A}{Q_1} = \\dfrac{Q_1 - Q_2}{Q_1} = 1 - \\dfrac{Q_2}{Q_1}$$</p>
|
||||
<p>Всегда $\\eta < 1$ — часть теплоты неизбежно отдаётся холодильнику.</p>
|
||||
<p style="margin-top:10px"><b>Цикл Карно</b> — идеальный обратимый цикл с максимальным КПД. Состоит из 4 этапов:</p>
|
||||
<ol style="margin:6px 0 8px 22px;line-height:1.75">
|
||||
<li><b>Изотермическое расширение</b> при $T_1$ (рабочее тело получает $Q_1$).</li>
|
||||
<li><b>Адиабатное расширение</b> (охлаждение от $T_1$ до $T_2$).</li>
|
||||
<li><b>Изотермическое сжатие</b> при $T_2$ (отдаёт $Q_2$).</li>
|
||||
<li><b>Адиабатное сжатие</b> (нагрев от $T_2$ до $T_1$).</li>
|
||||
</ol>
|
||||
<p>КПД цикла Карно зависит <b>только от температур</b>:</p>
|
||||
<p style="text-align:center;margin:10px 0">$$\\eta_{Карно} = \\dfrac{T_1 - T_2}{T_1} = 1 - \\dfrac{T_2}{T_1}$$</p>
|
||||
<p style="padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px"><b>Полезно:</b> увеличить $T_1$ выгоднее, чем равнозначно уменьшить $T_2$ — растёт знаменатель работы и абсолютная разность.</p>
|
||||
`);
|
||||
|
||||
/* THEORY 3 — Реальные двигатели и экология */
|
||||
html += makeCard('example', "Реальные двигатели и экология", "§15", `
|
||||
<p><b>Теорема Карно:</b> $\\eta_{Карно}$ — <b>максимально возможный</b> КПД для теплового двигателя между $T_1$ и $T_2$. Любой реальный двигатель имеет $\\eta \\le \\eta_{Карно}$.</p>
|
||||
<p><b>Реальные значения КПД:</b></p>
|
||||
<table style="width:100%;border-collapse:collapse;margin:10px 0;font-size:.92rem">
|
||||
<thead><tr style="background:var(--sec-acc-soft)"><th style="padding:8px;border:1px solid var(--border);text-align:left">Двигатель</th><th style="padding:8px;border:1px solid var(--border);text-align:center">КПД</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td style="padding:8px;border:1px solid var(--border)">ДВС бензиновый</td><td style="padding:8px;border:1px solid var(--border);text-align:center">25–35%</td></tr>
|
||||
<tr><td style="padding:8px;border:1px solid var(--border)">Дизельный</td><td style="padding:8px;border:1px solid var(--border);text-align:center">35–45%</td></tr>
|
||||
<tr><td style="padding:8px;border:1px solid var(--border)">Паровая турбина (АЭС, ТЭС)</td><td style="padding:8px;border:1px solid var(--border);text-align:center">30–40%</td></tr>
|
||||
<tr><td style="padding:8px;border:1px solid var(--border)">Газовая турбина (комб. цикл)</td><td style="padding:8px;border:1px solid var(--border);text-align:center">до 60%</td></tr>
|
||||
<tr><td style="padding:8px;border:1px solid var(--border)">Карно при $T_1=800$ К, $T_2=300$ К</td><td style="padding:8px;border:1px solid var(--border);text-align:center"><b>62,5%</b></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><b>Второй закон термодинамики</b> (формулировка Кельвина): невозможен процесс, единственным результатом которого было бы превращение теплоты в работу. Невозможен «вечный двигатель 2-го рода» с $\\eta = 100\\%$.</p>
|
||||
<p><b>Экологические проблемы:</b></p>
|
||||
<ul style="margin:6px 0 8px 22px;line-height:1.75">
|
||||
<li>тепловое загрязнение (нагрев водоёмов, атмосферы);</li>
|
||||
<li>$CO_2$ — парниковый эффект;</li>
|
||||
<li>$NO_x$, $SO_x$ — кислотные дожди;</li>
|
||||
<li>сажа, тяжёлые металлы — загрязнение воздуха.</li>
|
||||
</ul>
|
||||
<p>Развитие электротранспорта, ВИЭ и АЭС — путь к снижению этих эффектов.</p>
|
||||
`);
|
||||
|
||||
/* INTERACTIVE 1 — Цикл Карно на PV-диаграмме */
|
||||
html += `<div class="wg" id="p15-iv1">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Цикл Карно на $pV$-диаграмме</div></div>
|
||||
<div class="wg-help">Двигай ползунки $T_1, T_2$. Жёлтым — изотермы, фиолетовым — адиабаты. Заштрихованная область = работа за цикл.</div>
|
||||
<div class="sliders" id="p15-iv1-sliders"></div>
|
||||
<div style="background:var(--card);border:1px solid var(--border);border-radius:9px;padding:8px">
|
||||
<svg id="p15-iv1-svg" viewBox="0 0 480 360" width="100%" style="height:auto"></svg>
|
||||
</div>
|
||||
<div id="p15-iv1-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;line-height:1.85"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 2 — Калькулятор КПД */
|
||||
html += `<div class="wg" id="p15-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:flex;gap:10px;flex-wrap:wrap;justify-content:center;margin-bottom:10px" id="p15-iv2-tabs">
|
||||
<button class="btn primary" data-mode="QQ">Через $Q_1, Q_2$</button>
|
||||
<button class="btn" data-mode="AQ">Через $A, Q_1$</button>
|
||||
<button class="btn" data-mode="TT">Карно: $T_1, T_2$</button>
|
||||
</div>
|
||||
<div id="p15-iv2-inputs" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-bottom:10px"></div>
|
||||
<div class="actions" style="justify-content:center"><button class="btn primary" id="p15-iv2-go">Найти КПД</button></div>
|
||||
<div id="p15-iv2-out" style="margin-top:10px;padding:12px 14px;background:var(--card);border-radius:9px;font-size:.94rem;min-height:60px;line-height:1.85"></div>
|
||||
<div class="feedback" id="p15-iv2-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 3 — Возможен ли двигатель? */
|
||||
html += `<div class="wg" id="p15-iv3">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Возможен ли двигатель с таким КПД?</div></div>
|
||||
<div class="wg-help">6 ситуаций. Сравни заявленный КПД с теоремой Карно.</div>
|
||||
<div class="score-display"><span>Задание <b id="p15-iv3-i">1</b> / 6</span><span>Очки: <b id="p15-iv3-s">0</b> / 6</span></div>
|
||||
<div id="p15-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:60px"></div>
|
||||
<div style="display:flex;gap:8px;justify-content:center;flex-wrap:wrap">
|
||||
<button class="btn primary" data-ans="ok">Возможен</button>
|
||||
<button class="btn primary" data-ans="no">Нарушает 2-й закон</button>
|
||||
</div>
|
||||
<div class="feedback" id="p15-iv3-fb"></div>
|
||||
</div>`;
|
||||
|
||||
/* INTERACTIVE 4 — Тренажёр КПД */
|
||||
html += `<div class="wg" id="p15-iv4">
|
||||
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр КПД</div></div>
|
||||
<div class="wg-help">5 задач. Допуск $\\pm 5\\%$. Ответ — число (КПД в %, работа в кДж и т.п.).</div>
|
||||
<div class="score-display"><span>Задача <b id="p15-iv4-i">1</b> / 5</span><span>Очки: <b id="p15-iv4-s">0</b> / 5</span></div>
|
||||
<div id="p15-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;margin-bottom:10px;text-align:center;min-height:54px"></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="p15-iv4-ans" class="tinp" style="width:140px;text-align:center" step="any">
|
||||
<button class="btn primary" id="p15-iv4-go">Проверить</button>
|
||||
<button class="btn" id="p15-iv4-start">Заново</button>
|
||||
</div>
|
||||
<div class="feedback" id="p15-iv4-fb"></div>
|
||||
</div>`;
|
||||
|
||||
html += secNav('p14', 'final2');
|
||||
html += readButton('p15');
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* IV1 — Цикл Карно на PV-диаграмме */
|
||||
(function(){
|
||||
const R = 8.314;
|
||||
const NU = 1;
|
||||
const GAMMA = 5/3;
|
||||
const slBox = document.getElementById('p15-iv1-sliders');
|
||||
const svg = document.getElementById('p15-iv1-svg');
|
||||
const info = document.getElementById('p15-iv1-info');
|
||||
const seen = new Set(); let _done = false;
|
||||
|
||||
slBox.innerHTML = ''
|
||||
+ '<label>$T_1$ (нагреватель): <b id="p15-iv1-t1L">600</b> К <input type="range" id="p15-iv1-t1" min="400" max="800" value="600" step="10"></label>'
|
||||
+ '<label>$T_2$ (холодильник): <b id="p15-iv1-t2L">300</b> К <input type="range" id="p15-iv1-t2" min="100" max="350" value="300" step="10"></label>';
|
||||
renderMath(slBox);
|
||||
|
||||
function render(){
|
||||
const T1 = +document.getElementById('p15-iv1-t1').value;
|
||||
let T2 = +document.getElementById('p15-iv1-t2').value;
|
||||
if(T2 >= T1) T2 = T1 - 20;
|
||||
document.getElementById('p15-iv1-t1L').textContent = T1;
|
||||
document.getElementById('p15-iv1-t2L').textContent = T2;
|
||||
|
||||
// Точки цикла (PV-плоскость): V в л, p в "виртуальных атм" (используем RT/V напрямую)
|
||||
// Внутри: давление p = nu R T / V (Дж/м^3 если V в м^3); для шкалы — отнормируем
|
||||
const V_A = 1, V_B = 3; // л — изотерма T1
|
||||
const V_C = 5; // л — конец адиабаты от B
|
||||
// pV^gamma = const на адиабате BC: p_B * V_B^g = p_C * V_C^g
|
||||
// Также p_C = nu R T2 / V_C (точка C на изотерме T2)
|
||||
// Адиабата: T V^(g-1) = const → T1 * V_B^(g-1) = T2 * V_C^(g-1)
|
||||
// Тогда V_C из физики: V_C = V_B * (T1/T2)^(1/(g-1))
|
||||
const V_C_calc = V_B * Math.pow(T1/T2, 1/(GAMMA-1));
|
||||
const V_C_use = V_C_calc;
|
||||
// Аналогично для DA: T1 * V_A^(g-1) = T2 * V_D^(g-1)
|
||||
const V_D = V_A * Math.pow(T1/T2, 1/(GAMMA-1));
|
||||
|
||||
// Давления (произвольные единицы — RT/V, R=8.314, T в К, V в л → давление в Дж/л = кПа)
|
||||
function pIso(T, V){ return NU * R * T / V; } // кПа
|
||||
const p_A = pIso(T1, V_A);
|
||||
const p_B = pIso(T1, V_B);
|
||||
const p_C = pIso(T2, V_C_use);
|
||||
const p_D = pIso(T2, V_D);
|
||||
|
||||
// Q1 (получено на AB), Q2 (отдано на CD)
|
||||
const Q1 = NU * R * T1 * Math.log(V_B / V_A);
|
||||
const Q2 = NU * R * T2 * Math.log(V_C_use / V_D);
|
||||
const A = Q1 - Q2;
|
||||
const eta = 1 - T2 / T1;
|
||||
|
||||
// SVG: оси V (1..6 л), p (0..max)
|
||||
const W = 480, H = 360, pad = 50;
|
||||
const xmin = 0.5, xmax = 6.2;
|
||||
// верхняя граница p — для красивого масштаба
|
||||
const pmax = Math.max(p_A, p_B, p_C, p_D) * 1.1;
|
||||
const pmin = 0;
|
||||
const ux = (W - 2*pad) / (xmax - xmin);
|
||||
const uy = (H - 2*pad) / (pmax - pmin);
|
||||
const toX = v => pad + (v - xmin) * ux;
|
||||
const toY = p => H - pad - (p - pmin) * uy;
|
||||
|
||||
let g = '';
|
||||
// Сетка
|
||||
g += '<rect x="'+pad+'" y="'+pad+'" width="'+(W-2*pad)+'" height="'+(H-2*pad)+'" fill="#fafbfc" stroke="#e5e7eb"/>';
|
||||
g += '<g stroke="#e5e7eb" stroke-width="1">';
|
||||
for(let v=1; v<=6; v++){
|
||||
g += '<line x1="'+toX(v)+'" y1="'+pad+'" x2="'+toX(v)+'" y2="'+(H-pad)+'"/>';
|
||||
}
|
||||
g += '</g>';
|
||||
// Оси
|
||||
g += '<line x1="'+pad+'" y1="'+(H-pad)+'" x2="'+(W-pad)+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
|
||||
g += '<line x1="'+pad+'" y1="'+pad+'" x2="'+pad+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
|
||||
g += '<text x="'+(W-pad+4)+'" y="'+(H-pad+4)+'" font-size="12" fill="#0f172a">V, л</text>';
|
||||
g += '<text x="'+(pad-10)+'" y="'+(pad-6)+'" font-size="12" fill="#0f172a">p, кПа</text>';
|
||||
// Метки оси V
|
||||
for(let v=1; v<=6; v++){
|
||||
g += '<text x="'+(toX(v)-3)+'" y="'+(H-pad+14)+'" font-size="10" fill="#64748b">'+v+'</text>';
|
||||
}
|
||||
// Метки оси p (4 деления)
|
||||
for(let i=1;i<=4;i++){
|
||||
const pv = pmax*i/4;
|
||||
g += '<text x="'+(pad-30)+'" y="'+(toY(pv)+3)+'" font-size="10" fill="#64748b">'+pv.toFixed(0)+'</text>';
|
||||
g += '<line x1="'+pad+'" y1="'+toY(pv)+'" x2="'+(W-pad)+'" y2="'+toY(pv)+'" stroke="#e5e7eb" stroke-dasharray="3 3"/>';
|
||||
}
|
||||
|
||||
// Построим путь по циклу (для заливки) и отдельно линии цвета
|
||||
function pathIso(T, v1, v2, N){
|
||||
N = N || 60;
|
||||
let d = '';
|
||||
for(let i=0;i<=N;i++){
|
||||
const v = v1 + (v2-v1)*i/N;
|
||||
const p = pIso(T, v);
|
||||
d += (i===0?'M':' L') + toX(v).toFixed(2) + ',' + toY(p).toFixed(2);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
function pathAdi(pStart, vStart, vEnd, N){
|
||||
N = N || 60;
|
||||
const C = pStart * Math.pow(vStart, GAMMA);
|
||||
let d = '';
|
||||
for(let i=0;i<=N;i++){
|
||||
const v = vStart + (vEnd-vStart)*i/N;
|
||||
const p = C / Math.pow(v, GAMMA);
|
||||
d += (i===0?'M':' L') + toX(v).toFixed(2) + ',' + toY(p).toFixed(2);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
// Полный замкнутый путь: A→B (изо T1) → C (адиа) → D (изо T2) → A (адиа)
|
||||
const dAB = pathIso(T1, V_A, V_B);
|
||||
const dBC = pathAdi(p_B, V_B, V_C_use);
|
||||
const dCD = pathIso(T2, V_C_use, V_D);
|
||||
const dDA = pathAdi(p_D, V_D, V_A);
|
||||
|
||||
// Заливка площади цикла
|
||||
const fullPath = dAB + ' ' + dBC.replace(/^M/, 'L') + ' ' + dCD.replace(/^M/, 'L') + ' ' + dDA.replace(/^M/, 'L') + ' Z';
|
||||
g += '<path d="'+fullPath+'" fill="#fbbf24" fill-opacity="0.20" stroke="none"/>';
|
||||
|
||||
// Линии
|
||||
g += '<path d="'+dAB+'" stroke="#ea580c" stroke-width="2.5" fill="none"/>'; // изо T1
|
||||
g += '<path d="'+dBC+'" stroke="#7c3aed" stroke-width="2.5" fill="none"/>'; // адиа
|
||||
g += '<path d="'+dCD+'" stroke="#2563eb" stroke-width="2.5" fill="none"/>'; // изо T2
|
||||
g += '<path d="'+dDA+'" stroke="#7c3aed" stroke-width="2.5" fill="none" stroke-dasharray="6 3"/>'; // адиа сжатия
|
||||
|
||||
// Точки A,B,C,D
|
||||
function pt(x,y,lab,col){
|
||||
return '<circle cx="'+x+'" cy="'+y+'" r="4.5" fill="'+col+'" stroke="#fff" stroke-width="2"/>'
|
||||
+ '<text x="'+(x+7)+'" y="'+(y-7)+'" font-size="12" font-weight="700" fill="'+col+'">'+lab+'</text>';
|
||||
}
|
||||
g += pt(toX(V_A), toY(p_A), 'A', '#ea580c');
|
||||
g += pt(toX(V_B), toY(p_B), 'B', '#ea580c');
|
||||
g += pt(toX(V_C_use), toY(p_C), 'C', '#2563eb');
|
||||
g += pt(toX(V_D), toY(p_D), 'D', '#2563eb');
|
||||
|
||||
// Легенда
|
||||
g += '<g font-size="11" font-family="Inter,sans-serif">';
|
||||
g += '<rect x="'+(W-pad-150)+'" y="'+(pad+6)+'" width="146" height="58" rx="6" fill="#ffffff" stroke="#e5e7eb"/>';
|
||||
g += '<line x1="'+(W-pad-140)+'" y1="'+(pad+20)+'" x2="'+(W-pad-118)+'" y2="'+(pad+20)+'" stroke="#ea580c" stroke-width="2.5"/>';
|
||||
g += '<text x="'+(W-pad-112)+'" y="'+(pad+24)+'" fill="#0f172a">изотерма T₁</text>';
|
||||
g += '<line x1="'+(W-pad-140)+'" y1="'+(pad+38)+'" x2="'+(W-pad-118)+'" y2="'+(pad+38)+'" stroke="#2563eb" stroke-width="2.5"/>';
|
||||
g += '<text x="'+(W-pad-112)+'" y="'+(pad+42)+'" fill="#0f172a">изотерма T₂</text>';
|
||||
g += '<line x1="'+(W-pad-140)+'" y1="'+(pad+56)+'" x2="'+(W-pad-118)+'" y2="'+(pad+56)+'" stroke="#7c3aed" stroke-width="2.5"/>';
|
||||
g += '<text x="'+(W-pad-112)+'" y="'+(pad+60)+'" fill="#0f172a">адиабаты</text>';
|
||||
g += '</g>';
|
||||
|
||||
svg.innerHTML = g;
|
||||
|
||||
// Инфо
|
||||
info.innerHTML = ''
|
||||
+ '<div><b>$T_1 = '+T1+'$ К, $T_2 = '+T2+'$ К</b></div>'
|
||||
+ '<div>$Q_1 = \\nu RT_1\\ln(V_B/V_A) \\approx '+Q1.toFixed(1)+'$ Дж <span style="color:#ea580c">(получено)</span></div>'
|
||||
+ '<div>$Q_2 = \\nu RT_2\\ln(V_C/V_D) \\approx '+Q2.toFixed(1)+'$ Дж <span style="color:#2563eb">(отдано)</span></div>'
|
||||
+ '<div>$A = Q_1 - Q_2 \\approx '+A.toFixed(1)+'$ Дж <span style="color:#d97706">(работа за цикл)</span></div>'
|
||||
+ '<div style="margin-top:6px;font-weight:700">$\\eta_{Карно} = 1 - T_2/T_1 = '+(eta*100).toFixed(1)+'\\%$</div>';
|
||||
renderMath(info);
|
||||
|
||||
seen.add(T1+'_'+T2);
|
||||
if(!_done && seen.size >= 4){ _done = true; addXp(10,'p15-iv1'); bumpProgress('p15', 15); }
|
||||
}
|
||||
document.getElementById('p15-iv1-t1').addEventListener('input', render);
|
||||
document.getElementById('p15-iv1-t2').addEventListener('input', render);
|
||||
render();
|
||||
})();
|
||||
|
||||
/* IV2 — Калькулятор КПД */
|
||||
(function(){
|
||||
const tabs = document.getElementById('p15-iv2-tabs');
|
||||
const inpsBox = document.getElementById('p15-iv2-inputs');
|
||||
const out = document.getElementById('p15-iv2-out');
|
||||
const fb = document.getElementById('p15-iv2-fb');
|
||||
const used = new Set(); let _done = false;
|
||||
let mode = 'QQ';
|
||||
|
||||
function field(id, label, val){
|
||||
return '<label style="display:block;font-size:.9rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border)">'+label+' <input type="number" id="'+id+'" class="tinp" style="width:100%;margin-top:6px" value="'+val+'" step="any"></label>';
|
||||
}
|
||||
function build(){
|
||||
let h = '';
|
||||
if(mode === 'QQ'){
|
||||
h += field('p15-iv2-Q1','$Q_1$, Дж','1000');
|
||||
h += field('p15-iv2-Q2','$Q_2$, Дж','700');
|
||||
} else if(mode === 'AQ'){
|
||||
h += field('p15-iv2-A','$A$, Дж','300');
|
||||
h += field('p15-iv2-Q1','$Q_1$, Дж','1000');
|
||||
} else {
|
||||
h += field('p15-iv2-T1','$T_1$, К','600');
|
||||
h += field('p15-iv2-T2','$T_2$, К','300');
|
||||
}
|
||||
inpsBox.innerHTML = h;
|
||||
renderMath(inpsBox);
|
||||
out.innerHTML = '';
|
||||
fb.style.display = 'none';
|
||||
}
|
||||
function num(id){ const el = document.getElementById(id); return el ? parseFloat((el.value||'').replace(',','.')) : NaN; }
|
||||
function calc(){
|
||||
let res = '', eta = NaN;
|
||||
if(mode === 'QQ'){
|
||||
const Q1 = num('p15-iv2-Q1'), Q2 = num('p15-iv2-Q2');
|
||||
if(![Q1,Q2].every(isFinite) || Q1 <= 0){ feedback(fb,false,'✗ Введи корректные $Q_1 > 0$ и $Q_2$.'); return; }
|
||||
eta = (Q1 - Q2) / Q1;
|
||||
res = '$\\eta = \\dfrac{Q_1 - Q_2}{Q_1} = \\dfrac{'+Q1+' - '+Q2+'}{'+Q1+'} = '+eta.toFixed(4)+'$';
|
||||
} else if(mode === 'AQ'){
|
||||
const A = num('p15-iv2-A'), Q1 = num('p15-iv2-Q1');
|
||||
if(![A,Q1].every(isFinite) || Q1 <= 0){ feedback(fb,false,'✗ Введи $A$ и $Q_1 > 0$.'); return; }
|
||||
eta = A / Q1;
|
||||
res = '$\\eta = \\dfrac{A}{Q_1} = \\dfrac{'+A+'}{'+Q1+'} = '+eta.toFixed(4)+'$';
|
||||
} else {
|
||||
const T1 = num('p15-iv2-T1'), T2 = num('p15-iv2-T2');
|
||||
if(![T1,T2].every(isFinite) || T1 <= 0){ feedback(fb,false,'✗ Введи $T_1 > 0$ и $T_2$.'); return; }
|
||||
if(T2 >= T1){ feedback(fb,false,'✗ Должно быть $T_2 < T_1$.'); return; }
|
||||
eta = 1 - T2/T1;
|
||||
res = '$\\eta_{Карно} = 1 - \\dfrac{T_2}{T_1} = 1 - \\dfrac{'+T2+'}{'+T1+'} = '+eta.toFixed(4)+'$';
|
||||
}
|
||||
out.innerHTML = '<div style="margin-bottom:8px">'+res+'</div>'
|
||||
+ '<div><b>Ответ:</b> <span style="font-weight:700;color:var(--pri2);font-size:1.05rem">$\\eta \\approx '+(eta*100).toFixed(2)+'\\%$</span></div>';
|
||||
renderMath(out);
|
||||
feedback(fb, true, '✓ Вычислено.');
|
||||
used.add(mode);
|
||||
if(!_done && used.size === 3){ _done = true; addXp(10,'p15-iv2'); bumpProgress('p15', 15); }
|
||||
}
|
||||
tabs.querySelectorAll('button').forEach(b => {
|
||||
b.addEventListener('click', () => {
|
||||
mode = b.dataset.mode;
|
||||
tabs.querySelectorAll('button').forEach(x => { x.className = 'btn'; });
|
||||
b.className = 'btn primary';
|
||||
build();
|
||||
});
|
||||
});
|
||||
document.getElementById('p15-iv2-go').addEventListener('click', calc);
|
||||
build();
|
||||
})();
|
||||
|
||||
/* IV3 — Возможен ли двигатель? */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ q:'Бензиновый ДВС с заявленным $\\eta = 30\\%$.', ans:'ok', why:'30% — типичный КПД ДВС.' },
|
||||
{ q:'Двигатель с $\\eta = 99\\%$ при $T_1 = 400$ К, $T_2 = 300$ К. Карно даёт $25\\%$.', ans:'no', why:'$99\\% > \\eta_{Карно} = 25\\%$ — нарушает теорему Карно.' },
|
||||
{ q:'Двигатель с $\\eta = 60\\%$ при $T_1 = 1000$ К, $T_2 = 400$ К. Карно даёт ровно $60\\%$.', ans:'ok', why:'$\\eta = \\eta_{Карно}$ — идеальный случай (предел).' },
|
||||
{ q:'Двигатель с $\\eta = 100\\%$ (полностью теплота → работа).', ans:'no', why:'Запрещает 2-й закон термодинамики (вечный двигатель 2-го рода).' },
|
||||
{ q:'Паровая турбина с $\\eta = 35\\%$ при $T_1 = 600$ К, $T_2 = 320$ К. Карно даёт $\\approx 47\\%$.', ans:'ok', why:'$35\\% < 47\\%$ — реальное значение, всё в порядке.' },
|
||||
{ q:'Двигатель с $\\eta = 50\\%$ при $T_1 = 500$ К, $T_2 = 300$ К. Карно даёт $40\\%$.', ans:'no', why:'$50\\% > \\eta_{Карно} = 40\\%$ — превышает предел Карно.' },
|
||||
];
|
||||
let i = 0, score = 0; let _done = false;
|
||||
const box = document.getElementById('p15-iv3');
|
||||
function show(){
|
||||
const qEl = document.getElementById('p15-iv3-q');
|
||||
const fb = document.getElementById('p15-iv3-fb');
|
||||
if(i >= Q.length){
|
||||
qEl.innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
renderMath(qEl);
|
||||
if(!_done){
|
||||
_done = true;
|
||||
if(score === Q.length){ addXp(15,'p15-iv3'); bumpProgress('p15', 25); }
|
||||
else if(score >= 4){ addXp(8,'p15-iv3'); bumpProgress('p15', 15); }
|
||||
}
|
||||
return;
|
||||
}
|
||||
document.getElementById('p15-iv3-i').textContent = (i+1);
|
||||
document.getElementById('p15-iv3-s').textContent = score;
|
||||
qEl.innerHTML = Q[i].q;
|
||||
renderMath(qEl);
|
||||
fb.style.display = 'none';
|
||||
}
|
||||
box.querySelectorAll('button[data-ans]').forEach(b => {
|
||||
b.addEventListener('click', () => {
|
||||
if(i >= Q.length) return;
|
||||
const fb = document.getElementById('p15-iv3-fb');
|
||||
if(b.dataset.ans === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! '+Q[i].why+' Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. '+Q[i].why+' Дальше ▶');
|
||||
document.getElementById('p15-iv3-s').textContent = score;
|
||||
i++;
|
||||
setTimeout(show, 1800);
|
||||
});
|
||||
});
|
||||
show();
|
||||
})();
|
||||
|
||||
/* IV4 — Тренажёр КПД */
|
||||
(function(){
|
||||
const Q = [
|
||||
{ q:'Двигатель получил $Q_1 = 1000$ Дж и отдал $Q_2 = 700$ Дж. КПД в %?', ans:30, tol:2, hint:'$\\eta = (1000-700)/1000 = 30\\%$' },
|
||||
{ q:'Двигатель Карно работает между $T_1 = 600$ К и $T_2 = 300$ К. КПД в %?', ans:50, tol:2, hint:'$\\eta = 1 - 300/600 = 50\\%$' },
|
||||
{ q:'$T_1 = 500$ К, $T_2 = 300$ К. Что выгоднее: нагреть $T_1$ на 100 К (введи $1$) или охладить $T_2$ на 100 К (введи $2$)?', ans:2, tol:0.4, hint:'$T_1 \\to 600$: $\\eta = 0{,}5$. $T_2 \\to 200$: $\\eta \\approx 0{,}6$. Холодильник выгоднее.' },
|
||||
{ q:'ДВС бензиновый совершил работу $A = 30$ кДж за цикл при $\\eta = 25\\%$. Сколько $Q_1$ в кДж?', ans:120, tol:6, hint:'$Q_1 = A/\\eta = 30/0{,}25 = 120$ кДж' },
|
||||
{ q:'Идеальный Карно: $T_1 = 800$ К, $T_2 = 400$ К. КПД в %?', ans:50, tol:2, hint:'$\\eta = 1 - 400/800 = 50\\%$' },
|
||||
];
|
||||
let i = 0, score = 0;
|
||||
function show(){
|
||||
if(i >= Q.length){
|
||||
document.getElementById('p15-iv4-q').innerHTML = '<b>Готово!</b> Результат: ' + score + ' / ' + Q.length;
|
||||
if(score === Q.length){ addXp(15, 'p15-iv4'); bumpProgress('p15', 25); }
|
||||
else if(score >= 3){ addXp(8, 'p15-iv4'); bumpProgress('p15', 15); }
|
||||
return;
|
||||
}
|
||||
document.getElementById('p15-iv4-i').textContent = (i+1);
|
||||
document.getElementById('p15-iv4-s').textContent = score;
|
||||
document.getElementById('p15-iv4-q').innerHTML = Q[i].q;
|
||||
document.getElementById('p15-iv4-ans').value = '';
|
||||
renderMath(document.getElementById('p15-iv4-q'));
|
||||
document.getElementById('p15-iv4-fb').style.display = 'none';
|
||||
}
|
||||
function go(){
|
||||
if(i >= Q.length) return;
|
||||
const fb = document.getElementById('p15-iv4-fb');
|
||||
const raw = document.getElementById('p15-iv4-ans').value.replace(',', '.');
|
||||
const ans = parseFloat(raw);
|
||||
if(isNaN(ans)){ feedback(fb, false, '✗ Введи число.'); return; }
|
||||
if(Math.abs(ans - Q[i].ans) <= Q[i].tol + 0.001){ score++; feedback(fb, true, '✓ Верно! '+Q[i].hint+'. Дальше ▶'); }
|
||||
else feedback(fb, false, '✗ Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+'. Дальше ▶');
|
||||
document.getElementById('p15-iv4-s').textContent = score;
|
||||
i++;
|
||||
setTimeout(show, 1800);
|
||||
}
|
||||
document.getElementById('p15-iv4-go').addEventListener('click', go);
|
||||
document.getElementById('p15-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
|
||||
document.getElementById('p15-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
|
||||
show();
|
||||
})();
|
||||
|
||||
wireReadBtn('p15');
|
||||
}
|
||||
|
||||
function build_final2(){
|
||||
const box = document.getElementById('final2-body');
|
||||
let html = '';
|
||||
html += makeCard('theory', "Финал главы 2", "★", `
|
||||
<p><b>Финал главы 2</b> — этот параграф в разработке (Phase 1+).</p>
|
||||
<p>Здесь появятся: теория, формулы, разобранные примеры и 3–4 интерактива в стиле «алгебры 11» — таблицы, симуляции, ползунки, drag-and-drop и автопроверяемые тренажёры.</p>
|
||||
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem">
|
||||
<b>Phase 0:</b> создан скелет учебника. <b>Phase 2+:</b> наполнение этого § содержанием по учебнику «Физика 10» (Беларусь, 2019).
|
||||
</p>
|
||||
`);
|
||||
|
||||
/* Часть А — Шпаргалка главы (5 mini-карточек) */
|
||||
const SHEET = [
|
||||
{ t:'§ 11 · Внутренняя энергия', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#0ea5e9" stroke-width="2" style="width:18px;height:18px"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="3"/></svg>', body:'$U = \\dfrac{i}{2}\\nu RT$. Зависит только от $T$. $i = 3$ (одноат.), $i = 5$ (двухат.).' },
|
||||
{ t:'§ 12 · Работа', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#10b981" stroke-width="2" style="width:18px;height:18px"><path d="M3 18l6-6 4 4 8-10"/></svg>', body:'Изобарно $A = p\\Delta V$. Изотермически $A = \\nu RT\\ln(V_2/V_1)$. Изохорно $A = 0$.' },
|
||||
{ t:'§ 13 · Теплота', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2" style="width:18px;height:18px"><path d="M12 3c-3 5-5 8-5 11a5 5 0 0 0 10 0c0-3-2-6-5-11z"/></svg>', body:'$Q = cm\\Delta T$, $Q = \\lambda m$, $Q = rm$, $Q = qm$. Уравнение теплового баланса.' },
|
||||
{ t:'§ 14 · 1-й закон ТД', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2" style="width:18px;height:18px"><path d="M4 12h16"/><path d="M12 4l8 8-8 8"/></svg>', body:'$Q = \\Delta U + A_{газ}$. Изотерм.: $Q = A$. Изохорно: $Q = \\Delta U$. Адиабатно: $\\Delta U = -A$.' },
|
||||
{ t:'§ 15 · Тепл. двигатели', icon:'<svg viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2" style="width:18px;height:18px"><circle cx="12" cy="12" r="9"/><path d="M12 3v9l6 4"/></svg>', body:'$\\eta = (Q_1-Q_2)/Q_1$. Карно: $\\eta = 1 - T_2/T_1$ — максимальный КПД.' },
|
||||
];
|
||||
|
||||
html += `<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon theory">${ICONS.theory}</span>
|
||||
<span class="card-title">Шпаргалка главы 2 — Термодинамика</span>
|
||||
<span class="card-num">Итог</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Ключевые формулы и идеи всех 5 параграфов главы — повтори перед битвой с боссами.</p>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-top:10px">
|
||||
${SHEET.map(s => `<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">
|
||||
${s.icon}
|
||||
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.88rem">${s.t}</div>
|
||||
</div>
|
||||
<div style="font-size:.92rem;line-height:1.55">${s.body}</div>
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
/* Часть Б — 5 боссов intro */
|
||||
html += `<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon rule">${ICONS.rule}</span>
|
||||
<span class="card-title">Боссы главы 2</span>
|
||||
<span class="card-num">5</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>5 интегрированных задач по §§11–15. За каждого побеждённого босса: <b>+10 XP, +18% к прогрессу</b>. Победишь всех — ачивка <b>«Мастер термодинамики»</b> и <b>+50 XP бонус</b>.</p>
|
||||
<p style="margin-top:6px;font-size:.92rem;color:var(--muted)">Константы: $R = 8{,}3$ Дж/(моль·К), $c_{вода} = 4200$ Дж/(кг·К), $\\lambda_{лёд} = 330$ кДж/кг, $r_{вода} = 2260$ кДж/кг. Допуск $\\pm 5\\%$.</p>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
html += '<div id="ch2-bosses-container"></div>';
|
||||
|
||||
html += `<div style="margin-top:18px;padding:18px 20px;background:linear-gradient(135deg,var(--pri-soft),var(--sec-acc-soft));border-radius:14px;border:1.5px solid var(--pri);text-align:center" id="ch2-final-summary">
|
||||
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:var(--pri2);font-size:1.1rem;margin-bottom:6px">Прогресс по боссам</div>
|
||||
<div id="ch2-boss-overall" style="font-size:.95rem;color:var(--text);margin-bottom:10px">0 / 5 боссов побеждено</div>
|
||||
<div style="height:12px;background:var(--card);border-radius:8px;overflow:hidden;border:1px solid var(--border)">
|
||||
<div id="ch2-boss-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,#d97706,#fbbf24);transition:width .35s"></div>
|
||||
</div>
|
||||
<div id="ch2-final-reward" style="margin-top:14px;display:none;padding:14px;background:var(--card);border-radius:11px;border:2px solid #f59e0b">
|
||||
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:#92400e;font-size:1.05rem;margin-bottom:6px">Мастер термодинамики</div>
|
||||
<div style="font-size:.92rem;margin-bottom:10px">Глава 2 пройдена! Все 5 боссов повержены. +50 XP бонус.</div>
|
||||
<a class="btn primary" href="/textbook/physics-10-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('p15', null);
|
||||
html += readButton('final2');
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
wireReadBtn('final2');
|
||||
|
||||
/* Боссы */
|
||||
const BOSSES = [
|
||||
{
|
||||
n:1, color:'#0ea5e9',
|
||||
title:'Хранитель Внутренней Энергии',
|
||||
tag:'§ 11',
|
||||
q:'$\\nu = 1$ моль одноатомного газа при $T = 400$ К. Найди $U$ в кДж (до сотых).',
|
||||
ans:4.98, tol:0.1,
|
||||
hint:'$U = \\tfrac{3}{2}\\nu RT = 1{,}5 \\cdot 1 \\cdot 8{,}3 \\cdot 400 = 4980$ Дж $\\approx 4{,}98$ кДж.'
|
||||
},
|
||||
{
|
||||
n:2, color:'#10b981',
|
||||
title:'Поршневой Гигант',
|
||||
tag:'§ 12',
|
||||
q:'Изобарно при $p = 2$ атм газ расширили от $5$ до $15$ л. Работа газа в Дж?',
|
||||
ans:2000, tol:60,
|
||||
hint:'$A = p\\Delta V = 2 \\cdot 10^5 \\cdot 10 \\cdot 10^{-3} = 2000$ Дж.'
|
||||
},
|
||||
{
|
||||
n:3, color:'#f59e0b',
|
||||
title:'Кипящий Левиафан',
|
||||
tag:'§ 13',
|
||||
q:'Нагреть $3$ кг воды от $20°$C до $100°$C и испарить. Найди $Q_{общ}$ в МДж (до десятых).',
|
||||
ans:7.8, tol:0.3,
|
||||
hint:'$Q_1 = cm\\Delta T = 4200 \\cdot 3 \\cdot 80 \\approx 1{,}01$ МДж. $Q_2 = rm = 2{,}26 \\cdot 3 = 6{,}78$ МДж. Сумма $\\approx 7{,}8$ МДж.'
|
||||
},
|
||||
{
|
||||
n:4, color:'#7c3aed',
|
||||
title:'Сторож Первого Закона',
|
||||
tag:'§ 14',
|
||||
q:'Газ получил $Q = 800$ Дж и совершил работу $A = 500$ Дж. Найди $\\Delta U$ в Дж.',
|
||||
ans:300, tol:10,
|
||||
hint:'$\\Delta U = Q - A = 800 - 500 = 300$ Дж.'
|
||||
},
|
||||
{
|
||||
n:5, color:'#dc2626',
|
||||
title:'Магистр Термодинамики',
|
||||
tag:'§ 15',
|
||||
q:'Двигатель Карно: $T_1 = 500$ К, $T_2 = 300$ К, $Q_1 = 1000$ Дж за цикл. Найди работу $A$ в Дж.',
|
||||
ans:400, tol:15,
|
||||
hint:'$\\eta = 1 - 300/500 = 0{,}4$. $A = \\eta Q_1 = 0{,}4 \\cdot 1000 = 400$ Дж.'
|
||||
},
|
||||
];
|
||||
|
||||
const cont = document.getElementById('ch2-bosses-container');
|
||||
const STATE_KEY = 'physics10_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)=>{
|
||||
return '<div class="boss-card" id="boss2-'+b.n+'-card" style="padding:16px;background:var(--card);border-radius:12px;border:2px solid '+b.color+';margin-bottom:14px">'
|
||||
+'<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap">'
|
||||
+'<svg viewBox="0 0 24 24" fill="none" stroke="'+b.color+'" stroke-width="2.2" style="width:28px;height:28px;flex-shrink:0"><polygon points="12,2 22,20 2,20"/></svg>'
|
||||
+'<div style="font-family:\'Unbounded\',sans-serif;font-weight:800;color:'+b.color+';font-size:1.05rem">Босс '+b.n+': '+b.title+'</div>'
|
||||
+'<div style="margin-left:auto;font-size:.78rem;color:var(--muted);padding:3px 8px;background:var(--sec-acc-soft);border-radius:6px">'+b.tag+'</div>'
|
||||
+'</div>'
|
||||
+'<div class="boss-q" id="boss2-'+b.n+'-q" style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:1rem;line-height:1.5;margin-bottom:10px">'+b.q+'</div>'
|
||||
+'<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">'
|
||||
+'<span style="font-family:\'JetBrains Mono\',monospace;font-size:.92rem">ответ =</span>'
|
||||
+'<input type="number" id="boss2-'+b.n+'-ans" class="tinp" style="width:130px;text-align:center" step="0.01" placeholder="число">'
|
||||
+'<button class="btn primary" id="boss2-'+b.n+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атаковать</button>'
|
||||
+'<button class="btn" id="boss2-'+b.n+'-hint">Подсказка</button>'
|
||||
+'</div>'
|
||||
+'<div class="feedback" id="boss2-'+b.n+'-fb"></div>'
|
||||
+'</div>';
|
||||
}).join('');
|
||||
renderMath(cont);
|
||||
|
||||
function refreshOverall(){
|
||||
const won = BOSS_STATE.filter(s => s.defeated).length;
|
||||
const txt = document.getElementById('ch2-boss-overall');
|
||||
const fill = document.getElementById('ch2-boss-overall-fill');
|
||||
if(txt) txt.textContent = won + ' / ' + BOSSES.length + ' боссов побеждено';
|
||||
if(fill) fill.style.width = (won * 100 / BOSSES.length) + '%';
|
||||
if(won >= BOSSES.length){
|
||||
const reward = document.getElementById('ch2-final-reward');
|
||||
if(reward && reward.style.display === 'none'){
|
||||
reward.style.display = 'block';
|
||||
if(!STATE.achievements.has('ch2_done')){
|
||||
achievement('ch2_done','Мастер термодинамики');
|
||||
addXp(50, 'ch2-bonus');
|
||||
bumpProgress('final2', 30);
|
||||
if(window.confetti){ try{ confetti(); }catch(e){} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOSSES.forEach((b, idx)=>{
|
||||
const card = document.getElementById('boss2-'+b.n+'-card');
|
||||
const goBtn = document.getElementById('boss2-'+b.n+'-go');
|
||||
const hintBtn = document.getElementById('boss2-'+b.n+'-hint');
|
||||
const ansInp = document.getElementById('boss2-'+b.n+'-ans');
|
||||
if(BOSS_STATE[idx].defeated){
|
||||
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
|
||||
card.classList.add('glow');
|
||||
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.textContent = '✓ Повержен';
|
||||
ansInp.disabled = true;
|
||||
}
|
||||
goBtn.addEventListener('click', ()=>{
|
||||
if(BOSS_STATE[idx].defeated) return;
|
||||
const fb = document.getElementById('boss2-'+b.n+'-fb');
|
||||
const raw = ansInp.value.replace(',', '.');
|
||||
const val = parseFloat(raw);
|
||||
if(isNaN(val)){ feedback(fb, false, '✗ Введи число.'); return; }
|
||||
const tol = (typeof b.tol === 'number') ? b.tol : Math.max(0.05 * Math.abs(b.ans), 0.05);
|
||||
if(Math.abs(val - b.ans) < tol + 0.001){
|
||||
BOSS_STATE[idx].defeated = true; saveBosses();
|
||||
feedback(fb, true, '✓ Босс '+b.n+' повержен! +10 XP. '+b.hint);
|
||||
addXp(10, 'boss-ch2-'+b.n);
|
||||
bumpProgress('final2', 18);
|
||||
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.textContent = '✓ Повержен';
|
||||
ansInp.disabled = true;
|
||||
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
|
||||
card.classList.add('glow','pulse');
|
||||
setTimeout(()=>card.classList.remove('pulse'), 900);
|
||||
refreshOverall();
|
||||
} else {
|
||||
feedback(fb, false, '✗ Промах. Попробуй ещё. Подсказка доступна.');
|
||||
}
|
||||
});
|
||||
hintBtn.addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('boss2-'+b.n+'-fb');
|
||||
fb.className = 'feedback ok';
|
||||
fb.innerHTML = '<b>Подсказка:</b> '+b.hint;
|
||||
fb.style.display = 'block';
|
||||
fb.style.background = 'var(--warn-bg)';
|
||||
fb.style.color = '#92400e';
|
||||
fb.style.borderLeftColor = 'var(--warn)';
|
||||
renderMath(fb);
|
||||
});
|
||||
ansInp.addEventListener('keydown', e=>{ if(e.key === 'Enter') goBtn.click(); });
|
||||
});
|
||||
|
||||
refreshOverall();
|
||||
}
|
||||
|
||||
/* ===== Search ===== */
|
||||
|
||||
Reference in New Issue
Block a user