790c2e9445
Сделано:
1. /css/alg7-fx.css — универсальные эффекты:
- shake (тряска) при неправильном ответе
- pulse (зелёное свечение) при правильном
- combo-badge (огненный шильдик ×3, ×5, ×10) при сериях
- streak-индикатор в углу с пульсацией
- sparkles (искры) при успехе
- стили для двух новых визуализаторов
2. /js/alg7-fx.js — система комбо + визуализаторы:
- MutationObserver автоматически отслеживает .feedback по всем
четырём главам без правки feedback() в каждой
- комбо-милестоны: 3 → +5 XP, 5 → +15, 10 → +50, 15 → +75, 20 → +100
- бонус автоматически уходит через window.addXp(), который
уже есть на window благодаря top-level function declarations
- ALG7.buildQuadSumViz() — большой квадрат (a+b)² с 4 цветными
областями (a², ab, ab, b²); слайдеры a, b; режим (a+b)/(a-b);
клик по области → подсветка в формуле; живые числа
- ALG7.buildDiffSquaresViz() — 3-этапная анимация a²-b²=(a-b)(a+b):
1) большой квадрат с вырезанной угловой b²
2) пунктирная линия разреза в L-форме
3) перестроенный прямоугольник со сторонами (a-b)×(a+b)
3. Подключено во всех 4 главах одной строкой <link>/<script>.
4. Ch2 §12: добавлен 4-й интерактив — геометрическая визуализация
квадрата суммы/разности. Школьник видит ПОЧЕМУ (a+b)²=a²+2ab+b².
5. Ch2 §13: добавлен 3-й интерактив — анимированное геометрическое
доказательство разности квадратов. Жмёшь «Шаг» → L-форма
расклеивается и собирается в прямоугольник.
Эффекты работают везде где есть .feedback — все боссы, все
тренажёры, все викторины. Не требует правки логики каждой главы.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1523 lines
121 KiB
HTML
1523 lines
121 KiB
HTML
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||
<meta http-equiv="Pragma" content="no-cache">
|
||
<meta http-equiv="Expires" content="0">
|
||
<title>Алгебра 7 · Глава 4 · Системы линейных уравнений</title>
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
|
||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
|
||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
|
||
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
|
||
<script src="/js/api.js" defer></script>
|
||
<script src="/js/xp.js" defer></script>
|
||
<link rel="stylesheet" href="/css/alg7-fx.css">
|
||
<script src="/js/alg7-fx.js" defer></script>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Manrope:wght@600;700;800;900&family=Unbounded:wght@700;800;900&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root{
|
||
--bg:#fafafa; --card:#fff; --card-soft:#f8fafc; --text:#0f172a; --ink:#0f172a; --muted:#64748b;
|
||
--border:#e2e8f0; --sh:0 1px 3px rgba(0,0,0,.06); --sh2:0 4px 14px rgba(0,0,0,.08);
|
||
--pri:#0891b2; --pri2:#0e7490; --pri-soft:#cffafe;
|
||
--acc:#22d3ee; --acc2:#0891b2; --acc-soft:#ecfeff;
|
||
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
|
||
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
|
||
}
|
||
.dark{--bg:#04141a; --card:#0a1b22; --card-soft:#0d2229; --text:#e0fcff; --ink:#e0fcff; --muted:#7aa8b3; --border:#163842}
|
||
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
|
||
html,body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;font-size:15px}
|
||
button,input,select,textarea{font-family:inherit;font-size:inherit}
|
||
button{cursor:pointer;border:0;background:transparent;color:inherit}
|
||
a{color:inherit;text-decoration:none}
|
||
.ic{width:16px;height:16px;display:inline-block;flex-shrink:0;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}
|
||
|
||
.hdr{position:relative;background:linear-gradient(110deg,#164e63 0%,#0891b2 55%,#22d3ee 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(165,243,252,.2);min-height:130px}
|
||
.hdr::before{content:'ГЛАВА 4';position:absolute;right:-12px;top:50%;transform:translateY(-50%);font-family:'Unbounded',sans-serif;font-size:clamp(5rem,15vw,11rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(209,250,255,.12);line-height:1;pointer-events:none;user-select:none;z-index:0}
|
||
.hdr-row{position:relative;z-index:1;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
|
||
.hdr h1{font-family:'Unbounded',sans-serif;font-size:1.5rem;font-weight:900;letter-spacing:-.01em;line-height:1.3;padding-top:4px}
|
||
.hdr-sub{font-size:.85rem;opacity:.88;margin-top:6px;font-weight:500;line-height:1.4}
|
||
.hdr-side{margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
|
||
.hdr-btn{padding:7px 12px;border-radius:9px;background:rgba(255,255,255,.14);color:#fff;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s;text-decoration:none}
|
||
.hdr-btn:hover{background:rgba(255,255,255,.24)}
|
||
|
||
.main{max-width:1240px;margin:0 auto;padding:22px;width:100%;display:grid;grid-template-columns:1fr 280px;gap:24px}
|
||
@media(max-width:980px){.main{grid-template-columns:1fr;padding:14px}}
|
||
.col-main{min-width:0}
|
||
|
||
.hero{background:linear-gradient(135deg,var(--pri-soft) 0%,var(--acc-soft) 50%,var(--pri-soft) 100%);background-size:200% 200%;animation:heroShift 12s ease-in-out infinite;border:1px solid var(--border);border-radius:18px;padding:24px 22px;margin-bottom:24px;position:relative;overflow:hidden}
|
||
@keyframes heroShift{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
|
||
.hero::before{content:'{';position:absolute;right:0;top:-30px;font-size:clamp(2rem,12vw,8rem);font-weight:900;color:var(--pri);opacity:.10;line-height:1;pointer-events:none;font-family:'Unbounded',sans-serif}
|
||
.hero h2{font-family:'Unbounded',sans-serif;font-size:1.55rem;font-weight:800;color:var(--pri2);margin-bottom:10px;letter-spacing:-.01em}
|
||
.hero p{font-size:.95rem;color:var(--text);opacity:.88;margin-bottom:14px;max-width:640px}
|
||
.hero-row{display:flex;gap:14px;flex-wrap:wrap;align-items:center}
|
||
.btn-primary{padding:11px 22px;background:linear-gradient(135deg,var(--pri),var(--pri2));color:#fff;border-radius:11px;font-weight:700;font-size:.92rem;display:inline-flex;align-items:center;gap:8px;box-shadow:var(--sh2);transition:transform .15s,box-shadow .15s}
|
||
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 28px rgba(8,145,178,.32)}
|
||
.hero-progress{flex:1;min-width:200px;max-width:280px}
|
||
.hp-label{font-size:.74rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:5px}
|
||
.hp-bar{height:8px;background:rgba(8,145,178,.18);border-radius:5px;overflow:hidden}
|
||
.hp-fill{height:100%;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:5px;width:0%;transition:width .6s cubic-bezier(.16,1,.3,1)}
|
||
.hp-text{font-size:.78rem;color:var(--muted);font-weight:700;margin-top:4px;display:block}
|
||
.hero-xp-badge{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:linear-gradient(135deg,var(--warn,#f59e0b),var(--pri));color:#fff;border-radius:99px;font-size:.82rem;font-weight:800;letter-spacing:.02em;box-shadow:0 4px 12px rgba(8,145,178,.22);font-family:'Unbounded',sans-serif}
|
||
|
||
.psel{margin-bottom:24px}
|
||
.psel-title{font-size:.72rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}
|
||
.psel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}
|
||
.psel-card{background:var(--card);border:1.5px solid var(--border);border-radius:13px;padding:14px;cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;text-align:left;position:relative}
|
||
.psel-card:hover{transform:translateY(-3px);box-shadow:var(--sh2);border-color:var(--pri)}
|
||
.psel-card.active{border-color:var(--pri);background:linear-gradient(135deg,var(--pri-soft),var(--card));box-shadow:var(--sh2)}
|
||
.psel-card.active::after{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:13px 13px 0 0}
|
||
.psel-num{font-family:'Unbounded',sans-serif;font-size:.72rem;font-weight:800;color:var(--pri);text-transform:uppercase;letter-spacing:.08em;margin-bottom:5px}
|
||
.psel-name{font-size:.86rem;font-weight:700;color:var(--text);line-height:1.3;margin-bottom:8px}
|
||
.psel-prog{height:4px;background:rgba(8,145,178,.10);border-radius:3px;overflow:hidden}
|
||
.psel-prog-fill{height:100%;background:var(--pri);width:0%;transition:width .4s}
|
||
.psel-card.final{background:linear-gradient(135deg,#fff5e1,#fef3c7)}
|
||
.psel-card.final .psel-num{color:var(--warn)}
|
||
|
||
.sec[id="sec-p21"]{ --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
|
||
.sec[id="sec-p22"]{ --sec-acc:#06b6d4; --sec-acc-d:#0891b2; --sec-acc-soft:#cffafe; }
|
||
.sec[id="sec-p23"]{ --sec-acc:#2563eb; --sec-acc-d:#1d4ed8; --sec-acc-soft:#dbeafe; }
|
||
.sec[id="sec-p24"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
|
||
.sec[id="sec-p25"]{ --sec-acc:#db2777; --sec-acc-d:#9d174d; --sec-acc-soft:#fce7f3; }
|
||
.sec[id="sec-final4"]{ --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
|
||
|
||
.sec{display:none;position:relative;animation:fadeIn .35s ease}
|
||
.sec.active{display:block}
|
||
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
||
.sec::before{content:attr(data-watermark);position:absolute;right:-20px;top:10%;font-family:'Unbounded',sans-serif;font-size:clamp(6rem,18vw,14rem);font-weight:900;color:transparent;-webkit-text-stroke:1.5px var(--sec-acc-soft,var(--pri-soft));line-height:1;pointer-events:none;user-select:none;z-index:0;opacity:.35}
|
||
.sec-header{margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--sec-acc-soft,var(--pri-soft));position:relative;z-index:1}
|
||
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--sec-acc,var(--pri)),var(--sec-acc-d,var(--pri2)));color:#fff;border-radius:7px;font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;letter-spacing:.04em;margin-bottom:8px}
|
||
.sec-h{font-family:'Unbounded',sans-serif;font-size:1.6rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));letter-spacing:-.01em;line-height:1.25}
|
||
|
||
.card{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:18px 20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.04),0 8px 24px rgba(8,145,178,.06);position:relative;z-index:1;transition:transform .25s cubic-bezier(.16,1,.3,1),box-shadow .25s}
|
||
.card:hover{transform:translateY(-2px);box-shadow:0 4px 10px rgba(0,0,0,.06),0 16px 36px rgba(8,145,178,.12)}
|
||
.card-header{display:flex;align-items:center;gap:10px;margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border)}
|
||
.card-icon{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff}
|
||
.card-icon.repeat{background:#0ea5e9}.card-icon.theory{background:#8b5cf6}.card-icon.algo{background:#f59e0b}.card-icon.rule{background:#ec4899}.card-icon.example{background:#10b981}.card-icon.oral{background:#06b6d4}
|
||
.card-icon .ic{width:18px;height:18px}
|
||
.card-title{font-family:'Unbounded',sans-serif;font-size:.82rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);flex:1}
|
||
.card-num{font-size:.74rem;font-weight:700;color:var(--muted);background:var(--sec-acc-soft,var(--pri-soft));padding:3px 7px;border-radius:5px}
|
||
.card-body{font-size:.94rem;line-height:1.65}
|
||
.card-body p{margin-bottom:8px}
|
||
.card-body p:last-child{margin-bottom:0}
|
||
|
||
.wg{background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)));border:1.5px solid var(--sec-acc,var(--pri));border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1}
|
||
.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px}
|
||
.wg-badge{padding:4px 9px;background:var(--sec-acc,var(--pri));color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
|
||
.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));flex:1}
|
||
.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--sec-acc-soft,var(--pri-soft)));border-left:4px solid var(--warn,#f59e0b);padding:9px 14px;border-radius:9px}
|
||
|
||
.btn{padding:8px 16px;border-radius:8px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:600;font-size:.88rem;transition:background .15s,border-color .15s,transform .1s}
|
||
.btn:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
|
||
.btn:active{transform:scale(.96)}
|
||
.btn.primary{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))}
|
||
.btn.primary:hover{background:var(--sec-acc-d,var(--pri2));border-color:var(--sec-acc-d,var(--pri2))}
|
||
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s;font-family:'JetBrains Mono',monospace}
|
||
.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))}
|
||
|
||
.feedback{padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none}
|
||
.feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)}
|
||
.feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)}
|
||
|
||
.col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto}
|
||
.sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)}
|
||
.sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}
|
||
.sidecard-row{margin-bottom:8px;font-size:.86rem;line-height:1.6}
|
||
.sidecard-row b{color:var(--pri);font-weight:700}
|
||
.sidecard-row:last-child{margin-bottom:0}
|
||
@media(max-width:980px){.col-side{position:static;max-height:none}}
|
||
|
||
.xp-card{background:linear-gradient(135deg,var(--acc-soft),var(--pri-soft));border:1.5px solid var(--acc);border-radius:12px;padding:14px;margin-bottom:14px}
|
||
.xp-card-title{font-size:.68rem;font-weight:800;color:var(--acc2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between}
|
||
.xp-level{font-size:1.1rem;font-weight:900;color:var(--acc2);font-family:'Unbounded',sans-serif}
|
||
.xp-bar{height:9px;background:rgba(34,211,238,.20);border-radius:6px;overflow:hidden;margin:7px 0}
|
||
.xp-fill{height:100%;background:linear-gradient(90deg,var(--acc),var(--pri));border-radius:6px;transition:width .5s cubic-bezier(.4,0,.2,1)}
|
||
.xp-nums{font-size:.74rem;color:var(--muted);display:flex;justify-content:space-between}
|
||
|
||
.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
|
||
.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px}
|
||
.spoiler summary::-webkit-details-marker{display:none}
|
||
.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px}
|
||
.spoiler[open] summary::before{content:'\2212'}
|
||
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
|
||
|
||
.sec-nav{display:flex;gap:10px;margin-top:24px;padding-top:20px;border-top:1px solid var(--border);justify-content:space-between;flex-wrap:wrap}
|
||
.foot{text-align:center;padding:30px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border);margin-top:30px}
|
||
|
||
.ach-popup{position:fixed;top:80px;right:18px;background:linear-gradient(135deg,#0891b2,#22d3ee);color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(8,145,178,.45);z-index:1002;display:none;align-items:center;gap:8px;max-width:340px}
|
||
.ach-popup.show{display:flex}
|
||
|
||
.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s}
|
||
.dnd-pool.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid}
|
||
.dnd-pool.col{flex-direction:column;align-items:stretch}
|
||
.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%}
|
||
.dnd-chip:hover{transform:translateY(-1px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)}
|
||
.dnd-chip.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(8,145,178,.22)}
|
||
.dnd-chip.dragging{opacity:.28}
|
||
.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
|
||
.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer}
|
||
.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)}
|
||
.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px}
|
||
.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))}
|
||
.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
|
||
.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)}
|
||
.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px}
|
||
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
|
||
.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px}
|
||
.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5}
|
||
.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--sec-acc-d,var(--pri2));margin-left:4px}
|
||
.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--sec-acc,var(--pri))}
|
||
.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px}
|
||
.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem}
|
||
|
||
.hp-boss{height:14px;background:rgba(220,38,38,.12);border-radius:9px;overflow:hidden;border:1px solid #fecaca;margin:8px 0}
|
||
.hp-boss-fill{height:100%;background:linear-gradient(90deg,#dc2626,#f59e0b);border-radius:9px;transition:width .5s cubic-bezier(.4,0,.2,1)}
|
||
|
||
.col-side-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.42);z-index:9990;display:none}
|
||
.col-side-backdrop.show{display:block}
|
||
@media(max-width:980px){
|
||
.col-side{position:fixed;top:0;right:0;height:100vh;width:300px;max-width:88vw;background:var(--bg);box-shadow:-12px 0 24px rgba(0,0,0,.18);padding:18px 16px;overflow-y:auto;transform:translateX(100%);transition:transform .25s ease;z-index:9991;max-height:none}
|
||
.col-side.open{transform:none}
|
||
}
|
||
|
||
.gloss-term{border-bottom:1.5px dotted var(--sec-acc,var(--pri));cursor:help;color:var(--sec-acc-d,var(--pri2));font-weight:600;padding:0 1px}
|
||
.gloss-term:hover{background:var(--sec-acc-soft,var(--pri-soft));border-radius:3px}
|
||
.gloss-tip{position:fixed;max-width:320px;padding:11px 14px;background:var(--card);border:1.5px solid var(--sec-acc,var(--pri));border-radius:11px;font-size:.84rem;line-height:1.55;box-shadow:0 12px 32px rgba(0,0,0,.18);z-index:9994;display:none;pointer-events:none;color:var(--text)}
|
||
.gloss-tip.show{display:block}
|
||
.gloss-tip b{color:var(--sec-acc-d,var(--pri2));font-size:.92rem}
|
||
|
||
.search-modal{position:fixed;inset:0;background:rgba(15,23,42,.55);backdrop-filter:blur(4px);z-index:9993;display:none;align-items:flex-start;justify-content:center;padding-top:14vh}
|
||
.search-modal.show{display:flex}
|
||
.search-box{background:var(--bg);border:1px solid var(--border);border-radius:14px;width:560px;max-width:92vw;max-height:70vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 64px rgba(0,0,0,.4)}
|
||
.search-input{padding:14px 16px;font-size:1rem;border:0;border-bottom:1px solid var(--border);background:transparent;color:var(--text);outline:none}
|
||
.search-results{flex:1;overflow-y:auto;padding:6px 0}
|
||
.search-row{display:block;padding:8px 16px;cursor:pointer;border-bottom:1px solid var(--border);text-align:left;background:transparent;border:0;width:100%;color:var(--text)}
|
||
.search-row:hover,.search-row.active{background:var(--sec-acc-soft,var(--pri-soft))}
|
||
.search-row .sr-kind{font-size:.7rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px}
|
||
.search-row .sr-title{font-weight:700;font-size:.92rem;color:var(--text)}
|
||
.search-row .sr-desc{font-size:.8rem;color:var(--muted);margin-top:2px}
|
||
.search-empty{padding:20px;text-align:center;color:var(--muted);font-size:.88rem}
|
||
.search-foot{padding:8px 14px;border-top:1px solid var(--border);font-size:.74rem;color:var(--muted);display:flex;gap:14px}
|
||
.search-foot kbd{padding:2px 6px;background:var(--card);border:1px solid var(--border);border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:.72rem}
|
||
|
||
.boss-card{padding:16px;background:var(--card);border-radius:12px;border:2px solid var(--bad,#dc2626);margin-bottom:14px}
|
||
.boss-head{display:flex;align-items:center;gap:10px;margin-bottom:10px}
|
||
.boss-title{font-family:'Unbounded',sans-serif;font-weight:800;color:#7f1d1d;font-size:1.04rem;flex:1}
|
||
.boss-stage{font-size:.85rem;color:var(--muted)}
|
||
.boss-q{font-size:1rem;line-height:1.55;padding:11px 13px;background:var(--card-soft);border-radius:8px;margin-bottom:9px;border-left:3px solid var(--bad,#dc2626)}
|
||
|
||
.coord{background:#fafafa;border:1px solid var(--border);border-radius:10px;display:block;max-width:100%;margin:0 auto}
|
||
.dark .coord{background:#0f172a}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="hdr">
|
||
<div class="hdr-row">
|
||
<div>
|
||
<h1>Алгебра 7 · Глава 4</h1>
|
||
<div class="hdr-sub">Системы двух линейных уравнений с двумя переменными</div>
|
||
</div>
|
||
<div class="hdr-side">
|
||
<a href="/textbook/algebra-7" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> К алгебре 7</a>
|
||
<button id="search-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4-4"/></svg> Поиск</button>
|
||
<button id="sidebar-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="14" y2="18"/></svg> Шпаргалка</button>
|
||
<button id="theme-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg><span id="theme-lab">Тёмная</span></button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="main">
|
||
<div class="col-main">
|
||
|
||
<section class="hero">
|
||
<h2>Системы линейных уравнений — две переменные, два уравнения</h2>
|
||
<p>Здесь мы изучаем <b>линейные уравнения с двумя переменными</b> и их <b>графики</b> — прямые. Учимся решать <b>системы</b>: графически, способом <b>подстановки</b> и способом <b>сложения</b>. Финал — <b>текстовые задачи</b>, которые удобно решаются именно системой двух уравнений.</p>
|
||
<div class="hero-row">
|
||
<button class="btn-primary" onclick="goTo('p21')"><svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg> Начать § 21</button>
|
||
<div class="hero-progress">
|
||
<span class="hp-label">Прогресс по главе</span>
|
||
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
|
||
<span id="hero-hp-text" class="hp-text">0%</span>
|
||
</div>
|
||
<div id="hero-xp-badge" class="hero-xp-badge"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="psel">
|
||
<div class="psel-title">Параграфы главы</div>
|
||
<div id="psel-grid" class="psel-grid"></div>
|
||
</section>
|
||
|
||
<section id="sec-p21" class="sec" data-watermark="ax+by=c"><div class="sec-header"><span class="sec-num">§ 21</span><h2 class="sec-h">Линейное уравнение с двумя переменными</h2></div><div id="p21-body"></div></section>
|
||
<section id="sec-p22" class="sec" data-watermark="∕"><div class="sec-header"><span class="sec-num">§ 22</span><h2 class="sec-h">График линейного уравнения $ax + by = c$</h2></div><div id="p22-body"></div></section>
|
||
<section id="sec-p23" class="sec" data-watermark="{"><div class="sec-header"><span class="sec-num">§ 23</span><h2 class="sec-h">Система линейных уравнений с двумя переменными</h2></div><div id="p23-body"></div></section>
|
||
<section id="sec-p24" class="sec" data-watermark="±"><div class="sec-header"><span class="sec-num">§ 24</span><h2 class="sec-h">Способы решения системы (подстановка, сложение)</h2></div><div id="p24-body"></div></section>
|
||
<section id="sec-p25" class="sec" data-watermark="?"><div class="sec-header"><span class="sec-num">§ 25</span><h2 class="sec-h">Решение текстовых задач с помощью системы</h2></div><div id="p25-body"></div></section>
|
||
<section id="sec-final4" class="sec" data-watermark="★"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#0891b2,#22d3ee)">Финал главы</span><h2 class="sec-h">Итоги. 5 боссов главы 4</h2></div><div id="final4-body"></div></section>
|
||
|
||
</div>
|
||
<aside class="col-side" id="col-side"><div id="sidebar-content"></div></aside>
|
||
<div class="col-side-backdrop" id="col-side-backdrop"></div>
|
||
</main>
|
||
|
||
<footer class="foot">Интерактивный учебник «Алгебра 7» · Глава 4 · Системы линейных уравнений · LearnSpace</footer>
|
||
|
||
<div id="ach-popup" class="ach-popup"><svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><polygon points="12,2 22,20 2,20"/></svg><span id="ach-text">Достижение!</span></div>
|
||
<div id="gloss-tip" class="gloss-tip"></div>
|
||
<div id="search-modal" class="search-modal" role="dialog">
|
||
<div class="search-box">
|
||
<input type="text" id="search-input" class="search-input" placeholder="Поиск…" autocomplete="off">
|
||
<div id="search-results" class="search-results"></div>
|
||
<div class="search-foot"><span><kbd>↑↓</kbd> навигация</span><span><kbd>Enter</kbd> открыть</span><span><kbd>Esc</kbd> закрыть</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
'use strict';
|
||
|
||
const STATE = { current:'p21', progress:{p21:0,p22:0,p23:0,p24:0,p25:0,final4:0}, achievements:new Map(), xp:0, level:1 };
|
||
const TOTAL_PARAS = 6;
|
||
const _TB_SLUG = 'algebra-7-ch4';
|
||
|
||
function calcLevel(xp){ return Math.floor(Math.sqrt((xp||0)/100))+1; }
|
||
function _xpForLevel(lv){ return (lv-1)*(lv-1)*100; }
|
||
|
||
const ACH_LABELS = {
|
||
start:'Начало главы 4!',
|
||
p22_done:'Графики линейных уравнений!',
|
||
p24_done:'Подстановка и сложение освоены!',
|
||
p25_done:'Текстовые задачи на системы!',
|
||
ch4_done:'Глава 4 пройдена! Алгебра 7 — финал!',
|
||
};
|
||
|
||
function loadProgress(){
|
||
try{
|
||
const s=localStorage.getItem('algebra7_ch4_progress'); if(s) Object.assign(STATE.progress, JSON.parse(s));
|
||
const a=localStorage.getItem('algebra7_ch4_achievements');
|
||
if(a){ const p=JSON.parse(a); if(Array.isArray(p)) p.forEach(id=>STATE.achievements.set(id, ACH_LABELS[id]||id)); else if(p&&typeof p==='object'){ for(const[id,t] of Object.entries(p)) STATE.achievements.set(id,(t&&t!==id)?t:(ACH_LABELS[id]||id)); } }
|
||
STATE.xp=+(localStorage.getItem('algebra7_xp')||0); STATE.level=calcLevel(STATE.xp);
|
||
}catch(e){}
|
||
}
|
||
function saveProgress(){
|
||
try{
|
||
localStorage.setItem('algebra7_ch4_progress', JSON.stringify(STATE.progress));
|
||
localStorage.setItem('algebra7_ch4_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
|
||
localStorage.setItem('algebra7_xp', String(STATE.xp));
|
||
}catch(e){}
|
||
}
|
||
function bumpProgress(key, delta){
|
||
STATE.progress[key]=Math.max(0,Math.min(100,(STATE.progress[key]||0)+delta));
|
||
saveProgress(); refreshProgressUI();
|
||
if(STATE.progress[key]>=50) markParaRead(key);
|
||
if(STATE.progress[key]>=100){
|
||
if(key==='p22') achievement('p22_done');
|
||
else if(key==='p24') achievement('p24_done');
|
||
else if(key==='p25') achievement('p25_done');
|
||
else if(key==='final4') achievement('ch4_done');
|
||
}
|
||
}
|
||
|
||
const _markedRead=new Set();
|
||
let _pendingProgressBody=null, _progressTimer=null;
|
||
function _flushProgress(){
|
||
const body=_pendingProgressBody; _pendingProgressBody=null; if(!body) return;
|
||
const tok=(window.LS&&LS.getToken)?LS.getToken():''; if(!tok) return;
|
||
fetch('/api/textbooks/'+_TB_SLUG+'/progress',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+tok},body:JSON.stringify(body),keepalive:true}).catch(()=>{});
|
||
}
|
||
function _queueProgress(patch){ _pendingProgressBody=Object.assign(_pendingProgressBody||{},patch); if(_progressTimer) clearTimeout(_progressTimer); _progressTimer=setTimeout(_flushProgress, 600); }
|
||
function markLastPara(id){ _queueProgress({last_para:id}); }
|
||
function markParaRead(id){ if(_markedRead.has(id)) return; _markedRead.add(id); _queueProgress({mark_read:id}); }
|
||
window.addEventListener('beforeunload', _flushProgress);
|
||
function loadServerReadState(){
|
||
const tok=(window.LS&&LS.getToken)?LS.getToken():''; if(!tok) return;
|
||
fetch('/api/textbooks/'+_TB_SLUG,{headers:{'Authorization':'Bearer '+tok}}).then(r=>r.ok?r.json():null).then(d=>{
|
||
if(!d||!d.progress) return;
|
||
(d.progress.read||[]).forEach(k=>{_markedRead.add(k); if((STATE.progress[k]||0)<50) STATE.progress[k]=100;});
|
||
saveProgress(); refreshProgressUI();
|
||
}).catch(()=>{});
|
||
}
|
||
|
||
function addXp(n,src){
|
||
if(!n) return;
|
||
const prev=STATE.level; STATE.xp=Math.max(0,(STATE.xp||0)+n); STATE.level=calcLevel(STATE.xp);
|
||
saveProgress(); refreshProgressUI();
|
||
if(window.LS&&window.LS.xp) window.LS.xp.add(n,'algebra7-ch4-'+(src||'misc'));
|
||
if(STATE.level>prev){
|
||
const pop=document.getElementById('ach-popup');
|
||
if(pop){ document.getElementById('ach-text').textContent='Уровень '+STATE.level+'!'; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),2600); }
|
||
if(window.confetti) try{confetti();}catch(e){}
|
||
}
|
||
}
|
||
|
||
function refreshProgressUI(){
|
||
const total=Math.round(Object.values(STATE.progress).reduce((a,b)=>a+b,0)/TOTAL_PARAS);
|
||
const f=document.getElementById('hero-hp-fill'); if(f) f.style.width=total+'%';
|
||
const t=document.getElementById('hero-hp-text'); if(t) t.textContent=total+'% пройдено';
|
||
document.querySelectorAll('[data-prog-card]').forEach(el=>{ const k=el.dataset.progCard; const fl=el.querySelector('.psel-prog-fill'); if(fl) fl.style.width=(STATE.progress[k]||0)+'%'; });
|
||
const xpBadge=document.getElementById('hero-xp-badge');
|
||
if(xpBadge){ xpBadge.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:13px;height:13px"><polygon points="12 2 22 20 2 20"/></svg> Ур. '+STATE.level+' \xb7 '+(STATE.xp||0)+' XP'; }
|
||
if(STATE.current && document.getElementById('sidebar-content')){ try{ buildSidebar(STATE.current); }catch(e){} }
|
||
}
|
||
|
||
function achievement(id,text){
|
||
if(STATE.achievements.has(id)) return;
|
||
STATE.achievements.set(id, text||ACH_LABELS[id]||id); saveProgress();
|
||
const pop=document.getElementById('ach-popup');
|
||
if(pop){ document.getElementById('ach-text').textContent=text||ACH_LABELS[id]||id; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),3300); }
|
||
addXp(20,'ach-'+id);
|
||
}
|
||
|
||
const PARAS = [
|
||
{ id:'p21', num:'§ 21', name:'Линейное уравнение с 2 переменными', sub:'$ax + by = c$' },
|
||
{ id:'p22', num:'§ 22', name:'График уравнения', sub:'Прямая на плоскости' },
|
||
{ id:'p23', num:'§ 23', name:'Система уравнений', sub:'$\\{a_1x+b_1y=c_1$' },
|
||
{ id:'p24', num:'§ 24', name:'Подстановка и сложение', sub:'Два алгоритма' },
|
||
{ id:'p25', num:'§ 25', name:'Текстовые задачи', sub:'Через систему' },
|
||
{ id:'final4', num:'★', name:'Финал главы', sub:'Итоги \xB7 5 боссов', final:true },
|
||
];
|
||
|
||
function buildParaSelector(){
|
||
const g=document.getElementById('psel-grid'); g.innerHTML='';
|
||
PARAS.forEach(p=>{
|
||
const card=document.createElement('div');
|
||
card.className='psel-card'+(p.final?' final':'');
|
||
card.dataset.id=p.id; card.dataset.progCard=p.id;
|
||
card.innerHTML='<div class="psel-num">'+p.num+'</div><div class="psel-name">'+p.name+'</div><div class="psel-prog"><div class="psel-prog-fill"></div></div>';
|
||
card.addEventListener('click', ()=>goTo(p.id));
|
||
g.appendChild(card);
|
||
});
|
||
}
|
||
|
||
const BUILT=new Set();
|
||
const BUILDERS = { p21:()=>buildP21(), p22:()=>buildP22(), p23:()=>buildP23(), p24:()=>buildP24(), p25:()=>buildP25(), final4:()=>buildFinal4() };
|
||
function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } }
|
||
function goTo(id){
|
||
STATE.current=id; ensureBuilt(id);
|
||
document.querySelectorAll('.sec').forEach(s=>s.classList.remove('active'));
|
||
const el=document.getElementById('sec-'+id); if(el) el.classList.add('active');
|
||
document.querySelectorAll('.psel-card').forEach(c=>c.classList.toggle('active', c.dataset.id===id));
|
||
buildSidebar(id);
|
||
window.scrollTo({top:0,behavior:'smooth'});
|
||
if((STATE.progress[id]||0)<10) bumpProgress(id, 10);
|
||
if(window.renderMathInElement) setTimeout(()=>renderMath(el), 0);
|
||
setTimeout(()=>{ try{ wrapGlossary(el); }catch(e){} }, 60);
|
||
markLastPara(id);
|
||
}
|
||
|
||
const SIDEBARS = {
|
||
p21:{title:'Шпаргалка \xA721',rows:[
|
||
['Уравнение','$ax + by = c$ — линейное с двумя переменными'],
|
||
['Решение','пара чисел $(x_0; y_0)$, при которой равенство верно'],
|
||
['Решений','бесконечно много'],
|
||
['Выразить $y$','$y = \\dfrac{c - ax}{b}$ (при $b \\ne 0$)'],
|
||
]},
|
||
p22:{title:'Шпаргалка \xA722',rows:[
|
||
['График','прямая на координатной плоскости'],
|
||
['$b \\ne 0$','график — прямая $y = -\\dfrac{a}{b}x + \\dfrac{c}{b}$'],
|
||
['$b = 0, a \\ne 0$','прямая, параллельная оси $Oy$ ($x = c/a$)'],
|
||
['$a = 0, b \\ne 0$','прямая, параллельная оси $Ox$ ($y = c/b$)'],
|
||
['Построение','две точки → прямая'],
|
||
]},
|
||
p23:{title:'Шпаргалка \xA723',rows:[
|
||
['Система','две прямые на одной плоскости'],
|
||
['Решение','пара $(x_0; y_0)$ — общая точка'],
|
||
['Пересекаются','одно решение'],
|
||
['Параллельны','решений нет'],
|
||
['Совпадают','бесконечно много решений'],
|
||
]},
|
||
p24:{title:'Шпаргалка \xA724',rows:[
|
||
['Подстановка','выразил из 1-го → подставил во 2-е'],
|
||
['Сложение','уравнял коэф. → сложил/вычел → одна переменная'],
|
||
['Когда что','подстановка — если легко выразить; сложение — когда есть противопол. коэф.'],
|
||
]},
|
||
p25:{title:'Шпаргалка \xA725',rows:[
|
||
['Шаг 1','что неизвестно? — это $x$ и $y$'],
|
||
['Шаг 2','составь 2 уравнения по условию'],
|
||
['Шаг 3','реши систему любым способом'],
|
||
['Шаг 4','проверь смысл и запиши ответ'],
|
||
]},
|
||
final4:{title:'Финал главы',rows:[
|
||
['\xA721–\xA725','теория главы 4'],
|
||
['Боссов','5'],
|
||
['Награда','+100 XP за полное прохождение'],
|
||
['Алгебра 7','полностью пройдена!'],
|
||
]},
|
||
};
|
||
|
||
const TIPS=[
|
||
{sec:'p21',html:'Линейное уравнение $ax + by = c$ имеет <b>бесконечно много</b> решений. Каждое решение — это <b>пара</b> чисел $(x; y)$.'},
|
||
{sec:'p22',html:'График проходит через любые две его точки. Удобно искать точки пересечения с осями: при $x = 0$ найти $y$, при $y = 0$ найти $x$.'},
|
||
{sec:'p23',html:'Графически система — это <b>две прямые</b>. Решение — координаты <b>общей точки</b>. Если прямые параллельны — решений нет.'},
|
||
{sec:'p24',html:'Подстановка лучше, если в одном уравнении легко выразить переменную ($x = ...$). Сложение — когда сразу видны противоположные коэф.'},
|
||
{sec:'p25',html:'В задачах на системы — <b>две</b> неизвестные. Внимательно подбирай, что обозначить $x$, что $y$, и составь <b>два</b> уравнения.'},
|
||
{sec:'final4',html:'5 боссов. После их прохождения вся Алгебра 7 будет в твоём арсенале!'},
|
||
];
|
||
|
||
function buildSidebar(id){
|
||
const box=document.getElementById('sidebar-content');
|
||
const sb=SIDEBARS[id]||SIDEBARS.p21;
|
||
let html='';
|
||
const xpForLv=_xpForLevel(STATE.level), xpNext=_xpForLevel(STATE.level+1);
|
||
const xpInLv=STATE.xp-xpForLv, xpRange=xpNext-xpForLv;
|
||
const xpPct=xpRange>0?Math.round(xpInLv/xpRange*100):100;
|
||
html+='<div class="xp-card"><div class="xp-card-title"><span>XP-прогресс</span><span class="xp-level">Ур. '+STATE.level+'</span></div><div class="xp-bar"><div class="xp-fill" style="width:'+xpPct+'%"></div></div><div class="xp-nums"><span>'+STATE.xp+' XP</span><span>'+xpNext+' XP</span></div></div>';
|
||
html+='<div class="sidecard"><h4>'+sb.title+'</h4>';
|
||
sb.rows.forEach(([k,v])=>{ html+='<div class="sidecard-row"><b>'+k+'</b>'+(v?' — '+v:'')+'</div>'; });
|
||
html+='</div>';
|
||
const tip=TIPS.find(t=>t.sec===id)||TIPS[0];
|
||
html+='<div class="sidecard" style="background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--pri-soft));border-color:var(--warn,#f59e0b)"><h4 style="color:#92400e;display:flex;align-items:center;gap:6px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><polygon points="12,2 22,20 2,20"/></svg>Подсказка</h4><div class="sidecard-row" style="margin-bottom:0;font-size:.84rem;line-height:1.55">'+tip.html+'</div></div>';
|
||
if(STATE.achievements.size>0){
|
||
html+='<div class="sidecard"><h4>Достижения <span style="color:var(--warn);float:right">'+STATE.achievements.size+'</span></h4>';
|
||
[...STATE.achievements.values()].slice(-4).forEach(text=>{ html+='<div class="sidecard-row" style="font-size:.78rem;color:var(--ok)">✓ '+text+'</div>'; });
|
||
html+='</div>';
|
||
}
|
||
box.innerHTML=html;
|
||
if(window.renderMathInElement) try{ renderMath(box); }catch(e){}
|
||
}
|
||
|
||
function initTheme(){
|
||
const t=localStorage.getItem('algebra7_ch4_theme')||'light';
|
||
if(t==='dark') document.documentElement.classList.add('dark');
|
||
document.getElementById('theme-lab').textContent=t==='dark'?'Светлая':'Тёмная';
|
||
document.getElementById('theme-btn').addEventListener('click', ()=>{
|
||
document.documentElement.classList.toggle('dark');
|
||
const dark=document.documentElement.classList.contains('dark');
|
||
localStorage.setItem('algebra7_ch4_theme', dark?'dark':'light');
|
||
document.getElementById('theme-lab').textContent=dark?'Светлая':'Тёмная';
|
||
});
|
||
}
|
||
|
||
function renderMath(root){ if(window.renderMathInElement){ try{ renderMathInElement(root, {delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false}); }catch(e){} } }
|
||
function feedback(elm, ok, text){ if(!elm) return; elm.className='feedback '+(ok?'ok':'fail'); elm.innerHTML=text||(ok?'✓ Верно!':'✗ Неверно'); elm.style.display='block'; try{renderMath(elm);}catch(e){} }
|
||
function fmt(n){ if(!isFinite(n)) return '?'; if(Number.isInteger(n)) return String(n); return Math.abs(n-Math.round(n))<1e-9?String(Math.round(n)):(+n.toFixed(6)).toString(); }
|
||
|
||
const ICONS = {
|
||
repeat:'<svg class="ic" viewBox="0 0 24 24"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>',
|
||
theory:'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>',
|
||
algo:'<svg class="ic" viewBox="0 0 24 24"><polyline points="17 11 21 7 17 3"/><line x1="21" y1="7" x2="9" y2="7"/><polyline points="7 13 3 17 7 21"/><line x1="3" y1="17" x2="15" y2="17"/></svg>',
|
||
rule:'<svg class="ic" viewBox="0 0 24 24"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>',
|
||
example:'<svg class="ic" viewBox="0 0 24 24"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M12 2a7 7 0 0 0-4 13c1 1 2 2 2 4h4c0-2 1-3 2-4a7 7 0 0 0-4-13z"/></svg>',
|
||
oral:'<svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
|
||
};
|
||
function makeCard(kind, title, num, body){
|
||
const labels={repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно'};
|
||
return '<div class="card"><div class="card-header"><div class="card-icon '+kind+'">'+ICONS[kind]+'</div><div class="card-title">'+(labels[kind]||'')+(title&&title!==labels[kind]?' \xb7 '+title:'')+'</div>'+(num?'<div class="card-num">'+num+'</div>':'')+'</div><div class="card-body">'+body+'</div></div>';
|
||
}
|
||
function secNav(prev, next){
|
||
const NAMES={p21:'\xA721',p22:'\xA722',p23:'\xA723',p24:'\xA724',p25:'\xA725',final4:'Финал'};
|
||
let h='<div class="sec-nav">';
|
||
h+=prev?'<button class="btn" onclick="goTo(\''+prev+'\')"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> '+NAMES[prev]+'</button>':'<span></span>';
|
||
h+=next?'<button class="btn primary" onclick="goTo(\''+next+'\')">'+NAMES[next]+' <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>':'<span></span>';
|
||
h+='</div>'; return h;
|
||
}
|
||
|
||
let _confettiCanvas=null, _confettiParticles=[], _confettiRaf=null;
|
||
function confetti(){
|
||
if(!_confettiCanvas){ _confettiCanvas=document.createElement('canvas'); _confettiCanvas.style.cssText='position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999'; document.body.appendChild(_confettiCanvas); }
|
||
const c=_confettiCanvas; c.width=window.innerWidth; c.height=window.innerHeight;
|
||
const ctx=c.getContext('2d');
|
||
const colors=['#0891b2','#22d3ee','#67e8f9','#f59e0b','#10b981'];
|
||
for(let i=0;i<80;i++){ _confettiParticles.push({x:window.innerWidth/2+(Math.random()-.5)*200,y:window.innerHeight/2,vx:(Math.random()-.5)*14,vy:-10-Math.random()*10,g:.4,life:100,color:colors[i%colors.length],r:4+Math.random()*4,rot:0,vRot:(Math.random()-.5)*.3}); }
|
||
if(_confettiRaf) cancelAnimationFrame(_confettiRaf);
|
||
function frame(){ ctx.clearRect(0,0,c.width,c.height); _confettiParticles=_confettiParticles.filter(p=>{p.x+=p.vx;p.y+=p.vy;p.vy+=p.g;p.life--;p.rot+=p.vRot;ctx.save();ctx.translate(p.x,p.y);ctx.rotate(p.rot);ctx.fillStyle=p.color;ctx.fillRect(-p.r,-p.r/2,p.r*2,p.r);ctx.restore();return p.life>0&&p.y<c.height+50;}); if(_confettiParticles.length>0) _confettiRaf=requestAnimationFrame(frame); else{ ctx.clearRect(0,0,c.width,c.height); _confettiRaf=null; } }
|
||
frame();
|
||
}
|
||
|
||
function setupSorter(cfg){
|
||
const placed={}; const pool=document.getElementById(cfg.poolId); const scope=document.querySelector(cfg.scopeSelector);
|
||
if(!pool||!scope) return {placed,render:()=>{},reset:()=>{}};
|
||
pool.classList.add('dnd-pool'); if(cfg.columnLayout) pool.classList.add('col');
|
||
let armed=null;
|
||
function buildChip(it,isPlaced){ const e=document.createElement('div'); e.className='dnd-chip'+(isPlaced?' placed':''); e.dataset.id=it.id; e.innerHTML='<span class="dnd-txt">'+it.html+'</span><span class="dnd-x" title="Убрать">\xd7</span>'; attach(e,it.id); return e; }
|
||
function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return;
|
||
ev.preventDefault(); if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); }
|
||
function attachBoxTaps(){ scope.querySelectorAll('.drop-box').forEach(box=>{ box.addEventListener('click',ev=>{ if(!armed)return; if(ev.target.closest('.dnd-chip'))return; const di=box.querySelector('[data-cat]'); if(di){placed[armed]=di.dataset.cat;armed=null;render();} }); }); }
|
||
function render(){ pool.innerHTML=''; cfg.items.forEach(it=>{if(placed[it.id])return;const c=buildChip(it,false);if(armed===it.id)c.classList.add('armed');pool.appendChild(c);}); cfg.cats.forEach(cat=>{const box=scope.querySelector('.drop-items[data-cat="'+cat+'"]');if(!box)return;box.innerHTML='';cfg.items.forEach(it=>{if(placed[it.id]!==cat)return;box.appendChild(buildChip(it,true));});}); if(window.renderMathInElement)try{renderMath(scope);}catch(_){} }
|
||
attachBoxTaps(); render();
|
||
return {placed,render,reset(){ for(const k in placed) delete placed[k]; armed=null; render(); }};
|
||
}
|
||
|
||
const GLOSSARY = [
|
||
{ term:'линейное уравнение с двумя переменными', def:'Уравнение вида $ax + by = c$, где $a, b, c$ — числа.', sec:'p21', aliases:['линейное уравнение с двумя переменными','линейного уравнения с двумя переменными','линейных уравнений с двумя переменными'] },
|
||
{ term:'решение уравнения', def:'Пара чисел $(x_0; y_0)$, при подстановке которой уравнение становится верным равенством.', sec:'p21', aliases:['решение уравнения','решения уравнения','решением уравнения'] },
|
||
{ term:'график уравнения', def:'Множество всех точек $(x; y)$, координаты которых удовлетворяют уравнению. У линейного уравнения — прямая.', sec:'p22', aliases:['график уравнения','графика уравнения','графики уравнений'] },
|
||
{ term:'система уравнений', def:'Несколько уравнений, для которых ищут общие решения.', sec:'p23', aliases:['система уравнений','системы уравнений','систему уравнений','систем уравнений','системой уравнений'] },
|
||
{ term:'решение системы', def:'Пара чисел $(x_0; y_0)$, удовлетворяющая каждому уравнению системы.', sec:'p23', aliases:['решение системы','решения системы','решением системы'] },
|
||
{ term:'способ подстановки', def:'Из одного уравнения выражают переменную и подставляют в другое.', sec:'p24', aliases:['способ подстановки','способом подстановки','подстановки','подстановка'] },
|
||
{ term:'способ сложения', def:'Сложение или вычитание уравнений системы (часто после умножения на множители).', sec:'p24', aliases:['способ сложения','способом сложения','сложения','сложение'] },
|
||
];
|
||
|
||
function wrapGlossary(root){
|
||
if(!root||root.__glossDone) return;
|
||
const allAliases=[]; GLOSSARY.forEach((g,i)=>g.aliases.forEach(a=>allAliases.push({a,i})));
|
||
allAliases.sort((x,y)=>y.a.length-x.a.length);
|
||
const re=new RegExp('(?<![\\w\\u0400-\\u04ff-])('+allAliases.map(x=>x.a.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join('|')+')(?![\\w\\u0400-\\u04ff-])', 'iu');
|
||
const walker=document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { acceptNode(node){ const p=node.parentElement; if(!p) return NodeFilter.FILTER_REJECT; if(p.closest('.katex,.gloss-term,button,input,select,.wg-badge,.card-icon,.sec-num,.psel-num,.hdr,.ach-popup,script,style,.search-modal,.sidecard,.gloss-tip')) return NodeFilter.FILTER_REJECT; if(!re.test(node.nodeValue)) return NodeFilter.FILTER_REJECT; return NodeFilter.FILTER_ACCEPT; } });
|
||
const nodes=[]; let n; while((n=walker.nextNode())) nodes.push(n);
|
||
nodes.forEach(node=>{ const text=node.nodeValue; const out=document.createDocumentFragment(); let cursor=0; const global=new RegExp(re.source,'giu'); let m; while((m=global.exec(text))!==null){ if(m.index>cursor) out.appendChild(document.createTextNode(text.slice(cursor,m.index))); const found=m[0].toLowerCase(); const hit=allAliases.find(x=>x.a.toLowerCase()===found); const g=hit?GLOSSARY[hit.i]:null; const sp=document.createElement('span'); sp.className='gloss-term'; sp.dataset.gloss=g?g.term:''; sp.textContent=m[0]; out.appendChild(sp); cursor=m.index+m[0].length; } if(cursor<text.length) out.appendChild(document.createTextNode(text.slice(cursor))); node.parentNode.replaceChild(out,node); });
|
||
root.__glossDone=true;
|
||
}
|
||
function initGlossaryTip(){
|
||
const tip=document.getElementById('gloss-tip'); if(!tip) return;
|
||
let lockOpen=null;
|
||
function show(elm){ const g=GLOSSARY.find(x=>x.term===elm.dataset.gloss); if(!g) return; tip.innerHTML='<b>'+g.term[0].toUpperCase()+g.term.slice(1)+'</b><div style="margin-top:4px">'+g.def+'</div><div style="margin-top:6px;font-size:.72rem;color:var(--muted);text-transform:uppercase;letter-spacing:.06em">См. \xA7 '+g.sec.replace('p','')+'</div>'; if(window.renderMathInElement) renderMath(tip); const r=elm.getBoundingClientRect(); tip.classList.add('show'); const tw=tip.offsetWidth,th=tip.offsetHeight; let left=r.left,top=r.bottom+8; if(left+tw>window.innerWidth-12) left=window.innerWidth-tw-12; if(top+th>window.innerHeight-12) top=r.top-th-8; tip.style.left=Math.max(8,left)+'px'; tip.style.top=Math.max(8,top)+'px'; }
|
||
function hide(){ tip.classList.remove('show'); }
|
||
document.addEventListener('mouseover',e=>{ const elm=e.target.closest&&e.target.closest('.gloss-term'); if(elm&&!lockOpen) show(elm); });
|
||
document.addEventListener('mouseout',e=>{ const elm=e.target.closest&&e.target.closest('.gloss-term'); if(elm&&!lockOpen) hide(); });
|
||
document.addEventListener('click',e=>{ const elm=e.target.closest&&e.target.closest('.gloss-term'); if(elm){ if(lockOpen===elm){lockOpen=null;hide();}else{lockOpen=elm;show(elm);} }else if(lockOpen&&!e.target.closest('.gloss-tip')){lockOpen=null;hide();} });
|
||
}
|
||
|
||
const SEARCH_INDEX = (function(){
|
||
const arr=[];
|
||
PARAS.forEach(p=>arr.push({kind:'Параграф',title:p.num+' '+p.name,desc:p.sub||'',sec:p.id}));
|
||
GLOSSARY.forEach(g=>arr.push({kind:'Понятие',title:g.term,desc:g.def.replace(/\$/g,''),sec:g.sec}));
|
||
[
|
||
['Формула','ax + by = c — линейное уравнение','\xA721','p21'],
|
||
['Формула','система: a1x+b1y=c1, a2x+b2y=c2','\xA723','p23'],
|
||
['Способ','Подстановка: x=... из 1-го → во 2-е','\xA724','p24'],
|
||
['Способ','Сложение: уравнять коэф → сложить','\xA724','p24'],
|
||
].forEach(([k,t,d,s])=>arr.push({kind:k,title:t,desc:d,sec:s}));
|
||
return arr;
|
||
})();
|
||
function initSearch(){
|
||
const modal=document.getElementById('search-modal'),inp=document.getElementById('search-input'),out=document.getElementById('search-results'),btn=document.getElementById('search-btn');
|
||
if(!modal||!inp||!out) return;
|
||
let cur=0,rows=[];
|
||
function score(q,it){ const t=(it.title+' '+it.desc).toLowerCase(); if(t.includes(q)) return 100+(it.title.toLowerCase().startsWith(q)?50:0); let s=0; q.split(/\s+/).forEach(w=>{if(w&&t.includes(w))s+=10;}); return s; }
|
||
function rank(q){ q=q.trim().toLowerCase(); if(!q) return SEARCH_INDEX.slice(0,12); return SEARCH_INDEX.map(it=>({it,s:score(q,it)})).filter(x=>x.s>0).sort((a,b)=>b.s-a.s).slice(0,20).map(x=>x.it); }
|
||
function render(){ cur=0; if(!rows.length){out.innerHTML='<div class="search-empty">Ничего не найдено</div>';return;} out.innerHTML=rows.map((r,i)=>'<button class="search-row'+(i===0?' active':'')+'" data-i="'+i+'"><div class="sr-kind">'+r.kind+'</div><div class="sr-title">'+r.title+'</div>'+(r.desc?'<div class="sr-desc">'+(r.desc.length>90?r.desc.slice(0,90)+'…':r.desc)+'</div>':'')+'</button>').join(''); out.querySelectorAll('.search-row').forEach(b=>b.addEventListener('click',()=>{cur=+b.dataset.i;pick();})); }
|
||
function pick(){ const r=rows[cur]; if(!r) return; close(); goTo(r.sec); }
|
||
function move(d){ const items=out.querySelectorAll('.search-row'); if(!items.length) return; items[cur]&&items[cur].classList.remove('active'); cur=(cur+d+items.length)%items.length; items[cur].classList.add('active'); items[cur].scrollIntoView({block:'nearest'}); }
|
||
function open(){ modal.classList.add('show'); inp.value=''; rows=rank(''); render(); setTimeout(()=>inp.focus(),50); }
|
||
function close(){ modal.classList.remove('show'); }
|
||
btn&&btn.addEventListener('click',open);
|
||
modal.addEventListener('click',e=>{if(e.target===modal)close();});
|
||
inp.addEventListener('input',()=>{rows=rank(inp.value);render();});
|
||
inp.addEventListener('keydown',e=>{ if(e.key==='ArrowDown'){e.preventDefault();move(1);}else if(e.key==='ArrowUp'){e.preventDefault();move(-1);}else if(e.key==='Enter'){e.preventDefault();pick();}else if(e.key==='Escape'){e.preventDefault();close();} });
|
||
document.addEventListener('keydown',e=>{ if((e.ctrlKey||e.metaKey)&&(e.key==='k'||e.key==='K')){ e.preventDefault(); if(modal.classList.contains('show')) close(); else open(); } });
|
||
}
|
||
|
||
function initSidebarToggle(){
|
||
const side=document.getElementById('col-side'),back=document.getElementById('col-side-backdrop'),btn=document.getElementById('sidebar-btn');
|
||
if(!side||!btn) return;
|
||
function open(){ side.classList.add('open'); back.classList.add('show'); }
|
||
function close(){ side.classList.remove('open'); back.classList.remove('show'); }
|
||
btn.addEventListener('click',()=>{ if(side.classList.contains('open')) close(); else open(); });
|
||
back.addEventListener('click',close);
|
||
document.addEventListener('keydown',e=>{ if(e.key==='Escape') close(); });
|
||
}
|
||
|
||
function init(){
|
||
loadProgress(); initTheme(); initSidebarToggle(); initGlossaryTip(); initSearch();
|
||
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo('p21');
|
||
setTimeout(()=>achievement('start','Начало главы 4!'), 600);
|
||
if(window.LS&&window.LS.xp){
|
||
window.LS.xp.load().then(function(s){ if(s&&s.xp>STATE.xp){ STATE.xp=s.xp; STATE.level=calcLevel(STATE.xp); saveProgress(); refreshProgressUI(); if(STATE.current) buildSidebar(STATE.current); } });
|
||
}
|
||
}
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
|
||
function makeTrainer(opts){
|
||
let i=0, score=0;
|
||
const Q=opts.questions;
|
||
const parser = opts.parser || (v => parseFloat(String(v).replace(',','.')));
|
||
function show(){
|
||
if(i >= Q.length){
|
||
document.getElementById(opts.idPrefix+'-q').innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
|
||
if(opts.onComplete) opts.onComplete(score, Q.length);
|
||
return;
|
||
}
|
||
document.getElementById(opts.idPrefix+'-i').textContent = (i+1);
|
||
document.getElementById(opts.idPrefix+'-s').textContent = score;
|
||
document.getElementById(opts.idPrefix+'-q').innerHTML = Q[i].q;
|
||
document.getElementById(opts.idPrefix+'-ans').value = '';
|
||
renderMath(document.getElementById(opts.idPrefix+'-q'));
|
||
document.getElementById(opts.idPrefix+'-fb').style.display = 'none';
|
||
}
|
||
function go(){
|
||
if(i >= Q.length) return;
|
||
const fb = document.getElementById(opts.idPrefix+'-fb');
|
||
const raw = document.getElementById(opts.idPrefix+'-ans').value.trim();
|
||
if(raw === ''){ feedback(fb, false, '✗ Введи ответ.'); return; }
|
||
const expected = Q[i].a;
|
||
let ok = false;
|
||
if(typeof expected === 'function') ok = expected(raw);
|
||
else { const got = parser(raw); ok = !isNaN(got) && Math.abs(got - expected) < 1e-6; }
|
||
if(ok){ score++; feedback(fb, true, '✓ Верно! Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Неверно. Правильно: <b>'+(Q[i].show||expected)+'</b>. Дальше ▶');
|
||
document.getElementById(opts.idPrefix+'-s').textContent = score;
|
||
i++; setTimeout(show, 1100);
|
||
}
|
||
document.getElementById(opts.idPrefix+'-go').addEventListener('click', go);
|
||
document.getElementById(opts.idPrefix+'-ans').addEventListener('keydown', e=>{ if(e.key==='Enter') go(); });
|
||
const restart = document.getElementById(opts.idPrefix+'-start');
|
||
if(restart) restart.addEventListener('click', ()=>{ i=0; score=0; show(); });
|
||
show();
|
||
}
|
||
function trainerHTML(idPrefix, total, placeholder){
|
||
return '<div class="score-display"><span>Задача <b id="'+idPrefix+'-i">1</b> / '+total+'</span><span>Очки: <b id="'+idPrefix+'-s">0</b> / '+total+'</span></div>'
|
||
+'<div id="'+idPrefix+'-q" style="padding:14px;background:var(--sec-acc-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">'
|
||
+'<input type="text" id="'+idPrefix+'-ans" class="tinp" placeholder="'+(placeholder||'Ответ')+'" style="width:140px;text-align:center">'
|
||
+'<button class="btn primary" id="'+idPrefix+'-go">Проверить</button>'
|
||
+'<button class="btn" id="'+idPrefix+'-start">Заново</button>'
|
||
+'</div><div class="feedback" id="'+idPrefix+'-fb"></div>';
|
||
}
|
||
function readButton(paraId){
|
||
return '<div style="margin-top:18px;display:flex;justify-content:center">'
|
||
+'<button class="btn primary" id="'+paraId+'-read-btn">'
|
||
+'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>'
|
||
+' Я прочитал \xA7'+paraId.replace('p','')+' (+10 XP)'
|
||
+'</button></div>';
|
||
}
|
||
function wireReadBtn(paraId){
|
||
document.getElementById(paraId+'-read-btn').addEventListener('click', ()=>{
|
||
addXp(10, paraId+'-read'); bumpProgress(paraId, 30);
|
||
const b=document.getElementById(paraId+'-read-btn'); b.textContent='Прочитано! +10 XP'; b.disabled=true; b.style.opacity=.6;
|
||
});
|
||
}
|
||
|
||
/* Coordinate plane SVG — supports multiple lines */
|
||
function coordSVG(opts){
|
||
const w=opts.width||320, h=opts.height||280;
|
||
const xMin=opts.xMin||-5, xMax=opts.xMax||5, yMin=opts.yMin||-5, yMax=opts.yMax||5;
|
||
const padL=24, padR=14, padT=14, padB=24;
|
||
const W=w-padL-padR, H=h-padT-padB;
|
||
const sx = x => padL + (x - xMin) / (xMax - xMin) * W;
|
||
const sy = y => padT + (yMax - y) / (yMax - yMin) * H;
|
||
let svg = '<svg class="coord" viewBox="0 0 '+w+' '+h+'">';
|
||
for(let i=xMin;i<=xMax;i++) svg += '<line x1="'+sx(i)+'" y1="'+padT+'" x2="'+sx(i)+'" y2="'+(padT+H)+'" stroke="#e2e8f0" stroke-width="0.6"/>';
|
||
for(let i=yMin;i<=yMax;i++) svg += '<line x1="'+padL+'" y1="'+sy(i)+'" x2="'+(padL+W)+'" y2="'+sy(i)+'" stroke="#e2e8f0" stroke-width="0.6"/>';
|
||
svg += '<line x1="'+padL+'" y1="'+sy(0)+'" x2="'+(padL+W)+'" y2="'+sy(0)+'" stroke="#64748b" stroke-width="1.5"/>';
|
||
svg += '<line x1="'+sx(0)+'" y1="'+padT+'" x2="'+sx(0)+'" y2="'+(padT+H)+'" stroke="#64748b" stroke-width="1.5"/>';
|
||
svg += '<polyline points="'+(padL+W-6)+','+(sy(0)-4)+' '+(padL+W)+','+sy(0)+' '+(padL+W-6)+','+(sy(0)+4)+'" stroke="#64748b" stroke-width="1.5" fill="none"/>';
|
||
svg += '<polyline points="'+(sx(0)-4)+','+(padT+6)+' '+sx(0)+','+padT+' '+(sx(0)+4)+','+(padT+6)+'" stroke="#64748b" stroke-width="1.5" fill="none"/>';
|
||
svg += '<text x="'+(padL+W-4)+'" y="'+(sy(0)+14)+'" font-size="10" fill="#64748b" font-family="JetBrains Mono,monospace">x</text>';
|
||
svg += '<text x="'+(sx(0)+6)+'" y="'+(padT+8)+'" font-size="10" fill="#64748b" font-family="JetBrains Mono,monospace">y</text>';
|
||
for(let i=xMin;i<=xMax;i++){ if(i===0) continue; svg += '<text x="'+sx(i)+'" y="'+(sy(0)+13)+'" font-size="8" fill="#64748b" font-family="JetBrains Mono,monospace" text-anchor="middle">'+i+'</text>'; }
|
||
for(let i=yMin;i<=yMax;i++){ if(i===0) continue; svg += '<text x="'+(sx(0)-5)+'" y="'+(sy(i)+3)+'" font-size="8" fill="#64748b" font-family="JetBrains Mono,monospace" text-anchor="end">'+i+'</text>'; }
|
||
/* lines: each is {a,b,c, color, label} for ax+by=c, or {k,b, color, label} for y=kx+b */
|
||
(opts.lines||[]).forEach(L=>{
|
||
let k, B;
|
||
if('a' in L){
|
||
if(L.b===0){ /* vertical x = c/a */
|
||
const xv = L.c/L.a;
|
||
svg += '<line x1="'+sx(xv)+'" y1="'+padT+'" x2="'+sx(xv)+'" y2="'+(padT+H)+'" stroke="'+(L.color||'#0891b2')+'" stroke-width="2.4"/>';
|
||
if(L.label) svg += '<text x="'+(sx(xv)+4)+'" y="'+(padT+12)+'" font-size="10" fill="'+(L.color||'#0891b2')+'" font-weight="700" font-family="JetBrains Mono,monospace">'+L.label+'</text>';
|
||
return;
|
||
}
|
||
k = -L.a/L.b; B = L.c/L.b;
|
||
} else { k = L.k; B = L.b; }
|
||
const x1=xMin, y1=k*x1+B, x2=xMax, y2=k*x2+B;
|
||
function clip(x1,y1,x2,y2){
|
||
const pts=[];
|
||
[yMax,yMin].forEach(yE=>{ if((y1-yE)*(y2-yE)<=0 && y1!==y2){ const t=(yE-y1)/(y2-y1); const x=x1+t*(x2-x1); if(x>=xMin-.01 && x<=xMax+.01) pts.push([x,yE]); } });
|
||
[xMax,xMin].forEach(xE=>{ if((x1-xE)*(x2-xE)<=0 && x1!==x2){ const t=(xE-x1)/(x2-x1); const y=y1+t*(y2-y1); if(y>=yMin-.01 && y<=yMax+.01) pts.push([xE,y]); } });
|
||
return pts.slice(0,2);
|
||
}
|
||
const pts=clip(x1,y1,x2,y2);
|
||
if(pts.length>=2){
|
||
svg += '<line x1="'+sx(pts[0][0])+'" y1="'+sy(pts[0][1])+'" x2="'+sx(pts[1][0])+'" y2="'+sy(pts[1][1])+'" stroke="'+(L.color||'#0891b2')+'" stroke-width="2.4"/>';
|
||
if(L.label) svg += '<text x="'+(sx(pts[0][0])+6)+'" y="'+(sy(pts[0][1])-6)+'" font-size="10" fill="'+(L.color||'#0891b2')+'" font-weight="700" font-family="JetBrains Mono,monospace">'+L.label+'</text>';
|
||
}
|
||
});
|
||
(opts.points||[]).forEach(p=>{
|
||
svg += '<circle cx="'+sx(p.x)+'" cy="'+sy(p.y)+'" r="4" fill="'+(p.color||'#ef4444')+'" stroke="#fff" stroke-width="1.5"/>';
|
||
if(p.label) svg += '<text x="'+(sx(p.x)+6)+'" y="'+(sy(p.y)-6)+'" font-size="9" fill="'+(p.color||'#ef4444')+'" font-family="JetBrains Mono,monospace" font-weight="700">'+p.label+'</text>';
|
||
});
|
||
svg += '</svg>';
|
||
return svg;
|
||
}
|
||
|
||
/* BUILDERS — stubs filled below */
|
||
function buildP21(){
|
||
const box = document.getElementById('p21-body');
|
||
let html = '';
|
||
|
||
html += makeCard('theory', 'Линейное уравнение с двумя переменными', '21.1', `
|
||
<p><b>Линейным уравнением</b> с двумя переменными называется уравнение вида:</p>
|
||
\\[ax + by = c\\]
|
||
<p>где $a, b, c$ — числа, а $x, y$ — переменные.</p>
|
||
<p><b>Решением</b> уравнения называется упорядоченная <b>пара чисел</b> $(x_0; y_0)$, при подстановке которой уравнение становится верным равенством.</p>
|
||
<p>Например, для $x - y = 4$: пары $(5; 1)$, $(0; -4)$, $(\\tfrac{16}{7}; -\\tfrac{12}{7})$ — все являются решениями. Решений у линейного уравнения с двумя переменными — <b>бесконечно много</b>.</p>`);
|
||
|
||
html += makeCard('rule', 'Выражение одной переменной через другую', '21.2', `
|
||
<p>Чтобы найти много решений, удобно выразить одну переменную через другую. Если $b \\ne 0$:</p>
|
||
\\[y = \\dfrac{c - ax}{b}\\]
|
||
<p>Подставляя любое значение $x$, получаем соответствующее $y$.</p>
|
||
<p><b>Пример:</b> $x + 4y = 7 \\Rightarrow y = \\dfrac{7 - x}{4}$. При $x = 3$: $y = 1$. При $x = -1$: $y = 2$. При $x = 0$: $y = 1{,}75$.</p>`);
|
||
|
||
html += makeCard('example', 'Применение', '21.3', `
|
||
<p><b>Проверь:</b> является ли пара $(3; -20)$ решением $10x + y = 12$?</p>
|
||
<p>Подставим: $10 \\cdot 3 + (-20) = 30 - 20 = 10 \\ne 12$. <b>Нет</b>, не является.</p>
|
||
<p>А пара $(1; 2)$: $10 \\cdot 1 + 2 = 12$. <b>Да</b>, является.</p>
|
||
<p><b>Задача:</b> 2 пакета молока и пакет кефира стоят 5 р. 25 к. Записать уравнением: пусть пакет молока стоит $x$ р., кефира — $y$ р. Тогда $2x + y = 5{,}25$.</p>`);
|
||
|
||
html += '<div class="wg" id="p21-iv1">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Является ли пара решением?</div></div>'
|
||
+'<div class="wg-help">Подставь пару в уравнение и проверь, верно ли равенство.</div>'
|
||
+'<div class="score-display"><span>Задача <b id="p21-iv1-i">1</b> / 6</span><span>Очки: <b id="p21-iv1-s">0</b> / 6</span></div>'
|
||
+'<div id="p21-iv1-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.1rem;text-align:center;margin-bottom:10px"></div>'
|
||
+'<div style="display:flex;gap:10px;justify-content:center"><button class="btn primary" id="p21-iv1-yes" style="background:#10b981;border-color:#10b981">Да</button><button class="btn primary" id="p21-iv1-no" style="background:#dc2626;border-color:#dc2626">Нет</button></div>'
|
||
+'<div class="feedback" id="p21-iv1-fb"></div></div>';
|
||
|
||
html += '<div class="wg" id="p21-iv2">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Выразить $y$ через $x$</div></div>'
|
||
+'<div class="wg-help">По уравнению $ax + by = c$ найди коэффициенты в формуле $y = px + q$. Введи через запятую: <b>p, q</b>.</div>'
|
||
+trainerHTML('p21-iv2', 5, 'p, q')
|
||
+'</div>';
|
||
|
||
html += '<div class="wg" id="p21-iv3">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Найди значение по уравнению</div></div>'
|
||
+'<div class="wg-help">Дано уравнение и одно значение. Найди второе.</div>'
|
||
+trainerHTML('p21-iv3', 5, 'число')
|
||
+'</div>';
|
||
|
||
html += secNav(null, 'p22') + readButton('p21');
|
||
box.innerHTML = html; renderMath(box);
|
||
|
||
(function(){
|
||
const Q=[
|
||
{ e:'$x - y = 4$, пара $(5; 1)$', isOK:true },
|
||
{ e:'$10x + y = 12$, пара $(3; -20)$', isOK:false },
|
||
{ e:'$10x + y = 12$, пара $(1; 2)$', isOK:true },
|
||
{ e:'$10x + y = 12$, пара $(0{,}1; 11)$', isOK:true },
|
||
{ e:'$x + 4y = 7$, пара $(-1; 2)$', isOK:true },
|
||
{ e:'$3x + 5y = 18$, пара $(1; 3)$', isOK:true },
|
||
];
|
||
let i=0,score=0;
|
||
function show(){
|
||
if(i>=Q.length){ document.getElementById('p21-iv1-q').innerHTML='<b>Готово!</b> '+score+' / '+Q.length; if(score===Q.length){addXp(15,'p21-iv1');bumpProgress('p21',25);} else if(score>=4){addXp(8,'p21-iv1');bumpProgress('p21',15);} return; }
|
||
document.getElementById('p21-iv1-i').textContent=(i+1);
|
||
document.getElementById('p21-iv1-s').textContent=score;
|
||
document.getElementById('p21-iv1-q').innerHTML=Q[i].e;
|
||
renderMath(document.getElementById('p21-iv1-q'));
|
||
document.getElementById('p21-iv1-fb').style.display='none';
|
||
}
|
||
function ans(isYes){
|
||
if(i>=Q.length) return;
|
||
const fb=document.getElementById('p21-iv1-fb');
|
||
if(isYes===Q[i].isOK){ score++; feedback(fb,true,'✓ Верно!'); }
|
||
else feedback(fb,false,'✗ Правильно: '+(Q[i].isOK?'<b>да</b>':'<b>нет</b>'));
|
||
document.getElementById('p21-iv1-s').textContent=score;
|
||
i++; setTimeout(show,1100);
|
||
}
|
||
document.getElementById('p21-iv1-yes').addEventListener('click',()=>ans(true));
|
||
document.getElementById('p21-iv1-no').addEventListener('click',()=>ans(false));
|
||
show();
|
||
})();
|
||
|
||
function parsePair(v){ return String(v).trim().replace(',','.').split(/[,;\s]+/).filter(Boolean); }
|
||
makeTrainer({
|
||
idPrefix:'p21-iv2',
|
||
parser:(v)=>v,
|
||
questions:[
|
||
{ q:'$x + y = 5 \\Rightarrow y = px + q$. Введи p, q', a:(v)=>{const m=parsePair(v); return +m[0]===-1 && +m[1]===5;}, show:'-1, 5' },
|
||
{ q:'$2x + y = 7 \\Rightarrow y = ?$', a:(v)=>{const m=parsePair(v); return +m[0]===-2 && +m[1]===7;}, show:'-2, 7' },
|
||
{ q:'$3x - y = -5 \\Rightarrow y = ?$', a:(v)=>{const m=parsePair(v); return +m[0]===3 && +m[1]===5;}, show:'3, 5' },
|
||
{ q:'$x + 4y = 8 \\Rightarrow y = ?$', a:(v)=>{const m=parsePair(v); return Math.abs(+m[0]-(-0.25))<1e-6 && +m[1]===2;}, show:'-0.25, 2' },
|
||
{ q:'$-2x + 3y = 12 \\Rightarrow y = ?$', a:(v)=>{const m=parsePair(v); return Math.abs(+m[0]-(2/3))<1e-3 && +m[1]===4;}, show:'2/3, 4' },
|
||
],
|
||
onComplete:(s,n)=>{ if(s===n){addXp(15,'p21-iv2');bumpProgress('p21',25);} else if(s>=3){addXp(8,'p21-iv2');bumpProgress('p21',15);} }
|
||
});
|
||
|
||
makeTrainer({
|
||
idPrefix:'p21-iv3',
|
||
questions:[
|
||
{ q:'$x + y = 5$, $x = 3$. Найди $y$.', a:2 },
|
||
{ q:'$2x + y = 7$, $x = 1$. Найди $y$.', a:5 },
|
||
{ q:'$x - 3y = 6$, $y = 0$. Найди $x$.', a:6 },
|
||
{ q:'$5x + 2y = 14$, $x = 2$. Найди $y$.', a:2 },
|
||
{ q:'$3x - 4y = 1$, $y = 2$. Найди $x$.', a:3 },
|
||
],
|
||
onComplete:(s,n)=>{ if(s===n){addXp(12,'p21-iv3');bumpProgress('p21',22);} else if(s>=3){addXp(6,'p21-iv3');bumpProgress('p21',12);} }
|
||
});
|
||
|
||
wireReadBtn('p21');
|
||
}
|
||
function buildP22(){
|
||
const box = document.getElementById('p22-body');
|
||
let html = '';
|
||
|
||
html += makeCard('theory', 'График — это прямая', '22.1', `
|
||
<p><b>Графиком</b> линейного уравнения $ax + by = c$ называется множество всех точек координатной плоскости, координаты которых $(x; y)$ удовлетворяют уравнению.</p>
|
||
<p>Если $b \\ne 0$, выразим $y$: $y = -\\dfrac{a}{b}x + \\dfrac{c}{b}$. Это уже знакомая <b>линейная функция</b>, графиком которой является <b>прямая</b>.</p>
|
||
<p><b>Графиком линейного уравнения $ax + by = c$ всегда является прямая.</b></p>
|
||
<p>Например, $3x + 2y = 6 \\Rightarrow y = -1{,}5x + 3$.</p>
|
||
<div style="display:flex;justify-content:center;margin-top:10px">${coordSVG({xMin:-3,xMax:5,yMin:-3,yMax:5, lines:[{a:3,b:2,c:6,color:'#0891b2',label:'y=-1.5x+3'}]})}</div>`);
|
||
|
||
html += makeCard('rule', 'Особые случаи', '22.2', `
|
||
<p><b>1.</b> Если $b = 0, a \\ne 0$ — уравнение $ax = c$, т. е. $x = \\dfrac{c}{a}$. График — прямая, <b>параллельная оси $Oy$</b>.</p>
|
||
<div style="display:flex;justify-content:center;margin:10px 0">${coordSVG({xMin:-3,xMax:5,yMin:-3,yMax:5, lines:[{a:1,b:0,c:4,color:'#0891b2',label:'x=4'}]})}</div>
|
||
<p><b>2.</b> Если $a = 0, b \\ne 0$ — уравнение $by = c$, т. е. $y = \\dfrac{c}{b}$. График — прямая, <b>параллельная оси $Ox$</b>.</p>
|
||
<div style="display:flex;justify-content:center;margin:10px 0">${coordSVG({xMin:-3,xMax:5,yMin:-3,yMax:5, lines:[{k:0,b:2,color:'#0891b2',label:'y=2'}]})}</div>`);
|
||
|
||
html += makeCard('algo', 'Алгоритм построения', '22.3', `
|
||
<p>Чтобы построить график линейного уравнения, нужно:</p>
|
||
<ol style="padding-left:22px;line-height:1.85">
|
||
<li>выразить $y$ через $x$ (или $x$ через $y$);</li>
|
||
<li>найти координаты двух точек (например, при $x = 0$ и при $y = 0$);</li>
|
||
<li>отметить точки и провести через них прямую.</li>
|
||
</ol>
|
||
<p><b>Пример:</b> $2x + 3y = -6$. При $x = 3$: $y = -4$. При $x = -6$: $y = 2$. Соединяем точки $(3; -4)$ и $(-6; 2)$.</p>`);
|
||
|
||
html += '<div class="wg" id="p22-iv1">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">График уравнения $ax + by = c$ — слайдеры</div></div>'
|
||
+'<div class="wg-help">Подвигай ползунки $a, b, c$ и наблюдай, как меняется прямая.</div>'
|
||
+'<div class="sliders">'
|
||
+'<label>$a$ = <b id="p22-a-val">1</b><input type="range" id="p22-a-sl" min="-4" max="4" step="1" value="1"></label>'
|
||
+'<label>$b$ = <b id="p22-b-val">1</b><input type="range" id="p22-b-sl" min="-4" max="4" step="1" value="1"></label>'
|
||
+'<label>$c$ = <b id="p22-c-val">4</b><input type="range" id="p22-c-sl" min="-6" max="6" step="1" value="4"></label>'
|
||
+'</div>'
|
||
+'<div id="p22-iv1-svg" style="display:flex;justify-content:center"></div>'
|
||
+'<div id="p22-iv1-info" style="text-align:center;font-size:.92rem;margin-top:8px"></div></div>';
|
||
|
||
html += '<div class="wg" id="p22-iv2">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Принадлежит ли точка графику?</div></div>'
|
||
+'<div class="wg-help">Подставь координаты в уравнение и проверь.</div>'
|
||
+'<div class="score-display"><span>Задача <b id="p22-iv2-i">1</b> / 6</span><span>Очки: <b id="p22-iv2-s">0</b> / 6</span></div>'
|
||
+'<div id="p22-iv2-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.1rem;text-align:center;margin-bottom:10px"></div>'
|
||
+'<div style="display:flex;gap:10px;justify-content:center"><button class="btn primary" id="p22-iv2-yes" style="background:#10b981;border-color:#10b981">Да</button><button class="btn primary" id="p22-iv2-no" style="background:#dc2626;border-color:#dc2626">Нет</button></div>'
|
||
+'<div class="feedback" id="p22-iv2-fb"></div></div>';
|
||
|
||
html += '<div class="wg" id="p22-iv3">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Точки пересечения с осями</div></div>'
|
||
+'<div class="wg-help">Найди точки пересечения графика с осями координат. Введи через запятую: <b>точка_на_Ox, точка_на_Oy</b>.</div>'
|
||
+trainerHTML('p22-iv3', 4, 'x₀, y₀')
|
||
+'</div>';
|
||
|
||
html += secNav('p21', 'p23') + readButton('p22');
|
||
box.innerHTML = html; renderMath(box);
|
||
|
||
/* IV1 */
|
||
(function(){
|
||
const aSl=document.getElementById('p22-a-sl'), bSl=document.getElementById('p22-b-sl'), cSl=document.getElementById('p22-c-sl');
|
||
const aV=document.getElementById('p22-a-val'), bV=document.getElementById('p22-b-val'), cV=document.getElementById('p22-c-val');
|
||
const out=document.getElementById('p22-iv1-svg'), info=document.getElementById('p22-iv1-info');
|
||
function update(){
|
||
const a=+aSl.value, b=+bSl.value, c=+cSl.value;
|
||
aV.textContent=a; bV.textContent=b; cV.textContent=c;
|
||
let infoText;
|
||
if(a===0 && b===0){
|
||
out.innerHTML = coordSVG({xMin:-5,xMax:5,yMin:-5,yMax:5, lines:[]});
|
||
infoText = c===0 ? 'Уравнение $0 = 0$ — выполняется при любых $x, y$ (вся плоскость).' : 'Уравнение $0 = '+c+'$ — невозможно, графика нет.';
|
||
} else if(a===0){
|
||
out.innerHTML = coordSVG({xMin:-5,xMax:5,yMin:-5,yMax:5, lines:[{k:0,b:c/b,color:'#0891b2'}]});
|
||
infoText = 'Прямая параллельна оси $Ox$: $y = '+(c/b).toFixed(2)+'$';
|
||
} else if(b===0){
|
||
out.innerHTML = coordSVG({xMin:-5,xMax:5,yMin:-5,yMax:5, lines:[{a:a,b:0,c:c,color:'#0891b2'}]});
|
||
infoText = 'Прямая параллельна оси $Oy$: $x = '+(c/a).toFixed(2)+'$';
|
||
} else {
|
||
out.innerHTML = coordSVG({xMin:-5,xMax:5,yMin:-5,yMax:5, lines:[{a:a,b:b,c:c,color:'#0891b2'}]});
|
||
infoText = 'Прямая: $y = '+(-a/b).toFixed(2)+'x + '+(c/b).toFixed(2)+'$';
|
||
}
|
||
info.innerHTML = '<b>'+a+'x + '+b+'y = '+c+'</b><br><span style="font-size:.85rem;color:var(--muted)">'+infoText+'</span>';
|
||
renderMath(info);
|
||
}
|
||
aSl.addEventListener('input', update); bSl.addEventListener('input', update); cSl.addEventListener('input', update); update();
|
||
})();
|
||
|
||
/* IV2 */
|
||
(function(){
|
||
const Q=[
|
||
{ e:'$A(4; -1)$ принадлежит $3x - 4y = 12$?', isOK:true },
|
||
{ e:'$B(4; 0)$ принадлежит $3x - 4y = 12$?', isOK:true },
|
||
{ e:'$C(2; -1{,}5)$ принадлежит $3x - 4y = 12$?', isOK:true },
|
||
{ e:'$D(0; -3)$ принадлежит $3x - 4y = 12$?', isOK:true },
|
||
{ e:'$M(1; 1)$ принадлежит $2x - y = 3$?', isOK:false },
|
||
{ e:'$K(0; 4)$ принадлежит $x + y = 4$?', isOK:true },
|
||
];
|
||
let i=0,score=0;
|
||
function show(){
|
||
if(i>=Q.length){ document.getElementById('p22-iv2-q').innerHTML='<b>Готово!</b> '+score+' / '+Q.length; if(score===Q.length){addXp(12,'p22-iv2');bumpProgress('p22',22);} else if(score>=4){addXp(6,'p22-iv2');bumpProgress('p22',12);} return; }
|
||
document.getElementById('p22-iv2-i').textContent=(i+1);
|
||
document.getElementById('p22-iv2-s').textContent=score;
|
||
document.getElementById('p22-iv2-q').innerHTML=Q[i].e;
|
||
renderMath(document.getElementById('p22-iv2-q'));
|
||
document.getElementById('p22-iv2-fb').style.display='none';
|
||
}
|
||
function ans(isYes){
|
||
if(i>=Q.length) return;
|
||
const fb=document.getElementById('p22-iv2-fb');
|
||
if(isYes===Q[i].isOK){ score++; feedback(fb,true,'✓ Верно!'); }
|
||
else feedback(fb,false,'✗ Правильно: '+(Q[i].isOK?'<b>да</b>':'<b>нет</b>'));
|
||
document.getElementById('p22-iv2-s').textContent=score;
|
||
i++; setTimeout(show,1100);
|
||
}
|
||
document.getElementById('p22-iv2-yes').addEventListener('click',()=>ans(true));
|
||
document.getElementById('p22-iv2-no').addEventListener('click',()=>ans(false));
|
||
show();
|
||
})();
|
||
|
||
/* IV3 */
|
||
function parsePair(v){ return String(v).trim().replace(',','.').split(/[,;\s]+/).filter(Boolean); }
|
||
makeTrainer({
|
||
idPrefix:'p22-iv3',
|
||
parser:(v)=>v,
|
||
questions:[
|
||
{ q:'$x - y = 7$. Введи $x_0$ (при $y=0$), $y_0$ (при $x=0$)', a:(v)=>{const m=parsePair(v); return +m[0]===7 && +m[1]===-7;}, show:'7, -7' },
|
||
{ q:'$3x + y = 1$. Введи $x_0, y_0$', a:(v)=>{const m=parsePair(v); return Math.abs(+m[0]-(1/3))<1e-3 && +m[1]===1;}, show:'1/3, 1' },
|
||
{ q:'$2x - 4 = 0$ (т. е. $2x + 0y = 4$). Введи $x_0, y_0$ или «нет» если оси не пересекаются', a:(v)=>{const t=String(v).trim().toLowerCase(); if(t.includes('нет')||t==='—') return false; const m=parsePair(v); return +m[0]===2;}, show:'$x_0 = 2$, ось $Oy$ не пересекает' },
|
||
{ q:'$x + y = 5$. Введи $x_0, y_0$', a:(v)=>{const m=parsePair(v); return +m[0]===5 && +m[1]===5;}, show:'5, 5' },
|
||
],
|
||
onComplete:(s,n)=>{ if(s===n){addXp(12,'p22-iv3');bumpProgress('p22',22);} else if(s>=2){addXp(6,'p22-iv3');bumpProgress('p22',10);} }
|
||
});
|
||
|
||
wireReadBtn('p22');
|
||
}
|
||
function buildP23(){
|
||
const box = document.getElementById('p23-body');
|
||
let html = '';
|
||
|
||
html += makeCard('theory', 'Что такое система', '23.1', `
|
||
<p>Иногда нужно найти такие пары $(x; y)$, которые являются решением <b>одновременно двух</b> уравнений. Тогда говорят, что нужно решить <b>систему</b> уравнений.</p>
|
||
<p><b>Системой двух линейных уравнений с двумя переменными</b> называется запись вида:</p>
|
||
\\[\\begin{cases} a_1 x + b_1 y = c_1 \\\\ a_2 x + b_2 y = c_2 \\end{cases}\\]
|
||
<p>где $a_1, b_1, c_1, a_2, b_2, c_2$ — числа.</p>
|
||
<p><b>Решением системы</b> называется упорядоченная пара чисел $(x_0; y_0)$, являющаяся решением и первого, и второго уравнений.</p>
|
||
<p><b>Решить систему</b> — значит найти все её решения или доказать, что их нет.</p>`);
|
||
|
||
html += makeCard('example', 'Проверка решения', '23.2', `
|
||
<p>Проверим, является ли пара $(3; 2)$ решением системы:</p>
|
||
\\[\\begin{cases} 2x + y = 8 \\\\ 3x - 2y = 5 \\end{cases}\\]
|
||
<p>Подставим в каждое уравнение:</p>
|
||
<p>$\\;2 \\cdot 3 + 2 = 8$ — <b>верно</b>;</p>
|
||
<p>$\\;3 \\cdot 3 - 2 \\cdot 2 = 5$ — <b>верно</b>.</p>
|
||
<p>Значит, $(3; 2)$ — решение системы.</p>`);
|
||
|
||
html += makeCard('theory', 'Графический смысл', '23.3', `
|
||
<p>Графиком каждого уравнения системы является <b>прямая</b>. Решение системы — это координаты <b>общих точек</b> этих двух прямых.</p>
|
||
<p>Возможны <b>три случая</b>:</p>
|
||
<ul style="padding-left:22px;line-height:1.85">
|
||
<li><b>Прямые пересекаются</b> — одно решение (общая точка).</li>
|
||
<li><b>Прямые параллельны</b> — общих точек нет, <b>решений нет</b>.</li>
|
||
<li><b>Прямые совпадают</b> — <b>бесконечно много</b> решений.</li>
|
||
</ul>
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:10px;margin-top:12px">
|
||
<div style="text-align:center"><div style="font-size:.8rem;color:var(--muted);margin-bottom:4px">Пересекаются</div>${coordSVG({width:260,height:220,xMin:-4,xMax:6,yMin:-3,yMax:5,lines:[{a:1,b:1,c:3,color:'#0891b2'},{a:2,b:-1,c:2,color:'#dc2626'}],points:[{x:5/3,y:4/3,label:'(...)',color:'#10b981'}]})}<div style="font-size:.78rem;margin-top:4px">Одно решение</div></div>
|
||
<div style="text-align:center"><div style="font-size:.8rem;color:var(--muted);margin-bottom:4px">Параллельны</div>${coordSVG({width:260,height:220,xMin:-4,xMax:6,yMin:-3,yMax:5,lines:[{a:1,b:1,c:3,color:'#0891b2'},{a:1,b:1,c:0,color:'#dc2626'}]})}<div style="font-size:.78rem;margin-top:4px">Нет решений</div></div>
|
||
<div style="text-align:center"><div style="font-size:.8rem;color:var(--muted);margin-bottom:4px">Совпадают</div>${coordSVG({width:260,height:220,xMin:-4,xMax:6,yMin:-3,yMax:5,lines:[{a:1,b:1,c:3,color:'#0891b2'},{a:2,b:2,c:6,color:'#dc2626'}]})}<div style="font-size:.78rem;margin-top:4px">$\\infty$ решений</div></div>
|
||
</div>`);
|
||
|
||
html += '<div class="wg" id="p23-iv1">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Является ли пара решением системы?</div></div>'
|
||
+'<div class="wg-help">Подставь пару в оба уравнения и проверь.</div>'
|
||
+'<div class="score-display"><span>Задача <b id="p23-iv1-i">1</b> / 5</span><span>Очки: <b id="p23-iv1-s">0</b> / 5</span></div>'
|
||
+'<div id="p23-iv1-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;text-align:center;margin-bottom:10px"></div>'
|
||
+'<div style="display:flex;gap:10px;justify-content:center"><button class="btn primary" id="p23-iv1-yes" style="background:#10b981;border-color:#10b981">Да</button><button class="btn primary" id="p23-iv1-no" style="background:#dc2626;border-color:#dc2626">Нет</button></div>'
|
||
+'<div class="feedback" id="p23-iv1-fb"></div></div>';
|
||
|
||
html += '<div class="wg" id="p23-iv2">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Сколько решений у системы?</div></div>'
|
||
+'<div class="wg-help">Сравни прямые: пересекаются, параллельны или совпадают.</div>'
|
||
+'<div class="score-display"><span>Задача <b id="p23-iv2-i">1</b> / 6</span><span>Очки: <b id="p23-iv2-s">0</b> / 6</span></div>'
|
||
+'<div id="p23-iv2-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;text-align:center;margin-bottom:10px"></div>'
|
||
+'<div style="display:flex;gap:8px;justify-content:center;flex-wrap:wrap">'
|
||
+'<button class="btn primary" id="p23-iv2-one" style="background:#10b981;border-color:#10b981">Одно</button>'
|
||
+'<button class="btn primary" id="p23-iv2-none" style="background:#dc2626;border-color:#dc2626">Нет</button>'
|
||
+'<button class="btn primary" id="p23-iv2-inf" style="background:#7c3aed;border-color:#7c3aed">Бесконечно</button>'
|
||
+'</div><div class="feedback" id="p23-iv2-fb"></div></div>';
|
||
|
||
html += secNav('p22', 'p24') + readButton('p23');
|
||
box.innerHTML = html; renderMath(box);
|
||
|
||
(function(){
|
||
const Q=[
|
||
{ e:'$\\begin{cases}2x+y=8\\\\3x-2y=5\\end{cases}$, пара $(3; 2)$', isOK:true },
|
||
{ e:'$\\begin{cases}6x+2y=12\\\\8x-y=5\\end{cases}$, пара $(1; 3)$', isOK:true },
|
||
{ e:'$\\begin{cases}6x+2y=12\\\\8x-y=5\\end{cases}$, пара $(-2; 6)$', isOK:false },
|
||
{ e:'$\\begin{cases}x+y=60\\\\x-y=20\\end{cases}$, пара $(40; 20)$', isOK:true },
|
||
{ e:'$\\begin{cases}x+y=3\\\\2x-y=5\\end{cases}$, пара $(2; 1)$', isOK:false },
|
||
];
|
||
let i=0,score=0;
|
||
function show(){
|
||
if(i>=Q.length){ document.getElementById('p23-iv1-q').innerHTML='<b>Готово!</b> '+score+' / '+Q.length; if(score===Q.length){addXp(12,'p23-iv1');bumpProgress('p23',22);} else if(score>=3){addXp(6,'p23-iv1');bumpProgress('p23',12);} return; }
|
||
document.getElementById('p23-iv1-i').textContent=(i+1);
|
||
document.getElementById('p23-iv1-s').textContent=score;
|
||
document.getElementById('p23-iv1-q').innerHTML=Q[i].e;
|
||
renderMath(document.getElementById('p23-iv1-q'));
|
||
document.getElementById('p23-iv1-fb').style.display='none';
|
||
}
|
||
function ans(isYes){
|
||
if(i>=Q.length) return;
|
||
const fb=document.getElementById('p23-iv1-fb');
|
||
if(isYes===Q[i].isOK){ score++; feedback(fb,true,'✓ Верно!'); }
|
||
else feedback(fb,false,'✗ Правильно: '+(Q[i].isOK?'<b>да</b>':'<b>нет</b>'));
|
||
document.getElementById('p23-iv1-s').textContent=score;
|
||
i++; setTimeout(show,1100);
|
||
}
|
||
document.getElementById('p23-iv1-yes').addEventListener('click',()=>ans(true));
|
||
document.getElementById('p23-iv1-no').addEventListener('click',()=>ans(false));
|
||
show();
|
||
})();
|
||
|
||
(function(){
|
||
/* Compare k1, k2, b1, b2 для y=kx+b */
|
||
const Q=[
|
||
{ e:'$\\begin{cases}x+y=4\\\\x-y=6\\end{cases}$', ans:'one' },
|
||
{ e:'$\\begin{cases}3x-y=2\\\\-6x+2y=3\\end{cases}$', ans:'none' },
|
||
{ e:'$\\begin{cases}x-3y=5\\\\2x+y=4\\end{cases}$', ans:'one' },
|
||
{ e:'$\\begin{cases}2x-0{,}5y=4\\\\-x+0{,}25y=-2\\end{cases}$', ans:'inf' },
|
||
{ e:'$\\begin{cases}x+2y=5\\\\-x-2y=5\\end{cases}$', ans:'none' },
|
||
{ e:'$\\begin{cases}x+2y=5\\\\2x+4y=10\\end{cases}$', ans:'inf' },
|
||
];
|
||
let i=0,score=0;
|
||
function show(){
|
||
if(i>=Q.length){ document.getElementById('p23-iv2-q').innerHTML='<b>Готово!</b> '+score+' / '+Q.length; if(score===Q.length){addXp(15,'p23-iv2');bumpProgress('p23',28);} else if(score>=4){addXp(8,'p23-iv2');bumpProgress('p23',16);} return; }
|
||
document.getElementById('p23-iv2-i').textContent=(i+1);
|
||
document.getElementById('p23-iv2-s').textContent=score;
|
||
document.getElementById('p23-iv2-q').innerHTML=Q[i].e;
|
||
renderMath(document.getElementById('p23-iv2-q'));
|
||
document.getElementById('p23-iv2-fb').style.display='none';
|
||
}
|
||
function ans(a){
|
||
if(i>=Q.length) return;
|
||
const fb=document.getElementById('p23-iv2-fb');
|
||
if(a===Q[i].ans){ score++; feedback(fb,true,'✓ Верно!'); }
|
||
else{ const lab={one:'одно решение',none:'нет решений',inf:'бесконечно много'}; feedback(fb,false,'✗ Правильно: <b>'+lab[Q[i].ans]+'</b>'); }
|
||
document.getElementById('p23-iv2-s').textContent=score;
|
||
i++; setTimeout(show,1200);
|
||
}
|
||
document.getElementById('p23-iv2-one').addEventListener('click',()=>ans('one'));
|
||
document.getElementById('p23-iv2-none').addEventListener('click',()=>ans('none'));
|
||
document.getElementById('p23-iv2-inf').addEventListener('click',()=>ans('inf'));
|
||
show();
|
||
})();
|
||
|
||
wireReadBtn('p23');
|
||
}
|
||
function buildP24(){
|
||
const box = document.getElementById('p24-body');
|
||
let html = '';
|
||
|
||
html += makeCard('algo', 'Способ подстановки', '24.1', `
|
||
<p>Чтобы решить систему способом подстановки:</p>
|
||
<ol style="padding-left:22px;line-height:1.85">
|
||
<li>из одного уравнения выразить одну переменную через другую;</li>
|
||
<li>подставить это выражение во второе уравнение;</li>
|
||
<li>решить полученное уравнение с одной переменной;</li>
|
||
<li>найденное значение подставить в выражение из шага 1;</li>
|
||
<li>записать ответ — упорядоченную пару $(x_0; y_0)$.</li>
|
||
</ol>`);
|
||
|
||
html += makeCard('example', 'Подстановка — пример', '24.2', `
|
||
<p>Решить $\\begin{cases} 2x + y = 7 \\\\ 3x - 4y = 5 \\end{cases}$ способом подстановки.</p>
|
||
<p><b>1.</b> Из первого: $y = 7 - 2x$.</p>
|
||
<p><b>2.</b> Подставим во второе: $3x - 4(7 - 2x) = 5$.</p>
|
||
<p><b>3.</b> Решим: $3x - 28 + 8x = 5 \\Rightarrow 11x = 33 \\Rightarrow x = 3$.</p>
|
||
<p><b>4.</b> $y = 7 - 2 \\cdot 3 = 1$.</p>
|
||
<p><b>Ответ:</b> $(3; 1)$.</p>`);
|
||
|
||
html += makeCard('algo', 'Способ сложения', '24.3', `
|
||
<p>Чтобы решить систему способом сложения:</p>
|
||
<ol style="padding-left:22px;line-height:1.85">
|
||
<li>если коэффициенты при одной из переменных <b>не противоположны</b>, умножить уравнения на множители так, чтобы они стали противоположными;</li>
|
||
<li>сложить уравнения почленно — одна переменная исчезает;</li>
|
||
<li>решить получившееся уравнение;</li>
|
||
<li>подставить значение в одно из исходных уравнений и найти вторую переменную;</li>
|
||
<li>записать ответ.</li>
|
||
</ol>`);
|
||
|
||
html += makeCard('example', 'Сложение — пример', '24.4', `
|
||
<p>Решить $\\begin{cases} 2x + 5y = 16 \\\\ 3x - 2y = 5 \\end{cases}$ способом сложения.</p>
|
||
<p>Умножим 1-е на $2$, 2-е на $5$:</p>
|
||
<p>$\\begin{cases} 4x + 10y = 32 \\\\ 15x - 10y = 25 \\end{cases}$</p>
|
||
<p>Сложим: $19x = 57 \\Rightarrow x = 3$.</p>
|
||
<p>Подставим $x = 3$ в первое: $2 \\cdot 3 + 5y = 16 \\Rightarrow 5y = 10 \\Rightarrow y = 2$.</p>
|
||
<p><b>Ответ:</b> $(3; 2)$.</p>`);
|
||
|
||
html += '<div class="wg" id="p24-iv1">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Тренажёр способа подстановки</div></div>'
|
||
+'<div class="wg-help">Реши систему и введи через запятую: <b>x, y</b>.</div>'
|
||
+trainerHTML('p24-iv1', 5, 'x, y')
|
||
+'</div>';
|
||
|
||
html += '<div class="wg" id="p24-iv2">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Тренажёр способа сложения</div></div>'
|
||
+'<div class="wg-help">Реши систему и введи через запятую: <b>x, y</b>.</div>'
|
||
+trainerHTML('p24-iv2', 5, 'x, y')
|
||
+'</div>';
|
||
|
||
html += '<div class="wg" id="p24-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="p24-iv3-i">1</b> / 5</span><span>Очки: <b id="p24-iv3-s">0</b> / 5</span></div>'
|
||
+'<div id="p24-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;text-align:center;margin-bottom:10px"></div>'
|
||
+'<div style="display:flex;gap:10px;justify-content:center"><button class="btn primary" id="p24-iv3-sub" style="background:#0891b2;border-color:#0891b2">Подстановка</button><button class="btn primary" id="p24-iv3-add" style="background:#7c3aed;border-color:#7c3aed">Сложение</button></div>'
|
||
+'<div class="feedback" id="p24-iv3-fb"></div></div>';
|
||
|
||
html += secNav('p23', 'p25') + readButton('p24');
|
||
box.innerHTML = html; renderMath(box);
|
||
|
||
function parsePair(v){ return String(v).trim().replace(/,/g,' ').split(/[;\s]+/).filter(Boolean).map(s=>parseFloat(s.replace(',','.'))); }
|
||
|
||
makeTrainer({
|
||
idPrefix:'p24-iv1',
|
||
parser:(v)=>v,
|
||
questions:[
|
||
{ q:'$\\begin{cases}x = 4 + 5y \\\\ 4x - 3y = -1\\end{cases}$', a:(v)=>{const m=parsePair(v); return m[0]===-1 && m[1]===-1;}, show:'(-1; -1)' },
|
||
{ q:'$\\begin{cases}y = 3x - 3 \\\\ 3x - 2y = 0\\end{cases}$', a:(v)=>{const m=parsePair(v); return m[0]===2 && m[1]===3;}, show:'(2; 3)' },
|
||
{ q:'$\\begin{cases}x + y = 7 \\\\ 5x - 3y = 11\\end{cases}$', a:(v)=>{const m=parsePair(v); return m[0]===4 && m[1]===3;}, show:'(4; 3)' },
|
||
{ q:'$\\begin{cases}x - 3y = 6 \\\\ 2x - 5y = -4\\end{cases}$', a:(v)=>{const m=parsePair(v); return m[0]===-42 && m[1]===-16;}, show:'(-42; -16)' },
|
||
{ q:'$\\begin{cases}3x + y = 7 \\\\ 9x - 4y = -7\\end{cases}$', a:(v)=>{const m=parsePair(v); return m[0]===1 && m[1]===4;}, show:'(1; 4)' },
|
||
],
|
||
onComplete:(s,n)=>{ if(s===n){addXp(20,'p24-iv1');bumpProgress('p24',30);} else if(s>=3){addXp(10,'p24-iv1');bumpProgress('p24',18);} }
|
||
});
|
||
|
||
makeTrainer({
|
||
idPrefix:'p24-iv2',
|
||
parser:(v)=>v,
|
||
questions:[
|
||
{ q:'$\\begin{cases}3x - 2y = 5 \\\\ 2x + 5y = 16\\end{cases}$', a:(v)=>{const m=parsePair(v); return m[0]===3 && m[1]===2;}, show:'(3; 2)' },
|
||
{ q:'$\\begin{cases}2x + y = 13 \\\\ 3x - y = 2\\end{cases}$', a:(v)=>{const m=parsePair(v); return m[0]===3 && m[1]===7;}, show:'(3; 7)' },
|
||
{ q:'$\\begin{cases}4x + 7y = 40 \\\\ -4x + 9y = 24\\end{cases}$', a:(v)=>{const m=parsePair(v); return m[0]===3 && m[1]===4;}, show:'(3; 4)' },
|
||
{ q:'$\\begin{cases}5x - 2y = 3 \\\\ 7x + 2y = 9\\end{cases}$', a:(v)=>{const m=parsePair(v); return m[0]===1 && m[1]===1;}, show:'(1; 1)' },
|
||
{ q:'$\\begin{cases}9x + 4y = 8 \\\\ 5x + 2y = 3\\end{cases}$', a:(v)=>{const m=parsePair(v); return m[0]===-2 && Math.abs(m[1]-6.5)<1e-3;}, show:'(-2; 6,5)' },
|
||
],
|
||
onComplete:(s,n)=>{ if(s===n){addXp(20,'p24-iv2');bumpProgress('p24',30);} else if(s>=3){addXp(10,'p24-iv2');bumpProgress('p24',18);} }
|
||
});
|
||
|
||
(function(){
|
||
const Q=[
|
||
{ e:'$\\begin{cases}x = 4 + 5y \\\\ 4x - 3y = -1\\end{cases}$', best:'sub', why:'$x$ уже выражен' },
|
||
{ e:'$\\begin{cases}3x - 2y = 5 \\\\ 2x + 5y = 16\\end{cases}$', best:'add', why:'нет легко выражаемой переменной' },
|
||
{ e:'$\\begin{cases}y = 3x - 3 \\\\ 3x - 2y = 0\\end{cases}$', best:'sub', why:'$y$ уже выражен' },
|
||
{ e:'$\\begin{cases}2x + y = 13 \\\\ 3x - y = 2\\end{cases}$', best:'add', why:'противоположные коэф. при $y$ — сразу сложить' },
|
||
{ e:'$\\begin{cases}9x + 4y = 8 \\\\ 5x + 2y = 3\\end{cases}$', best:'add', why:'умножь 2-е на $-2$ — получишь противоположные коэф.' },
|
||
];
|
||
let i=0,score=0;
|
||
function show(){
|
||
if(i>=Q.length){ document.getElementById('p24-iv3-q').innerHTML='<b>Готово!</b> '+score+' / '+Q.length; if(score===Q.length){addXp(12,'p24-iv3');bumpProgress('p24',20);} else if(score>=3){addXp(6,'p24-iv3');bumpProgress('p24',10);} return; }
|
||
document.getElementById('p24-iv3-i').textContent=(i+1);
|
||
document.getElementById('p24-iv3-s').textContent=score;
|
||
document.getElementById('p24-iv3-q').innerHTML=Q[i].e;
|
||
renderMath(document.getElementById('p24-iv3-q'));
|
||
document.getElementById('p24-iv3-fb').style.display='none';
|
||
}
|
||
function ans(a){
|
||
if(i>=Q.length) return;
|
||
const fb=document.getElementById('p24-iv3-fb');
|
||
if(a===Q[i].best){ score++; feedback(fb,true,'✓ Верно! ('+Q[i].why+')'); }
|
||
else feedback(fb,false,'✗ Лучше — <b>'+(Q[i].best==='sub'?'подстановка':'сложение')+'</b>: '+Q[i].why);
|
||
document.getElementById('p24-iv3-s').textContent=score;
|
||
i++; setTimeout(show,1400);
|
||
}
|
||
document.getElementById('p24-iv3-sub').addEventListener('click',()=>ans('sub'));
|
||
document.getElementById('p24-iv3-add').addEventListener('click',()=>ans('add'));
|
||
show();
|
||
})();
|
||
|
||
wireReadBtn('p24');
|
||
}
|
||
function buildP25(){
|
||
const box = document.getElementById('p25-body');
|
||
let html = '';
|
||
|
||
html += makeCard('algo', 'Алгоритм решения задачи через систему', '25.1', `
|
||
<p>Чтобы решить задачу с помощью системы двух уравнений:</p>
|
||
<ol style="padding-left:22px;line-height:1.85">
|
||
<li>выяснить, о каких величинах идёт речь — что известно, что найти;</li>
|
||
<li>обозначить <b>две</b> неизвестные величины через $x$ и $y$;</li>
|
||
<li>составить <b>два уравнения</b> по условию задачи;</li>
|
||
<li>решить полученную систему;</li>
|
||
<li>записать ответ в соответствии с условием задачи (проверь смысл!).</li>
|
||
</ol>`);
|
||
|
||
html += makeCard('example', 'Задача про кофе и чай', '25.2', `
|
||
<p><b>Условие:</b> чашка кофе стоит 5 р., чашка чая — 3 р. За день продали 120 чашек кофе и чая и получили 500 р. Сколько чашек чего продали?</p>
|
||
<p><b>Решение:</b> пусть продали $x$ чашек кофе и $y$ чашек чая. Получим систему:</p>
|
||
<p>$\\begin{cases} x + y = 120 \\\\ 5x + 3y = 500 \\end{cases}$</p>
|
||
<p>Из первого: $y = 120 - x$. Подставим: $5x + 3(120 - x) = 500 \\Rightarrow 2x = 140 \\Rightarrow x = 70$. Тогда $y = 50$.</p>
|
||
<p><b>Ответ:</b> 70 чашек кофе и 50 чашек чая.</p>`);
|
||
|
||
html += makeCard('example', 'Задача про растворы', '25.3', `
|
||
<p><b>Условие:</b> сколько граммов 10%-го и 15%-го растворов соли нужно смешать, чтобы получить 100 г 12%-го раствора?</p>
|
||
<p>Пусть нужно $x$ г 10%-го и $y$ г 15%-го раствора. По массе: $x + y = 100$. По массе соли: $0{,}1x + 0{,}15y = 12$.</p>
|
||
<p>$\\begin{cases} x + y = 100 \\\\ 0{,}1x + 0{,}15y = 12 \\end{cases}$</p>
|
||
<p>Из 1-го $y = 100 - x$, подставим во 2-е: $0{,}1x + 0{,}15(100 - x) = 12 \\Rightarrow -0{,}05x = -3 \\Rightarrow x = 60$, $y = 40$.</p>
|
||
<p><b>Ответ:</b> 60 г 10%-го раствора и 40 г 15%-го раствора.</p>`);
|
||
|
||
html += makeCard('example', 'Задача про монеты', '25.4', `
|
||
<p><b>Условие:</b> кассир разменял 50-руб. купюру на 5-руб. купюры и 1-руб. монеты, всего 22 знака. Сколько купюр и монет?</p>
|
||
<p>Пусть $x$ — число 5-руб. купюр, $y$ — число 1-руб. монет.</p>
|
||
<p>$\\begin{cases} x + y = 22 \\\\ 5x + y = 50 \\end{cases}$</p>
|
||
<p>Вычтем из 2-го 1-е: $4x = 28 \\Rightarrow x = 7$, $y = 15$.</p>
|
||
<p><b>Ответ:</b> 7 купюр и 15 монет.</p>`);
|
||
|
||
html += '<div class="wg" id="p25-iv1">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Тренажёр текстовых задач</div></div>'
|
||
+'<div class="wg-help">Составь систему в уме и реши. Введи только ответ — через запятую <b>x, y</b> (или одно число, если в задаче спросили одно).</div>'
|
||
+trainerHTML('p25-iv1', 6, 'x, y или число')
|
||
+'</div>';
|
||
|
||
html += '<div class="wg" id="p25-iv2">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Какая система описывает задачу?</div></div>'
|
||
+'<div class="wg-help">Выбери систему, которая правильно моделирует условие.</div>'
|
||
+'<div class="score-display"><span>Задача <b id="p25-iv2-i">1</b> / 4</span><span>Очки: <b id="p25-iv2-s">0</b> / 4</span></div>'
|
||
+'<div id="p25-iv2-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1rem;margin-bottom:10px"></div>'
|
||
+'<div id="p25-iv2-opts" style="display:grid;gap:8px"></div>'
|
||
+'<div class="feedback" id="p25-iv2-fb"></div></div>';
|
||
|
||
html += secNav('p24', 'final4') + readButton('p25');
|
||
box.innerHTML = html; renderMath(box);
|
||
|
||
function parsePair(v){ return String(v).trim().replace(/,/g,' ').split(/[;\s]+/).filter(Boolean).map(s=>parseFloat(s.replace(',','.'))); }
|
||
|
||
makeTrainer({
|
||
idPrefix:'p25-iv1',
|
||
parser:(v)=>v,
|
||
questions:[
|
||
{ q:'2 кг груш и 1 кг яблок — 10 р. 5 кг груш дороже 3 кг яблок на 14 р. Введи: <b>цена груш, цена яблок</b>',
|
||
a:(v)=>{const m=parsePair(v); return m[0]===4 && m[1]===2;}, show:'4 р., 2 р.' },
|
||
{ q:'В копилке двухрублёвые и пятирублёвые. 5-рубл. на 32 меньше двухр., всего 120 р. Сколько двухрублёвых?',
|
||
a:(v)=>{const m=parsePair(v); return m[0]===40;}, show:'40 двухрублёвых' },
|
||
{ q:'2 карандаша и 3 тетради — 1 р. 40 к. 3 карандаша и 2 тетради — 1 р. 35 к. Сколько (коп.) стоят 5 карандашей и 6 тетрадей?',
|
||
a:(v)=>{const m=parsePair(v); return m[0]===325;}, show:'325 к.' },
|
||
{ q:'15 школьников купили 40 сувениров: девочки по 2, мальчики по 3. Сколько мальчиков, сколько девочек? Введи <b>дев., мал.</b>',
|
||
a:(v)=>{const m=parsePair(v); return m[0]===5 && m[1]===10;}, show:'5 дев., 10 мал.' },
|
||
{ q:'На двух полках 210 книг. Если с верхней убрать половину, а на нижнюю удвоить, станет 180. Сколько было на нижней?',
|
||
a:(v)=>{const m=parsePair(v); return m[0]===60;}, show:'60 на нижней (150 на верхней)' },
|
||
{ q:'Сумма двух чисел 80,5; 40% одного равны 75% другого. Найди меньшее.',
|
||
a:(v)=>{const m=parsePair(v); return Math.abs(m[0]-28)<.1;}, show:'28' },
|
||
],
|
||
onComplete:(s,n)=>{ if(s===n){addXp(20,'p25-iv1');bumpProgress('p25',35);} else if(s>=3){addXp(10,'p25-iv1');bumpProgress('p25',18);} }
|
||
});
|
||
|
||
(function(){
|
||
const Q=[
|
||
{ task:'На двух улицах 117 домов. На первой в 2 раза меньше, чем на второй. Пусть $x$ — число домов на первой улице. Какое уравнение составить?',
|
||
opts:[
|
||
{ e:'$2x - x = 117$', ok:false },
|
||
{ e:'$2x + x = 117$', ok:true },
|
||
{ e:'$x + \\dfrac{x}{2} = 117$', ok:false },
|
||
{ e:'$2x = 117$', ok:false },
|
||
]
|
||
},
|
||
{ task:'Два карандаша и три тетради — 1 р. 40 к. Два тетради и три карандаша — 1 р. 35 к. Пусть карандаш — $x$ к., тетрадь — $y$ к. Какая система?',
|
||
opts:[
|
||
{ e:'$\\begin{cases}2x+3y=140\\\\3x+2y=135\\end{cases}$', ok:true },
|
||
{ e:'$\\begin{cases}3x+2y=140\\\\2x+3y=135\\end{cases}$', ok:false },
|
||
{ e:'$\\begin{cases}2x+3y=1{,}40\\\\3x+2y=1{,}35\\end{cases}$', ok:false },
|
||
]
|
||
},
|
||
{ task:'В клетках 20 кроликов и цыплят, у них 58 ног. Пусть $x$ — кролики, $y$ — цыплята. Какая система?',
|
||
opts:[
|
||
{ e:'$\\begin{cases}x+y=20\\\\2x+4y=58\\end{cases}$', ok:false },
|
||
{ e:'$\\begin{cases}x+y=20\\\\4x+2y=58\\end{cases}$', ok:true },
|
||
{ e:'$\\begin{cases}x-y=20\\\\4x+2y=58\\end{cases}$', ok:false },
|
||
]
|
||
},
|
||
{ task:'В двух коробках 300 карандашей. Если в первой уменьшить вдвое, во второй удвоить — станет 240. Пусть $x, y$ — карандаши. Какая система?',
|
||
opts:[
|
||
{ e:'$\\begin{cases}x+y=300\\\\\\tfrac{x}{2}+2y=240\\end{cases}$', ok:true },
|
||
{ e:'$\\begin{cases}x-y=300\\\\2x+\\tfrac{y}{2}=240\\end{cases}$', ok:false },
|
||
{ e:'$\\begin{cases}x+y=300\\\\2x+\\tfrac{y}{2}=240\\end{cases}$', ok:false },
|
||
]
|
||
},
|
||
];
|
||
let i=0,score=0;
|
||
function show(){
|
||
if(i>=Q.length){ document.getElementById('p25-iv2-q').innerHTML='<b>Готово!</b> '+score+' / '+Q.length; if(score===Q.length){addXp(12,'p25-iv2');bumpProgress('p25',22);} else if(score>=2){addXp(6,'p25-iv2');bumpProgress('p25',12);} document.getElementById('p25-iv2-opts').innerHTML=''; return; }
|
||
document.getElementById('p25-iv2-i').textContent=(i+1);
|
||
document.getElementById('p25-iv2-s').textContent=score;
|
||
document.getElementById('p25-iv2-q').innerHTML=Q[i].task;
|
||
const opts=document.getElementById('p25-iv2-opts');
|
||
opts.innerHTML='';
|
||
Q[i].opts.forEach((o,j)=>{
|
||
const b=document.createElement('button');
|
||
b.className='btn';
|
||
b.style.cssText='padding:10px;text-align:left;display:block;width:100%';
|
||
b.innerHTML='<span style="color:var(--muted);margin-right:8px">'+(j+1)+')</span>'+o.e;
|
||
b.addEventListener('click', ()=>{
|
||
const fb=document.getElementById('p25-iv2-fb');
|
||
if(o.ok){ score++; feedback(fb,true,'✓ Верно!'); }
|
||
else feedback(fb,false,'✗ Неверно. Правильный — другой.');
|
||
document.getElementById('p25-iv2-s').textContent=score;
|
||
i++; setTimeout(show,1300);
|
||
});
|
||
opts.appendChild(b);
|
||
});
|
||
renderMath(document.getElementById('p25-iv2-q'));
|
||
renderMath(opts);
|
||
document.getElementById('p25-iv2-fb').style.display='none';
|
||
}
|
||
show();
|
||
})();
|
||
|
||
wireReadBtn('p25');
|
||
}
|
||
const BOSSES_CH4 = [
|
||
{
|
||
n:1, title:'Босс \xA721 — Линейное уравнение с 2 переменными', color:'#0891b2',
|
||
steps:[
|
||
{ q:'Является ли пара $(1; 2)$ решением $10x + y = 12$? «да»/«нет»', verify:(v)=>String(v).trim().toLowerCase().startsWith('д'), hint:'$10 + 2 = 12$.' },
|
||
{ q:'Из $x + 4y = 8$ выразить $y$ через $x$: $y = ?x + ?$. Введи свободный член (без $x$).', verify:(v)=>+v===2, hint:'$y = (8 - x)/4 = -x/4 + 2$.' },
|
||
{ q:'Сколько решений у $ax + by = c$ (при $a, b \\ne 0$)?', verify:(v)=>{const t=String(v).trim().toLowerCase(); return t.includes('бесконеч')||t.includes('много');}, hint:'Бесконечно много.' },
|
||
{ q:'$2x + y = 7$, $x = 1$. Найди $y$.', verify:(v)=>+v===5, hint:'$y = 7 - 2 = 5$.' },
|
||
{ q:'Пара $(3; -20)$ — решение $10x + y = 12$? «да»/«нет»', verify:(v)=>String(v).trim().toLowerCase().startsWith('н'), hint:'$30 - 20 = 10 \\ne 12$.' },
|
||
]
|
||
},
|
||
{
|
||
n:2, title:'Босс \xA722 — График уравнения', color:'#06b6d4',
|
||
steps:[
|
||
{ q:'График $ax + by = c$ — это всегда что? «прямая», «парабола» или «окружность»', verify:(v)=>String(v).trim().toLowerCase().startsWith('прям'), hint:'Линейное уравнение — линейная функция.' },
|
||
{ q:'$0 \\cdot x + 3y = 6$ — какова прямая? Введи «гориз» или «верт»', verify:(v)=>String(v).trim().toLowerCase().startsWith('гор'), hint:'$y = 2$ — параллельна оси $Ox$.' },
|
||
{ q:'$5x + 0 \\cdot y = 20$ — какова прямая? «гориз» или «верт»', verify:(v)=>String(v).trim().toLowerCase().startsWith('верт'), hint:'$x = 4$ — параллельна оси $Oy$.' },
|
||
{ q:'$x - y = 7$. Найди $x_0$ (точка на $Ox$).', verify:(v)=>+v===7, hint:'$y = 0 \\Rightarrow x = 7$.' },
|
||
{ q:'$3x + y = 1$. Найди $y_0$ (точка на $Oy$).', verify:(v)=>+v===1, hint:'$x = 0 \\Rightarrow y = 1$.' },
|
||
]
|
||
},
|
||
{
|
||
n:3, title:'Босс \xA723 — Система уравнений', color:'#2563eb',
|
||
steps:[
|
||
{ q:'Решение системы — это пара чисел или одно число? «пара» или «число»', verify:(v)=>String(v).trim().toLowerCase().startsWith('пар'), hint:'$(x_0; y_0)$ — два числа.' },
|
||
{ q:'Прямые параллельны. Сколько решений у системы? («одно», «нет», «бесконечно»)', verify:(v)=>String(v).trim().toLowerCase().startsWith('нет'), hint:'У параллельных прямых нет общих точек.' },
|
||
{ q:'Прямые пересекаются. Сколько решений?', verify:(v)=>String(v).trim().toLowerCase().startsWith('одн'), hint:'Одно — точка пересечения.' },
|
||
{ q:'Прямые совпадают. Сколько решений?', verify:(v)=>{const t=String(v).trim().toLowerCase(); return t.includes('бесконеч')||t.includes('много');}, hint:'Бесконечно много.' },
|
||
{ q:'Является ли $(1; 3)$ решением $\\begin{cases}6x+2y=12\\\\8x-y=5\\end{cases}$? «да»/«нет»', verify:(v)=>String(v).trim().toLowerCase().startsWith('д'), hint:'$6+6=12$, $8-3=5$.' },
|
||
]
|
||
},
|
||
{
|
||
n:4, title:'Босс \xA724 — Способы решения', color:'#7c3aed',
|
||
steps:[
|
||
{ q:'$\\begin{cases}x + y = 7 \\\\ 5x - 3y = 11\\end{cases}$ — найди $x$', verify:(v)=>+v===4, hint:'Из 1-го $y = 7 - x$, подставь.' },
|
||
{ q:'(та же система) — найди $y$', verify:(v)=>+v===3, hint:'$y = 7 - 4 = 3$.' },
|
||
{ q:'$\\begin{cases}2x + y = 13 \\\\ 3x - y = 2\\end{cases}$ — найди $x$ (способ сложения)', verify:(v)=>+v===3, hint:'Сложи: $5x = 15$.' },
|
||
{ q:'$\\begin{cases}2x + y = 13 \\\\ 3x - y = 2\\end{cases}$ — найди $y$', verify:(v)=>+v===7, hint:'$y = 13 - 2 \\cdot 3 = 7$.' },
|
||
{ q:'$\\begin{cases}5x - 2y = 3 \\\\ 7x + 2y = 9\\end{cases}$ — найди $x$', verify:(v)=>+v===1, hint:'Сложи: $12x = 12$.' },
|
||
]
|
||
},
|
||
{
|
||
n:5, title:'Финальный босс — Текстовые задачи (системы)', color:'#db2777',
|
||
steps:[
|
||
{ q:'$x + y = 120$, $5x + 3y = 500$ — найди $x$ (число чашек кофе)', verify:(v)=>+v===70, hint:'Подстановка $y = 120 - x$.' },
|
||
{ q:'(та же) — найди $y$ (число чашек чая)', verify:(v)=>+v===50, hint:'$y = 120 - 70 = 50$.' },
|
||
{ q:'Сумма двух чисел 80, разность 20. Найди большее.', verify:(v)=>+v===50, hint:'$x + y = 80$, $x - y = 20$, сложить.' },
|
||
{ q:'В клетках 20 кроликов и цыплят, у них 58 ног. Сколько кроликов?', verify:(v)=>+v===9, hint:'Кролики 4 ноги, цыплята 2. Система $x+y=20$, $4x+2y=58$.' },
|
||
{ q:'Купили 50-руб. на 5-руб. купюры и 1-руб. монеты, всего 22 знака. Сколько 5-рублёвок?', verify:(v)=>+v===7, hint:'$x+y=22$, $5x+y=50$. Вычти.' },
|
||
]
|
||
},
|
||
];
|
||
|
||
function buildFinal4(){
|
||
const box = document.getElementById('final4-body');
|
||
let html = '';
|
||
|
||
html += makeCard('theory', 'Что мы изучили', 'Итог', `
|
||
<p>В этой главе мы:</p>
|
||
<ul style="padding-left:22px;line-height:1.85">
|
||
<li>познакомились с <b>линейным уравнением с двумя переменными</b> $ax + by = c$ — у него <b>бесконечно много</b> решений;</li>
|
||
<li>научились строить <b>график</b> такого уравнения — это <b>прямая</b>;</li>
|
||
<li>узнали, что <b>система</b> двух уравнений — это <b>две прямые</b>: одно решение, нет решений или бесконечно много;</li>
|
||
<li>освоили <b>два способа решения</b>: подстановка и сложение;</li>
|
||
<li>научились <b>составлять системы</b> для решения текстовых задач.</li>
|
||
</ul>
|
||
<p>Это <b>финальная глава Алгебры 7</b>! Пройди 5 боссов — и весь курс будет в твоём арсенале.</p>`);
|
||
|
||
html += '<div id="bosses-container-ch4"></div>';
|
||
html += '<div style="margin-top:22px;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">'
|
||
+'<div style="font-family:\'Unbounded\',sans-serif;font-weight:800;color:var(--pri2);font-size:1.1rem;margin-bottom:6px">Прогресс по боссам</div>'
|
||
+'<div id="boss-overall-ch4" style="font-size:.95rem;color:var(--text);margin-bottom:10px">0 / 5 боссов побеждено</div>'
|
||
+'<div class="hp-boss"><div class="hp-boss-fill" id="boss-overall-fill-ch4" style="width:0%;background:linear-gradient(90deg,#0891b2,#22d3ee)"></div></div>'
|
||
+'</div>';
|
||
html += secNav('p25', null);
|
||
box.innerHTML = html; renderMath(box);
|
||
|
||
const cont = document.getElementById('bosses-container-ch4');
|
||
const BOSS_STATE = (function(){
|
||
try{ const s=localStorage.getItem('algebra7_ch4_bosses'); if(s) return JSON.parse(s); }catch(e){}
|
||
return BOSSES_CH4.map(()=>({stage:0,defeated:false}));
|
||
})();
|
||
function saveBosses(){ try{ localStorage.setItem('algebra7_ch4_bosses', JSON.stringify(BOSS_STATE)); }catch(e){} }
|
||
function refreshOverall(){
|
||
const won=BOSS_STATE.filter(b=>b.defeated).length;
|
||
const txt=document.getElementById('boss-overall-ch4'); if(txt) txt.textContent=won+' / '+BOSSES_CH4.length+' боссов побеждено';
|
||
const fill=document.getElementById('boss-overall-fill-ch4'); if(fill) fill.style.width=(won*100/BOSSES_CH4.length)+'%';
|
||
if(won>=BOSSES_CH4.length){ bumpProgress('final4',60); achievement('ch4_done','Алгебра 7 — пройдена полностью!'); }
|
||
}
|
||
cont.innerHTML = BOSSES_CH4.map((b,idx)=>{
|
||
return '<div class="boss-card" id="boss-card-'+idx+'" style="border-color:'+b.color+'">'
|
||
+'<div class="boss-head">'
|
||
+'<svg viewBox="0 0 24 24" fill="none" stroke="'+b.color+'" stroke-width="2.2" style="width:28px;height:28px"><polygon points="12,2 22,20 2,20"/></svg>'
|
||
+'<div class="boss-title" style="color:'+b.color+'">'+b.title+'</div>'
|
||
+'<div class="boss-stage" id="boss-'+idx+'-stage">Этап 1 / '+b.steps.length+'</div>'
|
||
+'</div>'
|
||
+'<div class="hp-boss" style="border-color:'+b.color+'66;background:'+b.color+'1a"><div class="hp-boss-fill" id="boss-'+idx+'-fill" style="width:0%;background:linear-gradient(90deg,'+b.color+',#f59e0b)"></div></div>'
|
||
+'<div class="boss-q" id="boss-'+idx+'-q" style="border-color:'+b.color+'"></div>'
|
||
+'<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">'
|
||
+'<input type="text" id="boss-'+idx+'-input" class="tinp" placeholder="Ответ" style="width:160px;text-align:center">'
|
||
+'<button class="btn primary" id="boss-'+idx+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атака</button>'
|
||
+'<button class="btn" id="boss-'+idx+'-hint">Подсказка</button>'
|
||
+'<button class="btn" id="boss-'+idx+'-restart">↻</button>'
|
||
+'</div>'
|
||
+'<div class="feedback" id="boss-'+idx+'-fb"></div>'
|
||
+'</div>';
|
||
}).join('');
|
||
|
||
BOSSES_CH4.forEach((b,idx)=>{
|
||
function show(){
|
||
const st=BOSS_STATE[idx];
|
||
const stageEl=document.getElementById('boss-'+idx+'-stage');
|
||
const fill=document.getElementById('boss-'+idx+'-fill');
|
||
const q=document.getElementById('boss-'+idx+'-q');
|
||
const fb=document.getElementById('boss-'+idx+'-fb');
|
||
if(st.defeated){
|
||
stageEl.textContent='✓ Побеждён'; fill.style.width='100%';
|
||
q.innerHTML='<b style="color:'+b.color+'">Босс повержен!</b>';
|
||
document.getElementById('boss-'+idx+'-go').disabled=true;
|
||
document.getElementById('boss-'+idx+'-go').style.opacity=.5;
|
||
return;
|
||
}
|
||
stageEl.textContent='Этап '+(st.stage+1)+' / '+b.steps.length;
|
||
fill.style.width=(st.stage*100/b.steps.length)+'%';
|
||
q.innerHTML=b.steps[st.stage].q;
|
||
document.getElementById('boss-'+idx+'-input').value='';
|
||
fb.style.display='none';
|
||
renderMath(q);
|
||
}
|
||
document.getElementById('boss-'+idx+'-go').addEventListener('click',()=>{
|
||
const st=BOSS_STATE[idx]; if(st.defeated) return;
|
||
const step=b.steps[st.stage];
|
||
const val=document.getElementById('boss-'+idx+'-input').value;
|
||
const fb=document.getElementById('boss-'+idx+'-fb');
|
||
if(!val.trim()){ feedback(fb,false,'✗ Введи ответ.'); return; }
|
||
if(step.verify(val)){
|
||
st.stage++;
|
||
if(st.stage>=b.steps.length){
|
||
st.defeated=true; saveBosses();
|
||
feedback(fb,true,'✓ Босс '+b.n+' побеждён! +20 XP');
|
||
addXp(20,'boss-'+b.n); bumpProgress('final4',18); refreshOverall();
|
||
setTimeout(show,1400);
|
||
if(window.confetti) try{confetti();}catch(e){}
|
||
}else{
|
||
saveBosses(); feedback(fb,true,'✓ Верно! +3 XP'); addXp(3,'boss-step'); setTimeout(show,1100);
|
||
}
|
||
}else{ feedback(fb,false,'✗ Промах.'); }
|
||
});
|
||
document.getElementById('boss-'+idx+'-hint').addEventListener('click',()=>{
|
||
const st=BOSS_STATE[idx]; if(st.defeated) return;
|
||
const fb=document.getElementById('boss-'+idx+'-fb');
|
||
fb.className='feedback ok';
|
||
fb.innerHTML='<span style="color:#92400e">\u{1F4A1} Подсказка:</span> '+b.steps[st.stage].hint;
|
||
fb.style.display='block';
|
||
fb.style.background='var(--warn-bg)'; fb.style.color='#92400e'; fb.style.borderLeftColor='var(--warn)';
|
||
renderMath(fb);
|
||
});
|
||
document.getElementById('boss-'+idx+'-restart').addEventListener('click',()=>{
|
||
BOSS_STATE[idx]={stage:0,defeated:false}; saveBosses();
|
||
document.getElementById('boss-'+idx+'-go').disabled=false;
|
||
document.getElementById('boss-'+idx+'-go').style.opacity=1;
|
||
show(); refreshOverall();
|
||
});
|
||
show();
|
||
});
|
||
|
||
refreshOverall();
|
||
}
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|