Files
Learn_System/frontend/js/trainer/generators.js
T
Maxim Dolgolyov 105e046087 feat(trainer): V4.1 группа 1 — 15 новых линейных генераторов (уравнения/неравенства/системы)
Реализована первая волна плана v4 (разнообразие задач по темам). Все «корень-вперёд»,
ответы чистые целые, подробные пошаговые решения, без правок страницы.

Уравнения (linear-eq): lin-both-frac (с делением на a−c≥2), lin-x-denom (a/(x+b)=c),
lin-k-over-x (k/x=c), lin-abs (|ax+b|=c, два корня, kind roots), lin-frac-eq-frac
(дробь=дробь крест-накрест), lin-nested-paren (вложенные скобки), lin-literal (ax+b=mx,
«выразите x»).

Неравенства (inequalities): ineq-both-sides (ax+b<cx+d), ineq-both-flip (смена знака,
c>a), ineq-paren (a(x+b)>c), ineq-count-int (число целых решений lo<x≤hi — двойное
неравенство через compute, обход ограничения одной полупрямой).

Системы (systems): sys-sum-diff (x+y=S, x−y=D), sys-subst (подстановка y=mx+k),
sys-word (текстовая на 2 неизвестных), sys-3x3 (3×3 тизер, answerVars длины 3).

Движок: минимальная аддитивная правка — kind system теперь показывает gen.display
(сюжетную формулировку), если он задан, вместо \begin{cases} (только для sys-word;
существующие sys-2x2/neg без display не затронуты). Итого 79 генераторов.

Верификация: смоук 8314 проверок — каждый генератор инстанцируется по сидам,
самопроверяется, принимает канонический ответ и отвергает неверный, ВСЕ шаги решения
рендерятся в LaTeX; геометрия не задета (6968/0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 13:16:37 +03:00

1418 lines
85 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
/* ════════════════════════════════════════════════════════════════════════
Генераторы задач тренажёра — ДАННЫЕ, не код. Таксономия: тема → навык.
Приём «корень-вперёд»: выбираем целый корень/множители и коэффициенты, затем
ВЫВОДИМ остальное так, чтобы ответ был целым, а задача — решаемой. Поэтому
самопроверка движка (verifyRoot) всегда проходит. Шаг решения —
{ note(подробный текст), tex(одно равенство) }; tex рендерится в KaTeX
(exprToLatex). Последний шаг «Проверка» подставляет корень — это и педагогика,
и наглядная демонстрация того, как движок проверяет ответ.
Виды задач:
• solve (деф.) — уравнение lhs = rhs, ученик находит x. Показывается уравнение.
• compute (kind:'compute') — вычислительная задача (проценты): на сцене —
текстовый prompt из display, а lhs:'x' / rhs:<значение> служат ТОЛЬКО для
проверки ответа подстановкой (latex уравнения не показывается).
Темы (7 класс, алгебра): Уравнения → Пропорции → Проценты. Дальше (Уровень 1):
текстовые задачи через LLM с той же подстановочной верификацией.
════════════════════════════════════════════════════════════════════════ */
(function (global) {
// Классы по программе наших учебников (Алгебра/Геометрия 7–9).
var TOPICS = [
{ key: 'linear-eq', label: 'Уравнения', subject: 'algebra', grade: 7, order: 1 },
{ key: 'proportions', label: 'Пропорции', subject: 'algebra', grade: 6, order: 2 },
{ key: 'percents', label: 'Проценты', subject: 'algebra', grade: 6, order: 3 },
{ key: 'simplify', label: 'Упрощение', subject: 'algebra', grade: 7, order: 4 },
{ key: 'powers', label: 'Степени', subject: 'algebra', grade: 7, order: 5 },
{ key: 'formulas', label: 'Формулы', subject: 'algebra', grade: 7, order: 6 },
{ key: 'inequalities', label: 'Неравенства', subject: 'algebra', grade: 7, order: 7 },
{ key: 'systems', label: 'Системы', subject: 'algebra', grade: 7, order: 8 },
{ key: 'quadratic', label: 'Квадратные', subject: 'algebra', grade: 8, order: 9 },
{ key: 'progressions', label: 'Прогрессии', subject: 'algebra', grade: 9, order: 10 },
{ key: 'gcd-lcm', label: 'НОД и НОК', subject: 'algebra', grade: 5, order: 1.4 },
{ key: 'fractions', label: 'Дроби', subject: 'algebra', grade: 5, order: 1.5 },
{ key: 'decimals', label: 'Десятичные', subject: 'algebra', grade: 5, order: 1.6 },
{ key: 'negatives', label: 'Отрицательные', subject: 'algebra', grade: 6, order: 1.7 },
{ key: 'applied', label: 'Задачи', subject: 'algebra', grade: 7, order: 11 },
{ key: 'g-angles', label: 'Углы', subject: 'geometry', grade: 7, order: 12 },
{ key: 'g-pyth', label: 'Пифагор', subject: 'geometry', grade: 8, order: 13 },
{ key: 'g-area', label: 'Площади', subject: 'geometry', grade: 8, order: 14 },
{ key: 'g-poly', label: 'Многоугольники', subject: 'geometry', grade: 8, order: 15 },
{ key: 'g-sim', label: 'Подобие', subject: 'geometry', grade: 8, order: 16 },
{ key: 'g-circle', label: 'Окружность', subject: 'geometry', grade: 9, order: 17 }
];
var GENERATORS = [
/* ═══ Тема: Уравнения ═══ */
/* ax + b = c */
{
id: 'lin-basic', topic: 'linear-eq', order: 1, subject: 'algebra', grade: 7,
title: 'ax + b = c',
pick: { a: [2, 9], b: [1, 20], root: [-9, 9] },
require: 'root != 0',
derive: { c: 'a*root + b', cmb: 'a*root' },
lhs: '{a}*x + {b}', rhs: '{c}', display: '{a}x + {b} = {c}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Перед нами линейное уравнение. Наша цель — оставить x одного в левой части. Сначала уберём свободное число {b}: перенесём его вправо, поменяв знак.', tex: '{a}x = {c} - {b}' },
{ note: 'Выполняем вычитание в правой части.', tex: '{a}x = {cmb}' },
{ note: 'Осталось избавиться от множителя при x — делим обе части уравнения на {a}.', tex: 'x = {cmb} / {a}' },
{ note: 'Получаем корень уравнения.', tex: 'x = {ans}' },
{ note: 'Проверка: подставим найденное значение в исходное уравнение — левая часть должна совпасть с правой.', tex: '{a}*({ans}) + {b} = {c}' }
]
},
/* a(x + b) = c */
{
id: 'lin-paren', topic: 'linear-eq', order: 2, subject: 'algebra', grade: 7,
title: 'a(x + b) = c',
pick: { a: [2, 8], b: [1, 12], root: [-9, 9] },
require: 'root != 0',
derive: { c: 'a*(root + b)', ca: 'root + b' },
lhs: '{a}*(x + {b})', rhs: '{c}', display: '{a}(x + {b}) = {c}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Слева число {a} умножается на скобку. Самый короткий путь — разделить обе части уравнения на {a}, чтобы убрать этот множитель.', tex: 'x + {b} = {ca}' },
{ note: 'Справа получилось целое число. Теперь переносим {b} вправо со сменой знака.', tex: 'x = {ca} - {b}' },
{ note: 'Получаем корень уравнения.', tex: 'x = {ans}' },
{ note: 'Проверка: подставляем корень в скобку исходного уравнения.', tex: '{a}*({ans} + {b}) = {c}' }
]
},
/* ax + b = cx + d */
{
id: 'lin-both-sides', topic: 'linear-eq', order: 3, subject: 'algebra', grade: 7,
title: 'ax + b = cx + d',
pick: { a: [3, 9], c: [1, 8], b: [1, 20], root: [-9, 9] },
constraint: 'c < a', require: 'root != 0',
derive: { d: '(a - c)*root + b', amc: 'a - c', dmb: '(a - c)*root' },
lhs: '{a}*x + {b}', rhs: '{c}*x + {d}', display: '{a}x + {b} = {c}x + {d}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Здесь x есть в обеих частях. Соберём все слагаемые с x слева, а числа — справа. Переносимые слагаемые меняют знак.', tex: '({a} - {c})x = {d} - {b}' },
{ note: 'Приводим подобные: вычитаем коэффициенты при x и отдельно числа.', tex: '{amc}x = {dmb}' },
{ note: 'Делим обе части на коэффициент при x, то есть на {amc}.', tex: 'x = {dmb} / {amc}' },
{ note: 'Получаем корень уравнения.', tex: 'x = {ans}' },
{ note: 'Проверка: подставим корень — обе части дадут одно и то же число.', tex: '{a}*({ans}) + {b} = {c}*({ans}) + {d}' }
]
},
/* a(x + b) = c(x + d) */
{
id: 'lin-paren-both', topic: 'linear-eq', order: 4, subject: 'algebra', grade: 7,
title: 'a(x+b) = c(x+d)',
pick: { a: [2, 6], c: [2, 6], b: [1, 10], root: [-6, 6] },
constraint: 'a != c',
derive: { V: 'a*(root + b)', d: 'V/c - root', ab: 'a*b', cd: 'c*(V/c - root)', amc: 'a - c', diff: 'c*(V/c - root) - a*b' },
require: 'mod(V, c) == 0 && root != 0',
lhs: '{a}*(x + {b})', rhs: '{c}*(x + {d})', display: '{a}(x + {b}) = {c}(x + {d})',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Скобки с двух сторон. Раскрываем их: умножаем множитель перед скобкой на каждое слагаемое внутри.', tex: '{a}x + {ab} = {c}x + {cd}' },
{ note: 'Переносим слагаемые с x влево, числа — вправо, и приводим подобные.', tex: '{amc}x = {diff}' },
{ note: 'Делим обе части на {amc}.', tex: 'x = {diff} / {amc}' },
{ note: 'Получаем корень уравнения.', tex: 'x = {ans}' },
{ note: 'Проверка: подставляем корень в обе скобки.', tex: '{a}*({ans} + {b}) = {c}*({ans} + {d})' }
]
},
/* x/a + b = c */
{
id: 'lin-frac-denom', topic: 'linear-eq', order: 5, subject: 'algebra', grade: 7,
title: 'x/a + b = c',
pick: { a: [2, 6], k: [-6, 6], b: [1, 12] },
require: 'k != 0',
derive: { root: 'a*k', c: 'k + b', cmb: 'k' },
lhs: 'x/{a} + {b}', rhs: '{c}', display: 'x/{a} + {b} = {c}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Слева — дробь x/{a} и число {b}. Сначала уберём свободное число: вычтем {b} из обеих частей.', tex: 'x/{a} = {cmb}' },
{ note: 'Чтобы избавиться от знаменателя {a}, умножаем обе части уравнения на {a}.', tex: 'x = {cmb} * {a}' },
{ note: 'Получаем корень уравнения.', tex: 'x = {ans}' },
{ note: 'Проверка: подставляем корень в исходное уравнение.', tex: '{ans}/{a} + {b} = {c}' }
]
},
/* (a·x)/b = c */
{
id: 'lin-coef-frac', topic: 'linear-eq', order: 6, subject: 'algebra', grade: 7,
title: 'ax/b = c',
pick: { a: [2, 5], b: [2, 5], m: [-5, 5] },
require: 'm != 0',
derive: { root: 'b*m', c: 'a*m', cb: 'a*m*b' },
lhs: '{a}*x/{b}', rhs: '{c}', display: '{a}x/{b} = {c}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Слева дробь, в числителе — {a}x. Умножаем обе части уравнения на знаменатель {b}, чтобы избавиться от дроби.', tex: '{a}x = {cb}' },
{ note: 'Теперь делим обе части на коэффициент {a}.', tex: 'x = {cb} / {a}' },
{ note: 'Получаем корень уравнения.', tex: 'x = {ans}' },
{ note: 'Проверка: подставляем корень в исходную дробь.', tex: '{a}*({ans})/{b} = {c}' }
]
},
/* (ax + b)/c = d */
{
id: 'lin-frac-eq', topic: 'linear-eq', order: 7, subject: 'algebra', grade: 7,
title: '(ax + b)/c = d',
pick: { a: [2, 6], b: [1, 12], c: [2, 6], root: [-6, 6] },
derive: { prod: 'a*root + b', d: '(a*root + b)/c', cd: 'a*root + b', cdmb: 'a*root' },
require: 'mod(a*root + b, c) == 0 && root != 0',
lhs: '({a}*x + {b})/{c}', rhs: '{d}', display: '({a}x + {b})/{c} = {d}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Вся левая часть делится на {c}. Умножаем обе части уравнения на {c}, чтобы убрать знаменатель.', tex: '{a}x + {b} = {cd}' },
{ note: 'Переносим число {b} в правую часть со сменой знака.', tex: '{a}x = {cdmb}' },
{ note: 'Делим обе части на {a}.', tex: 'x = {cdmb} / {a}' },
{ note: 'Получаем корень уравнения.', tex: 'x = {ans}' },
{ note: 'Проверка: подставляем корень в исходную дробь.', tex: '({a}*({ans}) + {b})/{c} = {d}' }
]
},
/* ═══ Тема: Пропорции ═══ */
/* a/b = c/x */
{
id: 'prop-x-right', topic: 'proportions', order: 1, subject: 'algebra', grade: 7,
title: 'a/b = c/x',
pick: { a: [2, 9], b: [2, 9], t: [2, 9] },
derive: { c: 'a*t', root: 'b*t', bc: 'b*a*t' },
lhs: '{a}/{b}', rhs: '{c}/x', display: '{a}/{b} = {c}/x',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Это пропорция — равенство двух отношений. По основному свойству пропорции произведение крайних членов равно произведению средних (умножаем «крест-накрест»).', tex: '{a}*x = {b} * {c}' },
{ note: 'Считаем произведение в правой части.', tex: '{a}x = {bc}' },
{ note: 'Делим обе части на {a}, чтобы найти x.', tex: 'x = {bc} / {a}' },
{ note: 'Получаем корень.', tex: 'x = {ans}' },
{ note: 'Проверка: при найденном x обе дроби равны.', tex: '{a}/{b} = {c}/{ans}' }
]
},
/* x/a = b/c */
{
id: 'prop-x-left', topic: 'proportions', order: 2, subject: 'algebra', grade: 7,
title: 'x/a = b/c',
pick: { a: [2, 9], c: [2, 9], s: [2, 9] },
derive: { b: 'c*s', root: 'a*s', ab: 'a*c*s' },
lhs: 'x/{a}', rhs: '{b}/{c}', display: 'x/{a} = {b}/{c}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Перед нами пропорция. Перемножаем её члены крест-накрест: числитель левой дроби на знаменатель правой и наоборот.', tex: '{c}*x = {a} * {b}' },
{ note: 'Считаем произведение в правой части.', tex: '{c}x = {ab}' },
{ note: 'Делим обе части на {c}.', tex: 'x = {ab} / {c}' },
{ note: 'Получаем корень.', tex: 'x = {ans}' },
{ note: 'Проверка: обе дроби равны.', tex: '{ans}/{a} = {b}/{c}' }
]
},
/* a/x = b/c */
{
id: 'prop-x-denom', topic: 'proportions', order: 3, subject: 'algebra', grade: 7,
title: 'a/x = b/c',
pick: { b: [2, 9], c: [2, 9], s: [2, 9] },
derive: { a: 'b*s', root: 'c*s', ac: 'b*s*c' },
lhs: '{a}/x', rhs: '{b}/{c}', display: '{a}/x = {b}/{c}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Пропорция, где неизвестное стоит в знаменателе. Перемножаем крест-накрест.', tex: '{b}*x = {a} * {c}' },
{ note: 'Считаем произведение в правой части.', tex: '{b}x = {ac}' },
{ note: 'Делим обе части на {b}.', tex: 'x = {ac} / {b}' },
{ note: 'Получаем корень.', tex: 'x = {ans}' },
{ note: 'Проверка: обе дроби равны.', tex: '{a}/{ans} = {b}/{c}' }
]
},
/* ═══ Тема: Проценты (вычислительные задачи) ═══ */
/* p% от числа a */
{
id: 'pct-of', topic: 'percents', order: 1, subject: 'algebra', grade: 7, kind: 'compute',
title: 'p% от числа',
pick: { pidx: [2, 10], abase: [1, 15] },
derive: { p: 'pidx*5', a: 'abase*20', val: 'pidx*abase' },
lhs: 'x', rhs: '{p}*{a}/100', display: 'Найдите {p}% от числа {a}',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Процент — это сотая доля числа. Чтобы найти {p}% от {a}, нужно умножить число на {p} и разделить на 100.', tex: 'x = {a}*{p}/100' },
{ note: 'Выполняем умножение и деление — получаем ответ.', tex: 'x = {ans}' }
]
},
/* сколько % составляет a от b */
{
id: 'pct-what', topic: 'percents', order: 2, subject: 'algebra', grade: 7, kind: 'compute',
title: 'Сколько процентов',
pick: { pidx: [2, 10], bbase: [1, 8] },
derive: { p: 'pidx*5', b: 'bbase*20', a: 'pidx*bbase' },
lhs: 'x', rhs: '100*{a}/{b}', display: 'Сколько процентов составляет {a} от {b}?',
answerVar: 'x', answer: 'p', integerAnswer: true,
solution: [
{ note: 'Чтобы узнать, какую часть {a} составляет от {b}, делим {a} на {b}. А чтобы перевести эту часть в проценты — умножаем результат на 100.', tex: 'x = {a}/{b}*100' },
{ note: 'Считаем — ответ получается в процентах.', tex: 'x = {ans}' }
]
},
/* p% числа равны a — найти число */
{
id: 'pct-whole', topic: 'percents', order: 3, subject: 'algebra', grade: 7, kind: 'compute',
title: 'Число по проценту',
pick: { pidx: [2, 10], wbase: [1, 12] },
derive: { p: 'pidx*5', whole: 'wbase*20', a: 'pidx*wbase' },
lhs: 'x', rhs: '100*{a}/{p}', display: '{p}% числа равны {a}. Найдите это число.',
answerVar: 'x', answer: 'whole', integerAnswer: true,
solution: [
{ note: 'Известно, что {p}% некоторого числа равны {a}. Значит само число во столько раз больше: умножаем {a} на 100 и делим на {p}.', tex: 'x = {a}*100/{p}' },
{ note: 'Считаем — получаем искомое число.', tex: 'x = {ans}' }
]
},
/* ═══ Тема: Упрощение выражений (проверка эквивалентностью) ═══ */
/* a·x + b·x → (a+b)x */
{
id: 'simp-like', topic: 'simplify', order: 1, subject: 'algebra', grade: 7, kind: 'simplify',
title: 'Привести подобные',
pick: { a: [2, 9], b: [2, 9] },
derive: { s: 'a + b' },
srcExpr: '{a}*x + {b}*x', answerExpr: '{s}*x', answerVars: ['x'],
display: 'Упростите: {a}x + {b}x',
solution: [
{ note: 'Слагаемые {a}x и {b}x подобны (одинаковая буквенная часть x). Складываем коэффициенты: {a} + {b} = {s}.', tex: '{a}x + {b}x' },
{ note: 'Получаем:', tex: '{s}x' }
]
},
/* a(x + b) → ax + ab */
{
id: 'simp-expand', topic: 'simplify', order: 2, subject: 'algebra', grade: 7, kind: 'simplify',
title: 'Раскрыть скобки',
pick: { a: [2, 9], b: [1, 9] },
derive: { ab: 'a*b' },
srcExpr: '{a}*(x + {b})', answerExpr: '{a}*x + {ab}', answerVars: ['x'],
display: 'Раскройте скобки: {a}(x + {b})',
solution: [
{ note: 'Умножаем множитель {a} на каждое слагаемое в скобке: {a}·x и {a}·{b}.', tex: '{a}(x + {b})' },
{ note: 'Получаем:', tex: '{a}x + {ab}' }
]
},
/* ═══ Тема: Квадратные уравнения (несколько корней) ═══ */
/* x² + bx + c = 0 — разложение по Виета (два корня r1, r2) */
{
id: 'quad-factored', topic: 'quadratic', order: 1, subject: 'algebra', grade: 8, kind: 'roots',
title: 'x² + bx + c = 0',
pick: { r1: [-7, 7], r2: [-7, 7] },
constraint: 'r1 != r2',
derive: { b: '-(r1 + r2)', c: 'r1*r2' },
lhs: 'x^2 + {b}*x + {c}', rhs: '0',
answerVar: 'x', answers: ['r1', 'r2'], integerAnswer: true,
solution: [
{ note: 'Квадратное уравнение приравнено к нулю. По теореме Виета ищем два числа: их сумма равна {r1}+{r2}, произведение — {c}. Это и есть корни. Раскладываем на множители:', tex: '(x - {r1})(x - {r2}) = 0' },
{ note: 'Произведение равно нулю, когда обнуляется множитель. Первый корень:', tex: 'x = {r1}' },
{ note: 'Второй корень:', tex: 'x = {r2}' }
]
},
/* x² − a² = 0 — разность квадратов (корни ±a) */
{
id: 'quad-diff', topic: 'quadratic', order: 2, subject: 'algebra', grade: 8, kind: 'roots',
title: 'x² a² = 0',
pick: { a: [2, 9] },
derive: { a2: 'a*a' },
lhs: 'x^2 - {a2}', rhs: '0',
answerVar: 'x', answers: ['a', '-a'], integerAnswer: true,
solution: [
{ note: 'Слева — разность квадратов: x² − {a2} = (x {a})(x + {a}). Раскладываем:', tex: '(x - {a})(x + {a}) = 0' },
{ note: 'Первый корень:', tex: 'x = {a}' },
{ note: 'Второй корень:', tex: 'x = -{a}' }
]
},
/* ═══ Тема: Степени ═══ */
/* вычислить aⁿ */
{
id: 'pow-eval', topic: 'powers', order: 1, subject: 'algebra', grade: 8, kind: 'compute',
title: 'Вычислить степень',
pick: { a: [2, 6], n: [2, 3] },
derive: { val: 'a^n' },
lhs: 'x', rhs: '{a}^{n}', display: 'Вычислите {a} в степени {n}',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Степень — это повторное умножение основания на себя.', tex: 'x = {a}^{n}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* xᵃ · xᵇ = xᵃ⁺ᵇ */
{
id: 'pow-mult', topic: 'powers', order: 2, subject: 'algebra', grade: 8, kind: 'simplify',
title: 'Произведение степеней',
pick: { a: [2, 5], b: [2, 5] },
derive: { s: 'a + b' },
srcExpr: 'x^{a}*x^{b}', answerExpr: 'x^{s}', answerVars: ['x'],
display: 'Упростите: x^{a}·x^{b}',
solution: [
{ note: 'Основание одинаковое (x) — при умножении степеней показатели складываются: {a} + {b} = {s}.', tex: 'x^{a} * x^{b}' },
{ note: 'Получаем:', tex: 'x^{s}' }
]
},
/* (xᵃ)ᵇ = xᵃᵇ */
{
id: 'pow-pow', topic: 'powers', order: 3, subject: 'algebra', grade: 8, kind: 'simplify',
title: 'Степень степени',
pick: { a: [2, 4], b: [2, 3] },
derive: { ab: 'a*b' },
srcExpr: '(x^{a})^{b}', answerExpr: 'x^{ab}', answerVars: ['x'],
display: 'Упростите: (x^{a})^{b}',
solution: [
{ note: 'При возведении степени в степень показатели перемножаются: {a}·{b} = {ab}.', tex: '(x^{a})^{b}' },
{ note: 'Получаем:', tex: 'x^{ab}' }
]
},
/* ═══ Тема: Формулы сокращённого умножения ═══ */
/* (x + a)² */
{
id: 'sq-sum', topic: 'formulas', order: 1, subject: 'algebra', grade: 8, kind: 'simplify',
title: 'Квадрат суммы',
pick: { a: [1, 9] },
derive: { a2: 'a*a', a2x: '2*a' },
srcExpr: '(x + {a})^2', answerExpr: 'x^2 + {a2x}*x + {a2}', answerVars: ['x'],
display: 'Раскройте: (x + {a})²',
solution: [
{ note: 'Это квадрат суммы. Формула: (a + b)² = a² + 2ab + b² (здесь a = x, b = {a}).', tex: '(x + {a})^2' },
{ note: 'Раскрываем и приводим к стандартному виду:', tex: 'x^2 + {a2x}*x + {a2}' }
]
},
/* (x a)² */
{
id: 'sq-diff', topic: 'formulas', order: 2, subject: 'algebra', grade: 8, kind: 'simplify',
title: 'Квадрат разности',
pick: { a: [1, 9] },
derive: { a2: 'a*a', a2x: '2*a' },
srcExpr: '(x - {a})^2', answerExpr: 'x^2 - {a2x}*x + {a2}', answerVars: ['x'],
display: 'Раскройте: (x {a})²',
solution: [
{ note: 'Это квадрат разности. Формула: (a − b)² = a² − 2ab + b² (здесь a = x, b = {a}).', tex: '(x - {a})^2' },
{ note: 'Раскрываем и приводим к стандартному виду:', tex: 'x^2 - {a2x}*x + {a2}' }
]
},
/* (x a)(x + a) = x² a² */
{
id: 'diff-sq', topic: 'formulas', order: 3, subject: 'algebra', grade: 8, kind: 'simplify',
title: 'Разность квадратов',
pick: { a: [2, 9] },
derive: { a2: 'a*a' },
srcExpr: '(x - {a})*(x + {a})', answerExpr: 'x^2 - {a2}', answerVars: ['x'],
display: 'Раскройте: (x {a})(x + {a})',
solution: [
{ note: 'Произведение разности и суммы — формула разности квадратов: (a − b)(a + b) = a² b².', tex: '(x - {a})*(x + {a})' },
{ note: 'Получаем:', tex: 'x^2 - {a2}' }
]
},
/* ═══ Тема: Линейные неравенства ═══ */
/* ax + b < c (a>0, знак сохраняется) */
{
id: 'ineq-lt', topic: 'inequalities', order: 1, subject: 'algebra', grade: 8, kind: 'inequality',
title: 'ax + b < c',
pick: { a: [2, 6], b: [1, 15], root: [-8, 8] },
derive: { c: 'a*root + b', cmb: 'a*root' },
lhs: '{a}*x + {b}', rhs: '{c}', dispOp: '<', relOp: '<', bound: 'root',
answerVar: 'x',
solution: [
{ note: 'Переносим свободный член {b} вправо:', tex: '{a}x < {cmb}' },
{ note: 'Делим обе части на {a} — число положительное, знак неравенства не меняется:', tex: 'x < {root}' }
]
},
/* ax + b ≥ c (a>0) */
{
id: 'ineq-ge', topic: 'inequalities', order: 2, subject: 'algebra', grade: 8, kind: 'inequality',
title: 'ax + b ≥ c',
pick: { a: [2, 6], b: [1, 15], root: [-8, 8] },
derive: { c: 'a*root + b', cmb: 'a*root' },
lhs: '{a}*x + {b}', rhs: '{c}', dispOp: '>=', relOp: '>=', bound: 'root',
answerVar: 'x',
solution: [
{ note: 'Переносим {b} вправо:', tex: '{a}x >= {cmb}' },
{ note: 'Делим на {a} (положительное) — знак сохраняется:', tex: 'x >= {root}' }
]
},
/* ax + b < c (коэффициент отрицательный → знак МЕНЯЕТСЯ) */
{
id: 'ineq-flip', topic: 'inequalities', order: 3, subject: 'algebra', grade: 8, kind: 'inequality',
title: 'ax + b < c (смена знака)',
pick: { a: [2, 6], b: [1, 15], root: [-8, 8] },
derive: { c: 'b - a*root', cmb: '-a*root' },
lhs: '-{a}*x + {b}', rhs: '{c}', dispOp: '<', relOp: '>', bound: 'root',
answerVar: 'x',
solution: [
{ note: 'Переносим {b} вправо:', tex: '-{a}x < {cmb}' },
{ note: 'Делим на отрицательное число (−{a}) — знак неравенства МЕНЯЕТСЯ на противоположный:', tex: 'x > {root}' }
]
},
/* ═══ Тема: Прогрессии (9 класс) ═══ */
/* n-й член арифметической прогрессии */
{
id: 'prog-arith-term', topic: 'progressions', order: 1, subject: 'algebra', grade: 9, kind: 'compute',
title: 'n-й член арифм. прогрессии',
pick: { a: [-10, 20], d: [-8, 8], n: [3, 12] }, require: 'd != 0',
derive: { val: 'a + (n - 1)*d' },
lhs: 'x', rhs: '{a} + ({n} - 1)*{d}', display: 'Арифметическая прогрессия: a₁ = {a}, d = {d}. Найдите {n}-й член.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Формула n-го члена арифметической прогрессии: aₙ = a₁ + (n − 1)·d. Подставляем:', tex: 'x = {a} + ({n} - 1)*{d}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* n-й член геометрической прогрессии */
{
id: 'prog-geom-term', topic: 'progressions', order: 2, subject: 'algebra', grade: 9, kind: 'compute',
title: 'n-й член геом. прогрессии',
pick: { b: [1, 5], q: [2, 3], n: [2, 4] },
derive: { val: 'b * q^(n - 1)' },
lhs: 'x', rhs: '{b} * {q}^({n} - 1)', display: 'Геометрическая прогрессия: b₁ = {b}, q = {q}. Найдите {n}-й член.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Формула n-го члена геометрической прогрессии: bₙ = b₁·q^(n−1). Подставляем:', tex: 'x = {b} * {q}^({n} - 1)' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* ═══ Тема: Углы (геометрия, 7 класс) ═══ */
/* третий угол треугольника */
{
id: 'ang-triangle', topic: 'g-angles', order: 1, subject: 'geometry', grade: 7, kind: 'compute',
title: 'Третий угол треугольника',
figure: { type: 'triangle-angles', angA: 'a', angB: 'b' },
figurePrompt: 'Найдите неизвестный угол треугольника (в градусах).',
pick: { a: [20, 80], b: [20, 80] }, derive: { val: '180 - a - b' }, require: 'val >= 15 && val <= 150',
lhs: 'x', rhs: '180 - {a} - {b}', display: 'В треугольнике два угла равны {a}° и {b}°. Найдите третий угол (в градусах).',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Сумма углов треугольника равна 180°. Значит третий угол:', tex: 'x = 180 - {a} - {b}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* смежный угол */
{
id: 'ang-adjacent', topic: 'g-angles', order: 2, subject: 'geometry', grade: 7, kind: 'compute',
title: 'Смежный угол',
figure: { type: 'adjacent-angles', ang: 'a' },
figurePrompt: 'Найдите неизвестный угол (в градусах).',
pick: { a: [25, 155] }, derive: { val: '180 - a' },
lhs: 'x', rhs: '180 - {a}', display: 'Один из смежных углов равен {a}°. Найдите другой смежный с ним угол.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Сумма смежных углов равна 180°. Значит:', tex: 'x = 180 - {a}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* внешний угол треугольника */
{
id: 'ang-exterior', topic: 'g-angles', order: 3, subject: 'geometry', grade: 7, kind: 'compute',
title: 'Внешний угол треугольника',
figure: { type: 'triangle-angles', angA: 'a', angB: 'b', ext: true },
figurePrompt: 'Найдите внешний угол треугольника (в градусах).',
pick: { a: [20, 80], b: [20, 80] }, derive: { val: 'a + b' }, require: 'val <= 160',
lhs: 'x', rhs: '{a} + {b}', display: 'Внешний угол треугольника равен сумме двух не смежных с ним внутренних углов. Эти углы равны {a}° и {b}°. Найдите внешний угол.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Внешний угол треугольника равен сумме двух не смежных с ним внутренних углов:', tex: 'x = {a} + {b}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* ═══ Тема: Теорема Пифагора (геометрия, 8 класс) ═══ */
/* гипотенуза по катетам (пифагорова тройка m,n) */
{
id: 'pyth-hyp', topic: 'g-pyth', order: 1, subject: 'geometry', grade: 8, kind: 'compute',
title: 'Гипотенуза (Пифагор)',
figure: { type: 'right-triangle', a: 'a', b: 'b', c: 'c', unknown: 'c' },
figurePrompt: 'Найдите гипотенузу прямоугольного треугольника (отмечена «?»).',
pick: { m: [2, 5], n: [1, 4] }, constraint: 'm > n',
derive: { a: 'm*m - n*n', b: '2*m*n', c: 'm*m + n*n' },
lhs: 'x', rhs: 'sqrt({a}^2 + {b}^2)', display: 'Катеты прямоугольного треугольника равны {a} и {b}. Найдите гипотенузу.',
answerVar: 'x', answer: 'c', integerAnswer: true,
solution: [
{ note: 'По теореме Пифагора c² = a² + b², значит c = √(a² + b²):', tex: 'x = sqrt({a}^2 + {b}^2)' },
{ note: 'Считаем (выходит целое — это пифагорова тройка):', tex: 'x = {ans}' }
]
},
/* катет по гипотенузе и катету */
{
id: 'pyth-leg', topic: 'g-pyth', order: 2, subject: 'geometry', grade: 8, kind: 'compute',
title: 'Катет (Пифагор)',
figure: { type: 'right-triangle', a: 'a', b: 'b', c: 'c', unknown: 'b' },
figurePrompt: 'Найдите неизвестный катет (отмечен «?»).',
pick: { m: [2, 5], n: [1, 4] }, constraint: 'm > n',
derive: { a: 'm*m - n*n', b: '2*m*n', c: 'm*m + n*n' },
lhs: 'x', rhs: 'sqrt({c}^2 - {a}^2)', display: 'Гипотенуза прямоугольного треугольника {c}, один катет {a}. Найдите второй катет.',
answerVar: 'x', answer: 'b', integerAnswer: true,
solution: [
{ note: 'По теореме Пифагора b² = c² − a², значит b = √(c² a²):', tex: 'x = sqrt({c}^2 - {a}^2)' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* ═══ Тема: Площади (геометрия, 8 класс) ═══ */
/* площадь прямоугольника */
{
id: 'area-rect', topic: 'g-area', order: 1, subject: 'geometry', grade: 8, kind: 'compute',
title: 'Площадь прямоугольника',
figure: { type: 'rectangle', w: 'a', h: 'b' },
figurePrompt: 'Найдите площадь прямоугольника.',
pick: { a: [2, 16], b: [2, 16] }, derive: { val: 'a*b' },
lhs: 'x', rhs: '{a}*{b}', display: 'Стороны прямоугольника {a} и {b}. Найдите его площадь.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Площадь прямоугольника — произведение его сторон:', tex: 'x = {a}*{b}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* площадь треугольника */
{
id: 'area-triangle', topic: 'g-area', order: 2, subject: 'geometry', grade: 8, kind: 'compute',
title: 'Площадь треугольника',
figure: { type: 'triangle-base-height', base: 'a', height: 'h' },
figurePrompt: 'Найдите площадь треугольника.',
pick: { a: [2, 16], h: [2, 16] }, require: 'mod(a*h, 2) == 0',
derive: { val: 'a*h/2' },
lhs: 'x', rhs: '{a}*{h}/2', display: 'Основание треугольника {a}, высота к нему {h}. Найдите площадь.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Площадь треугольника — половина произведения основания на высоту:', tex: 'x = {a}*{h}/2' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* площадь квадрата */
{
id: 'area-square', topic: 'g-area', order: 3, subject: 'geometry', grade: 8, kind: 'compute',
title: 'Площадь квадрата',
figure: { type: 'square', a: 'a' },
figurePrompt: 'Найдите площадь квадрата.',
pick: { a: [2, 20] }, derive: { val: 'a*a' },
lhs: 'x', rhs: '{a}^2', display: 'Сторона квадрата {a}. Найдите его площадь.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Площадь квадрата — сторона, возведённая в квадрат:', tex: 'x = {a}^2' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* ═══ Тема: Системы 2 линейных уравнений (7 класс) ═══
kind:'system' — ответ ПАРА (x; y). «Корень-вперёд»: берём решение (sx, sy) и
коэффициенты, выводим правые части c1/c2 так, что система имеет ровно это
решение (определитель ≠ 0). Движок рисует \begin{cases}, проверяет подстановкой. */
/* система с положительными коэффициентами */
{
id: 'sys-2x2', topic: 'systems', order: 1, subject: 'algebra', grade: 7, kind: 'system',
title: 'Система 2×2',
pick: { a1: [2, 4], b1: [2, 4], a2: [2, 4], b2: [2, 4], sx: [-6, 6], sy: [-6, 6] },
constraint: 'a1*b2 - a2*b1 != 0',
derive: { c1: 'a1*sx + b1*sy', c2: 'a2*sx + b2*sy', coefY: 'b1*a2 - b2*a1', rhsY: 'c1*a2 - c2*a1' },
eqs: [{ lhs: '{a1}*x + {b1}*y', rhs: '{c1}' }, { lhs: '{a2}*x + {b2}*y', rhs: '{c2}' }],
answers: { x: 'sx', y: 'sy' }, answerVars: ['x', 'y'], integerAnswer: true,
solution: [
{ note: 'Решаем методом сложения. Уравняем коэффициенты при x: умножим первое уравнение на {a2}, а второе — на {a1}.', tex: '' },
{ note: 'Вычтем из первого уравнения второе — слагаемые с x сократятся, останется только y:', tex: '{coefY}*y = {rhsY}' },
{ note: 'Разделим обе части на коэффициент при y:', tex: 'y = {sy}' },
{ note: 'Подставим y = {sy} в первое уравнение системы:', tex: '{a1}*x + {b1}*{sy} = {c1}' },
{ note: 'Решим его относительно x:', tex: 'x = {sx}' },
{ note: 'Ответ: x = {sx}, y = {sy}.', tex: '' }
]
},
/* система с отрицательными коэффициентами (сложнее) */
{
id: 'sys-2x2-neg', topic: 'systems', order: 2, subject: 'algebra', grade: 8, kind: 'system',
title: 'Система (с отрицательными)',
pick: { a1: [-4, 4], b1: [-4, 4], a2: [-4, 4], b2: [-4, 4], sx: [-7, 7], sy: [-7, 7] },
constraint: 'abs(a1) >= 2 && abs(b1) >= 2 && abs(a2) >= 2 && abs(b2) >= 2 && a1*b2 - a2*b1 != 0',
derive: { c1: 'a1*sx + b1*sy', c2: 'a2*sx + b2*sy', coefY: 'b1*a2 - b2*a1', rhsY: 'c1*a2 - c2*a1' },
eqs: [{ lhs: '{a1}*x + {b1}*y', rhs: '{c1}' }, { lhs: '{a2}*x + {b2}*y', rhs: '{c2}' }],
answers: { x: 'sx', y: 'sy' }, answerVars: ['x', 'y'], integerAnswer: true,
solution: [
{ note: 'Метод сложения (внимательно со знаками). Уравняем коэффициенты при x: умножим первое уравнение на {a2}, второе — на {a1}.', tex: '' },
{ note: 'Вычтем из первого уравнения второе — переменная x исключается:', tex: '{coefY}*y = {rhsY}' },
{ note: 'Разделим обе части на коэффициент при y:', tex: 'y = {sy}' },
{ note: 'Подставим y = {sy} в первое уравнение системы:', tex: '{a1}*x + {b1}*{sy} = {c1}' },
{ note: 'Найдём x:', tex: 'x = {sx}' },
{ note: 'Ответ: x = {sx}, y = {sy}.', tex: '' }
]
},
/* ═══ Тема: Задачи (текстовые, параметрические — 7 класс) ═══
kind:'compute' — условие в display, lhs:'x'/rhs:<формула> для проверки. */
/* путь = скорость × время */
{
id: 'app-move-dist', topic: 'applied', order: 1, subject: 'algebra', grade: 7, kind: 'compute',
title: 'Путь (движение)',
pick: { v: [10, 90], t: [2, 9] }, derive: { val: 'v*t' },
lhs: 'x', rhs: '{v}*{t}', display: 'Автомобиль ехал {t} ч со скоростью {v} км/ч. Какой путь он проехал (в км)?',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Путь равен произведению скорости на время:', tex: 'x = {v}*{t}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* время = путь / скорость */
{
id: 'app-move-time', topic: 'applied', order: 2, subject: 'algebra', grade: 7, kind: 'compute',
title: 'Время (движение)',
pick: { v: [10, 90], t: [2, 9] }, derive: { S: 'v*t', val: 't' },
lhs: 'x', rhs: '{S}/{v}', display: 'Расстояние между городами {S} км. Автомобиль едет со скоростью {v} км/ч. За сколько часов он доедет?',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Время равно пути, делённому на скорость:', tex: 'x = {S}/{v}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* скорость = путь / время */
{
id: 'app-move-speed', topic: 'applied', order: 3, subject: 'algebra', grade: 7, kind: 'compute',
title: 'Скорость (движение)',
pick: { v: [10, 90], t: [2, 9] }, derive: { S: 'v*t', val: 'v' },
lhs: 'x', rhs: '{S}/{t}', display: 'Поезд прошёл {S} км за {t} ч. Найдите его среднюю скорость (км/ч).',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Скорость равна пути, делённому на время:', tex: 'x = {S}/{t}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* содержание вещества в сплаве (проценты) */
{
id: 'app-alloy', topic: 'applied', order: 4, subject: 'algebra', grade: 7, kind: 'compute',
title: 'Сплав (проценты)',
pick: { mfac: [1, 9], pidx: [1, 9] }, derive: { m: 'mfac*10', p: 'pidx*10', val: 'mfac*pidx' },
lhs: 'x', rhs: '{m}*{p}/100', display: 'Сплав массой {m} кг содержит {p}% меди. Сколько килограммов меди в сплаве?',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Масса меди = масса сплава × процент ÷ 100:', tex: 'x = {m}*{p}/100' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* цена со скидкой */
{
id: 'app-discount', topic: 'applied', order: 5, subject: 'algebra', grade: 7, kind: 'compute',
title: 'Цена со скидкой',
pick: { pbase: [5, 30], didx: [1, 5] }, derive: { price: 'pbase*10', d: 'didx*10', val: 'pbase*(10 - didx)' },
lhs: 'x', rhs: '{price}*(100 - {d})/100', display: 'Товар стоил {price} руб. Скидка {d}%. Сколько он стоит после скидки (в рублях)?',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Новая цена = старая × (100 − скидка) ÷ 100:', tex: 'x = {price}*(100 - {d})/100' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* ═══ Площади (дополнение к g-area): трапеция, параллелограмм, ромб ═══ */
/* площадь трапеции */
{
id: 'area-trapezoid', topic: 'g-area', order: 4, subject: 'geometry', grade: 8, kind: 'compute',
title: 'Площадь трапеции',
figure: { type: 'trapezoid', bottom: 'a', top: 'b', height: 'h' },
figurePrompt: 'Найдите площадь трапеции.',
pick: { a: [2, 14], b: [2, 14], h: [2, 12] }, require: 'mod((a + b)*h, 2) == 0',
derive: { val: '(a + b)*h/2' },
lhs: 'x', rhs: '({a} + {b})*{h}/2', display: 'Основания трапеции {a} и {b}, высота {h}. Найдите площадь.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Площадь трапеции — полусумма оснований на высоту:', tex: 'x = ({a} + {b})*{h}/2' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* площадь параллелограмма */
{
id: 'area-parallelogram', topic: 'g-area', order: 5, subject: 'geometry', grade: 8, kind: 'compute',
title: 'Площадь параллелограмма',
figure: { type: 'parallelogram', base: 'a', height: 'h' },
figurePrompt: 'Найдите площадь параллелограмма.',
pick: { a: [2, 16], h: [2, 14] }, derive: { val: 'a*h' },
lhs: 'x', rhs: '{a}*{h}', display: 'Сторона параллелограмма {a}, высота к ней {h}. Найдите площадь.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Площадь параллелограмма — сторона на высоту к ней:', tex: 'x = {a}*{h}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* площадь ромба по диагоналям */
{
id: 'area-rhombus', topic: 'g-area', order: 6, subject: 'geometry', grade: 8, kind: 'compute',
title: 'Площадь ромба',
figure: { type: 'rhombus', d1: 'd1', d2: 'd2' },
figurePrompt: 'Найдите площадь ромба.',
pick: { d1: [2, 16], d2: [2, 16] }, require: 'mod(d1*d2, 2) == 0',
derive: { val: 'd1*d2/2' },
lhs: 'x', rhs: '{d1}*{d2}/2', display: 'Диагонали ромба {d1} и {d2}. Найдите площадь.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Площадь ромба — половина произведения диагоналей:', tex: 'x = {d1}*{d2}/2' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* ═══ Тема: Многоугольники (геометрия, 8 класс) ═══ */
/* сумма углов выпуклого n-угольника */
{
id: 'poly-angles-sum', topic: 'g-poly', order: 1, subject: 'geometry', grade: 8, kind: 'compute',
title: 'Сумма углов многоугольника',
figure: { type: 'regular-polygon', n: 'n' },
figurePrompt: 'Найдите сумму углов многоугольника на чертеже (в градусах).',
pick: { n: [3, 12] }, derive: { val: '180*(n - 2)' },
lhs: 'x', rhs: '180*({n} - 2)', display: 'Найдите сумму углов выпуклого {n}-угольника (в градусах).',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Сумма углов выпуклого n-угольника равна 180°·(n − 2):', tex: 'x = 180*({n} - 2)' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* угол правильного n-угольника */
{
id: 'poly-regular-angle', topic: 'g-poly', order: 2, subject: 'geometry', grade: 8, kind: 'compute',
title: 'Угол правильного многоугольника',
figure: { type: 'regular-polygon', n: 'n', markAngle: true },
figurePrompt: 'Найдите угол правильного многоугольника на чертеже (в градусах).',
pick: { n: [3, 20] }, require: 'mod(180*(n - 2), n) == 0',
derive: { val: '180*(n - 2)/n' },
lhs: 'x', rhs: '180*({n} - 2)/{n}', display: 'Найдите величину угла правильного {n}-угольника (в градусах).',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Угол правильного n-угольника = 180°·(n 2) ÷ n:', tex: 'x = 180*({n} - 2)/{n}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* ═══ Тема: Подобие (геометрия, 8 класс) ═══ */
/* сходственная сторона по коэффициенту подобия */
{
id: 'sim-side', topic: 'g-sim', order: 1, subject: 'geometry', grade: 8, kind: 'compute',
title: 'Сторона по коэффициенту',
figure: { type: 'two-similar', side: 'a', k: 'k', mode: 'side' },
figurePrompt: 'Найдите сходственную сторону подобного треугольника (отмечена «?»).',
pick: { a: [2, 15], k: [2, 5] }, derive: { val: 'a*k' },
lhs: 'x', rhs: '{a}*{k}', display: 'Треугольники подобны с коэффициентом {k}. Сторона первого равна {a}. Найдите сходственную сторону второго.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Сходственные стороны подобных фигур пропорциональны коэффициенту подобия:', tex: 'x = {a}*{k}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* периметр подобной фигуры */
{
id: 'sim-perimeter', topic: 'g-sim', order: 2, subject: 'geometry', grade: 8, kind: 'compute',
title: 'Периметр подобной фигуры',
figure: { type: 'two-similar', perim: 'p', k: 'k', mode: 'perimeter' },
figurePrompt: 'Найдите периметр подобной фигуры (отмечена «?»).',
pick: { p: [5, 30], k: [2, 5] }, derive: { val: 'p*k' },
lhs: 'x', rhs: '{p}*{k}', display: 'Фигуры подобны с коэффициентом {k}. Периметр первой равен {p}. Найдите периметр второй.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Периметры подобных фигур относятся как коэффициент подобия:', tex: 'x = {p}*{k}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* ═══ Тема: Дроби (5–6 класс) ═══
kind:'compute'; ответ может быть дробным — ученик вводит «3/4» (SimExpr посчитает). */
/* часть от числа (целый ответ: число кратно знаменателю) */
{
id: 'frac-of-number', topic: 'fractions', order: 1, subject: 'algebra', grade: 5, kind: 'compute',
title: 'Часть от числа',
pick: { n: [2, 6], a: [1, 5], mfac: [2, 9] }, require: 'a < n',
derive: { m: 'n*mfac', val: 'mfac*a' },
lhs: 'x', rhs: '{m}*{a}/{n}', display: 'Найдите {a}/{n} от числа {m}.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Чтобы найти часть от числа, умножаем число на числитель и делим на знаменатель:', tex: 'x = {m}*{a}/{n}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* сложение дробей с одинаковым знаменателем (ответ — дробь) */
{
id: 'frac-add-same', topic: 'fractions', order: 2, subject: 'algebra', grade: 5, kind: 'compute',
title: 'Сложение дробей',
pick: { n: [3, 9], a: [1, 6], b: [1, 6] }, require: 'a + b < 2*n',
derive: { val: '(a + b)/n' },
lhs: 'x', rhs: '({a} + {b})/{n}', display: 'Вычислите {a}/{n} + {b}/{n}. Ответ запишите дробью (например 3/4) или числом.',
answerVar: 'x', answer: 'val',
solution: [
{ note: 'При одинаковом знаменателе складываем числители, знаменатель оставляем:', tex: 'x = ({a} + {b})/{n}' },
{ note: 'Получаем значение:', tex: 'x = {ans}' }
]
},
/* ═══ Тема: НОД и НОК (5–6 класс) ═══
Оба числа строим из ДВУХ простых множителей p<q (из {2,3,5,7}) с показателями
1..2: a = p^e1·q^f1, b = p^e2·q^f2. Тогда разложение известно из параметров →
решение показывает СТАНДАРТНЫЙ школьный метод (разложение на простые множители):
НОД = общие множители в НАИМЕНЬШИХ степенях (min показателей),
НОК = все множители в НАИБОЛЬШИХ степенях (max показателей).
gcd/lcm — функции SimExpr, дают эталон для проверки; min/max — показатели для шагов.
(exprToLatex убирает ^1 → разложение печатается красиво: 2^2·3, а не 2^2·3^1.) */
/* наибольший общий делитель — через разложение на простые множители */
{
id: 'gcd-pair', topic: 'gcd-lcm', order: 1, subject: 'algebra', grade: 5, kind: 'compute',
title: 'НОД двух чисел',
pick: { g: [2, 9], m: [2, 8], n: [2, 8] }, constraint: 'm != n',
derive: { a: 'g*m', b: 'g*n', val: 'gcd(a, b)' },
require: 'a <= 90 && b <= 90',
factorize: [{ name: 'aFac', of: 'a' }, { name: 'bFac', of: 'b' }, { name: 'dFac', of: 'gcd(a, b)' }],
lhs: 'x', rhs: 'gcd({a}, {b})', display: 'Найдите наибольший общий делитель (НОД) чисел {a} и {b}.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Разложим {a} на простые множители:', tex: '{a} = {aFac}' },
{ note: 'Разложим {b} на простые множители:', tex: '{b} = {bFac}' },
{ note: 'НОД — произведение ОБЩИХ множителей (повторяющиеся берём в наименьшем количестве):', tex: 'x = {dFac}' },
{ note: 'Перемножаем:', tex: 'x = {ans}' }
]
},
/* наименьшее общее кратное — через разложение на простые множители */
{
id: 'lcm-pair', topic: 'gcd-lcm', order: 2, subject: 'algebra', grade: 6, kind: 'compute',
title: 'НОК двух чисел',
pick: { g: [2, 9], m: [2, 8], n: [2, 8] }, constraint: 'm != n',
derive: { a: 'g*m', b: 'g*n', val: 'lcm(a, b)' },
require: 'a <= 60 && b <= 60 && lcm(a, b) <= 240',
factorize: [{ name: 'aFac', of: 'a' }, { name: 'bFac', of: 'b' }, { name: 'kFac', of: 'lcm(a, b)' }],
lhs: 'x', rhs: 'lcm({a}, {b})', display: 'Найдите наименьшее общее кратное (НОК) чисел {a} и {b}.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Разложим {a} на простые множители:', tex: '{a} = {aFac}' },
{ note: 'Разложим {b} на простые множители:', tex: '{b} = {bFac}' },
{ note: 'НОК — произведение ВСЕХ множителей (общие берём в наибольшем количестве):', tex: 'x = {kFac}' },
{ note: 'Перемножаем:', tex: 'x = {ans}' }
]
},
/* ═══ Тема: Десятичные дроби (5 класс) ═══
Строим через десятые/сотые (целые ÷10, ÷100) → ответ печатается чисто. */
/* сложение десятичных */
{
id: 'dec-add', topic: 'decimals', order: 1, subject: 'algebra', grade: 5, kind: 'compute',
title: 'Сложение десятичных',
pick: { a: [1, 9], b: [1, 9] }, derive: { da: 'a/10', db: 'b/10', val: '(a + b)/10' },
lhs: 'x', rhs: '({a} + {b})/10', display: 'Вычислите {da} + {db}.',
answerVar: 'x', answer: 'val',
solution: [
{ note: 'Складываем десятичные дроби поразрядно (запятая под запятой):', tex: 'x = {da} + {db}' },
{ note: 'Получаем:', tex: 'x = {ans}' }
]
},
/* вычитание десятичных */
{
id: 'dec-sub', topic: 'decimals', order: 2, subject: 'algebra', grade: 5, kind: 'compute',
title: 'Вычитание десятичных',
pick: { a: [2, 9], b: [1, 8] }, constraint: 'a > b', derive: { da: 'a/10', db: 'b/10', val: '(a - b)/10' },
lhs: 'x', rhs: '({a} - {b})/10', display: 'Вычислите {da} {db}.',
answerVar: 'x', answer: 'val',
solution: [
{ note: 'Вычитаем десятичные дроби поразрядно:', tex: 'x = {da} - {db}' },
{ note: 'Получаем:', tex: 'x = {ans}' }
]
},
/* умножение десятичных */
{
id: 'dec-mult', topic: 'decimals', order: 3, subject: 'algebra', grade: 5, kind: 'compute',
title: 'Умножение десятичных',
pick: { a: [1, 9], b: [1, 9] }, derive: { da: 'a/10', db: 'b/10', val: '(a*b)/100' },
lhs: 'x', rhs: '({a}*{b})/100', display: 'Вычислите {da} · {db}.',
answerVar: 'x', answer: 'val',
solution: [
{ note: 'Перемножаем без запятых как целые, затем отделяем 2 знака после запятой (по сумме знаков у множителей):', tex: 'x = {da} * {db}' },
{ note: 'Получаем:', tex: 'x = {ans}' }
]
},
/* ═══ Тема: Отрицательные числа (6 класс) ═══
Словесные формулировки — без двусмысленных операторов в условии. */
/* сумма с отрицательными */
{
id: 'neg-add', topic: 'negatives', order: 1, subject: 'algebra', grade: 6, kind: 'compute',
title: 'Сумма (отрицательные)',
pick: { a: [-12, 12], b: [-12, 12] }, constraint: 'a != 0 && b != 0 && (a < 0 || b < 0)', derive: { val: 'a + b' },
lhs: 'x', rhs: '{a} + {b}', display: 'Найдите сумму чисел {a} и {b}.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Складываем с учётом знаков: при одинаковых знаках складываем модули; при разных — из большего модуля вычитаем меньший и ставим знак большего:', tex: 'x = {a} + {b}' },
{ note: 'Получаем:', tex: 'x = {ans}' }
]
},
/* разность с отрицательными */
{
id: 'neg-sub', topic: 'negatives', order: 2, subject: 'algebra', grade: 6, kind: 'compute',
title: 'Разность (отрицательные)',
pick: { a: [-12, 12], b: [-12, 12] }, constraint: 'a != 0 && b != 0 && (a < 0 || b < 0)', derive: { val: 'a - b' },
lhs: 'x', rhs: '{a} - ({b})', display: 'Из числа {a} вычтите {b}.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Вычесть число — значит прибавить ему противоположное:', tex: 'x = {a} - ({b})' },
{ note: 'Получаем:', tex: 'x = {ans}' }
]
},
/* произведение с отрицательными */
{
id: 'neg-mult', topic: 'negatives', order: 3, subject: 'algebra', grade: 6, kind: 'compute',
title: 'Произведение (отрицательные)',
pick: { a: [-9, 9], b: [-9, 9] }, constraint: 'a != 0 && b != 0 && (a < 0 || b < 0)', derive: { val: 'a*b' },
lhs: 'x', rhs: '{a}*{b}', display: 'Найдите произведение чисел {a} и {b}.',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Перемножаем модули; знак минус — если множители разных знаков, плюс — если одинаковых:', tex: 'x = {a}*{b}' },
{ note: 'Получаем:', tex: 'x = {ans}' }
]
},
/* ═══ Тема: Окружность (геометрия, 9 класс) ═══
Считаем с π ≈ 3,14 (как в учебниках) → ответ — КОНЕЧНАЯ десятичная дробь,
ученик вводит её (SimExpr принимает «31.4»). Чертёж показывает радиус/диаметр/угол. */
/* длина окружности по радиусу: C = 2πr */
{
id: 'circ-length', topic: 'g-circle', order: 1, subject: 'geometry', grade: 9, kind: 'compute',
title: 'Длина окружности',
figure: { type: 'circle', r: 'r', show: 'radius' },
figurePrompt: 'Найдите длину окружности (π ≈ 3,14).',
pick: { r: [1, 20] }, derive: { val: '2*3.14*r' },
lhs: 'x', rhs: '2*3.14*{r}', display: 'Радиус окружности равен {r}. Найдите длину окружности (π ≈ 3,14).',
answerVar: 'x', answer: 'val',
solution: [
{ note: 'Длина окружности вычисляется по формуле C = 2πr. Подставляем r = {r} и π ≈ 3,14:', tex: 'x = 2*3.14*{r}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* длина окружности по диаметру: C = πd */
{
id: 'circ-diam', topic: 'g-circle', order: 2, subject: 'geometry', grade: 9, kind: 'compute',
title: 'Длина по диаметру',
figure: { type: 'circle', d: 'd', show: 'diameter' },
figurePrompt: 'Найдите длину окружности (π ≈ 3,14).',
pick: { d: [2, 30] }, derive: { val: '3.14*d' },
lhs: 'x', rhs: '3.14*{d}', display: 'Диаметр окружности равен {d}. Найдите длину окружности (π ≈ 3,14).',
answerVar: 'x', answer: 'val',
solution: [
{ note: 'Длину окружности можно найти через диаметр: C = πd. Подставляем d = {d} и π ≈ 3,14:', tex: 'x = 3.14*{d}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* площадь круга: S = πr² */
{
id: 'circ-area', topic: 'g-circle', order: 3, subject: 'geometry', grade: 9, kind: 'compute',
title: 'Площадь круга',
figure: { type: 'circle', r: 'r', show: 'area' },
figurePrompt: 'Найдите площадь круга (π ≈ 3,14).',
pick: { r: [1, 15] }, derive: { val: '3.14*r^2' },
lhs: 'x', rhs: '3.14*{r}^2', display: 'Радиус круга равен {r}. Найдите его площадь (π ≈ 3,14).',
answerVar: 'x', answer: 'val',
solution: [
{ note: 'Площадь круга вычисляется по формуле S = πr². Подставляем r = {r} и π ≈ 3,14:', tex: 'x = 3.14*{r}^2' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* длина дуги: L = (n/360)·2πr; n = 45·k, требуем конечную дробь (r·k чётно) */
{
id: 'circ-arc', topic: 'g-circle', order: 4, subject: 'geometry', grade: 9, kind: 'compute',
title: 'Длина дуги',
figure: { type: 'circle-arc', r: 'r', angle: 'n' },
figurePrompt: 'Найдите длину дуги (π ≈ 3,14).',
pick: { r: [3, 12], k: [1, 7] }, require: 'mod(r*k, 2) == 0',
derive: { n: '45*k', val: '45*k/360*2*3.14*r' },
lhs: 'x', rhs: '{n}/360*2*3.14*{r}', display: 'Дуга окружности радиуса {r} опирается на центральный угол {n}°. Найдите длину дуги (π ≈ 3,14).',
answerVar: 'x', answer: 'val',
solution: [
{ note: 'Длина дуги — это часть длины окружности: L = (n/360)·2πr. Здесь n = {n}°, r = {r}, π ≈ 3,14:', tex: 'x = {n}/360*2*3.14*{r}' },
{ note: 'Считаем:', tex: 'x = {ans}' }
]
},
/* ═══════════════════════════════════════════════════════════════════════
V4.1 — Группа 1 (линейные): новые формы и формулировки. Все «корень-вперёд».
═══════════════════════════════════════════════════════════════════════ */
/* ── Уравнения ── */
/* ax + b = cx + d с гарантированным делением на (a−c)≥2 */
{
id: 'lin-both-frac', topic: 'linear-eq', order: 8, subject: 'algebra', grade: 7,
title: 'ax + b = cx + d (с делением)',
pick: { a: [5, 9], c: [1, 4], b: [1, 12], root: [-7, 7] },
constraint: 'a - c >= 2', require: 'root != 0',
derive: { d: '(a - c)*root + b', amc: 'a - c', dmb: '(a - c)*root' },
lhs: '{a}*x + {b}', rhs: '{c}*x + {d}', display: '{a}x + {b} = {c}x + {d}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'x есть в обеих частях. Соберём слагаемые с x слева, числа — справа (со сменой знака).', tex: '({a} - {c})x = {d} - {b}' },
{ note: 'Приводим подобные.', tex: '{amc}x = {dmb}' },
{ note: 'Делим обе части на коэффициент при x — на {amc}.', tex: 'x = {dmb} / {amc}' },
{ note: 'Получаем корень.', tex: 'x = {ans}' },
{ note: 'Проверка: подставляем корень — обе части совпадут.', tex: '{a}*({ans}) + {b} = {c}*({ans}) + {d}' }
]
},
/* a/(x + b) = c — x в знаменателе */
{
id: 'lin-x-denom', topic: 'linear-eq', order: 9, subject: 'algebra', grade: 8,
title: 'a/(x + b) = c',
pick: { c: [2, 6], b: [-5, 8], root: [-7, 9] },
require: 'root + b != 0 && root != 0',
derive: { a: 'c*(root + b)', rb: 'root + b' },
lhs: '{a}/(x + {b})', rhs: '{c}', display: '{a}/(x + {b}) = {c}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Знаменатель x + {b} не равен нулю. Умножим обе части на него.', tex: '{a} = {c}*(x + {b})' },
{ note: 'Разделим обе части на {c}.', tex: 'x + {b} = {rb}' },
{ note: 'Перенесём {b} вправо со сменой знака.', tex: 'x = {ans}' },
{ note: 'Проверка: подставляем корень в исходную дробь.', tex: '{a}/({ans} + {b}) = {c}' }
]
},
/* k/x = c — обратная пропорция */
{
id: 'lin-k-over-x', topic: 'linear-eq', order: 10, subject: 'algebra', grade: 8,
title: 'k/x = c',
pick: { c: [2, 9], root: [-9, 9] },
require: 'root != 0',
derive: { k: 'c*root' },
lhs: '{k}/x', rhs: '{c}', display: '{k}/x = {c}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Умножим обе части на x, чтобы убрать дробь.', tex: '{k} = {c}*x' },
{ note: 'Разделим обе части на {c}.', tex: 'x = {k} / {c}' },
{ note: 'Получаем корень.', tex: 'x = {ans}' },
{ note: 'Проверка: подставляем корень.', tex: '{k}/({ans}) = {c}' }
]
},
/* |ax + b| = c — два корня */
{
id: 'lin-abs', topic: 'linear-eq', order: 11, subject: 'algebra', grade: 8, kind: 'roots',
title: '|ax + b| = c',
pick: { a: [1, 5], r1: [-7, 3], r2: [-2, 8] },
constraint: 'r2 > r1',
derive: { b: '-a*(r1 + r2)/2', c: 'a*(r2 - r1)/2' },
require: 'mod(a*(r1 + r2), 2) == 0 && c > 0',
lhs: 'abs({a}*x + {b})', rhs: '{c}', display: '|{a}x + {b}| = {c}',
answerVar: 'x', answers: ['r1', 'r2'], integerAnswer: true,
solution: [
{ note: 'Модуль равен {c}, значит выражение под модулем равно {c} или −{c}. Рассмотрим два случая.', tex: '' },
{ note: 'Первый случай — выражение равно {c}:', tex: '{a}*x + {b} = {c}' },
{ note: 'Решаем и получаем корень:', tex: 'x = {r2}' },
{ note: 'Второй случай — выражение равно −{c}:', tex: '{a}*x + {b} = -{c}' },
{ note: 'Решаем и получаем второй корень:', tex: 'x = {r1}' }
]
},
/* (ax + b)/c = (dx + g)/f — дробь = дробь (крест-накрест) */
{
id: 'lin-frac-eq-frac', topic: 'linear-eq', order: 12, subject: 'algebra', grade: 8,
title: '(ax + b)/c = (dx + g)/f',
pick: { a: [2, 5], c: [2, 4], d: [1, 4], f: [2, 4], b: [1, 9], root: [-5, 6] },
constraint: 'a*f != c*d',
require: 'mod(f*(a*root + b) - c*d*root, c) == 0 && root != 0',
derive: { g: '(f*(a*root + b) - c*d*root)/c', fa: 'f*a', fb: 'f*b', cd: 'c*d', cg: 'c*g', cross: 'f*a - c*d', rc: '(f*a - c*d)*root' },
lhs: '({a}*x + {b})/{c}', rhs: '({d}*x + {g})/{f}', display: '({a}x + {b})/{c} = ({d}x + {g})/{f}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Перед нами равенство двух дробей. Умножаем крест-накрест: числитель левой на знаменатель правой и наоборот.', tex: '{f}*({a}*x + {b}) = {c}*({d}*x + {g})' },
{ note: 'Раскрываем скобки.', tex: '{fa}*x + {fb} = {cd}*x + {cg}' },
{ note: 'Собираем x слева, числа справа.', tex: '{cross}*x = {rc}' },
{ note: 'Делим на коэффициент при x.', tex: 'x = {ans}' },
{ note: 'Проверка: при найденном x обе дроби равны.', tex: '({a}*({ans}) + {b})/{c} = ({d}*({ans}) + {g})/{f}' }
]
},
/* a(b(x + c) + d) = e — вложенные скобки */
{
id: 'lin-nested-paren', topic: 'linear-eq', order: 13, subject: 'algebra', grade: 8,
title: 'a(b(x + c) + d) = e',
pick: { a: [2, 4], b: [2, 4], c: [-4, 6], d: [1, 8], root: [-5, 6] },
require: 'root != 0',
derive: { ev: 'a*(b*(root + c) + d)', inner: 'b*(root + c) + d', inner2: 'b*(root + c)', rootc: 'root + c' },
lhs: '{a}*({b}*(x + {c}) + {d})', rhs: '{ev}', display: '{a}({b}(x + {c}) + {d}) = {ev}',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Снимаем внешний множитель {a}: делим обе части на {a}.', tex: '{b}*(x + {c}) + {d} = {inner}' },
{ note: 'Переносим {d} вправо.', tex: '{b}*(x + {c}) = {inner2}' },
{ note: 'Делим на {b}.', tex: 'x + {c} = {rootc}' },
{ note: 'Переносим {c} и получаем корень.', tex: 'x = {ans}' },
{ note: 'Проверка: подставляем корень во вложенные скобки.', tex: '{a}*({b}*({ans} + {c}) + {d}) = {ev}' }
]
},
/* ax + b = mx — «выразите x» (буквенный коэффициент как параметр) */
{
id: 'lin-literal', topic: 'linear-eq', order: 14, subject: 'algebra', grade: 8,
title: 'ax + b = mx (выразите x)',
pick: { a: [2, 5], gap: [2, 6], root: [2, 8] },
derive: { m: 'a + gap', b: 'gap*root', mma: 'gap' },
lhs: '{a}*x + {b}', rhs: '{m}*x', display: '{a}x + {b} = {m}x',
answerVar: 'x', answer: 'root', integerAnswer: true,
solution: [
{ note: 'Соберём слагаемые с x в одной части: перенесём {a}x вправо, число оставим слева.', tex: '{b} = ({m} - {a})*x' },
{ note: 'Приводим подобные (коэффициент при x).', tex: '{b} = {mma}*x' },
{ note: 'Делим обе части на {mma}.', tex: 'x = {b} / {mma}' },
{ note: 'Получаем корень.', tex: 'x = {ans}' },
{ note: 'Проверка: подставляем корень.', tex: '{a}*({ans}) + {b} = {m}*({ans})' }
]
},
/* ── Неравенства ── */
/* ax + b < cx + d, a>c — знак сохраняется */
{
id: 'ineq-both-sides', topic: 'inequalities', order: 4, subject: 'algebra', grade: 8, kind: 'inequality',
title: 'ax + b < cx + d',
pick: { a: [3, 7], c: [1, 4], b: [1, 12], root: [-6, 8] },
constraint: 'a - c >= 2',
derive: { d: '(a - c)*root + b', amc: 'a - c', dmb: '(a - c)*root' },
lhs: '{a}*x + {b}', rhs: '{c}*x + {d}', dispOp: '<', relOp: '<', bound: 'root',
answerVar: 'x',
solution: [
{ note: 'Собираем x слева, числа справа (со сменой знака при переносе).', tex: '({a} - {c})x < {d} - {b}' },
{ note: 'Приводим подобные.', tex: '{amc}x < {dmb}' },
{ note: 'Делим на {amc} — число положительное, знак неравенства сохраняется.', tex: 'x < {root}' }
]
},
/* ax + b ≥ cx + d, c>a — смена знака */
{
id: 'ineq-both-flip', topic: 'inequalities', order: 5, subject: 'algebra', grade: 8, kind: 'inequality',
title: 'ax + b ≥ cx + d (смена знака)',
pick: { a: [1, 4], c: [3, 7], b: [1, 12], root: [-6, 8] },
constraint: 'c - a >= 2',
derive: { d: '(a - c)*root + b', amc: 'a - c', dmb: '(a - c)*root' },
lhs: '{a}*x + {b}', rhs: '{c}*x + {d}', dispOp: '>=', relOp: '<=', bound: 'root',
answerVar: 'x',
solution: [
{ note: 'Собираем x слева, числа справа.', tex: '({a} - {c})x >= {d} - {b}' },
{ note: 'Приводим подобные — коэффициент при x отрицательный.', tex: '{amc}*x >= {dmb}' },
{ note: 'Делим на отрицательное число {amc} — знак неравенства МЕНЯЕТСЯ.', tex: 'x <= {root}' }
]
},
/* a(x + b) > c — скобка в неравенстве */
{
id: 'ineq-paren', topic: 'inequalities', order: 6, subject: 'algebra', grade: 8, kind: 'inequality',
title: 'a(x + b) > c',
pick: { a: [2, 6], b: [1, 8], root: [-5, 8] },
derive: { c: 'a*(root + b)', rootb: 'root + b' },
lhs: '{a}*(x + {b})', rhs: '{c}', dispOp: '>', relOp: '>', bound: 'root',
answerVar: 'x',
solution: [
{ note: 'Делим обе части на {a} (положительное) — знак сохраняется.', tex: 'x + {b} > {rootb}' },
{ note: 'Переносим {b} вправо.', tex: 'x > {root}' }
]
},
/* Сколько целых решений у lo < x ≤ hi (двойное неравенство через compute) */
{
id: 'ineq-count-int', topic: 'inequalities', order: 7, subject: 'algebra', grade: 8, kind: 'compute',
title: 'Число целых решений',
pick: { lo: [-6, 3], span: [2, 8] },
derive: { hi: 'lo + span', val: 'span' },
lhs: 'x', rhs: '{hi} - {lo}', display: 'Сколько целых чисел x удовлетворяют условию {lo} < x ≤ {hi}?',
answerVar: 'x', answer: 'val', integerAnswer: true,
solution: [
{ note: 'Целые решения — это {lo}+1, {lo}+2, …, {hi}. Их количество равно {hi} − {lo}.', tex: 'x = {hi} - {lo}' },
{ note: 'Считаем.', tex: 'x = {ans}' }
]
},
/* ── Системы ── */
/* x + y = S, x y = D — сумма и разность */
{
id: 'sys-sum-diff', topic: 'systems', order: 3, subject: 'algebra', grade: 7, kind: 'system',
title: 'Система x + y = S, x y = D',
pick: { sx: [-7, 8], sy: [-7, 8] },
derive: { S: 'sx + sy', D: 'sx - sy', twoX: '2*sx', twoY: '2*sy' },
eqs: [{ lhs: 'x + y', rhs: '{S}' }, { lhs: 'x - y', rhs: '{D}' }],
answers: { x: 'sx', y: 'sy' }, answerVars: ['x', 'y'], integerAnswer: true,
solution: [
{ note: 'Сложим уравнения почленно — переменная y исключается.', tex: '2*x = {twoX}' },
{ note: 'Находим x.', tex: 'x = {sx}' },
{ note: 'Вычтем из первого уравнения второе — исключается x.', tex: '2*y = {twoY}' },
{ note: 'Находим y.', tex: 'y = {sy}' },
{ note: 'Ответ: x = {sx}, y = {sy}.', tex: '' }
]
},
/* y = mx + k, a2·x + b2·y = c2 — метод подстановки */
{
id: 'sys-subst', topic: 'systems', order: 4, subject: 'algebra', grade: 7, kind: 'system',
title: 'Система (подстановка)',
pick: { sx: [-5, 6], sy: [-6, 7], m: [-3, 4], a2: [1, 5], b2: [1, 4] },
constraint: 'a2 + b2*m != 0',
derive: { k: 'sy - m*sx', c2: 'a2*sx + b2*sy', detc: 'a2 + b2*m', rhsx: 'c2 - b2*k' },
eqs: [{ lhs: 'y', rhs: '{m}*x + {k}' }, { lhs: '{a2}*x + {b2}*y', rhs: '{c2}' }],
answers: { x: 'sx', y: 'sy' }, answerVars: ['x', 'y'], integerAnswer: true,
solution: [
{ note: 'Первое уравнение уже выражает y. Подставим его во второе.', tex: '{a2}*x + {b2}*({m}*x + {k}) = {c2}' },
{ note: 'Раскрываем скобки и приводим подобные при x.', tex: '{detc}*x = {rhsx}' },
{ note: 'Находим x.', tex: 'x = {sx}' },
{ note: 'Подставляем x в первое уравнение.', tex: 'y = {m}*({sx}) + {k}' },
{ note: 'Находим y.', tex: 'y = {sy}' },
{ note: 'Ответ: x = {sx}, y = {sy}.', tex: '' }
]
},
/* Текстовая задача на систему (мальчики/девочки) */
{
id: 'sys-word', topic: 'systems', order: 5, subject: 'algebra', grade: 7, kind: 'system',
title: 'Текстовая задача (система)',
pick: { sx: [5, 16], sy: [6, 18] },
constraint: 'sy > sx',
derive: { N: 'sx + sy', D: 'sy - sx', twoY: '2*sy' },
eqs: [{ lhs: 'x + y', rhs: '{N}' }, { lhs: 'y - x', rhs: '{D}' }],
display: 'В классе всего {N} учеников, причём девочек на {D} больше, чем мальчиков. Сколько мальчиков (x) и девочек (y)?',
answers: { x: 'sx', y: 'sy' }, answerVars: ['x', 'y'], integerAnswer: true,
solution: [
{ note: 'Обозначим мальчиков за x, девочек за y. Всего учеников:', tex: 'x + y = {N}' },
{ note: 'Девочек на {D} больше, чем мальчиков:', tex: 'y - x = {D}' },
{ note: 'Сложим уравнения — найдём удвоенное число девочек.', tex: '2*y = {twoY}' },
{ note: 'Находим число девочек y.', tex: 'y = {sy}' },
{ note: 'Тогда мальчиков:', tex: 'x = {N} - {sy}' },
{ note: 'Ответ: мальчиков {sx}, девочек {sy}.', tex: '' }
]
},
/* Система 3×3 (вводный тизер) */
{
id: 'sys-3x3', topic: 'systems', order: 6, subject: 'algebra', grade: 9, kind: 'system',
title: 'Система 3×3',
pick: { sx: [-4, 5], sy: [-4, 5], sz: [-4, 5] },
derive: { S: 'sx + sy + sz', Sxy: 'sx + sy', Syz: 'sy + sz' },
eqs: [{ lhs: 'x + y + z', rhs: '{S}' }, { lhs: 'x + y', rhs: '{Sxy}' }, { lhs: 'y + z', rhs: '{Syz}' }],
answers: { x: 'sx', y: 'sy', z: 'sz' }, answerVars: ['x', 'y', 'z'], integerAnswer: true,
solution: [
{ note: 'Вычтем из первого уравнения второе — найдём z.', tex: 'z = {S} - {Sxy}' },
{ note: 'Получаем z.', tex: 'z = {sz}' },
{ note: 'Вычтем из первого уравнения третье — найдём x.', tex: 'x = {S} - {Syz}' },
{ note: 'Получаем x.', tex: 'x = {sx}' },
{ note: 'Из второго уравнения найдём y.', tex: 'y = {Sxy} - {sx}' },
{ note: 'Ответ: x = {sx}, y = {sy}, z = {sz}.', tex: '' }
]
}
];
// Структурная сложность генератора (1 — простейшая форма, 3 — больше действий /
// скобки / дроби / переменная в обеих частях). Определяет, какой ВАРИАНТ внутри
// темы даётся на выбранном уровне сложности (не просто масштаб чисел).
var LEVELS = {
// Уравнения: ax+b=c → скобки/обе части/дроби → (ax+b)/c=d
'lin-basic': 1, 'lin-paren': 2, 'lin-both-sides': 2, 'lin-frac-denom': 2,
'lin-coef-frac': 2, 'lin-paren-both': 3, 'lin-frac-eq': 3,
// Пропорции
'prop-x-right': 1, 'prop-x-left': 1, 'prop-x-denom': 2,
// Проценты
'pct-of': 1, 'pct-what': 2, 'pct-whole': 2,
// Упрощение
'simp-like': 1, 'simp-expand': 2,
// Степени
'pow-eval': 1, 'pow-mult': 2, 'pow-pow': 3,
// Формулы сокр. умножения
'sq-sum': 2, 'sq-diff': 2, 'diff-sq': 3,
// Неравенства (смена знака — сложнее)
'ineq-lt': 1, 'ineq-ge': 1, 'ineq-flip': 3,
// Системы 2 уравнений
'sys-2x2': 2, 'sys-2x2-neg': 3,
// Задачи (текстовые)
'app-move-dist': 1, 'app-move-speed': 1, 'app-move-time': 2, 'app-alloy': 2, 'app-discount': 2,
// Квадратные
'quad-diff': 2, 'quad-factored': 3,
// Прогрессии
'prog-arith-term': 2, 'prog-geom-term': 3,
// Геометрия — Углы
'ang-adjacent': 1, 'ang-triangle': 2, 'ang-exterior': 2,
// Геометрия — Пифагор
'pyth-hyp': 2, 'pyth-leg': 3,
// Геометрия — Площади
'area-square': 1, 'area-rect': 1, 'area-triangle': 2,
'area-parallelogram': 2, 'area-trapezoid': 3, 'area-rhombus': 2,
// Геометрия — Многоугольники / Подобие
'poly-angles-sum': 1, 'poly-regular-angle': 2, 'sim-side': 1, 'sim-perimeter': 2,
// Геометрия — Окружность
'circ-length': 1, 'circ-diam': 1, 'circ-area': 2, 'circ-arc': 3,
// V4.1 — Линейные: новые формы
'lin-both-frac': 2, 'lin-x-denom': 3, 'lin-k-over-x': 2, 'lin-abs': 3,
'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,
// НОД/НОК / Дроби / Десятичные / Отрицательные
'gcd-pair': 1, 'lcm-pair': 2,
'frac-of-number': 1, 'frac-add-same': 2,
'dec-add': 1, 'dec-sub': 1, 'dec-mult': 2,
'neg-add': 1, 'neg-sub': 2, 'neg-mult': 2
};
GENERATORS.forEach(function (g) { g.level = LEVELS[g.id] || 1; });
function get(id) {
for (var i = 0; i < GENERATORS.length; i++) if (GENERATORS[i].id === id) return GENERATORS[i];
return null;
}
function byTopic(key) {
return GENERATORS.filter(function (g) { return g.topic === key; })
.sort(function (a, b) { return (a.order || 0) - (b.order || 0); });
}
global.TrainerGenerators = {
list: function () { return GENERATORS.slice(); },
topics: function () { return TOPICS.slice(); },
byTopic: byTopic,
get: get,
GENERATORS: GENERATORS,
TOPICS: TOPICS
};
})(typeof window !== 'undefined' ? window : globalThis);