feat(phys8 ch1): Phase 1 Wave 5 — §10 испарение + §11 кипение + Финал главы 1
§10 Испарение: - 3 теории: что такое испарение, факторы скорости, примеры - IV-1: симуляция с 28 частицами над поверхностью жидкости — испарение зависит от T (slider) и ветра (slider); испарившиеся становятся серыми и улетают вверх+вправо, при пропадании за края «возвращаются»; счётчик испарившихся; солнце + стрелки ветра - IV-2: викторина 6 пар сравнения - IV-3: DnD 8 факторов на ускоряет/замедляет - IV-4: 6 MCQ §11 Кипение + Q=Lm: - 3 теории: кипение, формула Lm, зависимость T_кип от давления - IV-1: ГЛАВНЫЙ ВИЗУАЛ — полный график T(t) «лёд→вода→пар» с 5 цветными сегментами и 2 плато (плавление 0°C, кипение 100°C), длительности пропорциональны реальным q_i / Q_total - IV-2: калькулятор Q=Lm с переводом в кВт·ч и эквивалент нагрева воды - IV-3: DnD 8 процессов на 3 категории (плавление/испарение/конденсация) - IV-4: 6 числовых задач (включая полный цикл лёд→пар) ФИНАЛ ГЛАВЫ 1: - Шпаргалка 6 формул и понятий - 7 интегрированных боссов: расчёт ΔT, смешивание, плавление, кипение, цепочка нагрева, КПД котла, полный цикл лёд→пар - Прогресс-бар победы + ачивка «Мастер теплоты» (+50 XP) при 7/7 - Per-boss XP (+10) и hint-кнопки ACH_LABELS дополнен thermal_master. Глава 1 «Тепловые явления» завершена: 11 § + финал = 12 секций, все с симуляциями, калькуляторами, DnD и тренажёрами. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -267,7 +267,8 @@ const ACH_LABELS = {
|
||||
p9_done:"Удельная теплота плавления и кристаллизации освоен!",
|
||||
p10_done:"Испарение жидкостей. Факторы, влияющие на скорость испарения освоен!",
|
||||
p11_done:"Кипение жидкостей. Удельная теплота парообразования освоен!",
|
||||
ch1_done:"Глава 1 пройдена!"
|
||||
ch1_done:"Глава 1 пройдена!",
|
||||
thermal_master:"Мастер теплоты — все боссы главы 1 повержены!"
|
||||
};
|
||||
|
||||
const SIDEBARS = {
|
||||
@@ -337,9 +338,24 @@ const SIDEBARS = {
|
||||
["железо","$\\lambda = 2{,}7 \\cdot 10^5$"],
|
||||
["Баланс","$Q_{нагр} + Q_{пл} + Q_{нагр.ж}$"]
|
||||
]},
|
||||
p10:{title:"Шпаргалка § 10",rows:[["В разработке","Phase 1 Wave 5"]]},
|
||||
p11:{title:"Шпаргалка § 11",rows:[["В разработке","Phase 1 Wave 5"]]},
|
||||
final1:{title:"Шпаргалка ★",rows:[["В разработке","Phase 1 Wave 5"]]}
|
||||
p10:{title:"Шпаргалка § 10",rows:[
|
||||
["Испарение","с поверхности при любой $T$"],
|
||||
["Скорость зависит от","$T$, $S$, ветра, рода жидкости"],
|
||||
["Жидкость","охлаждается (уходят быстрые)"],
|
||||
["Конденсация","пар $\\to$ жидкость, тепло выделяется"]
|
||||
]},
|
||||
p11:{title:"Шпаргалка § 11",rows:[
|
||||
["Кипение","по всему объёму при $T = T_{кип}$"],
|
||||
["Формула","$Q = Lm$"],
|
||||
["$L$ для воды","$2{,}26 \\cdot 10^6$ Дж/кг"],
|
||||
["$T_{кип}$ воды","100 °C (при 1 атм)"],
|
||||
["Давление $\\downarrow$","$T_{кип} \\downarrow$ (на горе)"]
|
||||
]},
|
||||
final1:{title:"Финал главы 1",rows:[
|
||||
["Формулы","$Q = cm\\Delta T$, $Q = qm$, $Q = \\lambda m$, $Q = Lm$"],
|
||||
["3 вида теплопередачи","проводность, конвекция, излучение"],
|
||||
["Награда","+50 XP + «Мастер теплоты»"]
|
||||
]}
|
||||
};
|
||||
|
||||
const TIPS=[
|
||||
@@ -352,9 +368,9 @@ const TIPS=[
|
||||
{sec:'p7',html:"При сгорании 1 кг топлива выделяется $q$ Дж энергии. Полное выделение: $Q = q m$. У бензина $q$ в 4,5 раза больше, чем у дров, — поэтому литр бензина греет дольше, чем литр дров."},
|
||||
{sec:'p8',html:"Пока лёд плавится, температура смеси «лёд + вода» сидит на 0 °C — даже если плита продолжает греть. На графике $T(t)$ это видно как горизонтальная площадка (плато)."},
|
||||
{sec:'p9',html:"Чтобы расплавить 1 кг льда (без нагрева!), нужно $\\lambda = 334$ кДж. Это столько же, сколько на нагрев той же массы воды от 0 до 80 °C. Поэтому лёд — хороший «холодильник»."},
|
||||
{sec:'p10',html:"Параграф § 10 будет реализован в Phase 1 Wave 5. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
|
||||
{sec:'p11',html:"Параграф § 11 будет реализован в Phase 1 Wave 5. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."},
|
||||
{sec:'final1',html:"Параграф ★ будет реализован в Phase 1 Wave 5. Используем хелперы из <code>phys.js</code> и <code>optics.js</code>."}
|
||||
{sec:'p10',html:"Лужа высыхает даже зимой — это испарение. Чем теплее и ветренее, тем быстрее. При этом сама жидкость <b>остывает</b>, потому что её покидают самые быстрые молекулы — вот почему мокрая рука зябнет."},
|
||||
{sec:'p11',html:"При кипении пузырьки пара образуются <b>внутри</b> жидкости. Чтобы перевести 1 кг воды (100 °C) в пар, нужно $L = 2{,}26 \\cdot 10^6$ Дж — это в 5,4 раза больше, чем на нагрев той же воды от 0 до 100 °C."},
|
||||
{sec:'final1',html:"Финал главы — синтез 11 параграфов. 7 интегрированных боссов на формулы $Q = cm\\Delta T$, $\\lambda m$, $Lm$, $qm$, баланс смешивания, КПД, цепные расчёты. За победу — +50 XP и ачивка «Мастер теплоты»."}
|
||||
];
|
||||
|
||||
const BUILDERS = {
|
||||
@@ -367,9 +383,9 @@ const BUILDERS = {
|
||||
p7: ()=>{ build_p7(); },
|
||||
p8: ()=>{ build_p8(); },
|
||||
p9: ()=>{ build_p9(); },
|
||||
p10: ()=>{ const box=document.getElementById('p10-body'); box.innerHTML = buildStub('p10', 'Испарение жидкостей. Факторы, влияющие на скорость испарения', 'Phase 1 Wave 5') + secNavFor('p10') + readButton('p10'); renderMath(box); wireReadBtn('p10'); },
|
||||
p11: ()=>{ const box=document.getElementById('p11-body'); box.innerHTML = buildStub('p11', 'Кипение жидкостей. Удельная теплота парообразования', 'Phase 1 Wave 5') + secNavFor('p11') + readButton('p11'); renderMath(box); wireReadBtn('p11'); },
|
||||
final1: ()=>{ const box=document.getElementById('final1-body'); box.innerHTML = buildStub('final1', 'Финал главы', 'Phase 1 Wave 5') + secNavFor('final1') + readButton('final1'); renderMath(box); wireReadBtn('final1'); }
|
||||
p10: ()=>{ build_p10(); },
|
||||
p11: ()=>{ build_p11(); },
|
||||
final1: ()=>{ build_final1(); }
|
||||
};
|
||||
|
||||
function calcLevel(xp){ return Math.floor(Math.sqrt((xp||0)/100))+1; }
|
||||
@@ -2968,6 +2984,592 @@ function _initP9_tasks(){
|
||||
render();
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
PHASE 1 · WAVE 5 — §10, §11, FINAL 1
|
||||
====================================================================== */
|
||||
|
||||
/* ======== §10 — Испарение жидкостей ======== */
|
||||
function build_p10(){
|
||||
const box = document.getElementById('p10-body');
|
||||
let h = '';
|
||||
|
||||
h += makeCard('theory', 'Что такое испарение', '§ 10.1',
|
||||
'<p><b>Испарение</b> — переход жидкости в пар <b>с поверхности</b>. Идёт при любой температуре (даже при 0 °C, даже зимой).</p>'
|
||||
+'<p>Механизм: молекулы у поверхности движутся хаотически. Самые быстрые из них преодолевают притяжение остальных и улетают в воздух — становятся паром.</p>'
|
||||
+'<p>Поэтому при испарении жидкость <b>остывает</b> — её покидают наиболее «горячие» молекулы, средняя $E_k$ оставшихся падает.</p>'
|
||||
);
|
||||
h += makeCard('rule', 'От чего зависит скорость испарения', '§ 10.2',
|
||||
'<ol style="padding-left:20px;margin:6px 0">'
|
||||
+'<li><b>Температура</b>: чем выше $T$, тем больше быстрых молекул, испарение интенсивнее.</li>'
|
||||
+'<li><b>Площадь свободной поверхности</b>: чем больше $S$, тем больше «выходов».</li>'
|
||||
+'<li><b>Движение воздуха</b> (ветер): унося пар, ветер не даёт ему вернуться обратно.</li>'
|
||||
+'<li><b>Род жидкости</b>: эфир, спирт испаряются быстро; вода медленнее; ртуть — почти не испаряется.</li>'
|
||||
+'</ol>'
|
||||
+'<p>Обратный процесс — <b>конденсация</b> (пар $\\to$ жидкость), при ней теплота <b>выделяется</b>.</p>'
|
||||
);
|
||||
h += makeCard('example', 'Где встречается', '§ 10.3',
|
||||
'<ul style="padding-left:20px;margin:6px 0">'
|
||||
+'<li>Пот охлаждает тело — испаряясь, он уносит тепло.</li>'
|
||||
+'<li>Мокрая рука или одежда зябнет на ветру.</li>'
|
||||
+'<li>Лужа на ветреной площади сохнет быстрее, чем в безветренной комнате.</li>'
|
||||
+'<li>Бельё сохнет быстрее на солнце и на ветру.</li>'
|
||||
+'<li>Зимой бельё, вынесенное на мороз, тоже постепенно высыхает (возгонка).</li>'
|
||||
+'</ul>'
|
||||
);
|
||||
|
||||
/* IV1 — симуляция «молекулы покидают поверхность» */
|
||||
h += '<div class="wg">'
|
||||
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Испарение с поверхности</div></div>'
|
||||
+'<div class="wg-help">Меняй температуру и ветер — наблюдай, как быстро молекулы покидают жидкость и уносятся в воздух.</div>'
|
||||
+'<div class="sliders" style="margin-bottom:10px">'
|
||||
+'<label>$T$, °C: <b id="p10-tv">40</b><input type="range" id="p10-t" min="5" max="90" step="5" value="40"></label>'
|
||||
+'<label>Ветер: <b id="p10-wv">умеренный</b><input type="range" id="p10-w" min="0" max="3" step="1" value="1"></label>'
|
||||
+'</div>'
|
||||
+'<svg id="p10-sim" viewBox="0 0 460 220" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
|
||||
+'<div class="score-display" style="margin-top:10px"><span>Испарилось молекул: <b id="p10-evap">0</b></span><span>Скорость: <b id="p10-rate">средняя</b></span></div>'
|
||||
+'</div>';
|
||||
|
||||
/* IV2 — викторина «лужа высохнет быстрее…» */
|
||||
h += '<div class="wg">'
|
||||
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Где жидкость испарится быстрее?</div></div>'
|
||||
+'<div class="wg-help">Сравни две ситуации.</div>'
|
||||
+'<div id="p10-quiz"></div>'
|
||||
+'<div class="actions"><button class="btn" id="p10-quiz-next">Следующий</button></div>'
|
||||
+'<div class="score-display" style="margin-top:10px"><span>Раунд: <b id="p10-quiz-r">1</b> / 6</span><span>Правильно: <b id="p10-quiz-ok">0</b></span></div>'
|
||||
+'</div>';
|
||||
|
||||
/* IV3 — DnD факторы */
|
||||
h += '<div class="wg">'
|
||||
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Ускоряет или замедляет испарение?</div></div>'
|
||||
+'<div class="wg-help">Перетащи факторы в две колонки.</div>'
|
||||
+'<div id="p10-dnd-pool"></div>'
|
||||
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px">'
|
||||
+'<div class="drop-box"><h5>Ускоряет</h5><div class="drop-items" data-cat="up"></div></div>'
|
||||
+'<div class="drop-box"><h5>Замедляет</h5><div class="drop-items" data-cat="dn"></div></div>'
|
||||
+'</div>'
|
||||
+'<div class="actions"><button class="btn primary" id="p10-dnd-check">Проверить</button><button class="btn" id="p10-dnd-reset">Сброс</button></div>'
|
||||
+'<div class="feedback" id="p10-dnd-fb"></div>'
|
||||
+'</div>';
|
||||
|
||||
/* IV4 — MCQ */
|
||||
h += '<div class="wg">'
|
||||
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 6 вопросов</div></div>'
|
||||
+'<div class="wg-help">4+ правильных — +15 XP.</div>'
|
||||
+'<div id="p10-mcq"></div>'
|
||||
+'<div class="score-display" style="margin-top:10px"><span>Вопрос: <b id="p10-mcq-i">1</b> / 6</span><span>Правильно: <b id="p10-mcq-ok">0</b></span></div>'
|
||||
+'</div>';
|
||||
|
||||
box.innerHTML = h + secNavFor('p10') + readButton('p10');
|
||||
renderMath(box);
|
||||
wireReadBtn('p10');
|
||||
|
||||
_initP10_sim();
|
||||
_initP10_quiz();
|
||||
_initP10_dnd();
|
||||
_initP10_mcq();
|
||||
}
|
||||
|
||||
function _initP10_sim(){
|
||||
_killSim('p10sim');
|
||||
const svg = document.getElementById('p10-sim'); if(!svg) return;
|
||||
const W = 460, H = 220;
|
||||
/* жидкость в нижней половине, частицы вылетают вверх */
|
||||
const liqY = 130;
|
||||
const N = 28;
|
||||
const ps = [];
|
||||
for(let i=0;i<N;i++) ps.push({ x: 80+Math.random()*300, y: liqY+10+Math.random()*70, vx: 0, vy: 0, evap: false });
|
||||
let T = 40, wind = 1;
|
||||
let evapCount = 0;
|
||||
function tick(){
|
||||
if(!_isVisible('p10')){ _SIMS.p10sim.raf = requestAnimationFrame(tick); return; }
|
||||
/* вероятность испарения для частиц у поверхности */
|
||||
const pEvap = (T / 90) * 0.018; /* при 90°C около 1.8% за тик */
|
||||
for(const p of ps){
|
||||
if(p.evap){
|
||||
p.x += p.vx; p.y += p.vy;
|
||||
p.vx += wind * 0.04;
|
||||
if(p.x > W + 10 || p.y < -10){
|
||||
/* "вернуть" молекулу обратно */
|
||||
p.x = 80 + Math.random()*300; p.y = liqY + 10 + Math.random()*70;
|
||||
p.vx = 0; p.vy = 0; p.evap = false;
|
||||
}
|
||||
} else {
|
||||
/* у поверхности (y близок к liqY) есть шанс испариться */
|
||||
if(p.y < liqY + 14 && Math.random() < pEvap){
|
||||
p.evap = true;
|
||||
p.vy = -1.0 - Math.random()*0.6;
|
||||
p.vx = (Math.random() - 0.4) * 0.5 + wind*0.2;
|
||||
evapCount++;
|
||||
} else {
|
||||
/* небольшое броуновское движение */
|
||||
p.x += (Math.random()-0.5)*0.6;
|
||||
p.y += (Math.random()-0.5)*0.6;
|
||||
if(p.x < 75) p.x = 75; if(p.x > 385) p.x = 385;
|
||||
if(p.y < liqY + 6) p.y = liqY + 6;
|
||||
if(p.y > 200) p.y = 200;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* draw */
|
||||
let s = '';
|
||||
/* сосуд + жидкость */
|
||||
s += '<rect x="60" y="125" width="340" height="85" fill="#bfdbfe" stroke="#0f172a" stroke-width="2" rx="4"/>';
|
||||
s += '<line x1="60" y1="'+liqY+'" x2="400" y2="'+liqY+'" stroke="#1d4ed8" stroke-width="1.6"/>';
|
||||
/* небо */
|
||||
s += '<rect x="0" y="0" width="'+W+'" height="125" fill="#f0f9ff"/>';
|
||||
/* солнце-температура */
|
||||
const tCol = window.PHYS.tempColor(T, 5, 90);
|
||||
s += '<circle cx="420" cy="30" r="16" fill="'+tCol+'" stroke="#0f172a" stroke-width="1.4"/>';
|
||||
s += '<text x="420" y="34" text-anchor="middle" font-family="Inter,sans-serif" font-size="11" font-weight="700" fill="#fff">'+T+'°</text>';
|
||||
/* ветер-стрелки */
|
||||
if(wind > 0){
|
||||
for(let i=0;i<wind;i++){
|
||||
const wy = 50 + i*15;
|
||||
s += window.PHYS.drawArrow(20, wy, 80, wy, '#0891b2', 1.6, 8);
|
||||
}
|
||||
}
|
||||
/* частицы */
|
||||
for(const p of ps){
|
||||
const col = p.evap ? '#94a3b8' : '#3b82f6';
|
||||
s += '<circle cx="'+p.x.toFixed(1)+'" cy="'+p.y.toFixed(1)+'" r="3.6" fill="'+col+'" stroke="#0f172a" stroke-width="0.4"/>';
|
||||
}
|
||||
svg.innerHTML = s;
|
||||
_SIMS.p10sim.raf = requestAnimationFrame(tick);
|
||||
}
|
||||
_SIMS.p10sim = { raf: 0 };
|
||||
_SIMS.p10sim.raf = requestAnimationFrame(tick);
|
||||
|
||||
function update(){
|
||||
T = +document.getElementById('p10-t').value;
|
||||
wind = +document.getElementById('p10-w').value;
|
||||
document.getElementById('p10-tv').textContent = T;
|
||||
document.getElementById('p10-wv').textContent = ['нет','слабый','умеренный','сильный'][wind];
|
||||
const rate = T*0.4 + wind*5;
|
||||
let rateLabel = 'низкая';
|
||||
if(rate > 25) rateLabel = 'высокая';
|
||||
else if(rate > 15) rateLabel = 'средняя';
|
||||
document.getElementById('p10-rate').textContent = rateLabel;
|
||||
document.getElementById('p10-evap').textContent = evapCount;
|
||||
}
|
||||
document.getElementById('p10-t').addEventListener('input', update);
|
||||
document.getElementById('p10-w').addEventListener('input', update);
|
||||
setInterval(()=>{ document.getElementById('p10-evap').textContent = evapCount; }, 500);
|
||||
update();
|
||||
}
|
||||
|
||||
function _initP10_quiz(){
|
||||
const QS = [
|
||||
{A:'лужа в безветренной комнате при 20 °C', B:'лужа на ветреной площади при 20 °C', ans:'B', why:'Ветер ускоряет испарение, унося пар.'},
|
||||
{A:'вода в стакане', B:'та же вода, разлитая по тарелке', ans:'B', why:'Больше площадь свободной поверхности — быстрее испарение.'},
|
||||
{A:'мокрая ткань на 20 °C', B:'та же ткань на 5 °C', ans:'A', why:'Выше $T$ — больше быстрых молекул.'},
|
||||
{A:'вода', B:'эфир (при той же $T$)', ans:'B', why:'У эфира слабее связи между молекулами, он испаряется в десятки раз быстрее воды.'},
|
||||
{A:'бельё в подвале', B:'бельё на солнце и ветру', ans:'B', why:'Солнечное излучение нагревает + ветер уносит пар.'},
|
||||
{A:'мокрая рука на воздухе', B:'мокрая рука в воде', ans:'A', why:'В воде испарения почти нет — нет свободной поверхности «жидкость-воздух».'}
|
||||
];
|
||||
let i = 0, ok = 0;
|
||||
function render(){
|
||||
const q = QS[i]; const wrap = document.getElementById('p10-quiz'); if(!wrap) return;
|
||||
wrap.innerHTML =
|
||||
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:8px">'
|
||||
+'<button class="btn" data-pick="A" style="padding:14px;text-align:left"><b>A.</b> '+q.A+'</button>'
|
||||
+'<button class="btn" data-pick="B" style="padding:14px;text-align:left"><b>B.</b> '+q.B+'</button>'
|
||||
+'</div>'
|
||||
+'<div class="feedback" id="p10-quiz-fb"></div>';
|
||||
document.getElementById('p10-quiz-r').textContent = (i+1);
|
||||
document.getElementById('p10-quiz-ok').textContent = ok;
|
||||
wrap.querySelectorAll('[data-pick]').forEach(btn=>{
|
||||
btn.addEventListener('click', ()=>{
|
||||
if(btn.disabled) return; wrap.querySelectorAll('[data-pick]').forEach(b=>b.disabled=true);
|
||||
const fb = document.getElementById('p10-quiz-fb');
|
||||
if(btn.dataset.pick === q.ans){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ Верно. '+q.why; addXp(3,'p10-quiz'); bumpProgress('p10', 4); }
|
||||
else { fb.className='feedback fail'; fb.innerHTML='✗ Не то. '+q.why; }
|
||||
document.getElementById('p10-quiz-ok').textContent = ok;
|
||||
});
|
||||
});
|
||||
}
|
||||
document.getElementById('p10-quiz-next').addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
|
||||
render();
|
||||
}
|
||||
|
||||
function _initP10_dnd(){
|
||||
const items = [
|
||||
{id:'up_t', cat:'up', html:'нагрев'},
|
||||
{id:'up_s', cat:'up', html:'большая площадь'},
|
||||
{id:'up_w', cat:'up', html:'ветер'},
|
||||
{id:'up_e', cat:'up', html:'эфир вместо воды'},
|
||||
{id:'dn_t', cat:'dn', html:'охлаждение'},
|
||||
{id:'dn_s', cat:'dn', html:'узкое горлышко'},
|
||||
{id:'dn_w', cat:'dn', html:'закрытая банка'},
|
||||
{id:'dn_m', cat:'dn', html:'ртуть вместо воды'}
|
||||
];
|
||||
const dnd = setupSorter({ poolId:'p10-dnd-pool', scopeSelector:'#sec-p10', cats:['up','dn'], items, columnLayout:false });
|
||||
document.getElementById('p10-dnd-check').addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('p10-dnd-fb');
|
||||
let wrong = 0; items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wrong++; });
|
||||
if(wrong===0){ fb.className='feedback ok'; fb.innerHTML='✓ Идеально! +15 XP. Четыре фактора: T, S, ветер, род жидкости.'; addXp(15,'p10-dnd'); bumpProgress('p10', 20); }
|
||||
else { fb.className='feedback fail'; fb.innerHTML='✗ Ошибок: '+wrong+'. Подсказка: ртуть и закрытая банка — это блокирует испарение.'; }
|
||||
});
|
||||
document.getElementById('p10-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p10-dnd-fb'); fb.style.display='none'; });
|
||||
}
|
||||
|
||||
function _initP10_mcq(){
|
||||
const QS = [
|
||||
{q:'При испарении жидкость …', opts:['нагревается','остывает','не меняет $T$','становится твёрже'], ans:1, why:'Уходят самые быстрые молекулы — средняя $E_k$ остающихся падает.'},
|
||||
{q:'При какой температуре идёт испарение?', opts:['только при $T_{кип}$','при любой $T$','только зимой','только под солнцем'], ans:1, why:'Испарение идёт всегда, даже у льда (возгонка).'},
|
||||
{q:'Почему мокрая рука зябнет на ветру?', opts:['ветер холодный','вода уносит тепло','испарение охлаждает','оба B и C'], ans:3, why:'И вода уносит тепло, и испаряясь, охлаждает кожу.'},
|
||||
{q:'Где жидкость испаряется быстрее?', opts:['в стакане','в тарелке','в бутылке','в пробирке'], ans:1, why:'В тарелке самая большая площадь свободной поверхности.'},
|
||||
{q:'Что произойдёт, если над паром резко охладить?', opts:['он замёрзнет','он расширится','он конденсируется','ничего'], ans:2, why:'Конденсация — пар возвращается в жидкость, тепло выделяется.'},
|
||||
{q:'Когда лужа высохнет быстрее?', opts:['ночью','днём','в тумане','в подвале'], ans:1, why:'Днём — больше тепла, обычно ветер.'}
|
||||
];
|
||||
let i = 0, ok = 0, done = 0, awarded = false;
|
||||
function render(){
|
||||
const q = QS[i]; const wrap = document.getElementById('p10-mcq'); if(!wrap) return;
|
||||
let h = '<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Вопрос '+(i+1)+'.</b> '+q.q+'</div><div style="display:grid;grid-template-columns:1fr;gap:6px">';
|
||||
q.opts.forEach((opt,k)=>{ h += '<button class="btn" data-k="'+k+'" style="text-align:left;padding:10px 14px">'+String.fromCharCode(65+k)+'. '+opt+'</button>'; });
|
||||
h += '</div><div class="feedback" id="p10-mcq-fb"></div><div class="actions"><button class="btn" id="p10-mcq-next">Следующий</button></div>';
|
||||
wrap.innerHTML = h;
|
||||
document.getElementById('p10-mcq-i').textContent = (i+1);
|
||||
document.getElementById('p10-mcq-ok').textContent = ok;
|
||||
wrap.querySelectorAll('[data-k]').forEach(btn=>{
|
||||
btn.addEventListener('click', ()=>{
|
||||
if(btn.disabled) return; wrap.querySelectorAll('[data-k]').forEach(b=>b.disabled=true);
|
||||
const k = +btn.dataset.k; const fb = document.getElementById('p10-mcq-fb');
|
||||
if(k===q.ans){ ok++; done++; fb.className='feedback ok'; fb.innerHTML='✓ Верно. '+q.why; addXp(2,'p10-mcq'); bumpProgress('p10', 3); }
|
||||
else { done++; fb.className='feedback fail'; fb.innerHTML='✗ Не то. '+q.why; }
|
||||
document.getElementById('p10-mcq-ok').textContent = ok;
|
||||
renderMath(wrap);
|
||||
if(done >= QS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p10-mcq-fb'); wf.className='feedback ok'; wf.innerHTML='✓ +15 XP — тренажёр пройден ('+ok+'/'+QS.length+').'; addXp(15,'p10-mcq-bonus'); bumpProgress('p10', 15); }, 600); }
|
||||
});
|
||||
});
|
||||
const nb = document.getElementById('p10-mcq-next'); if(nb) nb.addEventListener('click', ()=>{ i=(i+1)%QS.length; render(); });
|
||||
renderMath(wrap);
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
/* ======== §11 — Кипение, удельная теплота парообразования ======== */
|
||||
function build_p11(){
|
||||
const box = document.getElementById('p11-body');
|
||||
let h = '';
|
||||
|
||||
h += makeCard('theory', 'Что такое кипение', '§ 11.1',
|
||||
'<p><b>Кипение</b> — переход жидкости в пар <b>по всему объёму</b> при определённой температуре $T_{кип}$.</p>'
|
||||
+'<p>В жидкости всегда есть растворённый воздух с пузырьками пара. При $T = T_{кип}$ давление пара в пузырьках становится равно атмосферному, пузырьки растут, всплывают и лопаются — это и есть кипение.</p>'
|
||||
+'<p>Во время кипения температура жидкости <b>не меняется</b>, как и при плавлении.</p>'
|
||||
);
|
||||
h += makeCard('rule', '$Q = Lm$ — удельная теплота парообразования', '§ 11.2',
|
||||
'<p>Чтобы перевести массу $m$ жидкости (при $T = T_{кип}$) в пар, нужно:</p>'
|
||||
+'<p style="text-align:center;margin:8px 0">$$Q = L\\,m$$</p>'
|
||||
+'<p>$L$ — удельная теплота парообразования, Дж/кг. Для воды:</p>'
|
||||
+'<p style="text-align:center"><code>L = 2,26 · 10<sup>6</sup> Дж/кг = 2260 кДж/кг</code></p>'
|
||||
+'<p>Это огромная величина — поэтому ожог паром опаснее ожога кипятком (пар конденсируется на коже и выделяет $L$).</p>'
|
||||
);
|
||||
h += makeCard('example', '$T_{кип}$ зависит от давления', '§ 11.3',
|
||||
'<ul style="padding-left:20px;margin:6px 0">'
|
||||
+'<li>При нормальном давлении (1 атм $= 10^5$ Па) вода кипит при $100$ °C.</li>'
|
||||
+'<li>На горе $T_{кип}$ ниже (давление меньше). На Эвересте — около $70$ °C.</li>'
|
||||
+'<li>В скороварке давление выше — $T_{кип}$ выше ($120$ °C), еда варится быстрее.</li>'
|
||||
+'<li>В вакууме вода кипит при комнатной температуре.</li>'
|
||||
+'</ul>'
|
||||
);
|
||||
|
||||
/* IV1 — главный визуал: график «лёд → вода → пар» */
|
||||
h += '<div class="wg">'
|
||||
+'<div class="wg-header"><span class="wg-badge">IV-1</span><div class="wg-title">Полный цикл «лёд → вода → пар»</div></div>'
|
||||
+'<div class="wg-help">Нагреваем 1 кг льда от -20 °C до пара при 100 °C. График $T(t)$ имеет <b>два плато</b>: плавление и кипение.</div>'
|
||||
+'<svg id="p11-sim" viewBox="0 0 460 280" style="width:100%;height:auto;background:#f8fafc;border-radius:9px;border:1px solid var(--border)"></svg>'
|
||||
+'<div class="score-display" style="margin-top:10px;flex-direction:column;align-items:flex-start;gap:3px">'
|
||||
+'<span><span style="color:#2563eb">■</span> 1. Нагрев льда</span>'
|
||||
+'<span><span style="color:#f59e0b">■</span> 2. Плавление при 0 °C ($\\lambda m$)</span>'
|
||||
+'<span><span style="color:#dc2626">■</span> 3. Нагрев воды</span>'
|
||||
+'<span><span style="color:#ef4444">■</span> 4. Кипение при 100 °C ($L m$) — самое длинное плато!</span>'
|
||||
+'<span><span style="color:#7c3aed">■</span> 5. Нагрев пара</span>'
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
|
||||
/* IV2 — калькулятор Q = Lm */
|
||||
h += '<div class="wg">'
|
||||
+'<div class="wg-header"><span class="wg-badge">IV-2</span><div class="wg-title">Калькулятор $Q = Lm$</div></div>'
|
||||
+'<div class="wg-help">Сколько энергии нужно, чтобы перевести жидкость в пар при $T = T_{кип}$?</div>'
|
||||
+'<div class="sliders" style="margin-bottom:10px">'
|
||||
+'<label>Жидкость: <select id="p11-liq" class="tinp" style="width:auto;padding:6px 10px;font-size:.92rem">'
|
||||
+'<option value="2.26e6">вода ($L = 2{,}26 \\cdot 10^6$)</option>'
|
||||
+'<option value="9.0e5">спирт ($L = 9{,}0 \\cdot 10^5$)</option>'
|
||||
+'<option value="3.0e5">эфир ($L = 3{,}0 \\cdot 10^5$)</option>'
|
||||
+'<option value="2.85e5">ртуть ($L = 2{,}85 \\cdot 10^5$)</option>'
|
||||
+'</select></label>'
|
||||
+'<label>$m$, кг: <b id="p11-mv">1.0</b><input type="range" id="p11-m" min="0.1" max="5" step="0.1" value="1"></label>'
|
||||
+'</div>'
|
||||
+'<div class="score-display" style="margin-top:8px;flex-direction:column;align-items:flex-start;gap:4px">'
|
||||
+'<span>$Q$ = <b id="p11-q">2.26 МДж</b> = <b id="p11-qkw">0.63</b> кВт·ч</span>'
|
||||
+'<span style="font-size:.84rem;color:var(--muted)">Эквивалент: нагрев <b id="p11-eqkg">6.7</b> кг воды от 20 до 100 °C.</span>'
|
||||
+'</div>'
|
||||
+'</div>';
|
||||
|
||||
/* IV3 — DnD «фазовый переход» */
|
||||
h += '<div class="wg">'
|
||||
+'<div class="wg-header"><span class="wg-badge">IV-3</span><div class="wg-title">Какой переход?</div></div>'
|
||||
+'<div class="wg-help">Распредели процессы по типам.</div>'
|
||||
+'<div id="p11-dnd-pool"></div>'
|
||||
+'<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-top:10px">'
|
||||
+'<div class="drop-box"><h5>Плавление</h5><div class="drop-items" data-cat="melt"></div></div>'
|
||||
+'<div class="drop-box"><h5>Испарение / кипение</h5><div class="drop-items" data-cat="evap"></div></div>'
|
||||
+'<div class="drop-box"><h5>Конденсация / кристаллизация</h5><div class="drop-items" data-cat="cond"></div></div>'
|
||||
+'</div>'
|
||||
+'<div class="actions"><button class="btn primary" id="p11-dnd-check">Проверить</button><button class="btn" id="p11-dnd-reset">Сброс</button></div>'
|
||||
+'<div class="feedback" id="p11-dnd-fb"></div>'
|
||||
+'</div>';
|
||||
|
||||
/* IV4 — расчётные задачи */
|
||||
h += '<div class="wg">'
|
||||
+'<div class="wg-header"><span class="wg-badge">IV-4</span><div class="wg-title">Тренажёр: 6 расчётных задач</div></div>'
|
||||
+'<div class="wg-help">4+ верных — +15 XP.</div>'
|
||||
+'<div id="p11-task"></div>'
|
||||
+'<div class="score-display" style="margin-top:10px"><span>Задача: <b id="p11-task-i">1</b> / 6</span><span>Правильно: <b id="p11-task-ok">0</b></span></div>'
|
||||
+'</div>';
|
||||
|
||||
box.innerHTML = h + secNavFor('p11') + readButton('p11');
|
||||
renderMath(box);
|
||||
wireReadBtn('p11');
|
||||
|
||||
_initP11_graph();
|
||||
_initP11_calc();
|
||||
_initP11_dnd();
|
||||
_initP11_tasks();
|
||||
}
|
||||
|
||||
function _initP11_graph(){
|
||||
const svg = document.getElementById('p11-sim'); if(!svg) return;
|
||||
/* строим график T(t) для 1 кг воды/льда/пара
|
||||
сегменты по времени:
|
||||
1) -20→0 (лёд): t = m·c_ice·20 / P
|
||||
2) плавление: t = m·λ / P
|
||||
3) 0→100 (вода): t = m·c_w·100 / P
|
||||
4) кипение: t = m·L / P
|
||||
5) пар после 100, нагрев на 80 К (с pp = 2010 для пара)
|
||||
Используем относительные времена. */
|
||||
const m = 1;
|
||||
const cIce=2100, lam=3.34e5, cW=4200, L=2.26e6, cSt=2010;
|
||||
const t1 = m*cIce*20; /* 42 000 */
|
||||
const t2 = m*lam; /* 334 000 */
|
||||
const t3 = m*cW*100; /* 420 000 */
|
||||
const t4 = m*L; /* 2 260 000 */
|
||||
const t5 = m*cSt*80; /* 160 800 */
|
||||
const T1 = 0, T2 = 100, T3 = 180;
|
||||
const tot = t1+t2+t3+t4+t5; /* нормируем к 10 */
|
||||
const sc = 10/tot;
|
||||
/* points */
|
||||
let cur = 0;
|
||||
const segs = [
|
||||
{ tStart: cur, tEnd: (cur+=t1*sc)*1, Tstart: -20, Tend: 0, col: '#2563eb', label:'лёд' },
|
||||
{ tStart: (cur), tEnd: (cur+=t2*sc), Tstart: 0, Tend: 0, col: '#f59e0b', label:'плавление' },
|
||||
{ tStart: (cur), tEnd: (cur+=t3*sc), Tstart: 0, Tend: 100, col: '#dc2626', label:'вода' },
|
||||
{ tStart: (cur), tEnd: (cur+=t4*sc), Tstart: 100, Tend: 100, col: '#ef4444', label:'кипение' },
|
||||
{ tStart: (cur), tEnd: (cur+=t5*sc), Tstart: 100, Tend: 180, col: '#7c3aed', label:'пар' }
|
||||
];
|
||||
const W=460, H=280, pad=36;
|
||||
const r = window.PHYS.phaseGraphTT(W, H, pad, segs.map(s=>({tStart:s.tStart,tEnd:s.tEnd,Tstart:s.Tstart,Tend:s.Tend})), 10, -40, 200);
|
||||
/* Раскрашиваем сегменты разными цветами поверх */
|
||||
let extra = '';
|
||||
/* Горизонталь 0 и 100 */
|
||||
extra += '<line x1="'+pad+'" y1="'+r.toY(0)+'" x2="'+(W-pad)+'" y2="'+r.toY(0)+'" stroke="#f59e0b" stroke-width="1" stroke-dasharray="3 3" opacity="0.6"/>';
|
||||
extra += '<line x1="'+pad+'" y1="'+r.toY(100)+'" x2="'+(W-pad)+'" y2="'+r.toY(100)+'" stroke="#ef4444" stroke-width="1" stroke-dasharray="3 3" opacity="0.6"/>';
|
||||
/* подписи 0 и 100 */
|
||||
extra += '<text x="'+(pad+4)+'" y="'+(r.toY(0)-4)+'" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#92400e">0 °C (плавление)</text>';
|
||||
extra += '<text x="'+(pad+4)+'" y="'+(r.toY(100)-4)+'" font-family="JetBrains Mono,monospace" font-size="11" font-weight="700" fill="#7f1d1d">100 °C (кипение)</text>';
|
||||
/* перерисовываем сегменты */
|
||||
for(const s of segs){
|
||||
extra += '<line x1="'+r.toX(s.tStart)+'" y1="'+r.toY(s.Tstart)+'" x2="'+r.toX(s.tEnd)+'" y2="'+r.toY(s.Tend)+'" stroke="'+s.col+'" stroke-width="3.5"/>';
|
||||
}
|
||||
svg.innerHTML = r.svg + extra;
|
||||
}
|
||||
|
||||
function _initP11_calc(){
|
||||
function update(){
|
||||
const L = +document.getElementById('p11-liq').value;
|
||||
const m = +document.getElementById('p11-m').value;
|
||||
document.getElementById('p11-mv').textContent = m.toFixed(1);
|
||||
const Q = L*m;
|
||||
document.getElementById('p11-q').textContent = (Q/1e6).toFixed(2)+' МДж';
|
||||
document.getElementById('p11-qkw').textContent = (Q/3.6e6).toFixed(2);
|
||||
/* эквивалент: нагрев воды от 20 до 100 (ΔT=80, c=4200) */
|
||||
document.getElementById('p11-eqkg').textContent = (Q/(4200*80)).toFixed(1);
|
||||
}
|
||||
document.getElementById('p11-liq').addEventListener('change', update);
|
||||
document.getElementById('p11-m').addEventListener('input', update);
|
||||
update();
|
||||
}
|
||||
|
||||
function _initP11_dnd(){
|
||||
const items = [
|
||||
{id:'a', cat:'melt', html:'лёд тает в стакане'},
|
||||
{id:'b', cat:'melt', html:'свинец становится жидким'},
|
||||
{id:'c', cat:'evap', html:'лужа высыхает'},
|
||||
{id:'d', cat:'evap', html:'кипящий чайник пускает пар'},
|
||||
{id:'e', cat:'evap', html:'спирт испаряется с кожи'},
|
||||
{id:'f', cat:'cond', html:'роса утром на траве'},
|
||||
{id:'g', cat:'cond', html:'пар оседает на холодном стекле'},
|
||||
{id:'h', cat:'cond', html:'жидкое золото застывает в слитки'}
|
||||
];
|
||||
const dnd = setupSorter({ poolId:'p11-dnd-pool', scopeSelector:'#sec-p11', cats:['melt','evap','cond'], items, columnLayout:false });
|
||||
document.getElementById('p11-dnd-check').addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('p11-dnd-fb');
|
||||
let wrong = 0; items.forEach(it=>{ if(dnd.placed[it.id] !== it.cat) wrong++; });
|
||||
if(wrong===0){ fb.className='feedback ok'; fb.innerHTML='✓ Идеально! +15 XP. Все фазовые переходы — это пары прямой и обратный.'; addXp(15,'p11-dnd'); bumpProgress('p11', 20); }
|
||||
else { fb.className='feedback fail'; fb.innerHTML='✗ Ошибок: '+wrong+'. Подсказка: роса — это пар → жидкость (конденсация).'; }
|
||||
});
|
||||
document.getElementById('p11-dnd-reset').addEventListener('click', ()=>{ dnd.reset(); const fb=document.getElementById('p11-dnd-fb'); fb.style.display='none'; });
|
||||
}
|
||||
|
||||
function _initP11_tasks(){
|
||||
const TASKS = [
|
||||
{q:'Сколько энергии (в МДж) нужно, чтобы превратить 0,5 кг воды (100 °C) в пар? ($L = 2{,}26 \\cdot 10^6$)', ans: 1.13, tol: 0.04, why:'$Q = Lm = 2{,}26\\cdot10^6 \\cdot 0{,}5 = 1{,}13\\cdot10^6$ Дж = $1{,}13$ МДж.'},
|
||||
{q:'Сколько кг воды можно превратить в пар, имея 11,3 МДж энергии?', ans: 5, tol: 0.1, why:'$m = Q/L = 1{,}13\\cdot10^7/(2{,}26\\cdot10^6) = 5$ кг.'},
|
||||
{q:'$Q_1$ — нагрев 1 кг воды от 20 до 100 °C. $Q_2$ — испарение этой воды. Найди отношение $Q_2/Q_1$ (целое число).', ans: 7, tol: 0.5, why:'$Q_1 = 4200 \\cdot 80 = 336$ кДж. $Q_2 = 2260$ кДж. $Q_2/Q_1 \\approx 6{,}7 \\approx 7$.'},
|
||||
{q:'Спирт массой 2 кг полностью испаряется. Сколько кДж нужно? ($L = 9 \\cdot 10^5$)', ans: 1800, tol: 30, why:'$Q = 9\\cdot10^5 \\cdot 2 = 1{,}8\\cdot10^6$ Дж = $1800$ кДж.'},
|
||||
{q:'При конденсации 100 г пара (100 °C) сколько кДж выделится?', ans: 226, tol: 6, why:'$Q = Lm = 2{,}26\\cdot10^6 \\cdot 0{,}1 = 2{,}26\\cdot10^5$ Дж = $226$ кДж.'},
|
||||
{q:'1 кг льда (-10 °C) превратить полностью в пар (100 °C). Сколько МДж нужно? ($c_л=2100$, $\\lambda=3{,}34\\cdot10^5$, $c_в=4200$, $L=2{,}26\\cdot10^6$)', ans: 3.04, tol: 0.06, why:'$Q_1=21\\,000$, $Q_2=334\\,000$, $Q_3=420\\,000$, $Q_4=2\\,260\\,000$. Итого $3{,}04$ МДж.'}
|
||||
];
|
||||
let i = 0, ok = 0, done = 0, awarded = false;
|
||||
function render(){
|
||||
const t = TASKS[i]; const wrap = document.getElementById('p11-task'); if(!wrap) return;
|
||||
wrap.innerHTML =
|
||||
'<div style="padding:10px 14px;background:rgba(15,23,42,.04);border-radius:9px;margin-bottom:10px;font-size:.95rem;line-height:1.5"><b>Задача '+(i+1)+'.</b> '+t.q+'</div>'
|
||||
+'<div class="boss-row"><input type="number" step="0.01" class="tinp" id="p11-task-inp" placeholder="число" style="width:140px">'
|
||||
+'<button class="btn primary" id="p11-task-go">Ответ</button>'
|
||||
+'<button class="btn" id="p11-task-hint">Подсказка</button>'
|
||||
+'<button class="btn" id="p11-task-next">Следующая</button></div>'
|
||||
+'<div class="boss-hint-txt" id="p11-task-hint-txt">'+t.why+'</div>'
|
||||
+'<div class="feedback" id="p11-task-fb"></div>';
|
||||
document.getElementById('p11-task-i').textContent = (i+1);
|
||||
document.getElementById('p11-task-ok').textContent = ok;
|
||||
document.getElementById('p11-task-go').addEventListener('click', ()=>{
|
||||
const v = parseFloat((document.getElementById('p11-task-inp').value || '').replace(',','.'));
|
||||
const fb = document.getElementById('p11-task-fb');
|
||||
if(isNaN(v)){ fb.className='feedback fail'; fb.innerHTML='Введи число.'; return; }
|
||||
done++;
|
||||
if(Math.abs(v - t.ans) < t.tol){ ok++; fb.className='feedback ok'; fb.innerHTML='✓ Верно! '+t.why; addXp(4,'p11-task'); bumpProgress('p11', 6); }
|
||||
else { fb.className='feedback fail'; fb.innerHTML='✗ Не то. Правильный ответ: '+t.ans+'. '+t.why; }
|
||||
document.getElementById('p11-task-ok').textContent = ok;
|
||||
renderMath(wrap);
|
||||
if(done >= TASKS.length && !awarded && ok >= 4){ awarded = true; setTimeout(()=>{ const wf=document.getElementById('p11-task-fb'); wf.className='feedback ok'; wf.innerHTML='✓ +15 XP — расчёты сданы ('+ok+'/'+TASKS.length+').'; addXp(15,'p11-task-bonus'); bumpProgress('p11', 15); }, 600); }
|
||||
});
|
||||
document.getElementById('p11-task-hint').addEventListener('click', ()=>{ document.getElementById('p11-task-hint-txt').classList.toggle('show'); });
|
||||
document.getElementById('p11-task-next').addEventListener('click', ()=>{ i=(i+1)%TASKS.length; render(); });
|
||||
renderMath(wrap);
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
/* ======== ФИНАЛ ГЛАВЫ 1 ======== */
|
||||
function build_final1(){
|
||||
const box = document.getElementById('final1-body');
|
||||
let h = '';
|
||||
|
||||
/* Шпаргалка главы */
|
||||
h += '<div class="card" style="background:linear-gradient(135deg,var(--sec-acc-soft),var(--card));border:1.5px solid var(--sec-acc)">'
|
||||
+'<div class="card-header"><div class="card-icon rule">'+ICONS.rule+'</div><div class="card-title">Шпаргалка главы 1</div></div>'
|
||||
+'<div class="card-body" style="display:grid;grid-template-columns:1fr 1fr;gap:14px">'
|
||||
+'<div><b>Тепло</b> при изменении $T$: $Q = c m \\Delta T$<br><i>c — Дж/(кг·К). У воды 4200.</i></div>'
|
||||
+'<div><b>Тепло</b> при сгорании: $Q = q m$<br><i>q — Дж/кг. У бензина 4{,}6·10⁷.</i></div>'
|
||||
+'<div><b>Тепло</b> на плавление: $Q = \\lambda m$<br><i>λ — Дж/кг. У льда 3{,}34·10⁵.</i></div>'
|
||||
+'<div><b>Тепло</b> на парообразование: $Q = L m$<br><i>L — Дж/кг. У воды 2{,}26·10⁶.</i></div>'
|
||||
+'<div><b>Баланс</b>: $Q_{отд} = Q_{пол}$</div>'
|
||||
+'<div><b>3 вида теплопередачи</b>: проводность, конвекция, излучение</div>'
|
||||
+'</div></div>';
|
||||
|
||||
/* 7 интегрированных боссов */
|
||||
const BOSSES = [
|
||||
{n:1, title:'Нагрев + расчёт', q:'На сколько градусов нагреется 2 кг алюминия при подведении 92 кДж? ($c = 920$)', hint:'$\\Delta T = Q/(cm) = 92\\,000/(920 \\cdot 2) = 50$ К.', ans:50, tol:1, step:'1'},
|
||||
{n:2, title:'Смешивание воды', q:'Смешали 2 кг воды при $80$ °C и 3 кг воды при $20$ °C. Итоговая $T$ (°C)?', hint:'$T = (2\\cdot80 + 3\\cdot20)/5 = 220/5 = 44$ °C.', ans:44, tol:0.5, step:'1'},
|
||||
{n:3, title:'Плавление', q:'Сколько кДж нужно, чтобы расплавить 3 кг льда при 0 °C? ($\\lambda = 3{,}34\\cdot10^5$)', hint:'$Q = \\lambda m = 3{,}34\\cdot10^5 \\cdot 3 = 1{,}002\\cdot10^6$ Дж = $1002$ кДж.', ans:1002, tol:10, step:'1'},
|
||||
{n:4, title:'Кипение', q:'1 кг воды при 100 °C полностью испарили. Сколько МДж энергии затрачено? ($L = 2{,}26\\cdot10^6$)', hint:'$Q = Lm = 2{,}26$ МДж.', ans:2.26, tol:0.05, step:'0.01'},
|
||||
{n:5, title:'Цепная задача', q:'1 кг льда при -10 °C довели до воды при 50 °C. Сколько кДж? ($c_л=2100$, $\\lambda=3{,}34\\cdot10^5$, $c_в=4200$)', hint:'$Q_1 = 21\\,000$, $Q_2 = 334\\,000$, $Q_3 = 210\\,000$, итого $565\\,000$ Дж = $565$ кДж.', ans:565, tol:12, step:'1'},
|
||||
{n:6, title:'Топливо + КПД', q:'Котёл с $\\eta = 60\\%$ сжёг 5 кг дров. Какая полезная энергия в МДж? ($q = 10^7$)', hint:'$Q_{сгор} = 5\\cdot10^7 = 50$ МДж. $Q_{пол} = 0{,}6 \\cdot 50 = 30$ МДж.', ans:30, tol:0.5, step:'1'},
|
||||
{n:7, title:'Полный цикл', q:'Сколько МДж нужно, чтобы 0,5 кг льда (0 °C) превратить в пар (100 °C)? ($\\lambda = 3{,}34\\cdot10^5$, $c_в = 4200$, $L = 2{,}26\\cdot10^6$). Округли до сотых.', hint:'$Q_{пл}=167\\,000$, $Q_{нагр}=210\\,000$, $Q_{исп}=1\\,130\\,000$. Итого $1{,}51$ МДж.', ans:1.51, tol:0.04, step:'0.01'}
|
||||
];
|
||||
|
||||
h += '<div class="card" style="margin-top:14px"><div class="card-header"><div class="card-icon example">'+ICONS.example+'</div><div class="card-title">Боссы главы 1</div></div><div class="card-body">'
|
||||
+'<div class="boss-overall-bar" style="background:linear-gradient(135deg,rgba(15,23,42,.04),rgba(124,58,237,.04));border-radius:11px;padding:12px;display:flex;gap:14px;align-items:center;flex-wrap:wrap;margin-bottom:14px">'
|
||||
+'<span style="font-weight:700">Боссов побеждено: <b id="f1-won">0</b> / 7</span>'
|
||||
+'<div style="flex:1;min-width:160px;height:8px;background:rgba(0,0,0,.08);border-radius:4px;overflow:hidden">'
|
||||
+'<div id="f1-bar" style="height:100%;background:linear-gradient(90deg,var(--sec-acc),var(--sec-acc-d));width:0%;transition:width .4s"></div>'
|
||||
+'</div></div>'
|
||||
+'<div id="f1-bosses"></div>'
|
||||
+'</div></div>';
|
||||
|
||||
box.innerHTML = h + secNavFor('final1') + readButton('final1');
|
||||
renderMath(box);
|
||||
wireReadBtn('final1');
|
||||
|
||||
_initFinal1_bosses(BOSSES);
|
||||
}
|
||||
|
||||
function _initFinal1_bosses(BOSSES){
|
||||
const KEY = 'physics8_ch1_bosses';
|
||||
function loadState(){ try { return JSON.parse(localStorage.getItem(KEY) || '{}') || {}; } catch(e){ return {}; } }
|
||||
function saveState(s){ try { localStorage.setItem(KEY, JSON.stringify(s)); } catch(e){} }
|
||||
function updateBar(){
|
||||
const s = loadState();
|
||||
let won = 0; for(const k in s) if(s[k]) won++;
|
||||
document.getElementById('f1-won').textContent = won;
|
||||
document.getElementById('f1-bar').style.width = Math.round(won*100/BOSSES.length)+'%';
|
||||
if(won >= BOSSES.length && !STATE.achievements.has('thermal_master')){
|
||||
addXp(50, 'thermal-master');
|
||||
achievement('thermal_master');
|
||||
}
|
||||
return won;
|
||||
}
|
||||
function renderAll(){
|
||||
const cont = document.getElementById('f1-bosses');
|
||||
const state = loadState();
|
||||
let html = '';
|
||||
BOSSES.forEach(b=>{
|
||||
const solved = state[b.n];
|
||||
html += '<div class="boss-card'+(solved?' solved':'')+'" id="f1-boss-'+b.n+'" style="border:2px solid '+(solved?'#10b981':'var(--border)')+';border-radius:12px;padding:14px;margin-bottom:10px;background:var(--card)">'
|
||||
+'<div style="display:flex;gap:10px;align-items:center;margin-bottom:8px"><span style="font-family:Unbounded,sans-serif;font-size:.7rem;font-weight:800;padding:3px 8px;border-radius:99px;background:var(--sec-acc-soft);color:var(--sec-acc-d);text-transform:uppercase">Босс '+b.n+'</span><span style="font-weight:700">'+b.title+'</span></div>'
|
||||
+'<div style="padding:10px 12px;background:rgba(15,23,42,.04);border-radius:8px;margin-bottom:8px;font-size:.94rem;line-height:1.5">'+b.q+'</div>'
|
||||
+'<div class="boss-row">'
|
||||
+'<input type="number" step="'+b.step+'" class="tinp" id="f1-b'+b.n+'-inp" placeholder="число" style="width:140px"'+(solved?' value="'+b.ans+'" disabled':'')+'>'
|
||||
+'<button class="btn primary" id="f1-b'+b.n+'-go"'+(solved?' disabled':'')+'>Атаковать</button>'
|
||||
+'<button class="btn" id="f1-b'+b.n+'-hint">Подсказка</button>'
|
||||
+'</div>'
|
||||
+'<div class="boss-hint-txt" id="f1-b'+b.n+'-ht" style="margin-top:8px;padding:9px 13px;background:rgba(245,158,11,.12);border-left:3px solid #f59e0b;border-radius:6px;font-size:.86rem;display:none;line-height:1.5">'+b.hint+'</div>'
|
||||
+'<div class="feedback'+(solved?' ok':'')+'" id="f1-b'+b.n+'-fb" style="display:'+(solved?'block':'none')+'">'+(solved?'✓ Победа! +10 XP. Босс повержен.':'')+'</div>'
|
||||
+'</div>';
|
||||
});
|
||||
cont.innerHTML = html;
|
||||
BOSSES.forEach(b=>{
|
||||
const go = document.getElementById('f1-b'+b.n+'-go');
|
||||
const inp = document.getElementById('f1-b'+b.n+'-inp');
|
||||
const fb = document.getElementById('f1-b'+b.n+'-fb');
|
||||
const ht = document.getElementById('f1-b'+b.n+'-ht');
|
||||
const hintBtn = document.getElementById('f1-b'+b.n+'-hint');
|
||||
if(hintBtn) hintBtn.addEventListener('click', ()=>{ ht.style.display = ht.style.display==='block'?'none':'block'; });
|
||||
if(!go || go.disabled) return;
|
||||
go.addEventListener('click', ()=>{
|
||||
const v = parseFloat((inp.value || '').replace(',','.'));
|
||||
if(isNaN(v)){ fb.style.display='block'; fb.className='feedback fail'; fb.innerHTML='Введите число.'; return; }
|
||||
if(Math.abs(v - b.ans) < b.tol){
|
||||
fb.style.display='block'; fb.className='feedback ok'; fb.innerHTML='✓ Победа! +10 XP. '+b.hint;
|
||||
go.disabled = true; inp.disabled = true;
|
||||
document.getElementById('f1-boss-'+b.n).classList.add('solved');
|
||||
const s = loadState();
|
||||
if(!s[b.n]){ s[b.n]=true; saveState(s); addXp(10,'f1-boss-'+b.n); bumpProgress('final1', 12); }
|
||||
updateBar();
|
||||
renderMath(fb);
|
||||
} else {
|
||||
fb.style.display='block'; fb.className='feedback fail'; fb.innerHTML='✗ Не то. Перепроверь и попробуй снова.';
|
||||
}
|
||||
});
|
||||
inp.addEventListener('keydown', e=>{ if(e.key === 'Enter') go.click(); });
|
||||
});
|
||||
renderMath(cont);
|
||||
updateBar();
|
||||
}
|
||||
renderAll();
|
||||
}
|
||||
|
||||
function init(){
|
||||
loadProgress(); initTheme(); initSidebarToggle(); initSearch();
|
||||
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo(PARAS[0].id);
|
||||
|
||||
Reference in New Issue
Block a user