diff --git a/frontend/js/trainer/_trainer_engine.js b/frontend/js/trainer/_trainer_engine.js index 05d9b97..2bee38e 100644 --- a/frontend/js/trainer/_trainer_engine.js +++ b/frontend/js/trainer/_trainer_engine.js @@ -68,6 +68,17 @@ } function randInt(rng, lo, hi) { return lo + Math.floor(rng() * (hi - lo + 1)); } + /* Разложение числа на простые множители как строка «2*2*3» (без степеней, по + возрастанию). Для отображения в шагах решения (НОД/НОК и т.п.). */ + function primeFactorString(n) { + n = Math.abs(Math.round(n)); + if (!isFinite(n) || n < 2) return String(n || 0); + var fs = [], d = 2; + while (d * d <= n) { while (n % d === 0) { fs.push(d); n = n / d; } d++; } + if (n > 1) fs.push(n); + return fs.join('*'); + } + /* ── Уровни сложности: масштабирование диапазона pick ── level 2 — базовый (как задано); 1 — легче (меньше магнитуды, меньше отрицательных); 3 — сложнее (шире магнитуды). Универсально для всех @@ -322,6 +333,15 @@ var lhsExpr = render(gen.lhs || 'x', env); var rhsExpr = render(gen.rhs || 'x', env); var sEnv = assign(env, { ans: answer }); + // factorize: добавляет в шаги решения СТРОКУ разложения на простые множители + // (повторяющиеся простые, без степеней: «36» -> «2*2*3*3»). gen.factorize — + // массив { name, of }: name — ключ для {name} в шагах, of — выражение-число. + if (gen.factorize) { + for (var fzi = 0; fzi < gen.factorize.length; fzi++) { + var fz = gen.factorize[fzi]; + sEnv[fz.name] = primeFactorString(evalExpr(fz.of, env)); + } + } var answerExpr = gen.answerExpr ? render(gen.answerExpr, env) : null; var answerRel = (kind === 'inequality') ? { op: gen.relOp || '<', bound: evalExpr(gen.bound, env) } : null; // latex: уравнение (solve/roots) | выражение (simplify) | неравенство (inequality) diff --git a/frontend/js/trainer/generators.js b/frontend/js/trainer/generators.js index 02f211a..b2f182d 100644 --- a/frontend/js/trainer/generators.js +++ b/frontend/js/trainer/generators.js @@ -847,20 +847,17 @@ { id: 'gcd-pair', topic: 'gcd-lcm', order: 1, subject: 'algebra', grade: 5, kind: 'compute', title: 'НОД двух чисел', - pick: { ip: [0, 3], iq: [0, 3], ir: [0, 3] }, - constraint: 'ip != iq && ip != ir && iq != ir', - derive: { - p: '(ip==0)?2:((ip==1)?3:((ip==2)?5:7))', - q: '(iq==0)?2:((iq==1)?3:((iq==2)?5:7))', - r: '(ir==0)?2:((ir==1)?3:((ir==2)?5:7))', - a: 'p*q', b: 'p*r', val: 'gcd(a, b)' - }, + 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} = {p} * {q}' }, - { note: 'Разложим {b} на простые множители:', tex: '{b} = {p} * {r}' }, - { note: 'Общий простой множитель у обоих чисел — {p}. Значит, НОД равен {p}:', tex: 'x = {p}' } + { note: 'Разложим {a} на простые множители:', tex: '{a} = {aFac}' }, + { note: 'Разложим {b} на простые множители:', tex: '{b} = {bFac}' }, + { note: 'НОД — произведение ОБЩИХ множителей (повторяющиеся берём в наименьшем количестве):', tex: 'x = {dFac}' }, + { note: 'Перемножаем:', tex: 'x = {ans}' } ] }, @@ -868,20 +865,16 @@ { id: 'lcm-pair', topic: 'gcd-lcm', order: 2, subject: 'algebra', grade: 6, kind: 'compute', title: 'НОК двух чисел', - pick: { ip: [0, 3], iq: [0, 3], ir: [0, 3] }, - constraint: 'ip != iq && ip != ir && iq != ir', - derive: { - p: '(ip==0)?2:((ip==1)?3:((ip==2)?5:7))', - q: '(iq==0)?2:((iq==1)?3:((iq==2)?5:7))', - r: '(ir==0)?2:((ir==1)?3:((ir==2)?5:7))', - a: 'p*q', b: 'p*r', val: 'lcm(a, b)' - }, + 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} = {p} * {q}' }, - { note: 'Разложим {b} на простые множители:', tex: '{b} = {p} * {r}' }, - { note: 'НОК — произведение всех множителей, общий ({p}) берём один раз:', tex: 'x = {p} * {q} * {r}' }, + { note: 'Разложим {a} на простые множители:', tex: '{a} = {aFac}' }, + { note: 'Разложим {b} на простые множители:', tex: '{b} = {bFac}' }, + { note: 'НОК — произведение ВСЕХ множителей (общие берём в наибольшем количестве):', tex: 'x = {kFac}' }, { note: 'Перемножаем:', tex: 'x = {ans}' } ] }, diff --git a/frontend/trainer.html b/frontend/trainer.html index e420ac2..ca707c0 100644 --- a/frontend/trainer.html +++ b/frontend/trainer.html @@ -361,7 +361,7 @@

Тренажёр

-
Алгебра · 7–8 класс
+
Математика · 5–9 класс
@@ -685,13 +685,24 @@ topics.forEach(function (t) { if (t.subject && !seen[t.subject]) { seen[t.subject] = 1; out.push(t.subject); } }); return out; } + var SUBJ_LBL = { algebra: 'Алгебра', geometry: 'Геометрия' }; + // подпись в шапке: предмет + диапазон классов текущего предмета (универсально 5–9) + function updateSubjectPill() { + var pill = $('tr-subject'); if (!pill) return; + var gr = topics.filter(function (t) { return (t.subject || 'algebra') === curSubject && t.grade; }) + .map(function (t) { return t.grade; }); + var name = SUBJ_LBL[curSubject] || 'Математика'; + if (!gr.length) { pill.textContent = name; return; } + var lo = Math.min.apply(null, gr), hi = Math.max.apply(null, gr); + pill.textContent = name + ' · ' + (lo === hi ? (lo + ' класс') : (lo + '–' + hi + ' класс')); + } function renderSubjects() { + updateSubjectPill(); var el = $('tr-subjects'); if (!el) return; var subs = presentSubjects(); if (subs.length <= 1) { el.innerHTML = ''; return; } - var LBL = { algebra: 'Алгебра', geometry: 'Геометрия' }; el.innerHTML = subs.map(function (s) { - return ''; + return ''; }).join(''); } function skillPanelHeader() { @@ -943,7 +954,10 @@ function pickNext(lastSkill) { if (!TA) return; var last = (lastSkill !== undefined) ? lastSkill : (curGen ? skillKey(curGen) : null); - var id = TA.nextSkill({ ordered: ordered, progress: prog, queue: reviewQ, answered: sessAnswered, last: last }); + // адаптив в пределах ВЫБРАННОЙ темы: ведёт по её навыкам (простое→сложное) и + // возвращает ошибки, не перепрыгивая в другие темы (тему выбирает ученик в рейле). + var scope = skillsOf(curTopic); if (!scope || !scope.length) scope = ordered; + var id = TA.nextSkill({ ordered: scope, progress: prog, queue: reviewQ, answered: sessAnswered, last: last }); var g = id ? gens.filter(function (x) { return skillKey(x) === id; })[0] : null; if (g) { curGen = g; curTopic = g.topic; if (g.subject) curSubject = g.subject; renderSubjects(); renderTopics(); renderSkills(); } }