'use strict'; /* ─────────────────────────────────────────────────────────────────────────── seed_ctmath_rt2324_e1v1.js Чистый вариант-пробник для трека exam-prep `ctmath`. Источник: РТ–2023/2024, Этап I, Вариант 1 (РИКЗ, «Тематическое консультирование по математике»). 30 заданий: А1–А10 + В1–В20. Перенабрано вручную в KaTeX по PDF (визуальное чтение, НЕ OCR): F:\!Рабочие\ЦТ\Математика\Математика\РТ\2023-2024\МАТ РТ-1 23_24 В1.pdf variant=104 — следующий «чистый» вариант после РТ-2024/25 (101/102/103). Геометрия закодирована текстом (стандартная разметка фигур / углы словами) — отдельных чертежей не требуется (как у большинства существующих задач). Идемпотентность: upsert по UNIQUE(exam_key, variant, task_idx). Запуск: node backend/scripts/seed_ctmath_rt2324_e1v1.js # DRY-RUN (по умолчанию) node backend/scripts/seed_ctmath_rt2324_e1v1.js --apply # запись в БД ⚠️ Массовую запись в БД запускает ПОЛЬЗОВАТЕЛЬ вручную (авто-режим Claude Code блокирует продакшн-записи). Без --apply ничего не пишется. ─────────────────────────────────────────────────────────────────────────── */ const { DatabaseSync } = require('node:sqlite'); const path = require('path'); const APPLY = process.argv.includes('--apply'); const EXAM = 'ctmath'; const VARIANT = 104; const PROV = 'РТ–2023/2024, Этап I, Вариант 1'; const R = String.raw; /* opts: метки кириллица а–д (как в существующих строках ctmath; checkAnswerServer имеет ветку /^[а-д]$/). РТ-варианты 1..5 → а..д. */ const L = ['а', 'б', 'в', 'г', 'д']; const mc = (...html) => html.map((h, i) => [L[i], h]); /* ── 30 заданий ─────────────────────────────────────────────────────────── */ const TASKS = [ // ── Часть A: А1–А10 ────────────────────────────────────────────────────── { idx: 1, type: 'mc', topic: 'numbers', subtopic: 'num-real', diff: 1, text: R`Наименьшее целое число, принадлежащее интервалу $(-13{,}8;-7)$, равно:`, opts: mc('$-14$', '$-6$', '$-7$', '$-13$', '$-12$'), answer: 'г', sol: R`Интервалу $(-13{,}8;-7)$ принадлежат целые числа $-13,\,-12,\,-11,\,-10,\,-9,\,-8$. Наименьшее из них — число $-13$.`, ref: 'Алгебра, 8 класс, гл. 1, § 5' }, { idx: 2, type: 'mc', topic: 'expressions', subtopic: 'expr-polynomials', diff: 1, text: R`Укажите номер выражения, которое является произведением числа $m$ и суммы чисел $1{,}7$ и $3{,}5$.`, opts: mc('$1{,}7\cdot(3{,}5+m)$', '$1{,}7+3{,}5\cdot m$', '$1{,}7\cdot m+3{,}5$', '$3{,}5\cdot(1{,}7+m)$', '$m\cdot(1{,}7+3{,}5)$'), answer: 'д', sol: R`Произведение числа $m$ и суммы чисел $1{,}7$ и $3{,}5$ записывается как $m\cdot(1{,}7+3{,}5)$.`, ref: 'Алгебра, 7 класс, гл. 2, § 4' }, { idx: 3, type: 'mc', topic: 'stereometry', subtopic: 'ster-basics', diff: 2, text: R`В параллелепипеде $ABCDA_1B_1C_1D_1$ среди отрезков $BD$, $BD_1$, $AB_1$, $A_1B_1$, $B_1B$ укажите тот, который является диагональю параллелепипеда.`, opts: mc('$BD$', '$BD_1$', '$AB_1$', '$A_1B_1$', '$B_1B$'), answer: 'б', sol: R`Диагональю параллелепипеда называют отрезок, соединяющий две вершины, не принадлежащие одной грани. Среди перечисленных только $BD_1$ соединяет противоположные вершины параллелепипеда, поэтому $BD_1$ — диагональ.`, ref: 'Геометрия, 10 класс, разд. 1, § 1' }, { idx: 4, type: 'mc', topic: 'equations', subtopic: 'eq-linear', diff: 1, text: R`Определите, при каком из значений $x$, равных $4;\ 5;\ 1;\ 0{,}1;\ 8$, верно неравенство $\dfrac{320}{x}<50$.`, opts: mc('$4$', '$5$', '$1$', '$0{,}1$', '$8$'), answer: 'д', sol: R`При $x>0$ неравенство $\dfrac{320}{x}<50$ равносильно $x>6{,}4$. Из данных чисел этому условию удовлетворяет только $x=8$.`, ref: 'Алгебра, 7 класс, гл. 3, § 18' }, { idx: 5, type: 'mc', topic: 'functions', subtopic: 'fn-graphs', diff: 1, text: R`Укажите номер функции, график которой параллелен оси абсцисс.`, opts: mc('$y=-4$', '$y=4-2x$', '$y=\dfrac{2}{x}$', '$y=4^{x}$', '$y=4x$'), answer: 'а', sol: R`График параллелен оси абсцисс у постоянной функции $y=b$ (угловой коэффициент $k=0$). Из предложенных это $y=-4$.`, ref: 'Алгебра, 7 класс, гл. 3, § 20' }, { idx: 6, type: 'open', topic: 'functions', subtopic: 'fn-properties', diff: 2, text: R`Функция задана формулой $f(x)=|x|-6$. Укажите номера верных утверждений.
1) областью определения функции является множество всех действительных чисел;
2) функция возрастает на промежутке $(-\infty;0]$;
3) функция является чётной;
4) $f(-5)=-11$;
5) $f(3)<0$.
Ответ запишите цифрами в порядке возрастания, без пробелов.`, answer: '135', ansShow: '1, 3, 5', sol: R`$1)$ верно: $|x|-6$ определено при всех $x$. $\ 2)$ неверно: на $(-\infty;0]$ функция убывает. $\ 3)$ верно: $f(-x)=|-x|-6=|x|-6=f(x)$. $\ 4)$ неверно: $f(-5)=5-6=-1$. $\ 5)$ верно: $f(3)=3-6=-3<0$.`, ref: 'Алгебра, 8 класс, гл. 4, § 19' }, { idx: 7, type: 'mc', topic: 'word-sequences', subtopic: 'word-problems', diff: 2, text: R`Фермер привёз на осеннюю ярмарку некоторое количество картофеля и продал из этого количества $102$ кг. Сколько всего килограммов картофеля привёз фермер, если после продажи осталось $\dfrac{5}{11}$ всего привезённого картофеля?`, opts: mc('$224$ кг', '$204$ кг', '$192$ кг', '$187$ кг', '$169$ кг'), answer: 'г', sol: R`Проданная часть составляет $1-\dfrac{5}{11}=\dfrac{6}{11}$ всего картофеля и равна $102$ кг. Тогда всего привезено $102:\dfrac{6}{11}=\dfrac{102\cdot11}{6}=187$ кг.`, ref: 'Математика, 6 класс, гл. 3, § 10' }, { idx: 8, type: 'mc', topic: 'expressions', subtopic: 'expr-powers-roots', diff: 1, text: R`Значение выражения $\sqrt[3]{0{,}9}\cdot\sqrt[3]{30}$ равно:`, opts: mc('$3$', '$\sqrt[3]{12}$', '$\sqrt3$', '$\sqrt[6]{12}$', '$6$'), answer: 'а', sol: R`По свойству корня $n$-й степени $\sqrt[3]{0{,}9}\cdot\sqrt[3]{30}=\sqrt[3]{0{,}9\cdot30}=\sqrt[3]{27}=3$.`, ref: 'Алгебра, 10 класс, гл. 2, § 14' }, { idx: 9, type: 'mc', topic: 'stereometry', subtopic: 'ster-polyhedra', diff: 2, text: R`Треугольник $KMN$ — сечение треугольной пирамиды $SABC$ плоскостью, проходящей через точку $M$ — середину ребра $BC$ — параллельно плоскости $SAC$. Найдите периметр треугольника $KMN$, если каждое ребро пирамиды $SABC$ имеет длину $2\sqrt2$.`, opts: mc('$\dfrac{2\sqrt2}{3}$', '$\dfrac{3\sqrt2}{2}$', '$3\sqrt2$', '$6\sqrt2$', '$4\sqrt2$'), answer: 'в', sol: R`Секущая плоскость параллельна $SAC$ и проходит через середину $M$ ребра $BC$, поэтому она пересекает рёбра $AB$ и $SB$ в их серединах $N$ и $K$. Отрезки $MN$, $MK$, $NK$ — средние линии граней, каждый равен $\dfrac12\cdot2\sqrt2=\sqrt2$. Значит, периметр $KMN$ равен $3\sqrt2$.`, ref: 'Геометрия, 10 класс, разд. 1, § 3' }, { idx: 10, type: 'open', topic: 'trigonometry', subtopic: 'trig-identities', diff: 2, text: R`Укажите номера выражений, которые не имеют смысла.
1) $\arccos\dfrac{\sqrt3}{2}$;
2) $\arcsin\sqrt2$;
3) $\operatorname{ctg}\left(-\dfrac{3\pi}{2}\right)$;
4) $\operatorname{tg}\left(-\dfrac{3\pi}{2}\right)$;
5) $\operatorname{arctg}\dfrac{\sqrt3}{3}$.
Ответ запишите цифрами в порядке возрастания, без пробелов.`, answer: '24', ansShow: '2, 4', sol: R`$1)$ имеет смысл: $\arccos\dfrac{\sqrt3}{2}=\dfrac{\pi}{6}$. $\ 2)$ не имеет смысла: $\sqrt2\notin[-1;1]$. $\ 3)$ имеет смысл: $\operatorname{ctg}\left(-\dfrac{3\pi}{2}\right)=0$. $\ 4)$ не имеет смысла: $\operatorname{tg}\left(-\dfrac{3\pi}{2}\right)$ не существует, так как $\cos\left(-\dfrac{3\pi}{2}\right)=0$. $\ 5)$ имеет смысл: $\operatorname{arctg}\dfrac{\sqrt3}{3}=\dfrac{\pi}{6}$.`, ref: 'Алгебра, 10 класс, гл. 1, § 3; § 7' }, // ── Часть B: В1–В20 ────────────────────────────────────────────────────── { idx: 11, type: 'long', topic: 'numbers', subtopic: 'num-divisibility', diff: 2, text: R`Для начала каждого из предложений А–В подберите его окончание 1–6 так, чтобы получилось верное утверждение.
Начало:
А) Наибольший простой делитель числа $14$ равен …
Б) Наименьшее общее кратное чисел $5$ и $55$ равно …
В) Наибольший общий делитель чисел $16$ и $55$ равен …
Окончание:
1) $1$; 2) $110$; 3) $55$; 4) $5$; 5) $7$; 6) $2$.
Ответ запишите сочетанием букв и цифр, например: А1Б1В4.`, answer: 'А5Б3В1', ansShow: 'А5Б3В1', sol: R`А) Простые делители числа $14$ — это $2$ и $7$; наибольший равен $7$ — окончание 5. Б) Наименьшее общее кратное чисел $5$ и $55$ равно $55$ — окончание 3. В) Наибольший общий делитель чисел $16$ и $55$ равен $1$ — окончание 1.`, ref: 'Математика, 5 класс, ч. 1, гл. 1, § 12' }, { idx: 12, type: 'open', topic: 'stereometry', subtopic: 'ster-basics', diff: 3, text: R`$ABCDA_1B_1C_1D_1$ — куб. Отрезки $B_1D_1$ и $AD_1$ являются диагоналями граней $A_1B_1C_1D_1$ и $AA_1D_1D$ соответственно. Выберите верные утверждения.
1) прямая $C_1D_1$ перпендикулярна прямой $AD_1$;
2) прямая $B_1D_1$ параллельна прямой $BC$;
3) прямая $AD_1$ параллельна плоскости $BB_1C_1$;
4) прямая $B_1D_1$ перпендикулярна прямой $AD_1$;
5) прямая $AA_1$ параллельна прямой $B_1D_1$;
6) прямая $CC_1$ параллельна плоскости $BAA_1$.
Ответ запишите цифрами в порядке возрастания, без пробелов.`, answer: '136', ansShow: '1, 3, 6', sol: R`$1)$ верно: $C_1D_1$ перпендикулярна плоскости $AA_1D_1D$, значит $C_1D_1\perp AD_1$. $\ 2)$ неверно: $B_1D_1$ и $BC$ скрещиваются. $\ 3)$ верно: $AD_1\subset AA_1D_1D$, а эта грань параллельна грани $BB_1C_1C$. $\ 4)$ неверно: угол между $B_1D_1$ и $AD_1$ равен $60^\circ$. $\ 5)$ неверно: $AA_1\perp B_1D_1$. $\ 6)$ верно: $CC_1\subset CC_1D_1D$, а эта грань параллельна грани $BAA_1B_1$.`, ref: 'Геометрия, 10 класс, разд. 1–2' }, { idx: 13, type: 'open', topic: 'planimetry', subtopic: 'plan-triangles', diff: 1, text: R`В треугольнике $ABC$ известно, что $\angle ABC=40^\circ$, $\angle BAC=3x$, $\angle ACB=x$. Найдите градусную меру угла $ACB$.`, answer: '35', sol: R`По теореме о сумме градусных мер углов треугольника $3x+x+40^\circ=180^\circ$, откуда $4x=140^\circ$, $x=35^\circ$. Значит, $\angle ACB=35^\circ$.`, ref: 'Геометрия, 7 класс, гл. 4, § 19' }, { idx: 14, type: 'open', topic: 'word-sequences', subtopic: 'seq-progressions', diff: 2, text: R`Арифметическая прогрессия $(a_n)$ задана формулой $n$-го члена $a_n=6-2n$. Найдите номер члена этой прогрессии, равного $-70$.`, answer: '38', sol: R`По условию $a_n=-70$, тогда $6-2n=-70$, $-2n=-76$, $n=38$.`, ref: 'Алгебра, 9 класс, гл. 4, § 15' }, { idx: 15, type: 'open', topic: 'numbers', subtopic: 'num-divisibility', diff: 2, text: R`Найдите произведение наименьшего натурального двузначного простого числа и натурального числа, при делении которого на $5$ получается в неполном частном $13$ и в остатке $1$.`, answer: '726', sol: R`Наименьшее двузначное простое число — $11$. Натуральное число с неполным частным $13$ и остатком $1$ при делении на $5$ равно $13\cdot5+1=66$. Произведение: $11\cdot66=726$.`, ref: 'Математика, 5 класс, ч. 1, гл. 1, § 11' }, { idx: 16, type: 'open', topic: 'word-sequences', subtopic: 'word-problems', diff: 2, text: R`За керамическую плитку и её укладку заплатили $524$ рубля. Сколько стоит (в рублях) керамическая плитка, если стоимость её укладки составляет $31\%$ стоимости плитки?`, answer: '400', sol: R`Пусть стоимость плитки равна $x$ рублей, тогда стоимость укладки $0{,}31x$. Уравнение $x+0{,}31x=524$, $1{,}31x=524$, $x=400$.`, ref: 'Математика, 6 класс, гл. 2, § 1' }, { idx: 17, type: 'open', topic: 'expressions', subtopic: 'expr-powers-roots', diff: 3, text: R`Найдите значение выражения $\dfrac{a^{5}-a}{a^{5}-a^{9}}$ при $a=\dfrac{1}{\sqrt[4]{18}}$.`, answer: '-18', sol: R`$\dfrac{a^{5}-a}{a^{5}-a^{9}}=\dfrac{a(a^{4}-1)}{a^{5}(1-a^{4})}=-\dfrac{1}{a^{4}}$. При $a=\dfrac{1}{\sqrt[4]{18}}$ имеем $a^{4}=\dfrac{1}{18}$, поэтому значение равно $-18$.`, ref: 'Алгебра, 11 класс, гл. 1, § 1' }, { idx: 18, type: 'open', topic: 'functions', subtopic: 'fn-graphs', diff: 2, text: R`Дана функция $y=x^{2}$. График функции $y=g(x)$ получен из графика функции $y=x^{2}$ сдвигом вдоль оси абсцисс на $1$ единицу влево и вдоль оси ординат на $3$ единицы вниз. Найдите значение $g(-6)$.`, answer: '22', sol: R`Указанный сдвиг даёт $g(x)=(x+1)^{2}-3$. Тогда $g(-6)=(-6+1)^{2}-3=25-3=22$.`, ref: 'Алгебра, 9 класс, гл. 2, § 9' }, { idx: 19, type: 'open', topic: 'equations', subtopic: 'eq-linear', diff: 2, text: R`Найдите сумму наименьшего и наибольшего целых решений двойного неравенства $-130<\dfrac{4-3x}{0{,}5}<25$.`, answer: '20', sol: R`Умножив на $0{,}5$: $-65<4-3x<12{,}5$. Вычтя $4$: $-69<-3x<8{,}5$. Разделив на $-3$ (знаки меняются): $-2\dfrac560$ приводит к $v^{2}+5v-168\ge0$, решение $v\ge\dfrac{-5+\sqrt{697}}{2}\approx10{,}7$. Наименьшее целое значение $v=11$.`, ref: 'Алгебра, 9 класс, гл. 3, § 13' }, { idx: 30, type: 'open', topic: 'stereometry', subtopic: 'ster-angles-distances', diff: 5, text: R`$ABCA_1B_1C_1$ — правильная треугольная призма, все рёбра которой равны. Точка $N$ лежит на диагонали $A_1B$ грани $AA_1B_1B$ так, что $A_1N:NB=1:5$. Точки $M$ и $K$ лежат на рёбрах $CC_1$ и $CB$ соответственно так, что $CM:CC_1=1:4$, $CK:KB=1:3$. Найдите значение выражения $18\sqrt7\cdot\operatorname{tg}\varphi$, где $\varphi$ — угол между прямыми $C_1N$ и $KM$.`, answer: '70', sol: R`Пусть длина ребра призмы равна $a$. Прямая $BC_1\parallel KM$, поэтому угол между $C_1N$ и $KM$ равен углу $NC_1B$. Тогда $BC_1=a\sqrt2$, $A_1N=\dfrac{a\sqrt2}{6}$, $NB=\dfrac{5a\sqrt2}{6}$. Из треугольника $A_1BC_1$ по теореме косинусов $\cos\angle A_1BC_1=\dfrac34$; далее $C_1N=\dfrac{2a\sqrt2}{3}$ и $\cos\angle NC_1B=\dfrac{9}{16}$, $\sin\angle NC_1B=\dfrac{5\sqrt7}{16}$, $\operatorname{tg}\varphi=\dfrac{5\sqrt7}{9}$. Тогда $18\sqrt7\cdot\dfrac{5\sqrt7}{9}=2\cdot5\cdot7=70$.`, ref: 'Геометрия, 10 класс, разд. 2, § 4' }, ]; /* ── Сборка solution_html ────────────────────────────────────────────────── */ function ansShowOf(t) { if (t.ansShow != null) return t.ansShow; if (t.type === 'mc') return `${t.answer})`; return `$${t.answer}$`; } function buildSolution(t) { const ans = ansShowOf(t); let html = `${t.sol}
Ответ: ${ans}
`; if (t.ref) html += `
Учебник: ${t.ref}
`; return html; } /* ── Самопроверка (повтор логики checkAnswerServer из exam-prep.js) ────────── */ const EPS = 1e-6; function srvToNumber(s) { if (s == null) return NaN; let t = String(s).trim().replace(/\$/g, '').replace(/\s+/g, '').replace(',', '.'); const f = t.match(/^(-?\d+(?:\.\d+)?)\s*\/\s*(-?\d+(?:\.\d+)?)$/); if (f) { const n = Number(f[1]), d = Number(f[2]); return d === 0 ? NaN : n / d; } const n = Number(t); return Number.isFinite(n) ? n : NaN; } function checkAnswerServer(userInput, canonical) { if (userInput == null || canonical == null) return false; const c = String(canonical).trim(); if (/^[а-д]$/.test(c)) return String(userInput).trim().toLowerCase() === c.toLowerCase(); if (/^[^;]+;[^;]+$/.test(c)) return false; const cn = srvToNumber(c), un = srvToNumber(userInput); if (Number.isNaN(cn) || Number.isNaN(un)) return false; return Math.abs(cn - un) < EPS; } /* ── Валидация набора ──────────────────────────────────────────────────────── */ const problems = []; if (TASKS.length !== 30) problems.push(`Ожидалось 30 заданий, получено ${TASKS.length}`); const seen = new Set(); for (const t of TASKS) { if (seen.has(t.idx)) problems.push(`Дубль task_idx=${t.idx}`); seen.add(t.idx); if (t.idx < 1 || t.idx > 30) problems.push(`task_idx вне 1..30: ${t.idx}`); if (!['mc', 'open', 'long'].includes(t.type)) problems.push(`#${t.idx}: тип ${t.type}`); if (t.type === 'mc') { if (!Array.isArray(t.opts) || t.opts.length !== 5) problems.push(`#${t.idx}: mc должен иметь 5 вариантов`); if (!t.opts.some(o => o[0] === t.answer)) problems.push(`#${t.idx}: answer "${t.answer}" не среди меток`); } if (!t.text || !t.sol) problems.push(`#${t.idx}: пустой text/sol`); if (t.type !== 'long' && !checkAnswerServer(t.answer, t.answer)) problems.push(`#${t.idx}: answer "${t.answer}" не проходит self-check (Unicode-минус? пробел?)`); if (/−/.test(String(t.answer))) problems.push(`#${t.idx}: Unicode-минус в answer`); } /* ── Экспорт для тестов/тиража (без запуска main при require) ──────────────── */ module.exports = { TASKS, buildSolution, ansShowOf, checkAnswerServer, EXAM, VARIANT, PROV }; if (require.main !== module) return; /* ── Открытие БД ───────────────────────────────────────────────────────────── */ const DB = path.join(__dirname, '..', 'data', 'learnspace.db'); const db = new DatabaseSync(DB); const track = db.prepare(`SELECT exam_key, variants_count FROM exam_tracks WHERE exam_key=?`).get(EXAM); if (!track) { console.error(`✗ Трек '${EXAM}' не найден в exam_tracks. Прерывание.`); process.exit(1); } /* ── DRY-RUN сводка ────────────────────────────────────────────────────────── */ console.log(`\n=== seed_ctmath_rt2324_e1v1 (${PROV}) variant=${VARIANT} ===`); console.log(`Режим: ${APPLY ? 'APPLY (запись)' : 'DRY-RUN (только проверка)'}\n`); const byType = TASKS.reduce((a, t) => (a[t.type] = (a[t.type] || 0) + 1, a), {}); console.log('Типы:', JSON.stringify(byType), '\n'); console.log('idx | type | subtopic | d | answer'); console.log('----+------+-----------------------+---+----------'); for (const t of TASKS) { console.log(`${String(t.idx).padStart(3)} | ${t.type.padEnd(4)} | ${String(t.subtopic).padEnd(21)} | ${t.diff} | ${String(t.answer)}`); } if (problems.length) { console.error(`\n✗ ПРОБЛЕМЫ (${problems.length}):`); problems.forEach(p => console.error(' - ' + p)); console.error('\nЗапись отменена из-за ошибок валидации.'); db.close(); process.exit(1); } console.log('\n✓ Валидация и self-check ответов пройдены (30/30).'); /* ── APPLY: upsert ─────────────────────────────────────────────────────────── */ if (!APPLY) { console.log('\nDRY-RUN: ничего не записано. Для записи: node backend/scripts/seed_ctmath_rt2324_e1v1.js --apply\n'); db.close(); process.exit(0); } const upsert = db.prepare(` INSERT INTO exam_tasks (exam_key, variant, task_idx, task_type, text_html, figure_html, opts_json, answer, solution_html, topic, subtopic, difficulty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(exam_key, variant, task_idx) DO UPDATE SET task_type = excluded.task_type, text_html = excluded.text_html, figure_html = excluded.figure_html, opts_json = excluded.opts_json, answer = excluded.answer, solution_html = excluded.solution_html, topic = excluded.topic, subtopic = excluded.subtopic, difficulty = excluded.difficulty `); let n = 0; db.exec('BEGIN'); try { for (const t of TASKS) { upsert.run( EXAM, VARIANT, t.idx, t.type, t.text, t.fig || null, t.type === 'mc' ? JSON.stringify(t.opts) : null, t.answer, buildSolution(t), t.topic, t.subtopic, t.diff ); n++; } const distinct = db.prepare(`SELECT COUNT(DISTINCT variant) c FROM exam_tasks WHERE exam_key=? AND variant BETWEEN 101 AND 1999`).get(EXAM).c; db.prepare(`UPDATE exam_tracks SET variants_count=? WHERE exam_key=?`).run(distinct, EXAM); db.exec('COMMIT'); console.log(`\n✓ Записано/обновлено ${n} заданий (variant=${VARIANT}).`); console.log(`✓ exam_tracks.variants_count = ${distinct} (различных вариантов).`); console.log(`\nПробник доступен: /exam-prep/ctmath → «Варианты» → «РТ-2023/24 · этап I».\n`); } catch (e) { db.exec('ROLLBACK'); console.error('\n✗ Ошибка записи, откат транзакции:', e.message); process.exitCode = 1; } db.close();