feat(trainer): обратные и словесные задачи (+13 генераторов)

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) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-29 19:40:57 +03:00
parent 753bd7e289
commit 1b1c239015
+188
View File
@@ -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,