From 7e8082bda68d95c468bf2afa8f014445f78f2865 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Sat, 20 Jun 2026 10:24:59 +0300 Subject: [PATCH] =?UTF-8?q?feat(ctmath):=20=D0=B2=D0=B0=D1=80=D0=B8=D0=B0?= =?UTF-8?q?=D0=BD=D1=82=20112=20=E2=80=94=20=D0=A6=D0=A2-2015=20(30=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Пробник ЦТ по математике 2015, Вариант 1 (А1–А18 + В1–В12) для трека exam-prep ctmath. Источник: чистый PDF ЦТ 2015.pdf (10 изоморфных вариантов, таблица ответов стр.35). Ответы решены и сверены: читаемые ячейки столбца В1 (B2=-15,B3=7,B7=147,B8=-6 совпали) + изоморфные варианты 2–10. Фигурные задания (А4 центр.симметрия, А6 множество решений, А11 таблица-данные, А12 парабола, А15 координаты, В11 лог-выражение) адаптированы в самодостаточные авто-проверяемые формы с сохранением ответа (как в ЦТ-2014/вар.110). VARIANT_LABEL 112 -> 'ЦТ-2015'. DRY-RUN 30/30, self-check и структурный KaTeX — зелёные. Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2015_v1.js --apply Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/scripts/seed_ctmath_ct2015_v1.js | 389 +++++++++++++++++++++++ backend/src/routes/exam-prep.js | 1 + 2 files changed, 390 insertions(+) create mode 100644 backend/scripts/seed_ctmath_ct2015_v1.js diff --git a/backend/scripts/seed_ctmath_ct2015_v1.js b/backend/scripts/seed_ctmath_ct2015_v1.js new file mode 100644 index 0000000..5d6e5f8 --- /dev/null +++ b/backend/scripts/seed_ctmath_ct2015_v1.js @@ -0,0 +1,389 @@ +'use strict'; +/* ─────────────────────────────────────────────────────────────────────────── + seed_ctmath_ct2015_v1.js + Чистый вариант-пробник для трека exam-prep `ctmath`. + + Источник: Централизованное тестирование (ЦТ) по математике, 2015, Вариант 1. + Формат ЦТ тех лет: Часть А = А1–А18 (закрытые, 5 вариантов), Часть В = В1–В12 + (открытые). Всего 30 заданий. Перенабрано вручную в KaTeX по PDF: + F:\!Рабочие\ЦТ\Математика\Математика\ЦТ-ЦЭ\ЦТ 2015.pdf + (10 изоморфных вариантов; таблица ответов — стр. 35). + + ⚠️ Ответы. В таблице ответов колонка «Вариант 1» затемнена (часть ячеек + нечитаема). Поэтому ответы получены РЕШЕНИЕМ каждого задания и сверены: + (1) с читаемыми ячейками столбца В1 (B2=-15, B3=7, B7=147, B8=-6 — совпали); + (2) со структурно-изоморфными вариантами 2–10 (их столбцы читаемы). + variant=112 (после ЦЭ-2024 = 111). + + Адаптации заданий-«с-картинкой» (смысл/ответ сохранены, авто-проверка): + • А4 (выбор рисунка с центрально-симметричными фигурами) → эквивалентный MC + о центральной симметрии точки относительно начала координат; + • А6 (выбор рисунка с множеством решений системы) → та же система неравенств, + но спрашивается само множество решений (интервал); + • А11 (столбчатая диаграмма) → те же данные приведены таблицей в figure_html; + • А12 (выбор эскиза параболы) → верное утверждение о вершине/направлении ветвей + параболы $y=1-(x+3)^2$; + • А15 (треугольник по узлам сетки) → координаты вершин заданы в тексте + ($\cos\angle ABC=-\tfrac{5}{13}$ сохранён); + • В11 (громоздкое лог-выражение, неразборчиво в скане) → эквивалентная задача + на $2^{A}$ с сохранённым ответом $225=15^{2}$ (пэттерн столбца В11: $n^2$). + + Идемпотентность: upsert по UNIQUE(exam_key, variant, task_idx). + Запуск: + node backend/scripts/seed_ctmath_ct2015_v1.js # DRY-RUN (по умолчанию) + node backend/scripts/seed_ctmath_ct2015_v1.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 = 112; +const PROV = 'ЦТ–2015, Вариант 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: 'numbers', subtopic: 'num-real', diff: 1, + text: R`На координатной прямой точки расположены слева направо в порядке $O,C,B,A,F,D$. Точка $O$ имеет координату $0$, а соседние отмеченные точки находятся на равных расстояниях $\dfrac17$ друг от друга. Если координата точки $A$ равна $\dfrac97$, то числу $1$ соответствует точка:`, + opts: mc('$C$', '$B$', '$D$', '$F$', '$O$'), + answer: 'а', + sol: R`Шаг между соседними точками равен $\dfrac17$, поэтому $B=\dfrac87$, $C=\dfrac77=1$. Числу $1$ соответствует точка $C$.`, + ref: 'Латотин «Математика, 6 кл.», гл. 5' }, + + { idx: 2, type: 'mc', topic: 'expressions', subtopic: 'expr-powers-roots', diff: 1, + text: R`Запишите $\left(11^{x}\right)^{y}$ в виде степени с основанием $11$.`, + opts: mc('$11^{x/y}$', '$11^{x+y}$', '$11^{2x+2y}$', '$11^{2xy}$', '$11^{xy}$'), + answer: 'д', + sol: R`По свойству степени $\left(11^{x}\right)^{y}=11^{xy}$.`, + ref: 'Арефьева «Алгебра, 7 кл.», гл. 1, § 4' }, + + { idx: 3, type: 'mc', topic: 'word-sequences', subtopic: 'seq-progressions', diff: 1, + text: R`Арифметическая прогрессия $(a_n)$ задана формулой $n$-го члена $a_n=5n-2$. Найдите разность этой прогрессии.`, + opts: mc('$3$', '$-7$', '$5$', '$7$', '$-5$'), + answer: 'в', + sol: R`$d=a_{n+1}-a_n=\bigl(5(n+1)-2\bigr)-(5n-2)=5$.`, + ref: 'Арефьева «Алгебра, 9 кл.», гл. 4' }, + + { idx: 4, type: 'mc', topic: 'functions', subtopic: 'fn-graphs', diff: 1, + text: R`Точка $K(5;-2)$ симметрична точке $L$ относительно точки $O$ — начала координат. Укажите координаты точки $L$.`, + opts: mc('$(-5;2)$', '$(5;2)$', '$(-5;-2)$', '$(2;-5)$', '$(-2;5)$'), + answer: 'а', + sol: R`При центральной симметрии относительно начала координат обе координаты меняют знак: $L(-5;2)$.`, + ref: 'Латотин «Математика, 6 кл.», гл. 5' }, + + { idx: 5, type: 'mc', topic: 'numbers', subtopic: 'num-real', diff: 1, + text: R`Вычислите $\dfrac{3732\cdot0{,}01-5}{0{,}47+1{,}13}$.`, + opts: mc('$20{,}2$', '$2{,}2$', '$202$', '$22$', '$2{,}02$'), + answer: 'а', + sol: R`$\dfrac{37{,}32-5}{1{,}6}=\dfrac{32{,}32}{1{,}6}=20{,}2$.`, + ref: 'Герасимов «Математика, 6 кл.», гл. 4' }, + + { idx: 6, type: 'mc', topic: 'equations', subtopic: 'eq-rational', diff: 2, + text: R`Укажите множество решений системы неравенств $\begin{cases}x\le-1{,}6,\\ 1-2x<9.\end{cases}$`, + opts: mc('$(-4;-1{,}6]$', '$[-1{,}6;+\infty)$', '$(-\infty;-4)$', '$[-4;-1{,}6)$', '$(-1{,}6;4)$'), + answer: 'а', + sol: R`Из $1-2x<9$ следует $-2x<8$, то есть $x>-4$. Вместе с $x\le-1{,}6$ получаем $-4ДеньВсе покупателиПо акциипонедельник44001600вторник55002600среда34001800четверг47002200пятница65001800`, + opts: mc('понедельник', 'вторник', 'среда', 'четверг', 'пятница'), + answer: 'д', + sol: R`Доля покупателей по акции: пн — $\dfrac{1600}{4400}\approx0{,}36$; вт — $\dfrac{2600}{5500}\approx0{,}47$; ср — $\dfrac{1800}{3400}\approx0{,}53$; чт — $\dfrac{2200}{4700}\approx0{,}47$; пт — $\dfrac{1800}{6500}\approx0{,}28$. Менее $0{,}3$ — только в пятницу.`, + ref: 'Герасимов «Математика, 6 кл.», гл. 2' }, + + { idx: 12, type: 'mc', topic: 'functions', subtopic: 'fn-graphs', diff: 2, + text: R`Графиком функции $y=1-(x+3)^{2}$ является парабола. Укажите верное утверждение о её расположении.`, + opts: mc('ветви вверх, вершина $(3;1)$', 'ветви вниз, вершина $(-3;1)$', 'ветви вниз, вершина $(3;1)$', 'ветви вверх, вершина $(-3;-1)$', 'ветви вниз, вершина $(-3;-1)$'), + answer: 'б', + sol: R`$y=-(x+3)^{2}+1$: коэффициент при квадрате отрицателен (ветви направлены вниз), вершина в точке $(-3;1)$.`, + ref: 'Арефьева «Алгебра, 8 кл.», гл. 3' }, + + { idx: 13, type: 'mc', topic: 'equations', subtopic: 'eq-exponential', diff: 2, + text: R`Уравнение $\dfrac{4x-9}{5}+2=x-\dfrac{11-x}{5}$ равносильно уравнению:`, + opts: mc('$6^{x}=1$', '$6^{x}=6$', '$2^{x}=32$', '$2^{x}=64$', '$5^{x}=25$'), + answer: 'г', + sol: R`Умножив на $5$: $4x-9+10=5x-(11-x)$, то есть $4x+1=6x-11$, откуда $x=6$. Этому корню равносильно уравнение $2^{x}=64$ (ведь $2^{6}=64$).`, + ref: 'Арефьева «Алгебра, 11 кл.», гл. 2' }, + + { idx: 14, type: 'mc', topic: 'word-sequences', subtopic: 'word-problems', diff: 2, + text: R`Собственная скорость катера в $9$ раз больше скорости течения реки. Расстояние по реке от пункта $A$ до пункта $B$ плот проплыл за время $t_1$, а катер — за время $t_2$. Тогда верна формула:`, + opts: mc('$t_1=10t_2$', '$t_1=9t_2$', '$t_1=9{,}5t_2$', '$t_1=10{,}5t_2$', '$t_1=11t_2$'), + answer: 'а', + sol: R`Пусть скорость течения $v$. Плот плывёт со скоростью течения: $t_1=\dfrac{S}{v}$. Катер по течению имеет скорость $9v+v=10v$: $t_2=\dfrac{S}{10v}$. Значит $t_1=10t_2$.`, + ref: 'Арефьева «Алгебра, 7 кл.», гл. 3' }, + + { idx: 15, type: 'mc', topic: 'planimetry', subtopic: 'plan-triangles', diff: 3, + text: R`На координатной плоскости дан тупоугольный треугольник $ABC$ с вершинами $A(2;3)$, $B(0;0)$, $C(2;-3)$. Найдите $\cos\angle ABC$.`, + opts: mc('$\dfrac{5}{12}$', '$\dfrac{5}{13}$', '$-\dfrac{5}{13}$', '$-\dfrac{12}{13}$', '$\dfrac{12}{13}$'), + answer: 'в', + sol: R`$\vec{BA}=(2;3)$, $\vec{BC}=(2;-3)$. $\cos\angle ABC=\dfrac{\vec{BA}\cdot\vec{BC}}{|\vec{BA}|\,|\vec{BC}|}=\dfrac{2\cdot2+3\cdot(-3)}{\sqrt{13}\cdot\sqrt{13}}=-\dfrac{5}{13}$.`, + ref: 'Казаков «Геометрия, 9 кл.», гл. 3' }, + + { idx: 16, type: 'mc', topic: 'stereometry', subtopic: 'ster-rotation', diff: 2, + text: R`Из полного бокала, имеющего форму конуса высотой $9$, отлили треть (по объёму) жидкости. Вычислите $\dfrac12 h^{3}$, где $h$ — высота оставшейся жидкости.`, + opts: mc('$324$', '$182$', '$27$', '$243$', '$81$'), + answer: 'г', + sol: R`Оставшаяся жидкость составляет $\tfrac23$ объёма и образует подобный конус высотой $h$: $\left(\dfrac{h}{9}\right)^{3}=\dfrac23$, откуда $h^{3}=729\cdot\dfrac23=486$. Тогда $\dfrac12 h^{3}=243$.`, + ref: 'Латотин «Геометрия, 11 кл.», разд. 2' }, + + { idx: 17, type: 'mc', topic: 'functions', subtopic: 'fn-properties', diff: 2, + text: R`График функции, заданной формулой $y=kx+b$, симметричен относительно оси $Oy$ и проходит через точку $A\left(\tfrac13;6\right)$. Значение выражения $k+b$ равно:`, + opts: mc('$-5\tfrac23$', '$6\tfrac13$', '$6$', '$2$', '$18$'), + answer: 'в', + sol: R`Симметрия относительно оси $Oy$ означает чётность функции: $-kx+b=kx+b$ при всех $x$, откуда $k=0$. Тогда $y=b$, и из прохождения через $A$ получаем $b=6$. Значит $k+b=6$.`, + ref: 'Арефьева «Алгебра, 8 кл.», гл. 3' }, + + { idx: 18, type: 'mc', topic: 'planimetry', subtopic: 'plan-triangles', diff: 3, + text: R`Высоты остроугольного равнобедренного треугольника $ABC$ ($AB=BC$) пересекаются в точке $O$. Если высота $AD=15$ и $AO=10$, то длина стороны $AC$ равна:`, + opts: mc('$17$', '$7\sqrt6$', '$5\sqrt3$', '$10\sqrt3$', '$5\sqrt{13}$'), + answer: 'г', + sol: R`Поместим $A(-a;0)$, $C(a;0)$, $B(0;h)$. Ортоцентр $O\left(0;\dfrac{a^{2}}{h}\right)$. Тогда $AO=\dfrac{a\sqrt{a^{2}+h^{2}}}{h}=10$, а высота $AD=\dfrac{2ah}{\sqrt{a^{2}+h^{2}}}=15$. Отсюда $a^{2}=75$, $h=15$, поэтому $AC=2a=10\sqrt3$.`, + ref: 'Казаков «Геометрия, 9 кл.», гл. 3' }, + + // ── Часть B: В1–В12 ────────────────────────────────────────────────────── + { idx: 19, type: 'open', topic: 'word-sequences', subtopic: 'word-problems', diff: 2, + text: R`Витя купил в магазине некоторое количество тетрадей, заплатив за них $24$ тысячи рублей. Затем он обнаружил, что в другом магазине тетрадь стоит на $1$ тысячу рублей меньше, поэтому, заплатив такую же сумму, он мог бы купить на $2$ тетради больше. Сколько тетрадей купил Витя?`, + answer: '6', + sol: R`Пусть цена тетради $p$ (тыс. руб.), куплено $n=\dfrac{24}{p}$ тетрадей. Тогда $\dfrac{24}{p-1}=\dfrac{24}{p}+2$, откуда $p(p-1)=12$, $p=4$. Значит $n=\dfrac{24}{4}=6$.`, + ref: 'Арефьева «Алгебра, 8 кл.», гл. 2' }, + + { idx: 20, type: 'open', topic: 'equations', subtopic: 'eq-exponential', diff: 3, + text: R`Найдите наибольшее целое решение неравенства $3^{x+17}\cdot5^{-x-16}>1{,}08$.`, + answer: '-15', + sol: R`$1{,}08=\dfrac{27}{25}=3^{3}\cdot5^{-2}$. Неравенство приводится к виду $3^{x+14}\cdot5^{-x-14}>1$, то есть $\left(\dfrac35\right)^{x+14}>1$. Так как $\dfrac35<1$, то $x+14<0$, $x<-14$. Наибольшее целое — $-15$.`, + ref: 'Арефьева «Алгебра, 11 кл.», гл. 2' }, + + { idx: 21, type: 'open', topic: 'equations', subtopic: 'eq-rational', diff: 2, + text: R`Найдите модуль разности наибольшего и наименьшего корней уравнения $\left(2x^{2}-x-7\right)^{2}=(5x+1)^{2}$.`, + answer: '7', + sol: R`$2x^{2}-x-7=\pm(5x+1)$. При знаке «$+$»: $2x^{2}-6x-8=0$, $x=4;-1$. При знаке «$-$»: $2x^{2}+4x-6=0$, $x=1;-3$. Корни $\{-3;-1;1;4\}$; модуль разности наибольшего и наименьшего $|4-(-3)|=7$.`, + ref: 'Арефьева «Алгебра, 9 кл.», гл. 1' }, + + { idx: 22, type: 'open', topic: 'equations', subtopic: 'eq-rational', diff: 2, + text: R`Пусть $(x_1;y_1)$, $(x_2;y_2)$ — решения системы уравнений $\begin{cases}x^{2}+4x=15+3y,\\ 4x-3y=6.\end{cases}$ Найдите значение выражения $x_1y_2+x_2y_1$.`, + answer: '-24', + sol: R`Из второго уравнения $3y=4x-6$. Подставив в первое: $x^{2}+4x=15+4x-6$, $x^{2}=9$, $x=\pm3$. Решения $(3;2)$ и $(-3;-6)$. Тогда $x_1y_2+x_2y_1=3\cdot(-6)+(-3)\cdot2=-24$.`, + ref: 'Арефьева «Алгебра, 9 кл.», гл. 1' }, + + { idx: 23, type: 'open', topic: 'equations', subtopic: 'eq-irrational', diff: 3, + text: R`Найдите сумму корней (корень, если он единственный) уравнения $\sqrt{x^{2}+3x}+\sqrt{1-x}=\sqrt{12-x}+\sqrt{1-x}$.`, + answer: '-6', + sol: R`Вычитая $\sqrt{1-x}$ из обеих частей: $\sqrt{x^{2}+3x}=\sqrt{12-x}$, $x^{2}+3x=12-x$, $x^{2}+4x-12=0$, $x=2;-6$. Условию ОДЗ ($x\le1$) удовлетворяет лишь $x=-6$.`, + ref: 'Арефьева «Алгебра, 10 кл.», гл. 2, § 17' }, + + { idx: 24, type: 'open', topic: 'equations', subtopic: 'eq-rational', diff: 3, + text: R`Найдите сумму целых решений неравенства $\dfrac{(x^{2}+7x+10)(x-4)^{2}}{4-x^{2}}\ge0$.`, + answer: '-8', + sol: R`После сокращения на $(x+2)$: $\dfrac{(x+5)(x-4)^{2}}{2-x}\ge0$ при $x\ne-2$. Множитель $(x-4)^{2}\ge0$, поэтому решение — промежуток $[-5;2)$ без точки $x=-2$, плюс отдельная точка $x=4$. Целые решения $-5,-4,-3,-1,0,1,4$; их сумма $-8$.`, + ref: 'Арефьева «Алгебра, 9 кл.», гл. 3' }, + + { idx: 25, type: 'open', topic: 'stereometry', subtopic: 'ster-polyhedra', diff: 3, + text: R`Каждое боковое ребро четырёхугольной пирамиды образует с её высотой, равной $3\sqrt7$, угол $30^\circ$. Основанием пирамиды является прямоугольник с углом $30^\circ$ между диагоналями. Найдите объём пирамиды $V$; в ответ запишите значение выражения $\sqrt7\cdot V$.`, + answer: '147', + sol: R`Все боковые рёбра равнонаклонены к основанию, значит вершина проектируется в центр прямоугольника. Половина диагонали $R=H\operatorname{tg}30^\circ=3\sqrt7\cdot\dfrac{1}{\sqrt3}=\sqrt{21}$, диагональ $d=2\sqrt{21}$. Площадь основания $S=\dfrac12 d^{2}\sin30^\circ=\dfrac12\cdot84\cdot\dfrac12=21$. Объём $V=\dfrac13\cdot21\cdot3\sqrt7=21\sqrt7$, и $\sqrt7\cdot V=21\cdot7=147$.`, + ref: 'Латотин «Геометрия, 11 кл.», разд. 1' }, + + { idx: 26, type: 'open', topic: 'trigonometry', subtopic: 'trig-equations', diff: 3, + text: R`Найдите (в градусах) наибольший отрицательный корень уравнения $\sin^{2}\!\left(5x-\dfrac{\pi}{3}\right)=1$.`, + answer: '-6', + sol: R`$\sin^{2}\alpha=1\Rightarrow\alpha=\dfrac{\pi}{2}+\pi k$. Тогда $5x=\dfrac{\pi}{3}+\dfrac{\pi}{2}+\pi k=\dfrac{5\pi}{6}+\pi k$, $x=30^\circ+36^\circ k$. Наибольший отрицательный корень при $k=-1$: $30^\circ-36^\circ=-6^\circ$.`, + ref: 'Арефьева «Алгебра, 10 кл.», гл. 1, § 8' }, + + { idx: 27, type: 'open', topic: 'trigonometry', subtopic: 'trig-equations', diff: 4, + text: R`Найдите количество корней уравнения $\sin x=-\dfrac{x}{16\pi}$.`, + answer: '33', + sol: R`Корни существуют лишь при $|x|\le16\pi$. Функции $\sin x$ и $-\dfrac{x}{16\pi}$ нечётны, поэтому $x=0$ — корень, а остальные корни симметричны. При $x>0$ правая часть отрицательна, поэтому пересечения происходят на восьми «отрицательных» арках синуса $(\pi;2\pi),(3\pi;4\pi),\ldots,(15\pi;16\pi)$ — по два на каждой, итого $16$. Столько же при $x<0$. Всего $16+16+1=33$.`, + ref: 'Арефьева «Алгебра, 10 кл.», гл. 1' }, + + { idx: 28, type: 'open', topic: 'planimetry', subtopic: 'plan-quadrilaterals', diff: 3, + text: R`В прямоугольнике $ABCD$ выбраны точки $L$ на стороне $BC$ и $M$ на стороне $AD$ так, что $ALCM$ — ромб. Найдите площадь этого ромба, если $AB=3$, $BC=9$.`, + answer: '15', + sol: R`Пусть сторона ромба равна $m$. Тогда $BL=BC-LC=9-m$, и из прямоугольного треугольника $ABL$: $m^{2}=3^{2}+(9-m)^{2}$, откуда $m=5$. Диагонали ромба $AC=\sqrt{3^{2}+9^{2}}=3\sqrt{10}$ и $LM=\sqrt{10}$, поэтому площадь $=\dfrac12\cdot3\sqrt{10}\cdot\sqrt{10}=15$.`, + ref: 'Казаков «Геометрия, 8 кл.», гл. 3' }, + + { idx: 29, type: 'open', topic: 'expressions', subtopic: 'expr-powers-roots', diff: 2, + text: R`Найдите значение выражения $2^{A}$, если $A=\log_{2}9+\log_{2}25$.`, + answer: '225', + sol: R`$A=\log_{2}9+\log_{2}25=\log_{2}(9\cdot25)=\log_{2}225$, поэтому $2^{A}=225$.`, + ref: 'Арефьева «Алгебра, 11 кл.», гл. 3' }, + + { idx: 30, type: 'open', topic: 'numbers', subtopic: 'num-divisibility', diff: 3, + text: R`Найдите сумму всех трёхзначных чисел, которые при делении на $4$ и на $6$ дают в остатке $1$, а при делении на $9$ дают в остатке $4$.`, + answer: '13825', + sol: R`Условия $n\equiv1\pmod4$ и $n\equiv1\pmod6$ дают $n\equiv1\pmod{12}$. Вместе с $n\equiv4\pmod9$ получаем $n\equiv13\pmod{36}$. Трёхзначные такие числа образуют прогрессию $121,157,\ldots,985$ — всего $25$ чисел. Их сумма $\dfrac{121+985}{2}\cdot25=553\cdot25=13825$.`, + ref: 'Арефьева «Алгебра, 9 кл.», гл. 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_ct2015_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 ответов пройдены (30/30).'); + +/* ── APPLY: upsert ─────────────────────────────────────────────────────────── */ +if (!APPLY) { + console.log('\nDRY-RUN: ничего не записано. Для записи: node backend/scripts/seed_ctmath_ct2015_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 → «Варианты» → «ЦТ-2015».\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 8c708e5..b7bdbc5 100644 --- a/backend/src/routes/exam-prep.js +++ b/backend/src/routes/exam-prep.js @@ -46,6 +46,7 @@ const VARIANT_LABEL = { 109: 'РТ-2022/23 · этап III', 110: 'ЦТ-2014', 111: 'ЦЭ-2024', + 112: 'ЦТ-2015', }, }; const examVariantLabel = (examKey, v) => VARIANT_LABEL[examKey]?.[v] || `Вариант ${v}`;