fix(trainer): сложность = структура задачи, а не масштаб чисел
Пользователь верно заметил: масштабирование чисел (больше/меньше) — не настоящая сложность. Настоящая = больше действий, скобки, дроби, переменная в обеих частях. - генераторы размечены структурным level 1-3 (generators.js, LEVELS): напр. Уравнения ax+b=c (1) -> a(x+b)=c (2) -> a(x+b)=c(x+d) (3); Степени: вычислить -> произведение -> степень степени - контрол сложности выбирает ВАРИАНТ-генератор нужного уровня в теме (pickByLevel с клампом к доступным), а не масштабирует числа - клик по чипу навыка закрепляет конкретный вариант (pinned); Авто = адаптивный подбор (умная тренировка от простого к сложному) + показ ур.N текущего - кросс-тематический адаптив pickNext — только в Авто без закрепления - движковое _scaleRange/level оставлено как capability (T18), страница его НЕ использует - смоук движка 682/682, страница 36/36 (Сложный->ген ур.3, Лёгкий->ур.1); эмодзи/eval 0 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+40
-12
@@ -488,7 +488,8 @@
|
||||
var isTeacher = !!(ip && ip.isTeacher);
|
||||
var isAdmin = !!(ip && ip.isAdmin);
|
||||
var curSubject = 'algebra'; // фильтр предмета (Алгебра/Геометрия)
|
||||
var diffMode = 'auto'; // уровень сложности: 'auto' | 1 | 2 | 3
|
||||
var diffMode = 'auto'; // уровень сложности: 'auto' | 1 | 2 | 3 (= структурный вариант)
|
||||
var pinned = null; // закреплённый навык (id) при явном клике по чипу
|
||||
var customGens = []; // пользовательские генераторы (P13), тема «Авторские»
|
||||
function skillKey(g) { return g.skill || g.id; }
|
||||
function skillsOf(topicKey) {
|
||||
@@ -635,15 +636,35 @@
|
||||
$('tr-check').textContent = done ? 'Дальше' : 'Проверить';
|
||||
var sc = $('tr-stepcheck'); if (sc) sc.textContent = done ? 'Дальше' : 'Шаг';
|
||||
}
|
||||
// уровень сложности: ручной (1/2/3) или «Авто» — растёт с серией верных в сессии
|
||||
function currentLevel() {
|
||||
if (diffMode === 1 || diffMode === 2 || diffMode === 3) return diffMode;
|
||||
return streak >= 4 ? 3 : streak >= 2 ? 2 : 1;
|
||||
// ── Сложность = СТРУКТУРА задачи (какой вариант-генератор внутри темы),
|
||||
// а не масштаб чисел: ур.1 — простейшая форма, ур.3 — больше действий /
|
||||
// скобки / дроби / переменная в обеих частях ──
|
||||
function levelOf(g) { return (g && g.level) || 1; }
|
||||
function genById(id) {
|
||||
var i;
|
||||
for (i = 0; i < gens.length; i++) if (skillKey(gens[i]) === id) return gens[i];
|
||||
for (i = 0; i < customGens.length; i++) if (skillKey(customGens[i]) === id) return customGens[i];
|
||||
return null;
|
||||
}
|
||||
// выбрать генератор нужного структурного уровня в теме (кламп к доступным уровням)
|
||||
function pickByLevel(topicKey, level) {
|
||||
var ss = skillsOf(topicKey); if (!ss.length) return null;
|
||||
var lv = ss.map(levelOf);
|
||||
var L = Math.max(Math.min.apply(null, lv), Math.min(Math.max.apply(null, lv), level));
|
||||
var at = ss.filter(function (g) { return levelOf(g) === L; });
|
||||
if (!at.length) at = ss;
|
||||
return at[Math.floor(Math.random() * at.length)] || at[0];
|
||||
}
|
||||
// какой генератор давать: закреплённый навык > ручной уровень > текущий (адаптив/выбор)
|
||||
function chooseGen() {
|
||||
if (pinned) { var g = genById(pinned); if (g && g.topic === curTopic) return g; pinned = null; }
|
||||
if (diffMode === 1 || diffMode === 2 || diffMode === 3) { var bl = pickByLevel(curTopic, diffMode); if (bl) return bl; }
|
||||
return curGen;
|
||||
}
|
||||
function renderDifficulty() {
|
||||
var el = $('tr-difficulty'); if (!el) return;
|
||||
var opts = [['auto', 'Авто'], [1, 'Лёгкий'], [2, 'Средний'], [3, 'Сложный']];
|
||||
var autoLvl = (diffMode === 'auto') ? (' · ур.' + currentLevel()) : '';
|
||||
var autoLvl = (diffMode === 'auto') ? (' · ур.' + levelOf(curGen)) : '';
|
||||
el.innerHTML = '<span class="tr-diff-label">Сложность</span>' + opts.map(function (o) {
|
||||
var lbl = (o[0] === 'auto') ? ('Авто' + autoLvl) : o[1];
|
||||
return '<button class="tr-diff-btn' + (String(diffMode) === String(o[0]) ? ' on' : '') + '" type="button" data-d="' + o[0] + '">' + lbl + '</button>';
|
||||
@@ -781,12 +802,14 @@
|
||||
|
||||
function newProblem() {
|
||||
if (isWord()) { serveWordProblem(); return; }
|
||||
curGen = chooseGen() || curGen; // структурный вариант по уровню/закреплению
|
||||
if (curGen && curGen.topic) curTopic = curGen.topic;
|
||||
// strict:false + несколько попыток на случай редкой неудачи с ограничениями
|
||||
cur = null;
|
||||
var lvl = currentLevel();
|
||||
for (var i = 0; i < 6 && !cur; i++) cur = TE.instantiate(curGen, { seed: randSeed(), strict: false, level: lvl });
|
||||
for (var i = 0; i < 6 && !cur; i++) cur = TE.instantiate(curGen, { seed: randSeed(), strict: false });
|
||||
if (!cur) { $('tr-eq').textContent = 'Не удалось сгенерировать задачу'; return; }
|
||||
|
||||
renderSkills(); // подсветить активный навык (мог смениться вместе с уровнем)
|
||||
$('tr-skill').textContent = curGen.title;
|
||||
var eq = $('tr-eq');
|
||||
eq.classList.toggle('tr-eq-text', !cur.latex); // текстовый prompt (проценты/упрощение) — другим шрифтом
|
||||
@@ -842,7 +865,7 @@
|
||||
function advance() {
|
||||
if (smart && sessAnswered >= GOAL && !summaryShown) { showSummary(); return; }
|
||||
if (isWord()) { serveWordProblem(); return; } // банк — без адаптивного подбора
|
||||
if (smart) pickNext();
|
||||
if (smart && diffMode === 'auto' && !pinned) pickNext(); // кросс-тематический адаптив — только в Авто
|
||||
newProblem();
|
||||
}
|
||||
function showSummary() {
|
||||
@@ -1039,8 +1062,11 @@
|
||||
var b = e.target.closest('.tr-diff-btn'); if (!b) return;
|
||||
var d = b.getAttribute('data-d');
|
||||
diffMode = (d === 'auto') ? 'auto' : (+d);
|
||||
pinned = null; // выбор уровня снимает закрепление конкретного навыка
|
||||
renderDifficulty();
|
||||
if (cur && cur.kind !== 'word') newProblem(); // свежая задача на выбранном уровне
|
||||
if (cur && cur.kind === 'word') return;
|
||||
if (diffMode === 'auto' && smart) pickNext(); // вернуть адаптивный подбор
|
||||
newProblem(); // chooseGen возьмёт навык нужного структурного уровня
|
||||
});
|
||||
$('tr-subjects').addEventListener('click', function (e) {
|
||||
var b = e.target.closest('.tr-subbtn'); if (!b) return;
|
||||
@@ -1066,6 +1092,7 @@
|
||||
var b = e.target.closest('.tr-chip'); if (!b) return;
|
||||
var t = topics[+b.getAttribute('data-ti')]; if (!t) return;
|
||||
curTopic = t.key;
|
||||
pinned = null; // смена темы снимает закрепление навыка
|
||||
if (t.subject) { curSubject = t.subject; renderSubjects(); }
|
||||
renderTopics();
|
||||
if (t.word) { renderSkills(); loadWordPool(function () { serveWordProblem(); }); return; }
|
||||
@@ -1078,8 +1105,9 @@
|
||||
$('tr-skills').addEventListener('click', function (e) {
|
||||
var b = e.target.closest('.tr-skill'); if (!b) return;
|
||||
var ss = skillsOf(curTopic);
|
||||
curGen = ss[+b.getAttribute('data-si')] || curGen;
|
||||
renderSkills(); newProblem();
|
||||
var g = ss[+b.getAttribute('data-si')]; if (!g) return;
|
||||
curGen = g; pinned = skillKey(g); diffMode = 'auto'; // явный выбор навыка → закрепить
|
||||
renderDifficulty(); renderSkills(); newProblem();
|
||||
});
|
||||
$('tr-smart-btn').addEventListener('click', function () {
|
||||
smart = !smart;
|
||||
|
||||
Reference in New Issue
Block a user