From 1868e6cb9bc3c988d030bc6856c089be43ad4ef0 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 26 Jun 2026 13:25:17 +0300 Subject: [PATCH] =?UTF-8?q?feat(trainer):=20V4.1=20=D0=B3=D1=80=D1=83?= =?UTF-8?q?=D0=BF=D0=BF=D0=B0=202=20=E2=80=94=2017=20=D0=B3=D0=B5=D0=BD?= =?UTF-8?q?=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BE=D0=B2=20(=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D1=80=D1=86=D0=B8=D0=B8/=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=86=D0=B5=D0=BD=D1=82=D1=8B/=D1=82=D0=B5=D0=BA=D1=81?= =?UTF-8?q?=D1=82=D0=BE=D0=B2=D1=8B=D0=B5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Вторая волна плана v4. Все compute, «корень-вперёд» → чистые целые ответы, пошаговые решения; правок движка/страницы нет. Пропорции (proportions): prop-direct-word (прямая, цена/кол-во), prop-inverse-word (обратная, рабочие/дни), prop-scale-map (масштаб карты), prop-compound (тройная), prop-share-ratio (деление в отношении a:b). Проценты (percents): pct-increase (+p%), pct-decrease (−p%), pct-change (на сколько % выросло), pct-simple-interest (простые проценты), pct-compound-2y (сложные, 2 года), pct-restore-before (исходное до повышения — обратный процент). Текстовые (applied): app-meet (встречное), app-overtake (вдогонку), app-upstream (против течения), app-work-joint (совместная работа), app-mix-blend (концентрация смеси), app-profit-pct (прибыль в %). Итого 96 генераторов. Смоук 16508 проверок (каждый ген: инстанс/самопроверка/приём канон.ответа/отказ неверного/все шаги→LaTeX); геометрия не задета (6968/0). Co-Authored-By: Claude Opus 4.8 (1M context) --- frontend/js/trainer/generators.js | 270 ++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) diff --git a/frontend/js/trainer/generators.js b/frontend/js/trainer/generators.js index f7bfa62..ef72636 100644 --- a/frontend/js/trainer/generators.js +++ b/frontend/js/trainer/generators.js @@ -1341,6 +1341,272 @@ { note: 'Из второго уравнения найдём y.', tex: 'y = {Sxy} - {sx}' }, { note: 'Ответ: x = {sx}, y = {sy}, z = {sz}.', tex: '' } ] + }, + + /* ═══════════════════════════════════════════════════════════════════════ + V4.1 — Группа 2: пропорции, проценты, текстовые задачи. Все compute, + «корень-вперёд» → ответ — чистое целое. (t как имя param в тренажёре допустим.) + ═══════════════════════════════════════════════════════════════════════ */ + + /* ── Пропорции (сюжетные) ── */ + + /* прямая пропорция: цена/количество */ + { + id: 'prop-direct-word', topic: 'proportions', order: 4, subject: 'algebra', grade: 6, kind: 'compute', + title: 'Прямая пропорция', + pick: { u: [2, 9], n: [2, 6], m: [2, 9] }, constraint: 'm != n', + derive: { S: 'u*n', val: 'u*m' }, + lhs: 'x', rhs: '{S}*{m}/{n}', display: 'За {n} тетрадей заплатили {S} руб. Сколько рублей стоят {m} таких тетрадей?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Сначала найдём цену одной тетради: делим стоимость на количество.', tex: '{S} / {n} = {u}' }, + { note: 'Тогда {m} тетрадей стоят:', tex: 'x = {u} * {m}' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* обратная пропорция: рабочие/дни */ + { + id: 'prop-inverse-word', topic: 'proportions', order: 5, subject: 'algebra', grade: 6, kind: 'compute', + title: 'Обратная пропорция', + pick: { n: [2, 9], m: [2, 9], d2: [2, 9] }, constraint: 'n != m', + derive: { W: 'm*d2', d: 'm*d2/n', val: 'd2' }, + require: 'mod(m*d2, n) == 0 && d >= 2', + lhs: 'x', rhs: '{n}*{d}/{m}', display: '{n} рабочих выполнили заказ за {d} дней. За сколько дней выполнят тот же заказ {m} рабочих?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Это обратная пропорция: общий объём работы постоянен. Считаем «рабочие-дни»: {n} · {d}.', tex: '{n} * {d} = {W}' }, + { note: 'Делим общий объём на новое число рабочих {m}.', tex: 'x = {W} / {m}' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* масштаб карты */ + { + id: 'prop-scale-map', topic: 'proportions', order: 6, subject: 'algebra', grade: 6, kind: 'compute', + title: 'Масштаб карты', + pick: { kbase: [1, 9], a: [2, 12] }, + derive: { k: 'kbase*100', val: 'a*kbase' }, + lhs: 'x', rhs: '{a}*{k}/100', display: 'Масштаб карты 1 : {k}. Отрезок на карте равен {a} см. Какому расстоянию на местности это соответствует (в метрах)?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Масштаб 1 : {k} значит, что 1 см карты — это {k} см на местности. Отрезок {a} см — это {a}·{k} см.', tex: '{a} * {k}' }, + { note: 'Переводим сантиметры в метры — делим на 100.', tex: 'x = {a} * {k} / 100' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* сложная (тройная) пропорция */ + { + id: 'prop-compound', topic: 'proportions', order: 7, subject: 'algebra', grade: 7, kind: 'compute', + title: 'Тройная пропорция', + pick: { r: [1, 4], n: [2, 6], d: [2, 6], m: [2, 6], e2: [2, 6] }, + derive: { W: 'r*n*d', val: 'r*m*e2' }, + lhs: 'x', rhs: '{W}*{m}*{e2}/({n}*{d})', display: '{n} рабочих за {d} дней изготовили {W} деталей. Сколько деталей изготовят {m} рабочих за {e2} дней при той же производительности?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Найдём, сколько деталей делает один рабочий за один день: {W} ÷ ({n}·{d}).', tex: '{W} / ({n} * {d})' }, + { note: 'Тогда {m} рабочих за {e2} дней изготовят:', tex: 'x = {W} * {m} * {e2} / ({n} * {d})' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* деление в отношении a:b */ + { + id: 'prop-share-ratio', topic: 'proportions', order: 8, subject: 'algebra', grade: 6, kind: 'compute', + title: 'Деление в отношении', + pick: { a: [1, 7], b: [1, 7], u: [2, 20] }, + derive: { S: 'u*(a + b)', val: 'u*a' }, + lhs: 'x', rhs: '{S}*{a}/({a}+{b})', display: 'Сумму {S} руб разделили в отношении {a} : {b}. Сколько рублей получил первый?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Всего долей {a} + {b}. Одна доля равна {S} ÷ ({a}+{b}).', tex: '{S} / ({a} + {b}) = {u}' }, + { note: 'Первому достаётся {a} долей:', tex: 'x = {u} * {a}' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* ── Проценты ── */ + + /* увеличение на p% */ + { + id: 'pct-increase', topic: 'percents', order: 4, subject: 'algebra', grade: 6, kind: 'compute', + title: 'Увеличение на p%', + pick: { abase: [1, 9], pidx: [1, 8] }, + derive: { a: 'abase*100', p: 'pidx*5', val: 'abase*(100 + pidx*5)' }, + lhs: 'x', rhs: '{a}*(100 + {p})/100', display: 'Цену {a} руб повысили на {p}%. Сколько рублей стала стоить вещь?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Повышение на {p}% — это умножение на (100 + {p})/100.', tex: 'x = {a} * (100 + {p}) / 100' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* уменьшение на p% */ + { + id: 'pct-decrease', topic: 'percents', order: 5, subject: 'algebra', grade: 6, kind: 'compute', + title: 'Уменьшение на p%', + pick: { abase: [1, 9], pidx: [1, 18] }, + derive: { a: 'abase*100', p: 'pidx*5', val: 'abase*(100 - pidx*5)' }, + lhs: 'x', rhs: '{a}*(100 - {p})/100', display: 'Число {a} уменьшили на {p}%. Какое число получилось?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Уменьшение на {p}% — это умножение на (100 − {p})/100.', tex: 'x = {a} * (100 - {p}) / 100' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* на сколько процентов изменилось */ + { + id: 'pct-change', topic: 'percents', order: 6, subject: 'algebra', grade: 7, kind: 'compute', + title: 'На сколько процентов выросло', + pick: { abase: [2, 12], pidx: [1, 10] }, + derive: { a: 'abase*20', p: 'pidx*5', b: 'abase*(100 + pidx*5)/5', val: 'pidx*5' }, + lhs: 'x', rhs: '100*({b} - {a})/{a}', display: 'Цена выросла с {a} руб до {b} руб. На сколько процентов выросла цена?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Рост в рублях: {b} − {a}.', tex: '{b} - {a}' }, + { note: 'Какую долю это составляет от исходной цены — делим на {a} и умножаем на 100.', tex: 'x = 100 * ({b} - {a}) / {a}' }, + { note: 'Считаем (в процентах).', tex: 'x = {ans}' } + ] + }, + + /* простые проценты (вклад) */ + { + id: 'pct-simple-interest', topic: 'percents', order: 7, subject: 'algebra', grade: 8, kind: 'compute', + title: 'Простые проценты', + pick: { sbase: [1, 9], pidx: [1, 6], n: [1, 4] }, + derive: { S: 'sbase*100', p: 'pidx*5', val: 'S*(100 + p*n)/100' }, + lhs: 'x', rhs: '{S}*(100 + {p}*{n})/100', display: 'В банк положили {S} руб под {p}% годовых (простые проценты). Какая сумма будет на счёте через {n} г.?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'За {n} г. простыми процентами начислят {p}·{n}% от вклада.', tex: '{p} * {n}' }, + { note: 'Итог — вклад плюс проценты:', tex: 'x = {S} * (100 + {p} * {n}) / 100' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* сложные проценты (2 года) */ + { + id: 'pct-compound-2y', topic: 'percents', order: 8, subject: 'algebra', grade: 9, kind: 'compute', + title: 'Сложные проценты (2 года)', + pick: { sbase: [1, 3], pidx: [1, 4] }, + derive: { S: 'sbase*10000', p: 'pidx*10', val: 'S*(100 + p)^2/10000' }, + lhs: 'x', rhs: '{S}*(100 + {p})^2/10000', display: 'Вклад {S} руб под {p}% годовых с ежегодной капитализацией. Какая сумма будет через 2 года?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Капитализация: каждый год сумма умножается на (100+{p})/100. За 2 года — дважды.', tex: 'x = {S} * (100 + {p}) / 100 * (100 + {p}) / 100' }, + { note: 'Это умножение на квадрат множителя:', tex: 'x = {S} * (100 + {p})^2 / 10000' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* найти исходное до повышения */ + { + id: 'pct-restore-before', topic: 'percents', order: 9, subject: 'algebra', grade: 8, kind: 'compute', + title: 'Цена до повышения', + pick: { abase: [1, 9], pidx: [1, 8] }, + derive: { a: 'abase*100', p: 'pidx*5', b: 'abase*(100 + pidx*5)', val: 'abase*100' }, + lhs: 'x', rhs: '{b}*100/(100 + {p})', display: 'После повышения на {p}% цена стала {b} руб. Какой была цена до повышения (в рублях)?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Новая цена — это (100 + {p})% от старой, то есть старая × (100 + {p})/100 = {b}.', tex: '{b} = x * (100 + {p}) / 100' }, + { note: 'Выражаем старую цену:', tex: 'x = {b} * 100 / (100 + {p})' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* ── Текстовые: движение, работа, смеси, прибыль ── */ + + /* встречное движение */ + { + id: 'app-meet', topic: 'applied', order: 6, subject: 'algebra', grade: 7, kind: 'compute', + title: 'Встречное движение', + pick: { u: [20, 70], v: [20, 70], t: [2, 6] }, + derive: { S: '(u + v)*t', val: 't' }, + lhs: 'x', rhs: '{S}/({u}+{v})', display: 'Из двух городов навстречу выехали машины со скоростями {u} и {v} км/ч. Расстояние между городами {S} км. Через сколько часов они встретятся?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'При встречном движении скорости складываются — машины сближаются на ({u}+{v}) км/ч.', tex: '{u} + {v}' }, + { note: 'Время до встречи — расстояние ÷ скорость сближения.', tex: 'x = {S} / ({u} + {v})' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* движение вдогонку */ + { + id: 'app-overtake', topic: 'applied', order: 7, subject: 'algebra', grade: 8, kind: 'compute', + title: 'Движение вдогонку', + pick: { u: [10, 40], g: [5, 30], t: [2, 6] }, + derive: { v: 'u + g', d: 'g*t', val: 't' }, + lhs: 'x', rhs: '{d}/({v}-{u})', display: 'Велосипедист едет со скоростью {u} км/ч, мотоциклист — со скоростью {v} км/ч, отставая на {d} км. Через сколько часов мотоциклист догонит велосипедиста?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'При движении вдогонку скорость сближения — разность скоростей: {v} − {u}.', tex: '{v} - {u}' }, + { note: 'Время — отставание ÷ скорость сближения.', tex: 'x = {d} / ({v} - {u})' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* движение по реке (против течения) */ + { + id: 'app-upstream', topic: 'applied', order: 8, subject: 'algebra', grade: 7, kind: 'compute', + title: 'Скорость против течения', + pick: { v: [8, 25], c: [1, 6] }, constraint: 'c < v', + derive: { val: 'v - c' }, + lhs: 'x', rhs: '{v}-{c}', display: 'Собственная скорость лодки {v} км/ч, скорость течения {c} км/ч. С какой скоростью лодка идёт против течения (км/ч)?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Против течения скорость лодки уменьшается на скорость течения.', tex: 'x = {v} - {c}' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* совместная работа */ + { + id: 'app-work-joint', topic: 'applied', order: 9, subject: 'algebra', grade: 8, kind: 'compute', + title: 'Совместная работа', + pick: { t: [2, 6], a: [3, 14] }, constraint: 'a > t', + derive: { b: 'a*t/(a - t)', val: 't' }, + require: 'mod(a*t, a - t) == 0 && b >= 2 && b <= 60', + lhs: 'x', rhs: '{a}*{b}/({a}+{b})', display: 'Первый рабочий выполнит работу за {a} ч, второй — за {b} ч. За сколько часов они выполнят её, работая вместе?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'За час первый делает 1/{a} работы, второй 1/{b}. Вместе их производительности складываются.', tex: '1/{a} + 1/{b}' }, + { note: 'Время совместной работы — это произведение, делённое на сумму:', tex: 'x = {a} * {b} / ({a} + {b})' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* смешивание двух растворов */ + { + id: 'app-mix-blend', topic: 'applied', order: 10, subject: 'algebra', grade: 9, kind: 'compute', + title: 'Концентрация смеси', + pick: { m1: [1, 6], m2: [1, 6], p1: [10, 40], p2: [50, 90] }, + derive: { val: '(m1*p1 + m2*p2)/(m1 + m2)' }, + require: 'mod(m1*p1 + m2*p2, m1 + m2) == 0', + lhs: 'x', rhs: '({m1}*{p1} + {m2}*{p2})/({m1}+{m2})', display: 'Смешали {m1} кг раствора {p1}% и {m2} кг раствора {p2}%. Какова концентрация (в процентах) полученной смеси?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Вещества в первом растворе: {m1}·{p1}/100, во втором: {m2}·{p2}/100.', tex: '{m1} * {p1} / 100 + {m2} * {p2} / 100' }, + { note: 'Концентрация смеси — всё вещество ÷ общую массу, в процентах:', tex: 'x = ({m1} * {p1} + {m2} * {p2}) / ({m1} + {m2})' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] + }, + + /* прибыль в процентах */ + { + id: 'app-profit-pct', topic: 'applied', order: 11, subject: 'algebra', grade: 8, kind: 'compute', + title: 'Прибыль в процентах', + pick: { cbase: [1, 9], pidx: [1, 10] }, + derive: { c: 'cbase*100', p: 'pidx*5', s: 'cbase*(100 + pidx*5)', val: 'pidx*5' }, + lhs: 'x', rhs: '100*({s} - {c})/{c}', display: 'Товар купили за {c} руб, а продали за {s} руб. Сколько процентов прибыли получили?', + answerVar: 'x', answer: 'val', integerAnswer: true, + solution: [ + { note: 'Прибыль в рублях: {s} − {c}.', tex: '{s} - {c}' }, + { note: 'Сколько это процентов от закупочной цены — делим на {c} и умножаем на 100.', tex: 'x = 100 * ({s} - {c}) / {c}' }, + { note: 'Считаем.', tex: 'x = {ans}' } + ] } ]; @@ -1388,6 +1654,10 @@ 'lin-frac-eq-frac': 3, 'lin-nested-paren': 3, 'lin-literal': 3, 'ineq-both-sides': 2, 'ineq-both-flip': 3, 'ineq-paren': 2, 'ineq-count-int': 2, 'sys-sum-diff': 1, 'sys-subst': 2, 'sys-word': 2, 'sys-3x3': 3, + // V4.1 — Пропорции/Проценты/Текстовые + 'prop-direct-word': 1, 'prop-inverse-word': 2, 'prop-scale-map': 2, 'prop-compound': 3, 'prop-share-ratio': 2, + 'pct-increase': 2, 'pct-decrease': 2, 'pct-change': 3, 'pct-simple-interest': 2, 'pct-compound-2y': 3, 'pct-restore-before': 3, + 'app-meet': 2, 'app-overtake': 3, 'app-upstream': 2, 'app-work-joint': 3, 'app-mix-blend': 3, 'app-profit-pct': 3, // НОД/НОК / Дроби / Десятичные / Отрицательные 'gcd-pair': 1, 'lcm-pair': 2, 'frac-of-number': 1, 'frac-add-same': 2,