Files
Learn_System/backend/src/db/seed-theory.js
T
Maxim Dolgolyov be4d43105e LearnSpace: full-stack educational whiteboard platform
Node.js/Express backend + vanilla JS frontend.
Features: real-time collaborative whiteboard (SSE), multi-page support,
LaTeX formulas, shapes/connectors, coordinate systems, number lines,
compass, zoom/pan, Catmull-Rom pencil smoothing, ruler/protractor with
rotation & resize controls, minimap navigation overlay, auto-measurements,
multi-page thumbnails sidebar, PNG export, page templates.
Student/teacher workflows: classes, assignments, library, dashboard.
Mobile responsive. SQLite (better-sqlite3).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 10:10:37 +03:00

1007 lines
66 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Seed: все теоретические курсы с уроками
*
* Курсы:
* [math] Тригонометрия — 4 урока
* [math] Производные и интегралы — 4 урока
* [math] Теория вероятностей — 3 урока
* [phys] Механика — 4 урока
* [phys] Электричество и магнетизм — 4 урока
* [phys] Оптика и волны — 3 урока
* [chem] Строение вещества — 4 урока
* [chem] Органическая химия — 4 урока
* [bio] Клетка и генетика — 4 урока
* [inf] Алгоритмы — 3 урока
*
* Запуск: node src/db/seed-theory.js (из папки backend/)
*/
'use strict';
require('./migrate'); // применить все миграции (включая lesson_blocks v4)
const db = require('./db');
/* ── Guard: пропустить, если уже засеяно ─────────────────────────────── */
const TITLES = [
'Тригонометрия', 'Производные и интегралы', 'Теория вероятностей и статистика',
'Механика', 'Электричество и магнетизм', 'Оптика и волны',
'Строение вещества', 'Органическая химия', 'Клетка и генетика', 'Алгоритмы и структуры данных',
];
const already = db.prepare(
`SELECT 1 FROM courses WHERE title IN (${TITLES.map(() => '?').join(',')}) LIMIT 1`
).get(...TITLES);
if (already) { console.log('seed-theory: already seeded — skipping.'); process.exit(0); }
/* ── Найти/создать преподавателя ─────────────────────────────────────── */
let teacher = db.prepare("SELECT id FROM users WHERE role IN ('teacher','admin') LIMIT 1").get();
if (!teacher) {
const bcrypt = require('bcryptjs');
const hash = bcrypt.hashSync('teacher123', 10);
const r = db.prepare(
"INSERT INTO users (email, password_hash, name, role) VALUES (?, ?, ?, 'teacher')"
).run('teacher@learnspace.local', hash, 'Преподаватель');
teacher = { id: Number(r.lastInsertRowid) };
console.log('Created system teacher, id =', teacher.id);
}
const TEACHER = Number(teacher.id);
/* ── Хелперы ─────────────────────────────────────────────────────────── */
function J(obj) { return JSON.stringify(obj); }
function course(slug, title, desc, emoji, order) {
return Number(db.prepare(
`INSERT INTO courses (subject_slug, title, description, cover_emoji, order_index, is_published, created_by)
VALUES (?, ?, ?, ?, ?, 1, ?)`
).run(slug, title, desc, emoji, order, TEACHER).lastInsertRowid);
}
function section(courseId, title, order) {
return Number(db.prepare(
`INSERT INTO course_sections (course_id, title, order_index) VALUES (?, ?, ?)`
).run(courseId, title, order).lastInsertRowid);
}
function lesson(courseId, sectionId, title, order) {
return Number(db.prepare(
`INSERT INTO lessons (course_id, section_id, title, order_index, is_published) VALUES (?, ?, ?, ?, 1)`
).run(courseId, sectionId, title, order).lastInsertRowid);
}
function block(lid, type, order, data) {
db.prepare(
`INSERT INTO lesson_blocks (lesson_id, type, order_index, data) VALUES (?, ?, ?, ?)`
).run(lid, type, order, J(data));
}
/* ═══════════════════════════════════════════════════════════════════════
Транзакция
═══════════════════════════════════════════════════════════════════════ */
db.transaction(() => {
/* ══════════════════════════════════════════════════════════════
КУРС: Тригонометрия [math]
══════════════════════════════════════════════════════════════ */
const C_TRIG = course('math', 'Тригонометрия',
'Единичная окружность, тригонометрические функции, формулы и уравнения', '📐', 1);
const S_T1 = section(C_TRIG, 'Основы', 1);
const S_T2 = section(C_TRIG, 'Формулы', 2);
{
const L = lesson(C_TRIG, S_T1, 'Единичная окружность и углы', 1);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Единичная окружность' });
block(L, 'text', ++b, { text: 'Единичная окружность — окружность с центром в начале координат и радиусом 1. Любая точка на ней имеет координаты (cos α, sin α), где α — угол от положительной оси OX против часовой стрелки.' });
block(L, 'callout', ++b, { style: 'info', text: 'Радиус равен 1, поэтому sin α = y/1 = y, cos α = x/1 = x. Это упрощает все формулы.' });
block(L, 'heading', ++b, { level: 3, text: 'Радианы и градусы' });
block(L, 'text', ++b, { text: 'Один радиан — угол, при котором длина дуги равна радиусу. Полный оборот: 360° = 2π рад.' });
block(L, 'formula', ++b, { tex: '\\alpha_{\\text{рад}} = \\dfrac{\\alpha_{\\text{град}}}{180°} \\cdot \\pi' });
block(L, 'table', ++b, {
header: ['Градусы', '0°', '30°', '45°', '60°', '90°', '180°', '270°', '360°'],
rows: [['Радианы', '0', 'π/6', 'π/4', 'π/3', 'π/2', 'π', '3π/2', '2π']],
});
block(L, 'heading', ++b, { level: 3, text: 'Знаки в четвертях' });
block(L, 'table', ++b, {
header: ['Четверть', 'Углы', 'sin', 'cos', 'tg'],
rows: [
['I', '0°–90°', '+', '+', '+'],
['II', '90°–180°', '+', '', ''],
['III', '180°–270°', '', '', '+'],
['IV', '270°–360°', '', '+', ''],
],
});
block(L, 'callout', ++b, { style: 'success', text: 'Запоминалка: «Все Студенты Такие Кретины» (All Students Take Calculus) — в I все положительны, во II только sin, в III только tg, в IV только cos.' });
block(L, 'quiz', ++b, {
question: 'В какой четверти cos α < 0 и sin α > 0?',
options: ['I', 'II', 'III', 'IV'],
correctIndex: 1,
});
}
{
const L = lesson(C_TRIG, S_T1, 'Синус и косинус', 2);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Синус и косинус угла' });
block(L, 'text', ++b, { text: 'sin α — ордината точки на единичной окружности. cos α — абсцисса. Оба лежат в [−1, 1].' });
block(L, 'formula', ++b, { tex: '-1 \\le \\sin \\alpha \\le 1, \\quad -1 \\le \\cos \\alpha \\le 1' });
block(L, 'table', ++b, {
header: ['α', '0°', '30°', '45°', '60°', '90°'],
rows: [
['sin α', '0', '½', '√2/2', '√3/2', '1'],
['cos α', '1', '√3/2', '√2/2', '½', '0'],
],
});
block(L, 'callout', ++b, { style: 'success', text: 'Для sin: √0/2, √1/2, √2/2, √3/2, √4/2 = 0, ½, √2/2, √3/2, 1. Для cos — в обратном порядке.' });
block(L, 'heading', ++b, { level: 3, text: 'Основное тригонометрическое тождество' });
block(L, 'formula', ++b, { tex: '\\sin^2 \\alpha + \\cos^2 \\alpha = 1' });
block(L, 'text', ++b, { text: 'Следует из теоремы Пифагора: точка (cos α, sin α) лежит на окружности r = 1, поэтому x² + y² = 1.' });
block(L, 'quiz', ++b, {
question: 'sin²(60°) + cos²(60°) = ?',
options: ['0', '½', '1', '√3'],
correctIndex: 2,
});
block(L, 'flashcard', ++b, { front: 'sin 30° = ?', back: '½ = 0.5' });
block(L, 'flashcard', ++b, { front: 'cos 45° = ?', back: '√2/2 ≈ 0.707' });
}
{
const L = lesson(C_TRIG, S_T1, 'Тангенс и котангенс', 3);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Тангенс и котангенс' });
block(L, 'formula', ++b, { label: 'Тангенс', tex: '\\tan \\alpha = \\dfrac{\\sin \\alpha}{\\cos \\alpha}' });
block(L, 'formula', ++b, { label: 'Котангенс', tex: '\\cot \\alpha = \\dfrac{\\cos \\alpha}{\\sin \\alpha} = \\dfrac{1}{\\tan \\alpha}' });
block(L, 'callout', ++b, { style: 'warning', text: 'tg α не определён при cos α = 0 (α = 90° + 180°·n).\nctg α не определён при sin α = 0 (α = 180°·n).' });
block(L, 'table', ++b, {
header: ['α', '0°', '30°', '45°', '60°', '90°'],
rows: [
['tg α', '0', '√3/3', '1', '√3', '—'],
['ctg α', '—', '√3', '1', '√3/3', '0'],
],
});
block(L, 'quiz', ++b, {
question: 'tg 45° = ?',
options: ['0', '½', '1', '√3'],
correctIndex: 2,
});
}
{
const L = lesson(C_TRIG, S_T2, 'Формулы приведения и сложения', 4);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Формулы приведения' });
block(L, 'callout', ++b, { style: 'info', text: 'Правило: 1) определите четверть → знак результата; 2) если аргумент π/2 или 3π/2 — функция меняется на кофункцию (sin↔cos, tg↔ctg); π или 2π — не меняется.' });
block(L, 'table', ++b, {
header: ['', 'sin', 'cos', 'tg'],
rows: [
['π/2 α', 'cos α', 'sin α', 'ctg α'],
[ α', 'sin α', 'cos α', 'tg α'],
['π + α', 'sin α', 'cos α', 'tg α'],
['2π α', 'sin α', 'cos α', 'tg α'],
],
});
block(L, 'heading', ++b, { level: 2, text: 'Формулы сложения' });
block(L, 'formula', ++b, { tex: '\\sin(\\alpha \\pm \\beta) = \\sin\\alpha\\cos\\beta \\pm \\cos\\alpha\\sin\\beta' });
block(L, 'formula', ++b, { tex: '\\cos(\\alpha \\pm \\beta) = \\cos\\alpha\\cos\\beta \\mp \\sin\\alpha\\sin\\beta' });
block(L, 'formula', ++b, { tex: '\\tan(\\alpha \\pm \\beta) = \\dfrac{\\tan\\alpha \\pm \\tan\\beta}{1 \\mp \\tan\\alpha\\tan\\beta}' });
block(L, 'heading', ++b, { level: 3, text: 'Формулы двойного угла' });
block(L, 'formula', ++b, { tex: '\\sin 2\\alpha = 2\\sin\\alpha\\cos\\alpha' });
block(L, 'formula', ++b, { tex: '\\cos 2\\alpha = \\cos^2\\alpha - \\sin^2\\alpha = 2\\cos^2\\alpha - 1 = 1 - 2\\sin^2\\alpha' });
block(L, 'quiz', ++b, {
question: 'sin(π α) = ?',
options: ['sin α', 'sin α', 'cos α', 'cos α'],
correctIndex: 0,
});
block(L, 'flashcard', ++b, { front: 'sin(α + β) = ?', back: 'sin α · cos β + cos α · sin β' });
block(L, 'flashcard', ++b, { front: 'cos 2α = ?', back: 'cos²α sin²α = 2cos²α 1 = 1 2sin²α' });
}
/* ══════════════════════════════════════════════════════════════
КУРС: Производные и интегралы [math]
══════════════════════════════════════════════════════════════ */
const C_CALC = course('math', 'Производные и интегралы',
'Дифференциальное и интегральное исчисление: производная, правила, применение, первообразная', '∫', 2);
const S_C1 = section(C_CALC, 'Дифференциальное исчисление', 1);
const S_C2 = section(C_CALC, 'Интегральное исчисление', 2);
{
const L = lesson(C_CALC, S_C1, 'Понятие производной', 1);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Производная функции' });
block(L, 'text', ++b, { text: 'Производная — мгновенная скорость изменения функции. Геометрически: тангенс угла наклона касательной к графику.' });
block(L, 'formula', ++b, { label: 'Определение', tex: "f'(x) = \\lim_{\\Delta x \\to 0} \\frac{f(x + \\Delta x) - f(x)}{\\Delta x}" });
block(L, 'table', ++b, {
header: ['f(x)', "f'(x)"],
rows: [
['C', '0'], ['xⁿ', 'n·xⁿ⁻¹'], ['eˣ', 'eˣ'], ['aˣ', 'aˣ·ln a'],
['ln x', '1/x'], ['sin x', 'cos x'], ['cos x', 'sin x'],
['tg x', '1/cos²x'], ['√x', '1/(2√x)'],
],
});
block(L, 'formula', ++b, { label: 'Произведение', tex: '(fg)\' = f\'g + fg\'' });
block(L, 'formula', ++b, { label: 'Частное', tex: '\\left(\\dfrac{f}{g}\\right)\' = \\dfrac{f\'g - fg\'}{g^2}' });
block(L, 'formula', ++b, { label: 'Сложная функция', tex: '(f(g(x)))\' = f\'(g(x)) \\cdot g\'(x)' });
block(L, 'accordion', ++b, { title: 'Пример: f(x) = x³ 5x + 2', content: "f'(x) = 3x² 5\n(x³)' = 3x², (5x)' = 5, (2)' = 0" });
block(L, 'quiz', ++b, {
question: "(4x³)' = ?",
options: ['4x²', '12x²', '12x³', '4x⁴'],
correctIndex: 1,
});
block(L, 'flashcard', ++b, { front: "(xⁿ)' = ?", back: 'n · xⁿ⁻¹\nПример: (x⁵)\' = 5x⁴' });
}
{
const L = lesson(C_CALC, S_C1, 'Применение производной', 2);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Применение производной' });
block(L, 'callout', ++b, { style: 'info', text: "Алгоритм нахождения экстремумов:\n1. Найти f'(x)\n2. Решить f'(x) = 0\n3. Определить знак f' слева и справа\n4. «+→−» — максимум, «−→+» — минимум" });
block(L, 'formula', ++b, { label: 'Признак через вторую производную', tex: "f''(x_0) > 0 \\Rightarrow \\text{min}, \\quad f''(x_0) < 0 \\Rightarrow \\text{max}" });
block(L, 'formula', ++b, { label: 'Уравнение касательной', tex: "y = f(x_0) + f'(x_0)(x - x_0)" });
block(L, 'diagram', ++b, {
code: "graph LR\n A[\"Найти f'(x)\"] --> B[\"Нули f'(x)\"]\n B --> C[\"Знаки f'(x)\"]\n C --> D[\"min / max\"]\n D --> E[\"f''(x) — выпуклость\"]",
caption: 'Алгоритм исследования функции',
});
block(L, 'quiz', ++b, {
question: 'f(x) = x² 4x + 3. При каком x минимум?',
options: ['x = 0', 'x = 2', 'x = 3', 'x = 2'],
correctIndex: 1,
});
}
{
const L = lesson(C_CALC, S_C2, 'Первообразная и неопределённый интеграл', 3);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Первообразная' });
block(L, 'text', ++b, { text: 'F(x) — первообразная f(x), если F\'(x) = f(x). Первообразных бесконечно много: F(x) + C.' });
block(L, 'formula', ++b, { label: 'Неопределённый интеграл', tex: '\\int f(x)\\, dx = F(x) + C' });
block(L, 'table', ++b, {
header: ['Интеграл', 'Результат'],
rows: [
['∫ xⁿ dx', 'xⁿ⁺¹/(n+1) + C'], ['∫ dx/x', 'ln|x| + C'],
['∫ eˣ dx', 'eˣ + C'], ['∫ sin x dx', 'cos x + C'],
['∫ cos x dx', 'sin x + C'], ['∫ aˣ dx', 'aˣ/ln a + C'],
],
});
block(L, 'callout', ++b, { style: 'warning', text: 'Проверить интеграл: взять производную результата — должна получиться подынтегральная функция.' });
block(L, 'quiz', ++b, {
question: '∫ 3x² dx = ?',
options: ['6x + C', 'x³ + C', '3x³ + C', 'x² + C'],
correctIndex: 1,
});
block(L, 'flashcard', ++b, { front: '∫ cos x dx = ?', back: "sin x + C\nПроверка: (sin x)' = cos x ✓" });
}
{
const L = lesson(C_CALC, S_C2, 'Определённый интеграл и площадь', 4);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Определённый интеграл' });
block(L, 'formula', ++b, { label: 'Формула Ньютона–Лейбница', tex: '\\int_a^b f(x)\\, dx = F(b) - F(a)' });
block(L, 'text', ++b, { text: 'Геометрический смысл: площадь под графиком f(x) ≥ 0 на [a, b]. Ниже оси — вычитается.' });
block(L, 'formula', ++b, { label: 'Площадь между двумя кривыми', tex: 'S = \\int_a^b |f(x) - g(x)|\\, dx' });
block(L, 'table', ++b, {
header: ['Свойство', 'Запись'],
rows: [
['Линейность', '∫[αf + βg] = α∫f + β∫g'],
['Аддитивность', '∫ₐᵇ = ∫ₐᶜ + ∫ᶜᵇ'],
['Смена пределов', '∫ₐᵇ = −∫ᵦₐ'],
],
});
block(L, 'quiz', ++b, {
question: '∫₀² x dx = ?',
options: ['1', '2', '4', '½'],
correctIndex: 1,
});
block(L, 'alert', ++b, { style: 'exam', text: 'ЕГЭ профиль: отработайте алгоритм — найти первообразную → подставить пределы → вычесть F(a) из F(b).' });
}
/* ══════════════════════════════════════════════════════════════
КУРС: Теория вероятностей [math]
══════════════════════════════════════════════════════════════ */
const C_PROB = course('math', 'Теория вероятностей и статистика',
'Классическая вероятность, формулы сложения и умножения, случайные величины', '🎲', 3);
const S_P1 = section(C_PROB, 'Классическая вероятность', 1);
const S_P2 = section(C_PROB, 'Случайные величины', 2);
{
const L = lesson(C_PROB, S_P1, 'Классическое определение вероятности', 1);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Вероятность событий' });
block(L, 'formula', ++b, { label: 'Классическая вероятность', tex: 'P(A) = \\dfrac{m}{n}' });
block(L, 'text', ++b, { text: 'm — число благоприятных исходов, n — общее число равновозможных.\nВсегда: 0 ≤ P(A) ≤ 1.' });
block(L, 'table', ++b, {
header: ['Схема', 'Формула', 'Смысл'],
rows: [
['Перестановки', 'Pₙ = n!', 'Расставить n предметов'],
['Размещения', 'Aₙᵏ = n!/(nk)!', 'k из n с учётом порядка'],
['Сочетания', 'Cₙᵏ = n!/(k!(nk)!)', 'k из n без порядка'],
],
});
block(L, 'accordion', ++b, {
title: 'Пример: вероятность выпадения 3 при броске кубика',
content: 'n = 6, m = 1 → P = 1/6 ≈ 0.167',
});
block(L, 'quiz', ++b, {
question: 'В урне 4 белых и 6 чёрных. Вероятность вытащить белый?',
options: ['0.4', '0.6', '0.25', '0.5'],
correctIndex: 0,
});
}
{
const L = lesson(C_PROB, S_P1, 'Формулы сложения и умножения', 2);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Операции над событиями' });
block(L, 'formula', ++b, { label: 'Сложение (несовместные)', tex: 'P(A \\cup B) = P(A) + P(B)' });
block(L, 'formula', ++b, { label: 'Сложение (общий случай)', tex: 'P(A \\cup B) = P(A) + P(B) - P(A \\cap B)' });
block(L, 'formula', ++b, { label: 'Противоположное событие', tex: 'P(\\bar{A}) = 1 - P(A)' });
block(L, 'formula', ++b, { label: 'Умножение (независимые)', tex: 'P(A \\cap B) = P(A) \\cdot P(B)' });
block(L, 'formula', ++b, { label: 'Условная вероятность', tex: 'P(B|A) = \\dfrac{P(A \\cap B)}{P(A)}' });
block(L, 'quiz', ++b, {
question: 'Стрелок A попадает с P=0.7, B с P=0.8. Оба стреляют. P(хотя бы одного попадания)?',
options: ['1.5', '0.56', '0.94', '0.75'],
correctIndex: 2,
});
}
{
const L = lesson(C_PROB, S_P2, 'Случайные величины и математическое ожидание', 3);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Дискретная случайная величина' });
block(L, 'formula', ++b, { label: 'Математическое ожидание', tex: 'M(X) = \\sum_{i=1}^{n} x_i p_i' });
block(L, 'formula', ++b, { label: 'Дисперсия', tex: 'D(X) = \\sum_{i}(x_i - M(X))^2 p_i' });
block(L, 'formula', ++b, { label: 'СКО', tex: '\\sigma = \\sqrt{D(X)}' });
block(L, 'callout', ++b, { style: 'info', text: 'M(X) — «центр тяжести» распределения. σ имеет те же единицы, что X. Правило трёх сигм: 68% в [μ−σ, μ+σ], 95% в [μ−2σ, μ+2σ], 99.7% в [μ−3σ, μ+3σ].' });
block(L, 'formula', ++b, { label: 'Нормальное распределение (плотность)', tex: 'f(x) = \\dfrac{1}{\\sigma\\sqrt{2\\pi}} e^{-\\frac{(x-\\mu)^2}{2\\sigma^2}}' });
block(L, 'quiz', ++b, {
question: 'X принимает 1, 2, 3 с вероятностями 0.2, 0.5, 0.3. M(X) = ?',
options: ['1.5', '2.0', '2.1', '1.8'],
correctIndex: 2,
});
}
/* ══════════════════════════════════════════════════════════════
КУРС: Механика [phys]
══════════════════════════════════════════════════════════════ */
const C_MECH = course('phys', 'Механика',
'Кинематика, динамика, законы Ньютона, энергия и импульс', '🚀', 1);
const S_M1 = section(C_MECH, 'Кинематика', 1);
const S_M2 = section(C_MECH, 'Динамика', 2);
const S_M3 = section(C_MECH, 'Законы сохранения', 3);
{
const L = lesson(C_MECH, S_M1, 'Прямолинейное движение', 1);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Основные понятия кинематики' });
block(L, 'text', ++b, { text: 'Кинематика описывает движение без анализа причин.\n\nПеремещение s⃗ [м] — вектор от старта к финишу.\nСкорость v⃗ [м/с] — быстрота изменения положения.\nУскорение a⃗ [м/с²] — быстрота изменения скорости.' });
block(L, 'callout', ++b, { style: 'info', text: 'Путь ≠ перемещение. Путь — длина траектории (скаляр ≥ 0). При движении «туда-обратно» путь > 0, перемещение = 0.' });
block(L, 'formula', ++b, { label: 'Равномерное движение', tex: 'x = x_0 + v \\cdot t' });
block(L, 'formula', ++b, { label: 'Скорость при равноускоренном', tex: 'v = v_0 + a \\cdot t' });
block(L, 'formula', ++b, { label: 'Координата', tex: 'x = x_0 + v_0 t + \\dfrac{a t^2}{2}' });
block(L, 'formula', ++b, { label: 'Без времени', tex: 'v^2 = v_0^2 + 2a \\cdot s' });
block(L, 'quiz', ++b, {
question: 'Автомобиль разгоняется из покоя с a = 2 м/с². Путь за 5 с?',
options: ['10 м', '25 м', '50 м', '5 м'],
correctIndex: 1,
});
}
{
const L = lesson(C_MECH, S_M2, 'Законы Ньютона', 2);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Три закона Ньютона' });
block(L, 'text', ++b, { text: 'I закон (инерция): тело сохраняет покой или равномерное прямолинейное движение, если равнодействующая сил = 0.' });
block(L, 'formula', ++b, { label: 'II закон', tex: '\\vec{F} = m \\vec{a}' });
block(L, 'formula', ++b, { label: 'III закон', tex: '\\vec{F}_{12} = -\\vec{F}_{21}' });
block(L, 'text', ++b, { text: 'Силы в механике:\n• Тяжесть: F = mg (вниз)\n• Нормальная реакция N ⊥ опоре\n• Трение: F = μN (против движения)\n• Упругость: F = −kx (закон Гука)' });
block(L, 'quiz', ++b, {
question: 'К телу m = 4 кг приложена F = 12 Н (без трения). Ускорение?',
options: ['2 м/с²', '3 м/с²', '4 м/с²', '48 м/с²'],
correctIndex: 1,
});
block(L, 'flashcard', ++b, { front: 'Второй закон Ньютона', back: 'F⃗ = m·a⃗\nУскорение пропорционально силе и обратно пропорционально массе.' });
}
{
const L = lesson(C_MECH, S_M3, 'Работа и энергия', 3);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Работа и механическая энергия' });
block(L, 'formula', ++b, { label: 'Работа', tex: 'A = F \\cdot s \\cdot \\cos \\alpha' });
block(L, 'formula', ++b, { label: 'Кинетическая энергия', tex: 'E_k = \\dfrac{mv^2}{2}' });
block(L, 'formula', ++b, { label: 'Потенциальная энергия', tex: 'E_p = mgh' });
block(L, 'formula', ++b, { label: 'Закон сохранения', tex: 'E_k + E_p = \\text{const}' });
block(L, 'callout', ++b, { style: 'warning', text: 'С трением: E_k1 + E_p1 = E_k2 + E_p2 + Q, где Q — тепловые потери.' });
block(L, 'quiz', ++b, {
question: 'Мяч 0.5 кг падает с h = 20 м (g = 10 м/с²). Eₖ у земли?',
options: ['50 Дж', '100 Дж', '200 Дж', '10 Дж'],
correctIndex: 1,
});
}
{
const L = lesson(C_MECH, S_M3, 'Импульс и закон сохранения', 4);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Импульс тела' });
block(L, 'formula', ++b, { tex: '\\vec{p} = m \\vec{v}' });
block(L, 'formula', ++b, { label: 'Импульс силы', tex: '\\vec{F} \\cdot \\Delta t = \\Delta \\vec{p}' });
block(L, 'formula', ++b, { label: 'Закон сохранения импульса', tex: 'm_1 \\vec{v}_1 + m_2 \\vec{v}_2 = m_1 \\vec{v}_1\\prime + m_2 \\vec{v}_2\\prime' });
block(L, 'columns', ++b, {
cols: [
{ content: '<strong>Упругий удар</strong><br>Сохраняется и импульс, и кинетическая энергия.' },
{ content: '<strong>Неупругий удар</strong><br>Тела слипаются, сохраняется только импульс.' },
],
});
block(L, 'quiz', ++b, {
question: 'Вагон 30 т (v = 2 м/с) сцепляется с неподвижным 20 т. Скорость?',
options: ['1 м/с', '1.2 м/с', '1.5 м/с', '2 м/с'],
correctIndex: 1,
});
}
/* ══════════════════════════════════════════════════════════════
КУРС: Электричество и магнетизм [phys]
══════════════════════════════════════════════════════════════ */
const C_EM = course('phys', 'Электричество и магнетизм',
'Закон Кулона, электрическое поле, постоянный ток, магнитное поле, индукция', '⚡', 2);
const S_E1 = section(C_EM, 'Электростатика', 1);
const S_E2 = section(C_EM, 'Постоянный ток', 2);
const S_E3 = section(C_EM, 'Магнетизм', 3);
{
const L = lesson(C_EM, S_E1, 'Закон Кулона и электрическое поле', 1);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Электрический заряд' });
block(L, 'text', ++b, { text: 'Заряд квантуется: e ≈ 1.6·10⁻¹⁹ Кл. Закон сохранения: суммарный заряд замкнутой системы постоянен.' });
block(L, 'formula', ++b, { label: 'Закон Кулона', tex: 'F = k \\dfrac{|q_1 q_2|}{r^2}, \\quad k = 9 \\cdot 10^9 \\text{ Н·м}^2/\\text{Кл}^2' });
block(L, 'formula', ++b, { label: 'Напряжённость поля', tex: 'E = k \\dfrac{Q}{r^2}, \\quad E = \\dfrac{U}{d}' });
block(L, 'formula', ++b, { label: 'Потенциал', tex: '\\varphi = k \\dfrac{Q}{r}, \\quad U_{AB} = \\varphi_A - \\varphi_B' });
block(L, 'quiz', ++b, {
question: 'q₁ = q₂ = 10⁻⁶ Кл, r = 1 м. Сила?',
options: ['9·10⁻³ Н', '9·10⁻⁶ Н', '9·10⁻² Н', '9 Н'],
correctIndex: 0,
});
}
{
const L = lesson(C_EM, S_E2, 'Постоянный ток. Законы Ома', 2);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Законы Ома' });
block(L, 'formula', ++b, { label: 'Закон Ома (участок)', tex: 'I = \\dfrac{U}{R}' });
block(L, 'formula', ++b, { label: 'Закон Ома (полная цепь)', tex: 'I = \\dfrac{\\mathcal{E}}{R + r}' });
block(L, 'columns', ++b, {
cols: [
{ content: '<strong>Последовательно:</strong><br>R = R₁ + R₂<br>I одинаковый<br>U = U₁ + U₂' },
{ content: '<strong>Параллельно:</strong><br>1/R = 1/R₁ + 1/R₂<br>U одинаковое<br>I = I₁ + I₂' },
],
});
block(L, 'formula', ++b, { label: 'Мощность', tex: 'P = UI = I^2R = U^2/R' });
block(L, 'formula', ++b, { label: 'Закон Джоуля–Ленца', tex: 'Q = I^2Rt' });
block(L, 'quiz', ++b, {
question: 'R₁ = 3 Ом и R₂ = 6 Ом параллельно. Общее сопротивление?',
options: ['9 Ом', '4.5 Ом', '2 Ом', '18 Ом'],
correctIndex: 2,
});
}
{
const L = lesson(C_EM, S_E3, 'Магнитное поле. Сила Лоренца', 3);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Магнитное поле' });
block(L, 'formula', ++b, { label: 'Сила Лоренца', tex: 'F = qvB\\sin\\alpha' });
block(L, 'formula', ++b, { label: 'Сила Ампера', tex: 'F = BIl\\sin\\alpha' });
block(L, 'callout', ++b, { style: 'info', text: 'Направление силы Лоренца — правило левой руки: пальцы по v (+), ладонь на B, большой палец — F.' });
block(L, 'formula', ++b, { label: 'Радиус кругового движения заряда', tex: 'r = \\dfrac{mv}{qB}' });
block(L, 'formula', ++b, { label: 'Закон Фарадея (ЭДС индукции)', tex: '\\mathcal{E} = -\\dfrac{\\Delta\\Phi}{\\Delta t}, \\quad \\Phi = BS\\cos\\alpha' });
block(L, 'callout', ++b, { style: 'warning', text: 'Знак «−» — правило Ленца: индукционный ток противодействует изменению потока.' });
block(L, 'quiz', ++b, {
question: 'Протон (q=1.6·10⁻¹⁹, m=1.67·10⁻²⁷ кг) летит v=10⁶ м/с ⊥ полю B=0.1 Тл. Радиус?',
options: ['≈ 0.1 м', '≈ 0.01 м', '≈ 1 м', '≈ 10 м'],
correctIndex: 0,
});
}
{
const L = lesson(C_EM, S_E3, 'Колебательный контур и переменный ток', 4);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Электромагнитные колебания' });
block(L, 'formula', ++b, { label: 'Формула Томсона', tex: 'T = 2\\pi\\sqrt{LC}' });
block(L, 'formula', ++b, { label: 'Действующее значение', tex: 'I_{\\text{rms}} = \\dfrac{I_m}{\\sqrt{2}} \\approx 0.707\\, I_m' });
block(L, 'callout', ++b, { style: 'info', text: 'Приборы измеряют действующее значение. В розетке 220 В — это RMS, амплитуда ≈ 311 В.' });
block(L, 'formula', ++b, { label: 'Резонанс (X_L = X_C)', tex: '\\omega_0 = \\dfrac{1}{\\sqrt{LC}}, \\quad Z_{\\min} = R' });
block(L, 'quiz', ++b, {
question: 'Контур L=0.1 Гн, C=10 мкФ. Частота?',
options: ['≈ 159 Гц', '≈ 16 Гц', '≈ 1590 Гц', '≈ 100 Гц'],
correctIndex: 0,
});
block(L, 'flashcard', ++b, { front: 'Формула Томсона', back: 'T = 2π√(LC) — период свободных колебаний контура' });
}
/* ══════════════════════════════════════════════════════════════
КУРС: Оптика и волны [phys]
══════════════════════════════════════════════════════════════ */
const C_OPT = course('phys', 'Оптика и волны',
'Геометрическая оптика, линзы, волновые явления: интерференция, дифракция', '🔭', 3);
const S_O1 = section(C_OPT, 'Геометрическая оптика', 1);
const S_O2 = section(C_OPT, 'Волновая оптика', 2);
{
const L = lesson(C_OPT, S_O1, 'Отражение и преломление света', 1);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Законы геометрической оптики' });
block(L, 'callout', ++b, { style: 'info', text: 'c ≈ 3·10⁸ м/с. В среде с n: v = c/n. Стекло: n ≈ 1.5, вода: n ≈ 1.33.' });
block(L, 'formula', ++b, { label: 'Закон отражения', tex: '\\alpha_1 = \\alpha_2' });
block(L, 'formula', ++b, { label: 'Закон Снеллиуса', tex: 'n_1 \\sin\\alpha_1 = n_2 \\sin\\alpha_2' });
block(L, 'formula', ++b, { label: 'Критический угол полного отражения', tex: '\\sin\\theta_c = \\dfrac{n_2}{n_1}' });
block(L, 'text', ++b, { text: 'При переходе из более плотной среды (n₁ > n₂) при угле > θc возникает полное внутреннее отражение. На этом работает оптоволокно.' });
block(L, 'quiz', ++b, {
question: 'Луч из воды (n=1.33) в воздух (n=1). Критический угол?',
options: ['sin⁻¹(0.75) ≈ 48.75°', '90°', '45°', '30°'],
correctIndex: 0,
});
}
{
const L = lesson(C_OPT, S_O1, 'Линзы', 2);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Тонкая линза' });
block(L, 'formula', ++b, { label: 'Формула тонкой линзы', tex: '\\dfrac{1}{f} = \\dfrac{1}{d} + \\dfrac{1}{v}' });
block(L, 'formula', ++b, { label: 'Увеличение', tex: 'M = \\dfrac{|v|}{d}' });
block(L, 'table', ++b, {
header: ['Положение предмета', 'Изображение', 'Применение'],
rows: [
['d > 2f', 'Действит., уменьш., перевёрн.', 'Объективы'],
['f < d < 2f', 'Действит., увелич., перевёрн.', 'Проекторы'],
['d < f', 'Мнимое, увелич., прямое', 'Лупа, очки'],
],
});
block(L, 'accordion', ++b, {
title: 'Пример: f = 10 см, d = 15 см → где изображение?',
content: '1/v = 1/10 1/15 = 1/30 → v = 30 см\nМ = 30/15 = 2 (увеличенное, перевёрнутое)',
});
block(L, 'quiz', ++b, {
question: 'Линза f=10 см, предмет d=15 см. Изображение?',
options: ['30 см', '30 см', '20 см', '15 см'],
correctIndex: 0,
});
block(L, 'flashcard', ++b, { front: 'Формула тонкой линзы', back: '1/f = 1/d + 1/v\nf > 0 — собирающая, f < 0 — рассеивающая' });
}
{
const L = lesson(C_OPT, S_O2, 'Интерференция и дифракция', 3);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Волновые свойства света' });
block(L, 'text', ++b, { text: 'Свет — поперечная ЭМ волна. Длина волны: фиолетовый ≈ 400 нм, красный ≈ 700 нм.' });
block(L, 'formula', ++b, { label: 'Максимум интерференции', tex: '\\Delta = m\\lambda, \\quad m = 0, \\pm1, \\pm2, \\ldots' });
block(L, 'formula', ++b, { label: 'Минимум интерференции', tex: '\\Delta = (2m+1)\\dfrac{\\lambda}{2}' });
block(L, 'formula', ++b, { label: 'Дифракционная решётка', tex: 'd \\sin\\theta = m\\lambda' });
block(L, 'formula', ++b, { label: 'Закон Малюса (поляризация)', tex: 'I = I_0 \\cos^2\\theta' });
block(L, 'quiz', ++b, {
question: 'Решётка d=2 мкм, λ=600 нм, m=1. Угол θ?',
options: ['17.5°', '30°', '15°', '45°'],
correctIndex: 0,
});
}
/* ══════════════════════════════════════════════════════════════
КУРС: Строение вещества [chem]
══════════════════════════════════════════════════════════════ */
const C_ATOM = course('chem', 'Строение вещества',
'Атом, электронные оболочки, периодический закон, химическая связь', '⚛️', 1);
const S_A1 = section(C_ATOM, 'Строение атома', 1);
const S_A2 = section(C_ATOM, 'Периодический закон', 2);
const S_A3 = section(C_ATOM, 'Химическая связь', 3);
{
const L = lesson(C_ATOM, S_A1, 'Модели атома', 1);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Строение атома' });
block(L, 'text', ++b, { text: 'Атом = ядро (протоны p⁺ + нейтроны n⁰) + электронная оболочка (e⁻).' });
block(L, 'callout', ++b, { style: 'info', text: 'Протон: заряд +1, масса ≈ 1 а.е.м.\nНейтрон: заряд 0, масса ≈ 1 а.е.м.\nЭлектрон: заряд −1, масса ≈ 1/1836 а.е.м.' });
block(L, 'formula', ++b, { label: 'Массовое число', tex: 'A = Z + N' });
block(L, 'text', ++b, { text: 'Z — число протонов (порядковый номер). N — число нейтронов. Изотопы — одинаковый Z, разный N.' });
block(L, 'quiz', ++b, {
question: 'Сколько нейтронов в ²⁷₁₃Al?',
options: ['13', '14', '27', '40'],
correctIndex: 1,
});
}
{
const L = lesson(C_ATOM, S_A1, 'Электронные оболочки', 2);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Электронные оболочки атома' });
block(L, 'text', ++b, { text: 'Электроны занимают энергетические уровни n = 1, 2, 3… Максимум на уровне: 2n².' });
block(L, 'table', ++b, {
header: ['Уровень n', 'Обозначение', 'Макс. e⁻', 'Подуровни'],
rows: [['1', 'K', '2', 's'], ['2', 'L', '8', 's, p'], ['3', 'M', '18', 's, p, d'], ['4', 'N', '32', 's, p, d, f']],
});
block(L, 'text', ++b, { text: 'Порядок заполнения: 1s→2s→2p→3s→3p→4s→3d→4p→…\n\nПринцип Паули: ≤ 2 e⁻ на орбитали.\nПравило Гунда: на одном подуровне сначала по одному.\nПравило Клечковского: порядок по (n+l).' });
block(L, 'callout', ++b, { style: 'success', text: 'Углерод (Z=6): 1s² 2s² 2p² — на 2p два электрона на разных орбиталях (правило Гунда).' });
block(L, 'quiz', ++b, {
question: 'Электронная конфигурация Na (Z=11)?',
options: ['1s² 2s² 2p⁶ 3s¹', '1s² 2s² 2p⁷', '1s² 2s² 2p⁶ 3p¹', '1s² 2s² 3s² 3p⁵'],
correctIndex: 0,
});
block(L, 'flashcard', ++b, { front: 'Макс. e⁻ на 3-м уровне?', back: '2n² = 18\n3s²(2) + 3p⁶(6) + 3d¹⁰(10) = 18' });
}
{
const L = lesson(C_ATOM, S_A2, 'Периодический закон Менделеева', 3);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Периодический закон' });
block(L, 'text', ++b, { text: 'Свойства элементов периодически зависят от заряда ядра.\n\nПериод — строка (номер = число уровней). Группа — столбец (сходные свойства).' });
block(L, 'columns', ++b, {
cols: [
{ content: '<strong>В периоде (→):</strong><br>Z растёт<br>Радиус ↓<br>ЭО ↑<br>Металл. свойства ↓' },
{ content: '<strong>В группе (↓):</strong><br>Радиус ↑<br>ЭО ↓<br>Металл. свойства ↑' },
],
});
block(L, 'callout', ++b, { style: 'success', text: 'Самый электроотрицательный элемент — F (правый верхний). Самый активный металл — Fr (левый нижний).' });
block(L, 'quiz', ++b, {
question: 'Какой элемент имеет наибольший атомный радиус: Li, Na, K, Rb?',
options: ['Li', 'Na', 'K', 'Rb'],
correctIndex: 3,
});
}
{
const L = lesson(C_ATOM, S_A3, 'Типы химической связи', 4);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Химическая связь' });
block(L, 'table', ++b, {
header: ['Тип', 'Механизм', 'Пример', 'Прочность'],
rows: [
['Ковалентная неполярная', 'Обобществление e⁻ (одинак. атомы)', 'H₂, Cl₂', 'Высокая'],
['Ковалентная полярная', 'Обобществление e⁻ (разные неМе)', 'HCl, H₂O', 'Высокая'],
['Ионная', 'Передача e⁻ (Ме+неМе)', 'NaCl, CaO', 'Высокая'],
['Металлическая', 'Электронный газ', 'Fe, Cu', 'Средняя'],
['Водородная', 'H···F/O/N', 'H₂O···H₂O', 'Низкая'],
],
});
block(L, 'callout', ++b, { style: 'info', text: 'Водородная связь объясняет аномально высокую t° кипения воды (100°C против 80°C у H₂S).' });
block(L, 'quiz', ++b, {
question: 'Тип связи в NaCl?',
options: ['Ковалентная неполярная', 'Ковалентная полярная', 'Ионная', 'Металлическая'],
correctIndex: 2,
});
block(L, 'flashcard', ++b, { front: 'Чем отличается ковалентная полярная от неполярной?', back: 'Полярная: e⁻ пара смещена к более ЭО атому (разные эл-ты).\nНеполярная: e⁻ пара симметрична (одинаковые эл-ты).' });
}
/* ══════════════════════════════════════════════════════════════
КУРС: Органическая химия [chem]
══════════════════════════════════════════════════════════════ */
const C_ORG = course('chem', 'Органическая химия',
'Углеводороды, кислородсодержащие соединения, биополимеры', '🧪', 2);
const S_G1 = section(C_ORG, 'Углеводороды', 1);
const S_G2 = section(C_ORG, 'Кислородсодержащие', 2);
const S_G3 = section(C_ORG, 'Биополимеры', 3);
{
const L = lesson(C_ORG, S_G1, 'Алканы — предельные углеводороды', 1);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Алканы CₙH₂ₙ₊₂' });
block(L, 'formula', ++b, { tex: 'C_n H_{2n+2}' });
block(L, 'text', ++b, { text: 'Все связи C–C и C–H одинарные (σ-связи). Метан CH₄, этан C₂H₆, пропан C₃H₈, бутан C₄H₁₀…\n\nС C₄ возможна структурная изомерия.' });
block(L, 'text', ++b, { text: 'Химические свойства:\n1. Горение: CH₄ + 2O₂ → CO₂ + 2H₂O\n2. Замещение: CH₄ + Cl₂ → CH₃Cl + HCl (свет, SR)\n3. Крекинг: C₁₀H₂₂ → C₅H₁₂ + C₅H₁₀' });
block(L, 'callout', ++b, { style: 'warning', text: 'Реакции присоединения (AE) для алканов НЕВОЗМОЖНЫ — нет π-связей. Это главное отличие от алкенов.' });
block(L, 'quiz', ++b, {
question: 'Общая формула алканов?',
options: ['CₙHₙ', 'CₙH₂ₙ', 'CₙH₂ₙ₊₂', 'CₙH₂ₙ₋₂'],
correctIndex: 2,
});
block(L, 'flashcard', ++b, { front: 'Алканы / алкены / алкины — общие формулы?', back: 'Алканы: CₙH₂ₙ₊₂\nАлкены: CₙH₂ₙ\nАлкины: CₙH₂ₙ₋₂' });
}
{
const L = lesson(C_ORG, S_G1, 'Алкены и алкины', 2);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Ненасыщенные углеводороды' });
block(L, 'formula', ++b, { label: 'Алкены', tex: 'C_n H_{2n}' });
block(L, 'formula', ++b, { label: 'Алкины', tex: 'C_n H_{2n-2}' });
block(L, 'text', ++b, { text: 'Реакции присоединения алкенов:\n1. Гидрирование: CH₂=CH₂ + H₂ → C₂H₆ (Ni, t°)\n2. Галогенирование: CH₂=CH₂ + Br₂ → CH₂BrCH₂Br (обесцвечивает бромную воду!)\n3. Гидратация: CH₂=CH₂ + H₂O → C₂H₅OH\n4. Полимеризация: nCH₂=CH₂ → (CH₂–CH₂)ₙ' });
block(L, 'callout', ++b, { style: 'info', text: 'Правило Марковникова: H присоединяется к более гидрогенизированному углероду несимметричного алкена.' });
block(L, 'quiz', ++b, {
question: 'Какая реакция невозможна для алканов?',
options: ['Горение', 'Замещение', 'Присоединение', 'Крекинг'],
correctIndex: 2,
});
}
{
const L = lesson(C_ORG, S_G2, 'Спирты и карбоновые кислоты', 3);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Кислородсодержащие соединения' });
block(L, 'matching', ++b, {
question: 'Класс → функциональная группа:',
pairs: [
{ left: 'Спирт', right: 'OH' },
{ left: 'Альдегид', right: 'CHO' },
{ left: 'Кислота', right: 'COOH' },
{ left: 'Амин', right: 'NH₂' },
],
});
block(L, 'text', ++b, { text: 'Свойства спиртов:\n1. + Na: 2C₂H₅OH + 2Na → 2C₂H₅ONa + H₂↑\n2. Окисление: C₂H₅OH → CH₃CHO → CH₃COOH\n3. Дегидратация (140°): → эфир\n4. Дегидратация (170°): C₂H₅OH → CH₂=CH₂ + H₂O' });
block(L, 'text', ++b, { text: 'Этерификация (кислота + спирт):\nCH₃COOH + C₂H₅OH ⇌ CH₃COOC₂H₅ + H₂O (кат. H₂SO₄)\n\nРеакция обратима. Продукт — сложный эфир.' });
block(L, 'quiz', ++b, {
question: 'При дегидратации C₂H₅OH при 170°C образуется:',
options: ['Эфир', 'CH₂=CH₂', 'Ацетальдегид', 'Ацетилен'],
correctIndex: 1,
});
}
{
const L = lesson(C_ORG, S_G3, 'Углеводы и белки', 4);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Биополимеры' });
block(L, 'formula', ++b, { label: 'Углеводы', tex: 'C_n(H_2O)_m' });
block(L, 'text', ++b, { text: 'Моносахариды: глюкоза и фруктоза C₆H₁₂O₆ (изомеры).\nДисахариды: сахароза C₁₂H₂₂O₁₁.\nПолисахариды: крахмал, целлюлоза (C₆H₁₀O₅)ₙ.\n\nБрожение: C₆H₁₂O₆ → 2C₂H₅OH + 2CO₂' });
block(L, 'text', ++b, { text: 'Белки — полимеры из аминокислот, связанных пептидными связями (–CO–NH–).\n\nСтруктуры: первичная (последовательность), вторичная (α-спираль), третичная (3D-форма), четвертичная (несколько цепей).' });
block(L, 'callout', ++b, { style: 'warning', text: 'Денатурация — разрушение вторичной и выше структур (не первичной). Причины: нагрев, кислоты, щёлочи. Необратима.' });
block(L, 'quiz', ++b, {
question: 'Какая связь соединяет аминокислоты в белке?',
options: ['Ионная', 'Водородная', 'Пептидная (CONH)', 'Дисульфидная'],
correctIndex: 2,
});
block(L, 'timeline', ++b, {
items: [
{ date: '1869', title: 'ДНК выделена Мишером', text: 'Из лейкоцитов выделено «нуклеиновое вещество».' },
{ date: '1944', title: 'ДНК — носитель наследственности', text: 'Эйвери показал: именно ДНК передаёт наследственную информацию.' },
{ date: '1953', title: 'Двойная спираль', text: 'Уотсон и Крик предложили модель двойной спирали ДНК.' },
],
});
}
/* ══════════════════════════════════════════════════════════════
КУРС: Клетка и генетика [bio]
══════════════════════════════════════════════════════════════ */
const C_BIO = course('bio', 'Клетка и генетика',
'Строение клетки, фотосинтез, клеточное дыхание, законы Менделя, ДНК и синтез белка', '🧬', 1);
const S_B1 = section(C_BIO, 'Строение клетки', 1);
const S_B2 = section(C_BIO, 'Обмен веществ', 2);
const S_B3 = section(C_BIO, 'Генетика', 3);
{
const L = lesson(C_BIO, S_B1, 'Строение эукариотической клетки', 1);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Строение клетки' });
block(L, 'callout', ++b, { style: 'info', text: 'Прокариоты (бактерии) — нет ядра и мембранных органоидов. Эукариоты (животные, растения, грибы) — есть ядро.' });
block(L, 'table', ++b, {
header: ['Органоид', 'Функция'],
rows: [
['Клеточная мембрана', 'Барьер, транспорт, рецепция'],
['Ядро', 'Хранение ДНК, регуляция'],
['Митохондрии', 'Синтез АТФ (дыхание)'],
['Рибосомы', 'Синтез белков'],
['ЭПС шероховатая', 'Синтез и транспорт белков'],
['Аппарат Гольджи', 'Модификация, упаковка белков'],
['Лизосомы', 'Переваривание (аутолиз)'],
['Хлоропласты', 'Фотосинтез (только у растений)'],
],
});
block(L, 'diagram', ++b, {
code: "graph LR\n A[Профаза] --> B[Метафаза] --> C[Анафаза] --> D[Телофаза]\n D --> E[2 дочерние клетки 2n]",
caption: 'Фазы митоза',
});
block(L, 'quiz', ++b, {
question: 'Какой органоид синтезирует АТФ?',
options: ['Рибосома', 'Хлоропласт', 'Митохондрия', 'Лизосома'],
correctIndex: 2,
});
block(L, 'flashcard', ++b, { front: 'Митоз vs мейоз?', back: 'Митоз: 2n → 2n (соматические, идентичные)\nМейоз: 2n → n (гаметы, 2 деления, кроссинговер)' });
}
{
const L = lesson(C_BIO, S_B2, 'Фотосинтез и клеточное дыхание', 2);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Энергетический обмен клетки' });
block(L, 'formula', ++b, { label: 'Фотосинтез', tex: '6CO_2 + 6H_2O \\xrightarrow{h\\nu} C_6H_{12}O_6 + 6O_2' });
block(L, 'columns', ++b, {
cols: [
{ content: '<strong>Световые реакции</strong> (тилакоиды):<br>Фотолиз воды → O₂<br>АТФ (фотофосфорилирование)<br>НАДФН' },
{ content: '<strong>Тёмновые реакции</strong> (строма, цикл Калвина):<br>Фиксация CO₂<br>Синтез глюкозы<br>Расход АТФ и НАДФН' },
],
});
block(L, 'formula', ++b, { label: 'Клеточное дыхание', tex: 'C_6H_{12}O_6 + 6O_2 \\rightarrow 6CO_2 + 6H_2O + 38\\,\\text{АТФ}' });
block(L, 'table', ++b, {
header: ['Этап', 'Место', 'АТФ'],
rows: [
['Гликолиз', 'Цитоплазма', '2'],
['Цикл Кребса', 'Матрикс митохондрий', '2'],
['Окислит. фосфорилирование', 'Внутр. мембрана митохондрий', '34'],
],
});
block(L, 'callout', ++b, { style: 'info', text: 'Без O₂: анаэробный гликолиз → 2 АТФ. Аэробное дыхание → 38 АТФ, в 19 раз эффективнее!' });
block(L, 'quiz', ++b, {
question: 'На каком этапе дыхания выделяется CO₂?',
options: ['Гликолиз', 'Цикл Кребса', 'Окислительное фосфорилирование', 'Все этапы'],
correctIndex: 1,
});
}
{
const L = lesson(C_BIO, S_B3, 'Законы Менделя', 3);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Генетика наследственности' });
block(L, 'text', ++b, { text: 'Ген — участок ДНК, кодирующий признак. Аллели — варианты гена. Доминантный (A) подавляет рецессивный (a). Генотип — набор генов, фенотип — его проявление.' });
block(L, 'table', ++b, {
header: ['Скрещивание', 'Расщепление F₂ по фенотипу'],
rows: [
['Aa × Aa (моногибридное)', '3:1'],
['AaBb × AaBb (дигибридное)', '9:3:3:1'],
['Aa × Aa (неполное доминирование)', '1:2:1'],
['Aa × aa (анализирующее)', '1:1'],
],
});
block(L, 'callout', ++b, { style: 'warning', text: 'III закон (независимое наследование) нарушается при сцеплении генов — если они в одной хромосоме.' });
block(L, 'quiz', ++b, {
question: 'Скрещивание Aa × Aa. Вероятность фенотипа aa?',
options: ['25%', '50%', '75%', '100%'],
correctIndex: 0,
});
block(L, 'flashcard', ++b, { front: 'II закон Менделя (расщепление)', back: 'Aa × Aa:\nФенотип: 3 доминантных : 1 рецессивный\nГенотип: 1 AA : 2 Aa : 1 aa' });
}
{
const L = lesson(C_BIO, S_B3, 'ДНК и синтез белка', 4);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Молекула ДНК и реализация наследственности' });
block(L, 'text', ++b, { text: 'ДНК — двойная спираль. Принцип комплементарности: A–T (2 связи), G–C (3 связи).\nПравило Чаргаффа: %A = %T, %G = %C.' });
block(L, 'columns', ++b, {
cols: [
{ content: '<strong>Транскрипция</strong> (ядро):<br>ДНК → мРНК<br>РНК-полимераза<br>T заменяется на U' },
{ content: '<strong>Трансляция</strong> (рибосомы):<br>мРНК → белок<br>Кодон = 3 нуклеотида<br>Код универсален' },
],
});
block(L, 'formula', ++b, { label: 'Нуклеотиды ↔ аминокислоты', tex: 'N_{\\text{нукл}} = 3 \\times N_{\\text{а.к.}}' });
block(L, 'diagram', ++b, {
code: "graph LR\n DNA[ДНК] -->|Репликация| DNA2[ДНК-копия]\n DNA -->|Транскрипция| mRNA[мРНК]\n mRNA -->|Трансляция| P[Белок]",
caption: 'Центральная догма молекулярной биологии',
});
block(L, 'quiz', ++b, {
question: 'В ДНК 30% аденина. Сколько % тимина?',
options: ['30%', '20%', '70%', '60%'],
correctIndex: 0,
});
block(L, 'alert', ++b, { style: 'exam', text: 'В мРНК нет тимина — только урацил. При транскрипции: A→U, T→A, G→C, C→G.' });
}
/* ══════════════════════════════════════════════════════════════
КУРС: Алгоритмы и структуры данных [inf]
══════════════════════════════════════════════════════════════ */
const C_ALG = course('inf', 'Алгоритмы и структуры данных',
'Сложность, сортировки, деревья и графы', '💻', 1);
const S_L1 = section(C_ALG, 'Основы', 1);
const S_L2 = section(C_ALG, 'Сортировки', 2);
const S_L3 = section(C_ALG, 'Структуры данных', 3);
{
const L = lesson(C_ALG, S_L1, 'Сложность алгоритмов (O-нотация)', 1);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Big-O нотация' });
block(L, 'text', ++b, { text: 'O-нотация описывает верхнюю оценку роста времени выполнения при n→∞. Константы и малые слагаемые игнорируются: O(2n) = O(n).' });
block(L, 'table', ++b, {
header: ['Сложность', 'Название', 'Пример'],
rows: [
['O(1)', 'Константная', 'Доступ к элементу массива'],
['O(log n)', 'Логарифмическая', 'Бинарный поиск'],
['O(n)', 'Линейная', 'Линейный поиск'],
['O(n log n)', 'Квазилинейная', 'Быстрая сортировка'],
['O(n²)', 'Квадратичная', 'Сортировка пузырьком'],
['O(2ⁿ)', 'Экспоненциальная', 'Перебор подмножеств'],
],
});
block(L, 'code', ++b, {
lang: 'python',
code: `# O(log n) — бинарный поиск
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target: return mid
elif arr[mid] < target: left = mid + 1
else: right = mid - 1
return -1`,
});
block(L, 'quiz', ++b, {
question: 'Два вложенных цикла по n элементам — сложность?',
options: ['O(n)', 'O(n log n)', 'O(n²)', 'O(2ⁿ)'],
correctIndex: 2,
});
block(L, 'flashcard', ++b, { front: 'O(n log n) vs O(n²) при n=1000?', back: 'O(n log n) ≈ 10 000 оп.\nO(n²) = 1 000 000 оп.\nРазница в 100 раз!' });
}
{
const L = lesson(C_ALG, S_L2, 'Алгоритмы сортировки', 2);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Сравнение алгоритмов сортировки' });
block(L, 'table', ++b, {
header: ['Алгоритм', 'Лучшее', 'Среднее', 'Худшее', 'Память'],
rows: [
['Пузырьковая', 'O(n)', 'O(n²)', 'O(n²)', 'O(1)'],
['Вставками', 'O(n)', 'O(n²)', 'O(n²)', 'O(1)'],
['Слиянием', 'O(n log n)', 'O(n log n)', 'O(n log n)', 'O(n)'],
['Быстрая', 'O(n log n)', 'O(n log n)', 'O(n²)', 'O(log n)'],
['Кучей', 'O(n log n)', 'O(n log n)', 'O(n log n)', 'O(1)'],
],
});
block(L, 'code', ++b, {
lang: 'python',
code: `def quicksort(arr):
if len(arr) <= 1: return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
mid = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quicksort(left) + mid + quicksort(right)`,
});
block(L, 'callout', ++b, { style: 'warning', text: 'QuickSort в худшем случае (уже отсортированный + крайний pivot) деградирует до O(n²). Решение: случайный pivot.' });
block(L, 'ordering', ++b, {
question: 'Шаги сортировки слиянием в правильном порядке:',
items: [
'Разделить массив на две половины',
'Рекурсивно отсортировать левую',
'Рекурсивно отсортировать правую',
'Слить две отсортированные половины',
],
});
block(L, 'quiz', ++b, {
question: 'Какая сортировка гарантирует O(n log n) в худшем случае без доп. памяти?',
options: ['Быстрая', 'Слиянием', 'Кучей (HeapSort)', 'Пузырьковая'],
correctIndex: 2,
});
}
{
const L = lesson(C_ALG, S_L3, 'Деревья и графы', 3);
let b = 0;
block(L, 'heading', ++b, { level: 2, text: 'Бинарное дерево поиска (BST)' });
block(L, 'text', ++b, { text: 'В BST: всё левое поддерево < узел < всё правое. Поиск/вставка/удаление: O(h). У сбалансированного дерева h = O(log n).' });
block(L, 'code', ++b, {
lang: 'python',
code: `class Node:
def __init__(self, val):
self.val = val
self.left = self.right = None
def insert(root, val):
if not root: return Node(val)
if val < root.val: root.left = insert(root.left, val)
else: root.right = insert(root.right, val)
return root
def inorder(root): # обход: лево → корень → право
if root:
inorder(root.left)
print(root.val, end=' ')
inorder(root.right)`,
});
block(L, 'heading', ++b, { level: 3, text: 'Графы' });
block(L, 'table', ++b, {
header: ['Алгоритм', 'Задача', 'Сложность'],
rows: [
['BFS', 'Кратчайший путь (без весов)', 'O(V+E)'],
['DFS', 'Обход, топосортировка', 'O(V+E)'],
['Dijkstra', 'Кратчайший путь (≥0 веса)', 'O((V+E) log V)'],
['BellmanFord', 'Кратчайший путь (любые веса)', 'O(V·E)'],
],
});
block(L, 'diagram', ++b, {
code: "graph TD\n A((A)) -- 4 --> B((B))\n A -- 2 --> C((C))\n B -- 1 --> D((D))\n C -- 5 --> D\n B -- 3 --> E((E))\n D -- 1 --> E",
caption: 'Взвешенный граф',
});
block(L, 'quiz', ++b, {
question: 'Какой алгоритм находит кратчайшие пути при отрицательных весах рёбер?',
options: ['Dijkstra', 'BFS', 'BellmanFord', 'DFS'],
correctIndex: 2,
});
block(L, 'flashcard', ++b, { front: 'BFS vs DFS?', back: 'BFS — очередь, обход по уровням, кратчайший путь (без весов).\nDFS — стек/рекурсия, идёт вглубь, топосортировка, компоненты связности.' });
}
// ── Итог ──────────────────────────────────────────────────────────────
console.log(`✓ Theory seed complete:
[math] Тригонометрия (id=${C_TRIG}) — 4 урока
[math] Производные и интегралы (id=${C_CALC}) — 4 урока
[math] Теория вероятностей (id=${C_PROB}) — 3 урока
[phys] Механика (id=${C_MECH}) — 4 урока
[phys] Электричество и магнетизм (id=${C_EM}) — 4 урока
[phys] Оптика и волны (id=${C_OPT}) — 3 урока
[chem] Строение вещества (id=${C_ATOM}) — 4 урока
[chem] Органическая химия (id=${C_ORG}) — 4 урока
[bio] Клетка и генетика (id=${C_BIO}) — 4 урока
[inf] Алгоритмы (id=${C_ALG}) — 3 урока
Итого: 37 уроков`);
})(); // end transaction