From 1b1c239015f744c3f105a80e73d737ef9c9dcd8f Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Mon, 29 Jun 2026 19:40:57 +0300 Subject: [PATCH] =?UTF-8?q?feat(trainer):=20=D0=BE=D0=B1=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20=D0=B8=20=D1=81=D0=BB=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D1=81=D0=BD=D1=8B=D0=B5=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D0=B8?= =?UTF-8?q?=20(+13=20=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=BE=D0=B2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C2 плана: обратные задачи (найти параметр по ответу) и словесные — все compute, «корень-вперёд», с обозначениями искомой (answerSym) и KaTeX в условии/решении. Обратные: inv-lin-coef (найти a по корню), inv-lin-const (найти b), fb-vieta (коэффициент b по Виета), inv-square-side (сторона квадрата по периметру), inv-triangle-base (основание по площади и высоте), inv-circle-r (радиус по длине окружности), inv-prog-a1 (первый член по n-му и разности). Словесные: wd-buy (стоимость покупки), wd-fence (забор=периметр, +read-from-figure), wd-price-updown (подорожал и подешевел на p% — ловушка «не вернулось к исходной»), wd-pages (сколько % страниц осталось), wd-average-speed (средняя скорость рейса = 2v₁v₂/(v₁+v₂), не среднее арифм.), wd-recipe (пропорция: мука на порции). Итого 212 генераторов. Смоук v41 115980 (инстанс/самопроверка/приём ответа/answerSym/ шаги→LaTeX); автоматематика по всем дисплеям+пояснениям 10366/0; фигуры 19652. Прим.: форматы findError/multi/order/match требуют новой UI (многострочные шаги / несколько вводов / drag) — отдельной волной. Co-Authored-By: Claude Opus 4.8 (1M context) --- frontend/js/trainer/generators.js | 188 ++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) diff --git a/frontend/js/trainer/generators.js b/frontend/js/trainer/generators.js index eccf529..a205fd3 100644 --- a/frontend/js/trainer/generators.js +++ b/frontend/js/trainer/generators.js @@ -3183,6 +3183,190 @@ { note: 'Точное значение {p}% от {a}:', tex: 'x = {p} * {a} / 100' }, { note: 'Подойдёт близкая прикидка.', tex: 'x = {val}' } ] + }, + + /* ═══════════════════════════════════════════════════════════════════════ + V4 C2 — обратные задачи (найти параметр по ответу) и словесные задачи. + Все compute, «корень-вперёд»; обозначения искомой — answerSym. + ═══════════════════════════════════════════════════════════════════════ */ + + /* ── Обратные: найти коэффициент/параметр ── */ + + /* линейное: найти коэффициент a по известному корню */ + { + id: 'inv-lin-coef', topic: 'linear-eq', order: 17, subject: 'algebra', grade: 7, kind: 'compute', + title: 'Найти коэффициент a', answerSym: 'a', + pick: { a: [2, 9], b: [1, 15], root: [2, 9] }, derive: { c: 'a*root + b' }, + lhs: 'x', rhs: '({c} - {b})/{root}', display: 'При каком значении a корень уравнения a·x + {b} = {c} равен x = {root}?', + answerVar: 'x', answer: 'a', integerAnswer: true, + solution: [ + { note: 'Подставим корень x = {root} в уравнение.', tex: 'a*{root} + {b} = {c}' }, + { note: 'Выразим a.', tex: 'a = ({c} - {b}) / {root}' }, + { note: 'Считаем.', tex: 'a = {ans}' } + ] + }, + + /* линейное: найти свободный член b по корню */ + { + id: 'inv-lin-const', topic: 'linear-eq', order: 18, subject: 'algebra', grade: 7, kind: 'compute', + title: 'Найти свободный член b', answerSym: 'b', + pick: { a: [2, 9], b: [1, 15], root: [2, 9] }, derive: { c: 'a*root + b' }, + lhs: 'x', rhs: '{c} - {a}*{root}', display: 'При каком значении b корень уравнения {a}x + b = {c} равен x = {root}?', + answerVar: 'x', answer: 'b', integerAnswer: true, + solution: [ + { note: 'Подставим корень x = {root}.', tex: '{a}*{root} + b = {c}' }, + { note: 'Выразим b.', tex: 'b = {c} - {a}*{root}' }, + { note: 'Считаем.', tex: 'b = {ans}' } + ] + }, + + /* квадратное: найти b по теореме Виета (даны оба корня) */ + { + id: 'fb-vieta', topic: 'quadratic', order: 15, subject: 'algebra', grade: 8, kind: 'compute', + title: 'Найти b (Виета)', answerSym: 'b', + pick: { r1: [-7, 7], r2: [-7, 7] }, constraint: 'r1 != r2', + derive: { c: 'r1*r2', val: '-(r1 + r2)' }, + lhs: 'x', rhs: '-({r1} + {r2})', display: 'Корни уравнения x² + bx + {c} = 0 равны {r1} и {r2}. Найдите коэффициент b.', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'По теореме Виета сумма корней равна −b.', tex: 'b = -({r1} + {r2})' }, + { note: 'Считаем.', tex: 'b = {ans}' } + ] + }, + + /* сторона квадрата по периметру */ + { + id: 'inv-square-side', topic: 'g-area', order: 11, subject: 'geometry', grade: 8, kind: 'compute', + title: 'Сторона квадрата по периметру', answerSym: 'a', + pick: { a: [2, 20] }, derive: { P: '4*a' }, + lhs: 'x', rhs: '{P}/4', display: 'Периметр квадрата равен {P}. Найдите его сторону.', + answerVar: 'x', answer: 'a', integerAnswer: true, + solution: [ + { note: 'Периметр квадрата — это 4 стороны, значит сторона = P ÷ 4.', tex: 'a = {P} / 4' }, + { note: 'Считаем.', tex: 'a = {ans}' } + ] + }, + + /* основание треугольника по площади и высоте */ + { + id: 'inv-triangle-base', topic: 'g-area', order: 12, subject: 'geometry', grade: 8, kind: 'compute', + title: 'Основание по площади', answerSym: 'a', + pick: { a: [2, 16], h: [2, 16] }, require: 'mod(a*h, 2) == 0', derive: { S: 'a*h/2' }, + lhs: 'x', rhs: '2*{S}/{h}', display: 'Площадь треугольника равна {S}, высота равна {h}. Найдите основание.', + answerVar: 'x', answer: 'a', integerAnswer: true, + solution: [ + { note: 'Площадь = (основание · высота) / 2, отсюда основание = 2·S / h.', tex: 'a = 2*{S} / {h}' }, + { note: 'Считаем.', tex: 'a = {ans}' } + ] + }, + + /* радиус по длине окружности */ + { + id: 'inv-circle-r', topic: 'g-circle', order: 8, subject: 'geometry', grade: 9, kind: 'compute', + title: 'Радиус по длине окружности', answerSym: 'r', + pick: { r: [1, 20] }, derive: { C: '2*3.14*r', val: 'r' }, + lhs: 'x', rhs: '{C}/(2*3.14)', display: 'Длина окружности равна {C}. Найдите радиус (π ≈ 3,14).', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Из C = 2πr выразим радиус: r = C / (2π).', tex: 'r = {C} / (2*3.14)' }, + { note: 'Считаем.', tex: 'r = {ans}' } + ] + }, + + /* первый член прогрессии по n-му члену и разности */ + { + id: 'inv-prog-a1', topic: 'progressions', order: 11, subject: 'algebra', grade: 9, kind: 'compute', + title: 'Первый член прогрессии', answerSym: 'a', + pick: { a: [-5, 12], d: [-5, 5], n: [3, 10] }, require: 'd != 0', derive: { an: 'a + (n - 1)*d', val: 'a' }, + lhs: 'x', rhs: '{an} - ({n} - 1)*{d}', display: 'В арифметической прогрессии {n}-й член равен {an}, разность {d}. Найдите первый член.', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Из формулы aₙ = a₁ + (n − 1)d выразим a₁.', tex: 'a = {an} - ({n} - 1)*{d}' }, + { note: 'Считаем.', tex: 'a = {ans}' } + ] + }, + + /* ── Словесные задачи ── */ + + /* стоимость покупки */ + { + id: 'wd-buy', topic: 'applied', order: 13, subject: 'algebra', grade: 6, kind: 'compute', + title: 'Стоимость покупки', + pick: { n: [2, 9], p: [3, 30], m: [2, 9], q: [3, 30] }, derive: { val: 'n*p + m*q' }, + lhs: 'x', rhs: '{n}*{p} + {m}*{q}', display: 'Купили {n} кг яблок по {p} руб и {m} кг груш по {q} руб. Сколько рублей заплатили всего?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Стоимость яблок {n}·{p}, груш {m}·{q}, складываем.', tex: 'x = {n}*{p} + {m}*{q}' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* периметр участка (забор) */ + { + id: 'wd-fence', topic: 'g-area', order: 13, subject: 'geometry', grade: 7, kind: 'compute', + title: 'Забор по периметру', answerSym: 'P', + figure: { type: 'rectangle', w: 'a', h: 'b' }, + figurePrompt: 'Найдите длину забора (периметр участка), м.', + pick: { a: [3, 20], b: [3, 20] }, constraint: 'a != b', derive: { val: '2*(a + b)' }, + lhs: 'x', rhs: '2*({a} + {b})', display: 'Прямоугольный участок {a} м на {b} м огородили забором по периметру. Сколько метров забора потребовалось?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Длина забора — периметр прямоугольника: 2·(a + b).', tex: 'P = 2*({a} + {b})' }, + { note: 'Считаем.', tex: 'P = {ans}' } + ] + }, + + /* подорожание, затем подешевение на тот же процент */ + { + id: 'wd-price-updown', topic: 'percents', order: 12, subject: 'algebra', grade: 8, kind: 'compute', + title: 'Подорожал и подешевел', + pick: { pbase: [1, 9], pidx: [1, 4] }, derive: { price: 'pbase*100', p: 'pidx*10', val: 'pbase*100*(100 + pidx*10)/100*(100 - pidx*10)/100' }, + lhs: 'x', rhs: '{price}*(100 + {p})/100*(100 - {p})/100', display: 'Товар стоил {price} руб. Сначала подорожал на {p}%, затем подешевел на {p}%. Сколько рублей он стал стоить?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Подорожание — умножение на (100 + {p})/100, подешевение — на (100 − {p})/100.', tex: 'x = {price}*(100 + {p})/100*(100 - {p})/100' }, + { note: 'Считаем (получилось меньше исходной цены!).', tex: 'x = {ans}' } + ] + }, + + /* сколько страниц осталось */ + { + id: 'wd-pages', topic: 'percents', order: 13, subject: 'algebra', grade: 6, kind: 'compute', + title: 'Сколько страниц осталось', + pick: { Nbase: [1, 9], pidx: [1, 9] }, derive: { N: 'Nbase*100', p: 'pidx*10', val: 'Nbase*(100 - pidx*10)' }, + lhs: 'x', rhs: '{N}*(100 - {p})/100', display: 'В книге {N} страниц. Прочитано {p}%. Сколько страниц осталось прочитать?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Осталось (100 − {p})% страниц: {N}·(100 − {p})/100.', tex: 'x = {N}*(100 - {p})/100' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* средняя скорость кругового рейса (гармоническое среднее) */ + { + id: 'wd-average-speed', topic: 'applied', order: 14, subject: 'algebra', grade: 8, kind: 'compute', + title: 'Средняя скорость рейса', answerSym: 'v', + pick: { v1: [20, 80], v2: [20, 80] }, constraint: 'v1 != v2', require: 'mod(2*v1*v2, v1 + v2) == 0', + derive: { val: '2*v1*v2/(v1 + v2)' }, + lhs: 'x', rhs: '2*{v1}*{v2}/({v1} + {v2})', display: 'Туда автомобиль ехал со скоростью {v1} км/ч, обратно — {v2} км/ч. Найдите среднюю скорость на всём пути (км/ч).', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Средняя скорость = весь путь ÷ всё время = 2·v₁·v₂/(v₁ + v₂) — НЕ среднее арифметическое!', tex: 'v = 2*{v1}*{v2}/({v1} + {v2})' }, + { note: 'Считаем.', tex: 'v = {ans}' } + ] + }, + + /* пропорция: рецепт (мука на порции) */ + { + id: 'wd-recipe', topic: 'proportions', order: 9, subject: 'algebra', grade: 6, kind: 'compute', + title: 'Рецепт (пропорция)', + pick: { u: [20, 80], n: [2, 6], m: [2, 9] }, constraint: 'm != n', derive: { g: 'u*n', val: 'u*m' }, + lhs: 'x', rhs: '{g}*{m}/{n}', display: 'По рецепту на {n} порций нужно {g} г муки. Сколько граммов муки нужно на {m} порций?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'На одну порцию {g} ÷ {n} = {u} г. На {m} порций умножаем.', tex: 'x = {g} / {n} * {m}' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] } ]; @@ -3261,6 +3445,10 @@ 'ch-area-rect': 1, 'ch-lin-basic': 1, 'ch-pct-of': 2, 'ch-pyth-hyp': 2, 'vf-frac-compare': 2, 'vf-divisible': 1, 'vf-pyth': 3, 'vf-eq-root': 2, 'est-product': 2, 'est-percent': 2, + // V4 C2 — обратные и словесные задачи + 'inv-lin-coef': 2, 'inv-lin-const': 2, 'fb-vieta': 2, 'inv-square-side': 2, 'inv-triangle-base': 2, + 'inv-circle-r': 2, 'inv-prog-a1': 3, + 'wd-buy': 2, 'wd-fence': 2, 'wd-price-updown': 3, 'wd-pages': 2, 'wd-average-speed': 3, 'wd-recipe': 2, 'pyth-perimeter': 3, 'pyth-distance': 3, 'pyth-rect-diagonal': 2, 'pyth-space-diagonal': 3, 'area-rect-inverse': 2, 'area-l-shape': 3, 'area-sector': 3, 'poly-diagonals': 2, 'poly-find-n': 3, 'poly-exterior-sum': 2,