diff --git a/backend/tests/math5-page.test.js b/backend/tests/math5-page.test.js index 54a2a4c..2254237 100644 --- a/backend/tests/math5-page.test.js +++ b/backend/tests/math5-page.test.js @@ -116,6 +116,12 @@ test('ch1: §1 «как решать задачу», §2 «разрядная т assert.ok(doc.querySelector('#p13-out'), '§13: чекер делимости'); win.goTo('p14'); await wait(80); assert.equal(doc.querySelectorAll('#p14-grid [data-n]').length, 29, '§14: решето 2..30'); + win.goTo('p15'); await wait(80); + assert.ok(doc.querySelector('#p15-iv1 #p15-a'), '§15: задачи из жизни'); + win.goTo('p16'); await wait(80); + assert.ok(doc.querySelector('#p16-iv1 #p16-a'), '§16: задачи на движение'); + win.goTo('p17'); await wait(80); + assert.ok(doc.querySelector('#p17-q') && doc.querySelectorAll('#p17-hopt [data-oi]').length === 3, '§17: римские цифры + квиз'); win.goTo('final'); await wait(80); assert.ok(doc.querySelector('#fin-go'), 'финал: арена боссов'); win.bumpProgress('final', 100); await wait(20); @@ -123,6 +129,15 @@ test('ch1: §1 «как решать задачу», §2 «разрядная т assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | ')); }); +test('ch1: все § §1–§17 наполнены (нет заглушек движка)', async () => { + const { doc } = await loadDom('math_5_ch1.html'); + const win = doc.defaultView; + for (let n = 1; n <= 17; n++) { + win.goTo('p' + n); await wait(20); + assert.ok(!doc.querySelector('#p' + n + '-body .m6-placeholder'), '§' + n + ' наполнен (не заглушка)'); + } +}); + test('хаб math-5: 3 главы, курсовой финал, ачивка-полоса', async () => { const { doc, errors } = await loadDom('math_5_hub.html'); assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | ')); diff --git a/frontend/textbooks/math_5_ch1.html b/frontend/textbooks/math_5_ch1.html index 4527eb4..8dd183e 100644 --- a/frontend/textbooks/math_5_ch1.html +++ b/frontend/textbooks/math_5_ch1.html @@ -1188,6 +1188,241 @@ function buildP14(){ })(); } +/* ===================== § 15. МАТЕМАТИКА ВОКРУГ НАС ===================== */ +function buildP15(){ + var box=document.getElementById('p15-body'); var h=''; + h+=makeCard('oral','Где это в жизни','15.0', + '

Этот параграф — про то, как натуральные числа работают вне учебника. Покупки, расписание, расход топлива, спортивный счёт — везде нужно складывать, умножать и делить. Попробуй решить задачи так, будто это реальная ситуация.

'); + h+=makeCard('theory','Числа в жизни','15.1', + '

Чтобы решить практическую задачу, выпиши, что дано, и определи, каким действием искать ответ: «всего» — сложение, «осталось» — вычитание, «во столько раз / по стольку» — умножение или деление.

'); + h+=makeCard('example','Разбор по шагам','15.2', + '

В школу привезли $6$ коробок по $24$ учебника и $4$ коробки по $30$ тетрадей. Сколько всего предметов?

' + +'
    ' + +'
  1. Учебники: $6\\cdot24=144$.
  2. ' + +'
  3. Тетради: $4\\cdot30=120$.
  4. ' + +'
  5. Всего: $144+120=264$.
  6. ' + +'
  7. Ответ: $264$ предмета.
  8. ' + +'
'); + h+=makeCard('theory','А знаешь ли ты?','15.3', + '

Умение быстро прикидывать в уме экономит время и деньги. В супермаркете округли цены до рублей — и ты заранее знаешь примерную сумму в чеке, ещё не дойдя до кассы.

'); + h+='
Интерактив 1
Задачи из жизни
' + +'
Реши практическую задачу. Ответ — число, без единиц.
' + +'
Задача 1 / 6Очки: 0 / 6
' + +'
' + +'
' + +'
'; + h+='
Интерактив 2
Прикинь в уме
' + +'
Округли числа и выбери самую близкую оценку ответа.
' + +'
Вопрос 1 / 5Очки: 0 / 5
' + +'
' + +'
' + +'
'; + h+=secNav('p14','p16')+readBtn('p15'); + box.innerHTML=h; renderMath(box); + + (function(){ + var PROB=[ + {q:'В кинотеатре 18 рядов по 25 мест. Сколько всего мест?',a:450}, + {q:'Купили 3 кг конфет по 12 руб. и торт за 20 руб. Сколько заплатили?',a:56}, + {q:'Турист прошёл 36 км за 3 дня поровну. Сколько км в день?',a:12}, + {q:'В пачке 500 листов. Сколько листов в 8 пачках?',a:4000}, + {q:'Было 1000 руб., купили 4 книги по 150 руб. Сколько осталось?',a:400}, + {q:'Бак вмещает 50 л. Сколько литров в 6 таких баках?',a:300}, + {q:'Поезд из 12 вагонов по 54 места. Сколько всего мест?',a:648}, + {q:'240 яблок разложили в ящики по 30. Сколько ящиков?',a:8} + ]; + var order=[],i=0,score=0,cur=null; + function reorder(){ order=PROB.map(function(_,k){return k;}); for(var j=order.length-1;j>0;j--){ var k=_ri(0,j),t=order[j];order[j]=order[k];order[k]=t; } } + reorder(); + function show(){ if(i>=6){ document.getElementById('p15-q').innerHTML='Готово! '+score+' / 6'; if(score>=5){addXp(15,'p15-iv1');bumpProgress('p15',30);}else if(score>=3){addXp(8,'p15-iv1');bumpProgress('p15',16);} return; } + cur=PROB[order[i]]; document.getElementById('p15-i').textContent=i+1; + document.getElementById('p15-q').innerHTML=cur.q; + document.getElementById('p15-a').value=''; document.getElementById('p15-fb').style.display='none'; } + function go(){ if(i>=6)return; var fb=document.getElementById('p15-fb'), a=parseInt(document.getElementById('p15-a').value,10); + if(isNaN(a)){ feedback(fb,false,'Введи число.'); return; } + if(a===cur.a){ score++; feedback(fb,true,'✓ Верно! Ответ '+cur.a+'.'); } else feedback(fb,false,'✗ Нет. Правильный ответ: '+cur.a+'.'); + document.getElementById('p15-s').textContent=score; i++; setTimeout(show,1300); } + document.getElementById('p15-go').addEventListener('click',go); + document.getElementById('p15-a').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); + show(); + })(); + + (function(){ + var i=0,score=0,cur=null; + function gen(){ var a=_ri(18,42), b=_ri(18,42), exact=a*b, rb=Math.round(a/10)*10*(Math.round(b/10)*10); var opts=[rb, rb+ (Math.round(a/10)*10*10), Math.max(100,rb-(Math.round(b/10)*10*10))]; opts=opts.filter(function(x,k){return opts.indexOf(x)===k;}); while(opts.length<3)opts.push(rb+100*opts.length); var best=opts.reduce(function(p,c){return Math.abs(c-exact)0;j--){var k=_ri(0,j),t=opts[j];opts[j]=opts[k];opts[k]=t;} cur={a:a,b:b,exact:exact,opts:opts,best:best}; } + function show(){ if(i>=5){ document.getElementById('p15-eq').innerHTML='Готово! '+score+' / 5'; document.getElementById('p15-eopt').innerHTML=''; if(score>=4){addXp(15,'p15-iv2');bumpProgress('p15',30);}else if(score>=2){addXp(8,'p15-iv2');bumpProgress('p15',16);} return; } + gen(); document.getElementById('p15-ei').textContent=i+1; + document.getElementById('p15-eq').innerHTML='Оцени примерно: $'+cur.a+'\\cdot'+cur.b+'\\approx\\;?$'; renderMath(document.getElementById('p15-eq')); + var html=''; cur.opts.forEach(function(o){ html+=''; }); + document.getElementById('p15-eopt').innerHTML=html; + document.querySelectorAll('#p15-eopt [data-o]').forEach(function(b){ b.addEventListener('click',function(){ ans(+b.getAttribute('data-o')); }); }); + document.getElementById('p15-efb').style.display='none'; } + function ans(o){ if(i>=5)return; var fb=document.getElementById('p15-efb'); + if(o===cur.best){ score++; feedback(fb,true,'✓ Верно! Точное значение '+cur.exact+', ближе всего '+cur.best+'.'); } else feedback(fb,false,'✗ Ближе всего '+cur.best+' (точно '+cur.exact+').'); + document.getElementById('p15-es').textContent=score; i++; setTimeout(show,1400); } + show(); + })(); +} + +/* ===================== § 16. ДВИЖЕНИЕ, ВЗВЕШИВАНИЕ, ПЕРЕЛИВАНИЕ ===================== */ +function buildP16(){ + var box=document.getElementById('p16-body'); var h=''; + h+=makeCard('oral','Где это в жизни','16.0', + '

Задачи на движение, взвешивание и переливание развивают смекалку. Их любят на олимпиадах, ведь готовой формулы часто нет — нужно придумать план.

'); + h+=makeCard('rule','Три типа задач','16.1', + '

Движение: путь $=$ скорость $\\cdot$ время, $s=v\\cdot t$. Отсюда $v=s:t$ и $t=s:v$.

' + +'

Взвешивание: ищут фальшивую монету за наименьшее число взвешиваний на чашечных весах.

' + +'

Переливание: получают нужный объём, переливая воду между сосудами разной ёмкости.

'); + h+=makeCard('example','Разбор по шагам','16.2', + '

Велосипедист едет со скоростью $12$ км/ч. Какой путь он проедет за $3$ часа?

' + +'
    ' + +'
  1. Формула пути: $s=v\\cdot t$.
  2. ' + +'
  3. Подставляем: $s=12\\cdot3$.
  4. ' + +'
  5. Ответ: $s=36$ км.
  6. ' + +'
'); + h+=makeCard('theory','А знаешь ли ты?','16.3', + '

Задача о переливании из фильма «Крепкий орешек»: как отмерить ровно $4$ литра, имея кувшины на $3$ и $5$ литров? Наполни $5$-литровый, перелей в $3$-литровый — останется $2$ л; вылей тройку, перелей туда эти $2$ л, снова наполни пятёрку и долей доверху — в пятёрке станет ровно $4$ л!

'); + h+='
Интерактив 1
Задачи на движение
' + +'
Используй $s=v\\cdot t$. Ответ — число.
' + +'
Задача 1 / 6Очки: 0 / 6
' + +'
' + +'
' + +'
'; + h+='
Интерактив 2
Логические задачи
' + +'
Подумай и введи ответ числом.
' + +'
Задача 1 / 5Очки: 0 / 5
' + +'
' + +'
' + +'
'; + h+=secNav('p15','p17')+readBtn('p16'); + box.innerHTML=h; renderMath(box); + + (function(){ + var i=0,score=0,cur=null; + function gen(){ var t=_ri(0,2),v,tm,s; + if(t===0){ v=_ri(8,20); tm=_ri(2,6); cur={q:'Скорость '+v+' км/ч, время '+tm+' ч. Найди путь (км).',a:v*tm}; } + else if(t===1){ v=_ri(5,15); s=v*_ri(2,6); cur={q:'Путь '+s+' км пройден со скоростью '+v+' км/ч. Сколько часов в пути?',a:s/v}; } + else { tm=_ri(2,6); s=_ri(6,18)*tm; cur={q:'Путь '+s+' км пройден за '+tm+' ч. Какова скорость (км/ч)?',a:s/tm}; } } + function show(){ if(i>=6){ document.getElementById('p16-q').innerHTML='Готово! '+score+' / 6'; if(score>=5){addXp(15,'p16-iv1');bumpProgress('p16',30);}else if(score>=3){addXp(8,'p16-iv1');bumpProgress('p16',16);} return; } + gen(); document.getElementById('p16-i').textContent=i+1; + document.getElementById('p16-q').innerHTML=cur.q; + document.getElementById('p16-a').value=''; document.getElementById('p16-fb').style.display='none'; } + function go(){ if(i>=6)return; var fb=document.getElementById('p16-fb'), a=parseInt(document.getElementById('p16-a').value,10); + if(isNaN(a)){ feedback(fb,false,'Введи число.'); return; } + if(a===cur.a){ score++; feedback(fb,true,'✓ Верно! Ответ '+cur.a+'.'); } else feedback(fb,false,'✗ Нет. Правильно: '+cur.a+'.'); + document.getElementById('p16-s').textContent=score; i++; setTimeout(show,1300); } + document.getElementById('p16-go').addEventListener('click',go); + document.getElementById('p16-a').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); + show(); + })(); + + (function(){ + var PROB=[ + {q:'Верёвку разрезали на части. Сделали 5 разрезов. Сколько получилось кусков?',a:6}, + {q:'На столбе сидели 7 птиц. 3 улетели, 2 прилетели. Сколько птиц стало?',a:6}, + {q:'Сколько взвешиваний на чашечных весах нужно, чтобы среди 3 монет найти 1 фальшивую (лёгкую)?',a:1}, + {q:'Бревно пилят на 4 части. Каждый распил — 2 минуты. Сколько минут уйдёт?',a:6}, + {q:'Кувшины 3 л и 5 л. За сколько переливаний из 5-литрового получить ровно 4 л? (по разбору)',a:6}, + {q:'Часы бьют 6 ударов за 5 секунд. За сколько секунд они пробьют 12 ударов?',a:11} + ]; + var order=[],i=0,score=0,cur=null; + function reorder(){ order=PROB.map(function(_,k){return k;}); for(var j=order.length-1;j>0;j--){ var k=_ri(0,j),t=order[j];order[j]=order[k];order[k]=t; } } + reorder(); + function show(){ if(i>=5){ document.getElementById('p16-lq').innerHTML='Готово! '+score+' / 5'; if(score>=4){addXp(15,'p16-iv2');bumpProgress('p16',30);}else if(score>=2){addXp(8,'p16-iv2');bumpProgress('p16',16);} return; } + cur=PROB[order[i]]; document.getElementById('p16-li').textContent=i+1; + document.getElementById('p16-lq').innerHTML=cur.q; + document.getElementById('p16-la').value=''; document.getElementById('p16-lfb').style.display='none'; } + function go(){ if(i>=5)return; var fb=document.getElementById('p16-lfb'), a=parseInt(document.getElementById('p16-la').value,10); + if(isNaN(a)){ feedback(fb,false,'Введи число.'); return; } + if(a===cur.a){ score++; feedback(fb,true,'✓ Верно! Ответ '+cur.a+'.'); } else feedback(fb,false,'✗ Подумай ещё. Правильно: '+cur.a+'.'); + document.getElementById('p16-ls').textContent=score; i++; setTimeout(show,1400); } + document.getElementById('p16-lgo').addEventListener('click',go); + document.getElementById('p16-la').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); + show(); + })(); +} + +/* ===================== § 17. ИСТОРИЧЕСКИЕ СВЕДЕНИЯ О ЧИСЛАХ ===================== */ +function buildP17(){ + var box=document.getElementById('p17-body'); var h=''; + h+=makeCard('oral','Где это в жизни','17.0', + '

Числа были у всех народов, но записывали их по-разному. Эти древние системы счисления до сих пор встречаются: римские цифры — на циферблатах часов, в нумерации глав и веков.

'); + h+=makeCard('theory','Как считали разные народы','17.1', + '

Древний Египет: для единиц — палочки, для тысячи — цветок лотоса, для $100\\,000$ — лягушка.

' + +'

Рим: буквами — $I=1$, $V=5$, $X=10$, $L=50$, $C=100$, $D=500$, $M=1000$.

' + +'

Индия: около $1500$ лет назад появились знакомые нам цифры $0$–$9$ — их называют «арабскими», потому что в Европу они попали через арабов.

'); + h+=makeCard('rule','Чтение римских чисел','17.2', + '

Цифры складывают слева направо: $XVI=10+5+1=16$. Но если меньшая стоит перед большей — её вычитают: $IV=5-1=4$, $IX=9$, $XL=40$, $XC=90$.

'); + h+=makeCard('example','Разбор по шагам','17.3', + '

Прочитаем $XXVII$.

' + +'
    ' + +'
  1. $X+X=20$.
  2. ' + +'
  3. $V=5$, итого $25$.
  4. ' + +'
  5. $I+I=2$, итого $27$.
  6. ' + +'
  7. $XXVII=27$.
  8. ' + +'
'); + h+=makeCard('theory','А знаешь ли ты?','17.4', + '

Самое важное «изобретение» в истории чисел — это нуль. Без него не было бы позиционной записи. Нуль как полноценное число первыми стали использовать в Индии около $1500$ лет назад. Римляне же нуля не знали вовсе!

'); + h+='
Интерактив 1
Римские цифры
' + +'
Запиши римское число обычными (арабскими) цифрами.
' + +'
Вопрос 1 / 6Очки: 0 / 6
' + +'
' + +'
' + +'
'; + h+='
Интерактив 2
История чисел
' + +'
Выбери верный ответ.
' + +'
Вопрос 1 / 5Очки: 0 / 5
' + +'
' + +'
' + +'
'; + h+=secNav('p16','final')+readBtn('p17'); + box.innerHTML=h; renderMath(box); + + function toRoman(n){ var m=[[100,'C'],[90,'XC'],[50,'L'],[40,'XL'],[10,'X'],[9,'IX'],[5,'V'],[4,'IV'],[1,'I']],s=''; m.forEach(function(p){ while(n>=p[0]){s+=p[1];n-=p[0];} }); return s; } + (function(){ + var i=0,score=0,cur=null; + function gen(){ cur=_ri(1,89); } + function show(){ if(i>=6){ document.getElementById('p17-q').innerHTML='Готово! '+score+' / 6'; if(score>=5){addXp(15,'p17-iv1');bumpProgress('p17',30);}else if(score>=3){addXp(8,'p17-iv1');bumpProgress('p17',16);} return; } + gen(); document.getElementById('p17-i').textContent=i+1; + document.getElementById('p17-q').textContent=toRoman(cur); + document.getElementById('p17-a').value=''; document.getElementById('p17-fb').style.display='none'; } + function go(){ if(i>=6)return; var fb=document.getElementById('p17-fb'), a=parseInt(document.getElementById('p17-a').value,10); + if(isNaN(a)){ feedback(fb,false,'Введи число.'); return; } + if(a===cur){ score++; feedback(fb,true,'✓ Верно! '+toRoman(cur)+' = '+cur+'.'); } else feedback(fb,false,'✗ Нет. '+toRoman(cur)+' = '+cur+'.'); + document.getElementById('p17-s').textContent=score; i++; setTimeout(show,1300); } + document.getElementById('p17-go').addEventListener('click',go); + document.getElementById('p17-a').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); + show(); + })(); + + (function(){ + var QZ=[ + {q:'Какие цифры мы называем «арабскими»?', o:['0–9','римские','египетские'], a:0}, + {q:'Чему равно римское число L?', o:['50','100','500'], a:0}, + {q:'В какой стране впервые использовали нуль как число?', o:['Индия','Рим','Греция'], a:0}, + {q:'Что обозначала лягушка в египетской записи?', o:['100 000','1000','10'], a:0}, + {q:'Чему равно IX?', o:['9','11','6'], a:0}, + {q:'Знали ли римляне нуль?', o:['нет','да','только жрецы'], a:0}, + {q:'Чему равно римское M?', o:['1000','100','500'], a:0} + ]; + var order=[],i=0,score=0,cur=null; + function reorder(){ order=QZ.map(function(_,k){return k;}); for(var j=order.length-1;j>0;j--){ var k=_ri(0,j),t=order[j];order[j]=order[k];order[k]=t; } } + reorder(); + function show(){ if(i>=5){ document.getElementById('p17-hq').innerHTML='Готово! '+score+' / 5'; document.getElementById('p17-hopt').innerHTML=''; if(score>=4){addXp(15,'p17-iv2');bumpProgress('p17',30);}else if(score>=2){addXp(8,'p17-iv2');bumpProgress('p17',16);} return; } + cur=QZ[order[i]]; document.getElementById('p17-hi').textContent=i+1; + document.getElementById('p17-hq').innerHTML=cur.q; + var idx=[0,1,2]; for(var j=idx.length-1;j>0;j--){var k=_ri(0,j),t=idx[j];idx[j]=idx[k];idx[k]=t;} + var html=''; idx.forEach(function(oi){ html+=''; }); + document.getElementById('p17-hopt').innerHTML=html; + document.querySelectorAll('#p17-hopt [data-oi]').forEach(function(b){ b.addEventListener('click',function(){ ans(+b.getAttribute('data-oi')); }); }); + document.getElementById('p17-hfb').style.display='none'; } + function ans(oi){ if(i>=5)return; var fb=document.getElementById('p17-hfb'); + if(oi===cur.a){ score++; feedback(fb,true,'✓ Верно!'); } else feedback(fb,false,'✗ Нет. Верный ответ: '+cur.o[cur.a]+'.'); + document.getElementById('p17-hs').textContent=score; i++; setTimeout(show,1300); } + show(); + })(); +} + /* ===================== ДАННЫЕ САЙДБАРА / ГЛОССАРИЯ ===================== */ var SIDEBARS = { p1:{ title:'Шпаргалка § 1', rows:[ @@ -1261,6 +1496,21 @@ var SIDEBARS = { ['Составное','больше 2 делителей'], ['1','ни простое, ни составное'], ['Разложение','$60=2^2\\cdot3\\cdot5$'] ]}, + p15:{ title:'Шпаргалка § 15', rows:[ + ['«всего»','складываем'], + ['«осталось»','вычитаем'], + ['«по / в … раз»','умножаем или делим'], + ['Прикидка','округли и оцени ответ'] ]}, + p16:{ title:'Шпаргалка § 16', rows:[ + ['Путь','$s=v\\cdot t$'], + ['Скорость','$v=s:t$'], + ['Время','$t=s:v$'], + ['Смекалка','сначала придумай план'] ]}, + p17:{ title:'Шпаргалка § 17', rows:[ + ['Рим','$I,V,X,L,C,D,M$'], + ['Меньшая перед большей','вычитаем: $IV=4$'], + ['Арабские','цифры $0$–$9$, родом из Индии'], + ['Нуль','изобретён в Индии'] ]}, final:{ title:'Финал главы 1', rows:[ ['5 боссов','разряды, округление, действия, степень'], ['Победа','4 из 5 и больше'], @@ -1280,7 +1530,7 @@ var GLOSSARY = [ { term:'разряд', def:'Место цифры в записи числа: единицы, десятки, сотни и т. д.', sec:'p2', aliases:['разряд','разряда','разряде','разряды','разрядов'] }, { term:'класс', def:'Группа из трёх соседних разрядов: единицы, тысячи, миллионы.', sec:'p2', aliases:['класс','класса','классе','классы','классов'] } ]; -var BUILDERS = { p1:buildP1, p2:buildP2, p3:buildP3, p4:buildP4, p5:buildP5, p6:buildP6, p7:buildP7, p8:buildP8, p9:buildP9, p10:buildP10, p11:buildP11, p12:buildP12, p13:buildP13, p14:buildP14, final:buildFinal }; +var BUILDERS = { p1:buildP1, p2:buildP2, p3:buildP3, p4:buildP4, p5:buildP5, p6:buildP6, p7:buildP7, p8:buildP8, p9:buildP9, p10:buildP10, p11:buildP11, p12:buildP12, p13:buildP13, p14:buildP14, p15:buildP15, p16:buildP16, p17:buildP17, final:buildFinal }; Object.assign(window.M6, { sidebars:SIDEBARS, tips:TIPS, glossary:GLOSSARY, builders:BUILDERS });