feat(trainer): H — 3-уровневые подсказки + расширенная библиотека ошибок
3-уровневая подсказка (trainer.html, переиспользует solution[]): кнопка «Подсказка» раскрывает по клику: идею (текст шага без формулы) → первый шаг целиком → полное решение. Подпись кнопки меняется («Ещё подсказка» / «Показать решение» / «Решение показано»), уровень сбрасывается на новой задаче. Пошаговый режим — без изменений. Библиотека ошибок (analyzeMistake в _trainer_engine.js) — новые диагностики по соотношению ответа к верному (универсально, без данных по генератору): - ×2 → «забыли разделить на 2» (площадь треуг./трапеции/ромба, среднее); - ÷2 → «лишний раз поделили пополам»; - ×10 / ÷10 → «ошибка разряда: проверьте запятую». Дополняет прежние nodivide/sign/arith; всплывает в разборе неверного ответа. Смоук: hint-логика (inline parse + наличие узлов) OK; analyzeMistake 8/8 (2C→half, C/2→extra-half, 10C и C/10→decimal-place, −C→sign, верный→null). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -561,6 +561,16 @@
|
||||
// перепутан знак ответа
|
||||
if (correct !== 0 && Math.abs(value + correct) <= Math.max(tol, 1e-6 * Math.abs(correct)))
|
||||
return { type: 'sign', hint: 'Кажется, перепутан знак. Проверь знаки при переносе слагаемых через знак «=».' };
|
||||
// забыл разделить на 2 (половина в формуле: площадь треугольника/трапеции/ромба, среднее)
|
||||
if (correct !== 0 && Math.abs(value - 2 * correct) <= Math.max(tol, 1e-6 * Math.abs(2 * correct)))
|
||||
return { type: 'half', hint: 'Похоже, вы забыли разделить на 2. Во многих формулах есть деление пополам (площадь треугольника, трапеции, ромба, среднее значение).' };
|
||||
// лишний раз поделил пополам
|
||||
if (correct !== 0 && Math.abs(value - correct / 2) <= Math.max(tol, 1e-6 * Math.abs(correct / 2)))
|
||||
return { type: 'extra-half', hint: 'Кажется, вы лишний раз поделили на 2. Перечитайте формулу — возможно, делить пополам здесь не нужно.' };
|
||||
// ошибка разряда (в 10 раз) — типично в десятичных и процентах
|
||||
if (correct !== 0 && (Math.abs(value - 10 * correct) <= Math.max(tol, 1e-6 * Math.abs(10 * correct)) ||
|
||||
Math.abs(value - correct / 10) <= Math.max(tol, 1e-6 * Math.abs(correct / 10))))
|
||||
return { type: 'decimal-place', hint: 'Ответ отличается примерно в 10 раз — проверьте разряд: куда поставить запятую и на сколько умножать или делить.' };
|
||||
// близкая арифметическая ошибка
|
||||
if (Math.abs(value - correct) <= Math.max(1, Math.abs(correct) * 0.2))
|
||||
return { type: 'arith', hint: 'Очень близко — похоже на арифметическую ошибку в вычислениях. Пересчитай аккуратно.' };
|
||||
|
||||
+30
-7
@@ -516,7 +516,7 @@
|
||||
</button>
|
||||
<button class="tr-btn tr-ghost" id="tr-hint" type="button">
|
||||
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6M10 22h4M12 2a7 7 0 0 0-4 12.7c.6.5 1 1.3 1 2.1h6c0-.8.4-1.6 1-2.1A7 7 0 0 0 12 2Z"/></svg>
|
||||
Подсказка
|
||||
<span id="tr-hint-label">Подсказка</span>
|
||||
</button>
|
||||
<button class="tr-btn tr-ai-btn" id="tr-ai" type="button" title="ИИ-репетитор объяснит">
|
||||
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3l1.8 4.6L18.5 9l-4.7 1.4L12 15l-1.8-4.6L5.5 9l4.7-1.4z"/><path d="M19 14l.7 1.8L21.5 16.5l-1.8.7L19 19l-.7-1.8L16.5 16.5l1.8-.7z"/></svg>
|
||||
@@ -1112,6 +1112,7 @@
|
||||
$('tr-solution').style.display = 'none'; $('tr-solution').innerHTML = '';
|
||||
var aiBox = $('tr-ai-box'); if (aiBox) { aiBox.style.display = 'none'; aiBox.innerHTML = ''; }
|
||||
lastWrong = false;
|
||||
hintLevel = 0; setHintLabel('Подсказка');
|
||||
var card = $('tr-card'); if (card) { card.classList.remove('tr-correct'); card.classList.remove('tr-wrong'); }
|
||||
var pv = $('tr-preview'); if (pv) pv.innerHTML = '';
|
||||
setMode(false);
|
||||
@@ -1464,19 +1465,41 @@
|
||||
}).catch(function () { bodyEl.textContent = 'ИИ-репетитор недоступен — смотри решение ниже.'; revealSolution(); });
|
||||
}
|
||||
$('tr-ai').addEventListener('click', aiExplain);
|
||||
// 3-уровневая подсказка: идея (без формулы) → первый шаг целиком → полное решение.
|
||||
// Каждый клик «Подсказка» раскрывает следующий уровень (лестница строится из solution[]).
|
||||
var hintLevel = 0;
|
||||
function setHintLabel(t) { var el = $('tr-hint-label'); if (el) el.textContent = t; }
|
||||
$('tr-hint').addEventListener('click', function () {
|
||||
if (!cur) return;
|
||||
if (stepMode) {
|
||||
var sol = cur.solution || [];
|
||||
var idx = Math.min(stepList.length, Math.max(0, sol.length - 1));
|
||||
var st = sol[idx];
|
||||
var sol0 = cur.solution || [];
|
||||
var idx0 = Math.min(stepList.length, Math.max(0, sol0.length - 1));
|
||||
var st0 = sol0[idx0];
|
||||
var fb = $('tr-stepfb'); fb.className = 'tr-feedback warn';
|
||||
fb.innerHTML = 'Подсказка: ' + (st && st.latex ? (kat(st.latex, false) || esc(st.tex || '')) : esc((st && (st.tex || st.note)) || ('x = ' + fmt(cur.answer))));
|
||||
fb.innerHTML = 'Подсказка: ' + (st0 && st0.latex ? (kat(st0.latex, false) || esc(st0.tex || '')) : esc((st0 && (st0.tex || st0.note)) || ('x = ' + fmt(cur.answer))));
|
||||
return;
|
||||
}
|
||||
var sol = cur.solution || [];
|
||||
// лестница уровней: [идея (если есть текст), первый шаг, полное решение]
|
||||
var stages = [];
|
||||
if (sol.length && sol[0].note) stages.push('concept');
|
||||
if (sol.length) stages.push('firststep');
|
||||
stages.push('full');
|
||||
var i = Math.min(hintLevel, stages.length - 1);
|
||||
var stage = stages[i];
|
||||
var s = $('tr-solution');
|
||||
s.innerHTML = '<h4>Подсказка</h4>' + stepHtml((cur.solution || [])[0] || { note: '', tex: 'x = ' + fmt(cur.answer), latex: null }, 1);
|
||||
s.style.display = 'block';
|
||||
if (stage === 'concept') {
|
||||
s.innerHTML = '<h4>Подсказка</h4><div class="tr-step"><span class="tr-step-note"><span class="tr-step-n">1</span>' + renderMixed(sol[0].note) + '</span></div>';
|
||||
s.style.display = 'block';
|
||||
} else if (stage === 'firststep') {
|
||||
s.innerHTML = '<h4>Подсказка</h4>' + stepHtml(sol[0] || { note: '', tex: 'x = ' + fmt(cur.answer), latex: null }, 1);
|
||||
s.style.display = 'block';
|
||||
} else {
|
||||
revealSolution();
|
||||
}
|
||||
hintLevel = i + 1;
|
||||
if (hintLevel >= stages.length) setHintLabel('Решение показано');
|
||||
else setHintLabel(stages[hintLevel] === 'full' ? 'Показать решение' : 'Ещё подсказка');
|
||||
});
|
||||
$('tr-solve').addEventListener('click', function () { if (cur) revealAnswer(true); });
|
||||
$('tr-input').addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); check(); } });
|
||||
|
||||
Reference in New Issue
Block a user