diff --git a/backend/scripts/seed_ctmath_ct2017_v1.js b/backend/scripts/seed_ctmath_ct2017_v1.js new file mode 100644 index 0000000..21c90b6 --- /dev/null +++ b/backend/scripts/seed_ctmath_ct2017_v1.js @@ -0,0 +1,350 @@ +'use strict'; +/* ─────────────────────────────────────────────────────────────────────────── + seed_ctmath_ct2017_v1.js + Чистый вариант-пробник для трека exam-prep `ctmath`. + + Источник: Централизованное тестирование (ЦТ) по математике, 2017, Вариант 1. + Формат: Часть А = А1–А18, Часть В = В1–В12 (В1 — на соответствие). Всего 30 заданий. + Перенабрано вручную в KaTeX по PDF: F:\!Рабочие\ЦТ\Математика\Математика\ЦТ-ЦЭ\2017\CT-2017.pdf + (ответы — отдельный файл «Ответы ЦТ 2017.pdf», столбец «Вариант 1»). + + ⚠️ ВСЕ 30 ответов решены самостоятельно и СВЕРЕНЫ с официальной таблицей — полное + совпадение, включая B6=56, B8=-143, B11=121, B12=115. variant=118 (закрывает пробел + между ЦТ-2016 и ЦТ-2018). Прогнан через дедуп-гейт (check_variant_dups.js) — без повторов. + + Реконструкции заданий-«с-картинкой» (смысл/ответ сохранены, авто-проверка): + • А1 (вращение прямоугольников) → размеры сторона-ось/смежная даны числами (квадрат-сечение ⟺ ось=2·смежная → 3,5); + • А3 (график движения) → путь на участке BC задан числами (52 км/ч); + • А9 (треугольник по рисунку) → явно: BM — биссектриса угла B, AM/MC=AB/BC → 13,8; + • А11 (фигура на сетке) → площадь фигуры дана числом ($18$ см² = 28 % трапеции → 64 2/7); + • А14 (выбор параболы) → вершина/точка/направление ветвей в тексте. + Без авторских ссылок (политика «все учебники наши»). + + Идемпотентность: upsert по UNIQUE(exam_key, variant, task_idx). + Запуск: + node backend/scripts/seed_ctmath_ct2017_v1.js # DRY-RUN (по умолчанию) + node backend/scripts/seed_ctmath_ct2017_v1.js --apply # запись в БД + ⚠️ Массовую запись в БД запускает ПОЛЬЗОВАТЕЛЬ вручную. Без --apply ничего не пишется. + ─────────────────────────────────────────────────────────────────────────── */ + +const { DatabaseSync } = require('node:sqlite'); +const path = require('path'); + +const APPLY = process.argv.includes('--apply'); +const EXAM = 'ctmath'; +const VARIANT = 118; +const N_TASKS = 30; +const PROV = 'ЦТ–2017, Вариант 1'; +const R = String.raw; + +const L = ['а', 'б', 'в', 'г', 'д']; +const mc = (...html) => html.map((h, i) => [L[i], h]); + +/* ── 30 заданий ─────────────────────────────────────────────────────────── */ +const TASKS = [ + // ── Часть A: А1–А18 ────────────────────────────────────────────────────── + { idx: 1, type: 'mc', topic: 'stereometry', subtopic: 'ster-rotation', diff: 2, + text: R`Прямоугольник вращают вокруг указанной стороны (оси), образуя цилиндр. Осевым сечением цилиндра должен быть квадрат. Укажите номера прямоугольников (ось $\times$ смежная сторона): $1)\ 8\times8$; $\ 2)\ 8\times16$; $\ 3)\ 8\times4$; $\ 4)\ 4\times8$; $\ 5)\ 16\times8$.`, + opts: mc('$2,\ 3$', '$1,\ 5$', '$3,\ 5$', '$2,\ 4$', '$1,\ 3,\ 5$'), + answer: 'в', + sol: R`Осевое сечение — прямоугольник «ось $\times$ диаметр $=$ ось $\times2\cdot$смежная». Это квадрат, когда ось $=2\cdot$смежная: для $8\times4$ ($8=2\cdot4$) и $16\times8$ ($16=2\cdot8$). Значит $3$ и $5$.` }, + + { idx: 2, type: 'mc', topic: 'numbers', subtopic: 'num-real', diff: 1, + text: R`Выразите $737$ см $8$ мм в метрах с точностью до сотых.`, + opts: mc('$0{,}74$ м', '$7{,}37$ м', '$7{,}378$ м', '$7{,}38$ м', '$73{,}78$ м'), + answer: 'г', + sol: R`$737$ см $8$ мм $=7{,}378$ м $\approx7{,}38$ м.` }, + + { idx: 3, type: 'mc', topic: 'word-sequences', subtopic: 'word-problems', diff: 1, + text: R`По графику движения автомобиля на участке $BC$ путь изменился с $52$ км до $104$ км за $1$ ч. Найдите скорость движения на участке $BC$.`, + opts: mc('$26$ км/ч', '$52$ км/ч', '$78$ км/ч', '$104$ км/ч', '$60$ км/ч'), + answer: 'б', + sol: R`$v=\dfrac{104-52}{1}=52$ км/ч.` }, + + { idx: 4, type: 'mc', topic: 'expressions', subtopic: 'expr-fractions', diff: 2, + text: R`Выразите $a$ из равенства $\dfrac{3}{2b+1}=\dfrac{6}{a-b}$.`, + opts: mc('$a=5b+2$', '$a=5b-2$', '$a=15b-6$', '$a=15b+6$', '$a=3b+1$'), + answer: 'а', + sol: R`$3(a-b)=6(2b+1)$, $a-b=4b+2$, $a=5b+2$.` }, + + { idx: 5, type: 'mc', topic: 'expressions', subtopic: 'expr-powers-roots', diff: 2, + text: R`Значение выражения $8\sqrt3+\dfrac18\sqrt{192}$ равно:`, + opts: mc('$16\sqrt3$', '$\sqrt{195}$', '$\dfrac{65\sqrt{195}}{8}$', '$\dfrac{6\sqrt3}{8}$', '$9\sqrt3$'), + answer: 'д', + sol: R`$\sqrt{192}=8\sqrt3$, поэтому $\dfrac18\cdot8\sqrt3=\sqrt3$ и $8\sqrt3+\sqrt3=9\sqrt3$.` }, + + { idx: 6, type: 'mc', topic: 'word-sequences', subtopic: 'seq-progressions', diff: 1, + text: R`Последовательность $(a_n)$ задана формулой $a_n=3n^{2}-8n+9$. Второй член этой последовательности равен:`, + opts: mc('$12$', '$-16$', '$5$', '$16$', '$6$'), + answer: 'в', + sol: R`$a_2=3\cdot4-16+9=5$.` }, + + { idx: 7, type: 'mc', topic: 'trigonometry', subtopic: 'trig-identities', diff: 2, + text: R`Значение выражения $7\cos^{2}34^\circ+10\sin30^\circ+7\sin^{2}34^\circ$ равно:`, + opts: mc('$12$', '$17$', '$24$', '$7+10\sqrt3$', '$14+5\sqrt3$'), + answer: 'а', + sol: R`$7(\cos^{2}34^\circ+\sin^{2}34^\circ)+10\cdot\tfrac12=7+5=12$.` }, + + { idx: 8, type: 'mc', topic: 'numbers', subtopic: 'num-divisibility', diff: 1, + text: R`Среди утверждений укажите номер верного.
$1)$ число $451$ кратно числу $5$; $\ 2)$ число $9$ кратно числу $35$; $\ 3)$ число $2$ кратно числу $14$; $\ 4)$ число $116$ кратно числу $1$; $\ 5)$ число $43$ кратно числу $0$.`, + opts: mc('$1$', '$2$', '$3$', '$4$', '$5$'), + answer: 'г', + sol: R`Любое целое кратно $1$, поэтому $116$ кратно $1$ — верно (утверждение 4). Остальные неверны.` }, + + { idx: 9, type: 'mc', topic: 'planimetry', subtopic: 'plan-triangles', diff: 2, + text: R`В треугольнике $ABC$ отрезок $BM$ — биссектриса угла $B$ ($M$ на $AC$). Известно, что $AC=32$, $AM=12$, $BC=23$. Найдите длину стороны $AB$.`, + opts: mc('$10{,}2$', '$14{,}6$', '$13{,}8$', '$13{,}5$', '$10{,}4$'), + answer: 'в', + sol: R`Биссектриса делит сторону в отношении прилежащих сторон: $\dfrac{AM}{MC}=\dfrac{AB}{BC}$. $MC=32-12=20$, поэтому $AB=BC\cdot\dfrac{AM}{MC}=23\cdot\dfrac{12}{20}=13{,}8$.` }, + + { idx: 10, type: 'mc', topic: 'expressions', subtopic: 'expr-fractions', diff: 2, + text: R`Результат упрощения выражения $\sqrt{(2x-4{,}6)^{2}}+4{,}6$ при $-1$1)\ y=x^{2}+4x+3$; $\ 2)\ y=x^{2}-4x-3$; $\ 3)\ y=2x^{2}+4x+3$; $\ 4)\ y=2x^{2}+4x-3$; $\ 5)\ y=2x^{2}-4x+3$.`, + opts: mc('$1$', '$2$', '$3$', '$4$', '$5$'), + answer: 'в', + sol: R`У $y=2x^{2}+4x+3$ вершина в точке $(-1;1)$ ($x=-\tfrac{4}{4}=-1$, $y=2-4+3=1$), $y(0)=3$, ветви вверх. Это уравнение 3.` }, + + { idx: 15, type: 'mc', topic: 'stereometry', subtopic: 'ster-polyhedra', diff: 3, + text: R`$ABCDA_1B_1C_1D_1$ — куб. Точки $M$ и $N$ — середины рёбер $AD$ и $DC$, точка $K$ на ребре $A_1D_1$ с $KA_1:KD_1=1:3$. Сечением куба плоскостью, проходящей через точки $M$, $N$ и $K$, является:`, + opts: mc('восьмиугольник', 'треугольник', 'четырёхугольник', 'пятиугольник', 'шестиугольник'), + answer: 'в', + sol: R`Плоскость отсекает ребро $DD_1$ (оба конца по одну сторону) и пересекает четыре ребра: $AD$ (точка $M$), $DC$ ($N$), $A_1D_1$ ($K$) и $D_1C_1$. Четыре точки — сечение является четырёхугольником.` }, + + { idx: 16, type: 'mc', topic: 'equations', subtopic: 'eq-rational', diff: 2, + text: R`Найдите сумму наименьшего и наибольшего целых решений двойного неравенства $-448{,}9<2{,}9+9x<23{,}6$.`, + opts: mc('$-52$', '$-47$', '$-49$', '$-48$', '$-53$'), + answer: 'г', + sol: R`$-451{,}8<9x<20{,}7$, то есть $-50{,}2А) Окружность с центром $(-8;-2)$ и радиусом $4$ задаётся уравнением …
Б) Уравнение прямой, проходящей через точку $(-8;2)$ параллельно прямой $y=\tfrac14 x$, имеет вид …
В) График обратной пропорциональности, проходящий через точку $\left(\tfrac12;-\tfrac12\right)$, задаётся уравнением …
Окончания: $1)\ xy=2$; $\ 2)\ (x-8)^{2}+(y-2)^{2}=4$; $\ 3)\ -\tfrac14 x+y=4$; $\ 4)\ (x+8)^{2}+(y+2)^{2}=16$; $\ 5)\ 4xy+1=0$; $\ 6)\ \tfrac14 x+y=2$.`, + answer: 'А4Б3В5', + ansShow: 'А4Б3В5', + sol: R`А) $(x+8)^{2}+(y+2)^{2}=16$ (окончание 4). Б) $y-2=\tfrac14(x+8)$, то есть $-\tfrac14 x+y=4$ (окончание 3). В) $y=\tfrac{k}{x}$ через $\left(\tfrac12;-\tfrac12\right)$ даёт $k=-\tfrac14$, то есть $xy=-\tfrac14$, $4xy+1=0$ (окончание 5). Ответ: А4Б3В5.` }, + + { idx: 20, type: 'open', topic: 'word-sequences', subtopic: 'word-problems', diff: 3, + text: R`Конфеты в коробке упаковываются рядами, причём количество конфет в каждом ряду на $4$ больше количества рядов. Дизайн коробки изменили: добавили $2$ ряда, а в каждом ряду — по $1$ конфете. В результате количество конфет в коробке увеличилось на $25$. Сколько конфет упаковывалось в коробку первоначально?`, + answer: '45', + sol: R`Пусть рядов $r$, в ряду $r+4$. Тогда $(r+2)(r+5)-r(r+4)=3r+10=25$, $r=5$. Первоначально $5\cdot9=45$ конфет.` }, + + { idx: 21, type: 'open', topic: 'expressions', subtopic: 'expr-polynomials', diff: 3, + text: R`Известно, что при $a$, равном $-2$ и $4$, значение выражения $4a^{3}+3a^{2}-ab+c$ равно нулю. Найдите значение выражения $b+c$.`, + answer: '-34', + sol: R`При $a=-2$: $-20+2b+c=0$; при $a=4$: $304-4b+c=0$. Вычитая, $6b=324$, $b=54$, тогда $c=-88$, и $b+c=-34$.` }, + + { idx: 22, type: 'open', topic: 'equations', subtopic: 'eq-irrational', diff: 4, + text: R`Найдите произведение корней (корень, если он единственный) уравнения $x^{2}-5x-3=4\sqrt{x^{2}-5x+9}$.`, + answer: '-27', + sol: R`Пусть $u=x^{2}-5x$. Тогда $u-3=4\sqrt{u+9}$ ($u\ge3$); возведя в квадрат, $u^{2}-22u-135=0$, $u=27$. Из $x^{2}-5x-27=0$ произведение корней $-27$.` }, + + { idx: 23, type: 'open', topic: 'planimetry', subtopic: 'plan-quadrilaterals', diff: 4, + text: R`В параллелограмме с острым углом $45^\circ$ точка пересечения диагоналей удалена от прямых, содержащих неравные стороны, на расстояния $\dfrac{7\sqrt2}{2}$ и $2$. Найдите площадь параллелограмма.`, + answer: '56', + sol: R`Расстояние от центра до стороны — половина высоты. Высоты $H_1=7\sqrt2$ и $H_2=4$. Из $H=l\sin45^\circ$: стороны $b=14$, $a=4\sqrt2$. Площадь $=b\cdot H_2=14\cdot4=56$.` }, + + { idx: 24, type: 'open', topic: 'equations', subtopic: 'eq-logarithmic', diff: 4, + text: R`Пусть $x_0$ — наибольший корень уравнения $\log_2^{2}\dfrac{x}{32}+4\log_2 x-52=0$. Найдите значение выражения $7\sqrt[3]{x_0}$.`, + answer: '56', + sol: R`Пусть $t=\log_2 x$. Тогда $(t-5)^{2}+4t-52=0$, $t^{2}-6t-27=0$, $t=9$ или $t=-3$. Наибольший корень $x_0=2^{9}=512$, и $7\sqrt[3]{512}=7\cdot8=56$.` }, + + { idx: 25, type: 'open', topic: 'equations', subtopic: 'eq-exponential', diff: 4, + text: R`Решите неравенство $\left(\dfrac{1}{5-\sqrt{24}}\right)^{x+6}\ge\left(5-\sqrt{24}\right)^{\frac{4x+25}{x+4}}$. В ответ запишите сумму целых решений, принадлежащих промежутку $[-20;-2]$.`, + answer: '-12', + sol: R`Так как $\dfrac{1}{5-\sqrt{24}}=(5-\sqrt{24})^{-1}$ и $0<5-\sqrt{24}<1$, неравенство равносильно $-(x+6)\le\dfrac{4x+25}{x+4}$, что приводит к $\dfrac{(x+7)^{2}}{x+4}\ge0$. Решение: $x>-4$ или $x=-7$. На $[-20;-2]$ целые $-7,-3,-2$; их сумма $-12$.` }, + + { idx: 26, type: 'open', topic: 'functions', subtopic: 'fn-properties', diff: 4, + text: R`Найдите увеличенное в $9$ раз произведение абсцисс точек пересечения прямой $y=12$ и графика нечётной функции, которая определена на $(-\infty;0)\cup(0;+\infty)$ и при $x>0$ задаётся формулой $y=2^{3x-8}-20$.`, + answer: '-143', + sol: R`При $x>0$: $2^{3x-8}-20=12$, $2^{3x-8}=32$, $x=\tfrac{13}{3}$. По нечётности при $x<0$ получаем $x=-\tfrac{11}{3}$. Произведение $\tfrac{13}{3}\cdot\left(-\tfrac{11}{3}\right)=-\tfrac{143}{9}$; увеличенное в $9$ раз — $-143$.` }, + + { idx: 27, type: 'open', topic: 'stereometry', subtopic: 'ster-polyhedra', diff: 4, + text: R`Найдите площадь полной поверхности прямой треугольной призмы, описанной около шара, если площадь основания призмы равна $7{,}5$.`, + answer: '45', + sol: R`У описанной около шара призмы высота $h=2r$, а в основании вписана окружность радиуса $r$, поэтому площадь основания $S=rp=7{,}5$. Боковая поверхность $=2p\cdot2r=4\cdot rp=30$. Полная $=2\cdot7{,}5+30=45$.` }, + + { idx: 28, type: 'open', topic: 'equations', subtopic: 'eq-rational', diff: 4, + text: R`Найдите произведение наибольшего целого решения на количество целых решений неравенства $\dfrac{16}{6+|24-x|}>|24-x|$.`, + answer: '75', + sol: R`Пусть $u=|24-x|\ge0$. Тогда $16>u(6+u)$, $u^{2}+6u-16<0$, $0\le u<2$. Значит $|24-x|<2$, то есть $22Ответ: ${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 !== N_TASKS) problems.push(`Ожидалось ${N_TASKS} заданий, получено ${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 > N_TASKS) problems.push(`task_idx вне 1..${N_TASKS}: ${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_ct2017_v1 (${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 ответов пройдены (${N_TASKS}/${N_TASKS}).`); + +/* ── APPLY: upsert ─────────────────────────────────────────────────────────── */ +if (!APPLY) { + console.log('\nDRY-RUN: ничего не записано. Для записи: node backend/scripts/seed_ctmath_ct2017_v1.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 → «Варианты» → «ЦТ-2017».\n`); +} catch (e) { + db.exec('ROLLBACK'); + console.error('\n✗ Ошибка записи, откат транзакции:', e.message); + process.exitCode = 1; +} +db.close(); diff --git a/backend/src/routes/exam-prep.js b/backend/src/routes/exam-prep.js index e0a6344..563aff1 100644 --- a/backend/src/routes/exam-prep.js +++ b/backend/src/routes/exam-prep.js @@ -57,6 +57,7 @@ const VARIANT_LABEL = { 115: 'ЦТ-2019', 116: 'ЦТ-2020', 117: 'ЦТ-2021', + 118: 'ЦТ-2017', }, }; const examVariantLabel = (examKey, v) => VARIANT_LABEL[examKey]?.[v] || `Вариант ${v}`;