feat(math6): Глава 3 — Множество (§1–§5 + финал)
§1 множество/элементы/∅ (∈ или ∉ + счёт элементов); §2 способы задания (свойство→множество + проверка по свойству); §3 операции ∩/∪ (наглядно через Math6.venn + счёт результата); §4 круги Эйлера (задача с числами в областях + формула |A∪B|=|A|+|B|−|A∩B|); финал — 5 боссов. Добавлен Math6.venn (две окружности с заливкой областей и числами). Тесты math6: 16/16. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -85,6 +85,23 @@ test('ch2: проценты и пропорции — интерактивы +
|
|||||||
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
|
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('ch3: множество — интерактивы + финал', async () => {
|
||||||
|
const { doc, errors } = await loadDom('math_6_ch3.html');
|
||||||
|
const win = doc.defaultView;
|
||||||
|
assert.ok(doc.querySelectorAll('#p1-iv1 [data-v]').length === 2 && doc.querySelector('#p1-cq'), '∈/∉ и счёт §1');
|
||||||
|
win.goTo('p2'); await wait(80);
|
||||||
|
assert.ok(doc.querySelector('#p2-opts') && doc.querySelectorAll('#p2-iv2 [data-v]').length === 2, 'способы задания §2');
|
||||||
|
win.goTo('p3'); await wait(80);
|
||||||
|
assert.ok(doc.querySelector('#p3-fig svg circle') && doc.querySelectorAll('#p3-iv1 [data-op]').length === 2, 'круги Эйлера/операции §3');
|
||||||
|
win.goTo('p4'); await wait(80);
|
||||||
|
assert.ok(doc.querySelector('#p4-fig svg') && doc.querySelector('#p4-fq'), 'задача с кругами Эйлера §4');
|
||||||
|
win.goTo('final'); await wait(80);
|
||||||
|
assert.ok(doc.querySelector('#fin-go'), 'арена боссов §3');
|
||||||
|
win.bumpProgress('final', 100); await wait(20);
|
||||||
|
assert.ok(win.M6STATE.achievements.has('ch3_done'), 'достижение «Глава 3 пройдена»');
|
||||||
|
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
|
||||||
|
});
|
||||||
|
|
||||||
test('ch5: координатная плоскость — интерактивы §1–§3 + финал', async () => {
|
test('ch5: координатная плоскость — интерактивы §1–§3 + финал', async () => {
|
||||||
const { doc, errors } = await loadDom('math_6_ch5.html');
|
const { doc, errors } = await loadDom('math_6_ch5.html');
|
||||||
const win = doc.defaultView;
|
const win = doc.defaultView;
|
||||||
|
|||||||
@@ -177,4 +177,36 @@ M.pie = function (segs, opts) {
|
|||||||
return M.box(S, S, s, { maxw: opts.maxw || S });
|
return M.box(S, S, s, { maxw: opts.maxw || S });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* === КРУГИ ЭЙЛЕРА / ДИАГРАММА ВЕННА (два множества) ===
|
||||||
|
* opts: {a,b (подписи), shade:'inter'|'union'|'a'|'b'|'none', shadeColor,
|
||||||
|
* regions:{aOnly,inter,bOnly,outside}} — числа/текст в областях.
|
||||||
|
*/
|
||||||
|
M.venn = function (opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
var W = 300, H = 190, cA = { x: 115, y: 100 }, cB = { x: 185, y: 100 }, r = 66;
|
||||||
|
var sc = opts.shadeColor || '#7c3aed';
|
||||||
|
var s = '<defs><clipPath id="m6vA"><circle cx="' + cA.x + '" cy="' + cA.y + '" r="' + r + '"/></clipPath></defs>';
|
||||||
|
var cir = function (c, fill, op) { return '<circle cx="' + c.x + '" cy="' + c.y + '" r="' + r + '" fill="' + fill + '"' + (op != null ? ' opacity="' + op + '"' : '') + '/>'; };
|
||||||
|
if (opts.shade === 'union') { s += cir(cA, sc, 0.22) + cir(cB, sc, 0.22); }
|
||||||
|
else if (opts.shade === 'inter') { s += '<g clip-path="url(#m6vA)">' + cir(cB, sc, 0.4) + '</g>'; }
|
||||||
|
else if (opts.shade === 'a') { s += cir(cA, sc, 0.3) + '<g clip-path="url(#m6vA)">' + cir(cB, 'var(--card,#fff)', 1) + '</g>'; }
|
||||||
|
else if (opts.shade === 'b') { s += cir(cB, sc, 0.3) + '<g clip-path="url(#m6vA)">' + cir(cB, 'var(--card,#fff)', 1) + '</g>' + '<g clip-path="url(#m6vA)">' + cir(cB, sc, 0.3) + '</g>'; }
|
||||||
|
/* контуры */
|
||||||
|
s += '<circle cx="' + cA.x + '" cy="' + cA.y + '" r="' + r + '" fill="none" stroke="#4f46e5" stroke-width="2.5"/>';
|
||||||
|
s += '<circle cx="' + cB.x + '" cy="' + cB.y + '" r="' + r + '" fill="none" stroke="#e11d48" stroke-width="2.5"/>';
|
||||||
|
/* подписи множеств */
|
||||||
|
s += '<text x="' + (cA.x - 40) + '" y="' + (cA.y - r + 4) + '" font-size="16" font-weight="800" font-family="Unbounded,Inter,sans-serif" fill="#4f46e5">' + (opts.a || 'A') + '</text>';
|
||||||
|
s += '<text x="' + (cB.x + 30) + '" y="' + (cB.y - r + 4) + '" font-size="16" font-weight="800" font-family="Unbounded,Inter,sans-serif" fill="#e11d48">' + (opts.b || 'B') + '</text>';
|
||||||
|
/* числа/текст в областях */
|
||||||
|
var rg = opts.regions;
|
||||||
|
if (rg) {
|
||||||
|
function lab(x, y, t, col) { if (t == null || t === '') return ''; return '<text x="' + x + '" y="' + (y + 5) + '" text-anchor="middle" font-size="15" font-weight="700" font-family="JetBrains Mono,monospace" fill="' + (col || 'var(--text,#0f172a)') + '">' + t + '</text>'; }
|
||||||
|
s += lab(cA.x - 34, cA.y, rg.aOnly);
|
||||||
|
s += lab((cA.x + cB.x) / 2, cA.y, rg.inter);
|
||||||
|
s += lab(cB.x + 34, cB.y, rg.bOnly);
|
||||||
|
if (rg.outside != null) s += lab(W - 22, H - 14, rg.outside, 'var(--muted,#64748b)');
|
||||||
|
}
|
||||||
|
return M.box(W, H, s, { maxw: opts.maxw || W });
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -90,6 +90,292 @@ window.M6 = {
|
|||||||
sidebars: {}, tips: [], glossary: [], builders: {},
|
sidebars: {}, tips: [], glossary: [], builders: {},
|
||||||
footer: 'Интерактивный учебник «Математика 6» · Глава 3 · Множество · LearnSpace'
|
footer: 'Интерактивный учебник «Математика 6» · Глава 3 · Множество · LearnSpace'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ===================== ВСПОМОГАТЕЛЬНОЕ ===================== */
|
||||||
|
function _ri(a,b){ return a + Math.floor(Math.random()*(b-a+1)); }
|
||||||
|
function _pick(a){ return a[_ri(0,a.length-1)]; }
|
||||||
|
function _setStr(arr){ return '{' + arr.join(', ') + '}'; }
|
||||||
|
function _distinct(n,lo,hi){ var s=[],seen={}; while(s.length<n){ var v=_ri(lo,hi); if(!seen[v]){seen[v]=1;s.push(v);} } return s; }
|
||||||
|
function _inter(A,B){ return A.filter(function(x){ return B.indexOf(x)>=0; }); }
|
||||||
|
function _union(A,B){ var u=A.slice(); B.forEach(function(x){ if(u.indexOf(x)<0)u.push(x); }); return u; }
|
||||||
|
|
||||||
|
/* ===================== § 1. МНОЖЕСТВО. ЭЛЕМЕНТЫ. ПУСТОЕ МНОЖЕСТВО ===================== */
|
||||||
|
function buildP1(){
|
||||||
|
var box=document.getElementById('p1-body'); var h='';
|
||||||
|
h+=makeCard('theory','Множество и его элементы','1.1',
|
||||||
|
'<p><b>Множество</b> — это набор различных объектов, объединённых общим признаком. Объекты множества — его <b>элементы</b>. Множества обозначают большими буквами, элементы перечисляют в фигурных скобках: $A=\\{2;4;6;8\\}$.</p>'
|
||||||
|
+'<p>Запись $3\\in A$ читается «$3$ принадлежит $A$», $5\\notin A$ — «$5$ не принадлежит $A$».</p>');
|
||||||
|
h+=makeCard('rule','Пустое множество','1.2',
|
||||||
|
'<p><b>Пустое множество</b> $\\varnothing$ не содержит ни одного элемента (например, множество натуральных решений уравнения $x+1=0$).</p>'
|
||||||
|
+'<p>Множества <b>равны</b>, если состоят из одних и тех же элементов; порядок и повторы не важны: $\\{1;2;3\\}=\\{3;1;2\\}$.</p>');
|
||||||
|
h+='<div class="wg" id="p1-iv1"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><div class="wg-title">Принадлежит или нет?</div></div>'
|
||||||
|
+'<div class="wg-help">Определи, принадлежит ли элемент данному множеству.</div>'
|
||||||
|
+'<div class="score-display"><span>Вопрос <b id="p1-i">1</b> / 6</span><span>Очки: <b id="p1-s">0</b> / 6</span></div>'
|
||||||
|
+'<div id="p1-q" class="qbox"></div>'
|
||||||
|
+'<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap"><button class="btn primary" data-v="1">∈ принадлежит</button><button class="btn primary" data-v="0">∉ не принадлежит</button></div>'
|
||||||
|
+'<div class="feedback" id="p1-fb"></div></div>';
|
||||||
|
h+='<div class="wg" id="p1-iv2"><div class="wg-header"><span class="wg-badge">Интерактив 2</span><div class="wg-title">Сколько элементов?</div></div>'
|
||||||
|
+'<div class="wg-help">Сосчитай число элементов множества (у пустого — ноль).</div>'
|
||||||
|
+'<div class="score-display"><span>Вопрос <b id="p1-ci">1</b> / 5</span><span>Очки: <b id="p1-cs">0</b> / 5</span></div>'
|
||||||
|
+'<div id="p1-cq" class="qbox"></div>'
|
||||||
|
+'<div style="display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap"><input type="number" id="p1-ca" class="tinp" style="width:80px;text-align:center"><button class="btn primary" id="p1-cgo">Проверить</button></div>'
|
||||||
|
+'<div class="feedback" id="p1-cfb"></div></div>';
|
||||||
|
h+=secNav(null,'p2')+readBtn('p1');
|
||||||
|
box.innerHTML=h; renderMath(box);
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
var i=0,score=0,cur=null;
|
||||||
|
function gen(){ var A=_distinct(_ri(3,5),1,9), inside=_pick([true,false]); var e; if(inside)e=_pick(A); else { do{e=_ri(1,9);}while(A.indexOf(e)>=0); } cur={A:A,e:e,in:A.indexOf(e)>=0}; }
|
||||||
|
function show(){ if(i>=6){ document.getElementById('p1-q').innerHTML='<b>Готово!</b> '+score+' / 6'; if(score>=5){addXp(15,'p1-iv1');bumpProgress('p1',30);}else if(score>=3){addXp(8,'p1-iv1');bumpProgress('p1',16);} return; }
|
||||||
|
gen(); document.getElementById('p1-i').textContent=i+1; document.getElementById('p1-q').innerHTML='$A = '+_setStr(cur.A)+'$. Верно ли, что $'+cur.e+' \\in A$?'; renderMath(document.getElementById('p1-q'));
|
||||||
|
document.getElementById('p1-fb').style.display='none'; }
|
||||||
|
function ans(v){ if(i>=6)return; var fb=document.getElementById('p1-fb'), correct=cur.in?1:0;
|
||||||
|
if(v===correct){ score++; feedback(fb,true,'✓ Верно: $'+cur.e+(cur.in?' \\in ':' \\notin ')+'A$.'); } else feedback(fb,false,'✗ Нет: $'+cur.e+(cur.in?' \\in ':' \\notin ')+'A$.');
|
||||||
|
document.getElementById('p1-s').textContent=score; i++; setTimeout(show,1300); }
|
||||||
|
document.querySelectorAll('#p1-iv1 [data-v]').forEach(function(b){ b.addEventListener('click',function(){ ans(+b.getAttribute('data-v')); }); }); show();
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
var i=0,score=0,cur=null;
|
||||||
|
function gen(){ if(_ri(1,6)===1){ cur={str:'\\varnothing', n:0}; } else { var A=_distinct(_ri(2,6),1,12); cur={str:_setStr(A), n:A.length}; } }
|
||||||
|
function show(){ if(i>=5){ document.getElementById('p1-cq').innerHTML='<b>Готово!</b> '+score+' / 5'; if(score>=4){addXp(15,'p1-iv2');bumpProgress('p1',30);}else if(score>=2){addXp(8,'p1-iv2');bumpProgress('p1',16);} return; }
|
||||||
|
gen(); document.getElementById('p1-ci').textContent=i+1; document.getElementById('p1-cq').innerHTML='Сколько элементов в множестве $'+cur.str+'$?'; renderMath(document.getElementById('p1-cq'));
|
||||||
|
document.getElementById('p1-ca').value=''; document.getElementById('p1-cfb').style.display='none'; }
|
||||||
|
function go(){ if(i>=5)return; var fb=document.getElementById('p1-cfb'), v=parseInt(document.getElementById('p1-ca').value,10);
|
||||||
|
if(isNaN(v)){ feedback(fb,false,'Введи число.'); return; }
|
||||||
|
if(v===cur.n){ score++; feedback(fb,true,'✓ Верно: '+cur.n+'.'); } else feedback(fb,false,'✗ Нет. Элементов: '+cur.n+'.');
|
||||||
|
document.getElementById('p1-cs').textContent=score; i++; setTimeout(show,1200); }
|
||||||
|
document.getElementById('p1-cgo').addEventListener('click',go);
|
||||||
|
document.getElementById('p1-ca').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); show();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================== § 2. СПОСОБЫ ЗАДАНИЯ МНОЖЕСТВ ===================== */
|
||||||
|
function buildP2(){
|
||||||
|
var box=document.getElementById('p2-body'); var h='';
|
||||||
|
h+=makeCard('theory','Два способа задания','2.1',
|
||||||
|
'<p><b>Перечислением</b> — выписывают все элементы: $A=\\{2;4;6;8\\}$.</p>'
|
||||||
|
+'<p><b>Характеристическим свойством</b> — указывают признак: $A=\\{x \\mid x$ — чётное, $0<x<10\\}$ (читается «множество всех $x$ таких, что …»).</p>');
|
||||||
|
h+='<div class="wg" id="p2-iv1"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><div class="wg-title">Свойство → множество</div></div>'
|
||||||
|
+'<div class="wg-help">Выбери множество, заданное перечислением, которое соответствует описанию.</div>'
|
||||||
|
+'<div class="score-display"><span>Вопрос <b id="p2-i">1</b> / 5</span><span>Очки: <b id="p2-s">0</b> / 5</span></div>'
|
||||||
|
+'<div id="p2-q" class="qbox"></div><div id="p2-opts" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap"></div>'
|
||||||
|
+'<div class="feedback" id="p2-fb"></div></div>';
|
||||||
|
h+='<div class="wg" id="p2-iv2"><div class="wg-header"><span class="wg-badge">Интерактив 2</span><div class="wg-title">Принадлежит ли свойству?</div></div>'
|
||||||
|
+'<div class="wg-help">Принадлежит ли число множеству, заданному характеристическим свойством?</div>'
|
||||||
|
+'<div class="score-display"><span>Вопрос <b id="p2-vi">1</b> / 6</span><span>Очки: <b id="p2-vs">0</b> / 6</span></div>'
|
||||||
|
+'<div id="p2-vq" class="qbox"></div>'
|
||||||
|
+'<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap"><button class="btn primary" data-v="1">Да, принадлежит</button><button class="btn primary" data-v="0">Нет</button></div>'
|
||||||
|
+'<div class="feedback" id="p2-vfb"></div></div>';
|
||||||
|
h+=secNav('p1','p3')+readBtn('p2');
|
||||||
|
box.innerHTML=h; renderMath(box);
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
var Q=[
|
||||||
|
{d:'натуральные числа меньше 5', set:'\\{1;2;3;4\\}', wrong:['\\{0;1;2;3;4;5\\}','\\{1;2;3;4;5\\}']},
|
||||||
|
{d:'чётные числа от 1 до 10', set:'\\{2;4;6;8;10\\}', wrong:['\\{1;3;5;7;9\\}','\\{2;4;6;8\\}']},
|
||||||
|
{d:'делители числа 12', set:'\\{1;2;3;4;6;12\\}', wrong:['\\{1;2;3;4;6\\}','\\{2;3;4;6;12\\}']},
|
||||||
|
{d:'однозначные числа, кратные 3', set:'\\{3;6;9\\}', wrong:['\\{0;3;6;9\\}','\\{3;6;9;12\\}']},
|
||||||
|
{d:'нечётные числа от 1 до 7', set:'\\{1;3;5;7\\}', wrong:['\\{1;3;5\\}','\\{2;4;6\\}']}
|
||||||
|
];
|
||||||
|
var order=Q.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;}
|
||||||
|
var i=0,score=0,cur=null;
|
||||||
|
function show(){ if(i>=5){ document.getElementById('p2-q').innerHTML='<b>Готово!</b> '+score+' / 5'; document.getElementById('p2-opts').innerHTML=''; if(score>=4){addXp(15,'p2-iv1');bumpProgress('p2',30);}else if(score>=2){addXp(8,'p2-iv1');bumpProgress('p2',16);} return; }
|
||||||
|
cur=Q[order[i]]; document.getElementById('p2-i').textContent=i+1;
|
||||||
|
document.getElementById('p2-q').innerHTML='Какое множество — это «'+cur.d+'»?';
|
||||||
|
var opts=[cur.set,cur.wrong[0],cur.wrong[1]]; for(var j=opts.length-1;j>0;j--){var k=_ri(0,j),t=opts[j];opts[j]=opts[k];opts[k]=t;}
|
||||||
|
document.getElementById('p2-opts').innerHTML=opts.map(function(o){ return '<button class="btn primary" data-o="'+o+'">$'+o+'$</button>'; }).join('');
|
||||||
|
document.querySelectorAll('#p2-opts [data-o]').forEach(function(b){ b.addEventListener('click',function(){ ans(b.getAttribute('data-o')); }); }); renderMath(document.getElementById('p2-opts')); renderMath(document.getElementById('p2-q'));
|
||||||
|
document.getElementById('p2-fb').style.display='none'; }
|
||||||
|
function ans(o){ if(i>=5)return; var fb=document.getElementById('p2-fb');
|
||||||
|
if(o===cur.set){ score++; feedback(fb,true,'✓ Верно: $'+cur.set+'$.'); } else feedback(fb,false,'✗ Нет. Правильно: $'+cur.set+'$.');
|
||||||
|
document.getElementById('p2-s').textContent=score; i++; setTimeout(show,1400); }
|
||||||
|
show();
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
var PROPS=[
|
||||||
|
{t:'чётное число',f:function(n){return n%2===0;}},
|
||||||
|
{t:'число, кратное 3',f:function(n){return n%3===0;}},
|
||||||
|
{t:'число, большее 5',f:function(n){return n>5;}},
|
||||||
|
{t:'делитель числа 12',f:function(n){return 12%n===0;}},
|
||||||
|
{t:'нечётное число',f:function(n){return n%2===1;}}
|
||||||
|
];
|
||||||
|
var i=0,score=0,cur=null;
|
||||||
|
function gen(){ var p=_pick(PROPS), n=_ri(1,12); cur={p:p,n:n,ok:p.f(n)}; }
|
||||||
|
function show(){ if(i>=6){ document.getElementById('p2-vq').innerHTML='<b>Готово!</b> '+score+' / 6'; if(score>=5){addXp(15,'p2-iv2');bumpProgress('p2',30);}else if(score>=3){addXp(8,'p2-iv2');bumpProgress('p2',16);} return; }
|
||||||
|
gen(); document.getElementById('p2-vi').textContent=i+1; document.getElementById('p2-vq').innerHTML='Принадлежит ли $'+cur.n+'$ множеству $\\{x \\mid x$ — '+cur.p.t+'$\\}$?'; renderMath(document.getElementById('p2-vq'));
|
||||||
|
document.getElementById('p2-vfb').style.display='none'; }
|
||||||
|
function ans(v){ if(i>=6)return; var fb=document.getElementById('p2-vfb'), correct=cur.ok?1:0;
|
||||||
|
if(v===correct){ score++; feedback(fb,true,'✓ Верно!'); } else feedback(fb,false,'✗ Нет. $'+cur.n+'$ '+(cur.ok?'принадлежит':'не принадлежит')+'.');
|
||||||
|
document.getElementById('p2-vs').textContent=score; i++; setTimeout(show,1300); }
|
||||||
|
document.querySelectorAll('#p2-iv2 [data-v]').forEach(function(b){ b.addEventListener('click',function(){ ans(+b.getAttribute('data-v')); }); }); show();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================== § 3. ОПЕРАЦИИ НАД МНОЖЕСТВАМИ ===================== */
|
||||||
|
function buildP3(){
|
||||||
|
var box=document.getElementById('p3-body'); var h='';
|
||||||
|
h+=makeCard('theory','Пересечение и объединение','3.1',
|
||||||
|
'<p><b>Пересечение</b> $A\\cap B$ — множество элементов, принадлежащих <b>обоим</b> множествам сразу.</p>'
|
||||||
|
+'<p><b>Объединение</b> $A\\cup B$ — множество элементов, принадлежащих <b>хотя бы одному</b> из множеств.</p>'
|
||||||
|
+'<p>$A=\\{1;2;3;4\\}$, $B=\\{3;4;5;6\\}$: $A\\cap B=\\{3;4\\}$, $A\\cup B=\\{1;2;3;4;5;6\\}$.</p>');
|
||||||
|
h+='<div class="wg" id="p3-iv1"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><div class="wg-title">Операции наглядно</div></div>'
|
||||||
|
+'<div class="wg-help">Нажми операцию — увидишь закрашенную область и результат для $A=\\{1;2;3;4\\}$, $B=\\{3;4;5;6\\}$.</div>'
|
||||||
|
+'<div style="display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin-bottom:8px"><button class="btn primary" data-op="inter">$A\\cap B$</button><button class="btn" data-op="union">$A\\cup B$</button></div>'
|
||||||
|
+'<div id="p3-fig"></div><div id="p3-out" class="qbox"></div></div>';
|
||||||
|
h+='<div class="wg" id="p3-iv2"><div class="wg-header"><span class="wg-badge">Интерактив 2</span><div class="wg-title">Сосчитай результат</div></div>'
|
||||||
|
+'<div class="wg-help">Сколько элементов в указанном множестве?</div>'
|
||||||
|
+'<div class="score-display"><span>Вопрос <b id="p3-i">1</b> / 6</span><span>Очки: <b id="p3-s">0</b> / 6</span></div>'
|
||||||
|
+'<div id="p3-q" class="qbox"></div>'
|
||||||
|
+'<div style="display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap"><input type="number" id="p3-a" class="tinp" style="width:80px;text-align:center"><button class="btn primary" id="p3-go">Проверить</button></div>'
|
||||||
|
+'<div class="feedback" id="p3-fb"></div></div>';
|
||||||
|
h+=secNav('p2','p4')+readBtn('p3');
|
||||||
|
box.innerHTML=h; renderMath(box);
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
var A=[1,2,3,4], B=[3,4,5,6], fig=document.getElementById('p3-fig'), out=document.getElementById('p3-out');
|
||||||
|
function render(op){ var regions={aOnly:'1, 2', inter:'3, 4', bOnly:'5, 6'};
|
||||||
|
fig.innerHTML=Math6.venn({a:'A',b:'B',shade:op,regions:regions});
|
||||||
|
if(op==='inter') out.innerHTML='<div style="font-weight:700;color:var(--pri2)">$A\\cap B = \\{3;4\\}$ — общие элементы</div>';
|
||||||
|
else out.innerHTML='<div style="font-weight:700;color:var(--pri2)">$A\\cup B = \\{1;2;3;4;5;6\\}$ — все элементы</div>';
|
||||||
|
renderMath(out); }
|
||||||
|
document.querySelectorAll('#p3-iv1 [data-op]').forEach(function(b){ b.addEventListener('click',function(){ document.querySelectorAll('#p3-iv1 [data-op]').forEach(function(x){x.classList.remove('primary');}); b.classList.add('primary'); render(b.getAttribute('data-op')); }); });
|
||||||
|
render('inter');
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
var i=0,score=0,cur=null;
|
||||||
|
function gen(){ var A=_distinct(_ri(3,5),1,8), B=_distinct(_ri(3,5),1,8), op=_pick(['inter','union']); var r=op==='inter'?_inter(A,B):_union(A,B); cur={A:A,B:B,op:op,n:r.length}; }
|
||||||
|
function show(){ if(i>=6){ document.getElementById('p3-q').innerHTML='<b>Готово!</b> '+score+' / 6'; if(score>=5){addXp(15,'p3-iv2');bumpProgress('p3',30);}else if(score>=3){addXp(8,'p3-iv2');bumpProgress('p3',16);} return; }
|
||||||
|
gen(); document.getElementById('p3-i').textContent=i+1;
|
||||||
|
document.getElementById('p3-q').innerHTML='$A='+_setStr(cur.A)+'$, $B='+_setStr(cur.B)+'$.<br>Сколько элементов в $A'+(cur.op==='inter'?'\\cap':'\\cup')+'B$?'; renderMath(document.getElementById('p3-q'));
|
||||||
|
document.getElementById('p3-a').value=''; document.getElementById('p3-fb').style.display='none'; }
|
||||||
|
function go(){ if(i>=6)return; var fb=document.getElementById('p3-fb'), v=parseInt(document.getElementById('p3-a').value,10);
|
||||||
|
if(isNaN(v)){ feedback(fb,false,'Введи число.'); return; }
|
||||||
|
var r=cur.op==='inter'?_inter(cur.A,cur.B):_union(cur.A,cur.B);
|
||||||
|
if(v===cur.n){ score++; feedback(fb,true,'✓ Верно: '+_setStr(r)+' — '+cur.n+' элем.'); } else feedback(fb,false,'✗ Нет. '+(cur.op==='inter'?'A\\cap B':'A\\cup B')+' = '+_setStr(r)+' ('+cur.n+').');
|
||||||
|
document.getElementById('p3-s').textContent=score; i++; setTimeout(show,1500); }
|
||||||
|
document.getElementById('p3-go').addEventListener('click',go);
|
||||||
|
document.getElementById('p3-a').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); show();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================== § 4. КРУГИ ЭЙЛЕРА ===================== */
|
||||||
|
function buildP4(){
|
||||||
|
var box=document.getElementById('p4-body'); var h='';
|
||||||
|
h+=makeCard('theory','Круги Эйлера','4.1',
|
||||||
|
'<p>Множества удобно изображать кругами (круги Эйлера). Пересекающиеся круги показывают общую часть. Если в $A$ — $a$ элементов, в $B$ — $b$, а в пересечении — $c$, то в объединении: $|A\\cup B| = a + b - c$ (общие посчитаны дважды — вычитаем один раз).</p>');
|
||||||
|
h+='<div class="wg" id="p4-iv1"><div class="wg-header"><span class="wg-badge">Интерактив 1</span><div class="wg-title">Задача с кругами Эйлера</div></div>'
|
||||||
|
+'<div class="wg-help">Разнеси данные по кругам и ответь на вопрос.</div>'
|
||||||
|
+'<div class="score-display"><span>Задача <b id="p4-i">1</b> / 5</span><span>Очки: <b id="p4-s">0</b> / 5</span></div>'
|
||||||
|
+'<div id="p4-fig"></div><div id="p4-q" class="qbox" style="font-size:1rem"></div>'
|
||||||
|
+'<div style="display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap"><input type="number" id="p4-a" class="tinp" style="width:80px;text-align:center"><button class="btn primary" id="p4-go">Проверить</button></div>'
|
||||||
|
+'<div class="feedback" id="p4-fb"></div></div>';
|
||||||
|
h+='<div class="wg" id="p4-iv2"><div class="wg-header"><span class="wg-badge">Интерактив 2</span><div class="wg-title">Формула объединения</div></div>'
|
||||||
|
+'<div class="wg-help">Используй $|A\\cup B| = |A| + |B| - |A\\cap B|$.</div>'
|
||||||
|
+'<div class="score-display"><span>Задача <b id="p4-fi">1</b> / 5</span><span>Очки: <b id="p4-fs">0</b> / 5</span></div>'
|
||||||
|
+'<div id="p4-fq" class="qbox"></div>'
|
||||||
|
+'<div style="display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap"><input type="number" id="p4-fa" class="tinp" style="width:80px;text-align:center"><button class="btn primary" id="p4-fgo">Проверить</button></div>'
|
||||||
|
+'<div class="feedback" id="p4-ffb"></div></div>';
|
||||||
|
h+=secNav('p3','final')+readBtn('p4');
|
||||||
|
box.innerHTML=h; renderMath(box);
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
var i=0,score=0,cur=null;
|
||||||
|
function gen(){ var c=_ri(3,8), aOnly=_ri(4,12), bOnly=_ri(4,12), neither=_ri(0,6); var a=aOnly+c, b=bOnly+c, T=a+b-c+neither;
|
||||||
|
var ask=_pick(['aOnly','bOnly','neither']); cur={a:a,b:b,c:c,aOnly:aOnly,bOnly:bOnly,neither:neither,T:T,ask:ask}; }
|
||||||
|
function show(){ if(i>=5){ document.getElementById('p4-fig').innerHTML=''; document.getElementById('p4-q').innerHTML='<b>Готово!</b> '+score+' / 5'; if(score>=4){addXp(15,'p4-iv1');bumpProgress('p4',30);}else if(score>=2){addXp(8,'p4-iv1');bumpProgress('p4',16);} return; }
|
||||||
|
gen(); document.getElementById('p4-i').textContent=i+1;
|
||||||
|
document.getElementById('p4-fig').innerHTML=Math6.venn({a:'Матем.',b:'Физика',shade:'none',regions:{aOnly:cur.aOnly,inter:cur.c,bOnly:cur.bOnly,outside:cur.neither}});
|
||||||
|
var qq={aOnly:'Сколько любят <b>только математику</b>?', bOnly:'Сколько любят <b>только физику</b>?', neither:'Сколько <b>не любят ни то, ни другое</b>?'};
|
||||||
|
document.getElementById('p4-q').innerHTML='В классе '+cur.T+' учеников: '+cur.a+' любят математику, '+cur.b+' — физику, '+cur.c+' — оба предмета. '+qq[cur.ask];
|
||||||
|
document.getElementById('p4-a').value=''; document.getElementById('p4-fb').style.display='none'; }
|
||||||
|
function go(){ if(i>=5)return; var fb=document.getElementById('p4-fb'), v=parseInt(document.getElementById('p4-a').value,10), ans=cur[cur.ask];
|
||||||
|
if(isNaN(v)){ feedback(fb,false,'Введи число.'); return; }
|
||||||
|
if(v===ans){ score++; feedback(fb,true,'✓ Верно: '+ans+'.'); } else feedback(fb,false,'✗ Нет. Правильно: '+ans+' (см. круги).');
|
||||||
|
document.getElementById('p4-s').textContent=score; i++; setTimeout(show,1600); }
|
||||||
|
document.getElementById('p4-go').addEventListener('click',go);
|
||||||
|
document.getElementById('p4-a').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); show();
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
var i=0,score=0,cur=null;
|
||||||
|
function gen(){ var c=_ri(2,8), a=c+_ri(2,10), b=c+_ri(2,10); cur={a:a,b:b,c:c,ans:a+b-c}; }
|
||||||
|
function show(){ if(i>=5){ document.getElementById('p4-fq').innerHTML='<b>Готово!</b> '+score+' / 5'; if(score>=4){addXp(15,'p4-iv2');bumpProgress('p4',30);}else if(score>=2){addXp(8,'p4-iv2');bumpProgress('p4',16);} return; }
|
||||||
|
gen(); document.getElementById('p4-fi').textContent=i+1; document.getElementById('p4-fq').innerHTML='$|A|='+cur.a+'$, $|B|='+cur.b+'$, $|A\\cap B|='+cur.c+'$. Найди $|A\\cup B|$.'; renderMath(document.getElementById('p4-fq'));
|
||||||
|
document.getElementById('p4-fa').value=''; document.getElementById('p4-ffb').style.display='none'; }
|
||||||
|
function go(){ if(i>=5)return; var fb=document.getElementById('p4-ffb'), v=parseInt(document.getElementById('p4-fa').value,10);
|
||||||
|
if(isNaN(v)){ feedback(fb,false,'Введи число.'); return; }
|
||||||
|
if(v===cur.ans){ score++; feedback(fb,true,'✓ Верно: '+cur.a+'+'+cur.b+'−'+cur.c+'='+cur.ans+'.'); } else feedback(fb,false,'✗ Нет. '+cur.a+'+'+cur.b+'−'+cur.c+'='+cur.ans+'.');
|
||||||
|
document.getElementById('p4-fs').textContent=score; i++; setTimeout(show,1500); }
|
||||||
|
document.getElementById('p4-fgo').addEventListener('click',go);
|
||||||
|
document.getElementById('p4-fa').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); show();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================== ФИНАЛ ГЛАВЫ — БОССЫ ===================== */
|
||||||
|
function buildFinal(){
|
||||||
|
var box=document.getElementById('final-body'); var h='';
|
||||||
|
h+=makeCard('theory','Финал главы 3','★','<p>Пять боссов проверят множества, операции и круги Эйлера. Победи всех!</p>');
|
||||||
|
h+='<div class="wg" id="fin"><div class="wg-header"><span class="wg-badge">Боссы</span><div class="wg-title">Сразись с главой 3</div></div>'
|
||||||
|
+'<div class="hp-boss"><div class="hp-boss-fill" id="fin-hp" style="width:100%"></div></div>'
|
||||||
|
+'<div class="score-display"><span>Босс <b id="fin-i">1</b> / 5</span><span>Побеждено: <b id="fin-s">0</b> / 5</span></div>'
|
||||||
|
+'<div id="fin-name" style="font-weight:800;color:#b91c1c;text-align:center;margin-bottom:6px"></div>'
|
||||||
|
+'<div id="fin-q" class="boss-q"></div>'
|
||||||
|
+'<div style="display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap"><input type="number" id="fin-a" class="tinp" style="width:120px;text-align:center" placeholder="ответ"><button class="btn primary" id="fin-go">Удар!</button></div>'
|
||||||
|
+'<div class="feedback" id="fin-fb"></div></div>';
|
||||||
|
h+=secNav('p4',null)+readBtn('final','Завершить главу 3 (+10 XP)');
|
||||||
|
box.innerHTML=h; renderMath(box);
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
var bosses=[
|
||||||
|
function(){ var A=_distinct(_ri(4,6),1,9); return {name:'Счётчик', q:'Сколько элементов в множестве $'+_setStr(A)+'$?', ans:A.length}; },
|
||||||
|
function(){ var A=_distinct(4,1,8),B=_distinct(4,1,8); return {name:'Пересечение', q:'$A='+_setStr(A)+'$, $B='+_setStr(B)+'$. Сколько элементов в $A\\cap B$?', ans:_inter(A,B).length}; },
|
||||||
|
function(){ var A=_distinct(4,1,8),B=_distinct(4,1,8); return {name:'Объединение', q:'$A='+_setStr(A)+'$, $B='+_setStr(B)+'$. Сколько элементов в $A\\cup B$?', ans:_union(A,B).length}; },
|
||||||
|
function(){ var c=_ri(2,7),a=c+_ri(2,9),b=c+_ri(2,9); return {name:'Эйлер', q:'$|A|='+a+'$, $|B|='+b+'$, $|A\\cap B|='+c+'$. Найди $|A\\cup B|$.', ans:a+b-c}; },
|
||||||
|
function(){ var c=_ri(3,7),aOnly=_ri(5,12),bOnly=_ri(5,12),nei=_ri(1,5); var a=aOnly+c,b=bOnly+c,T=a+b-c+nei; return {name:'Класс', q:'В классе '+T+' человек: '+a+' играют в шахматы, '+b+' — в шашки, '+c+' — в обе игры. Сколько не играют ни в одну?', ans:nei}; }
|
||||||
|
];
|
||||||
|
var i=0,score=0,cur=null,done=false;
|
||||||
|
function show(){ if(i>=5){ done=true; document.getElementById('fin-name').textContent=''; document.getElementById('fin-q').innerHTML=(score>=4?'<b>Победа!</b> Глава 3 пройдена. ':'<b>Бой окончен.</b> ')+'Побеждено боссов: '+score+' / 5.'; document.getElementById('fin-hp').style.width=(score>=4?0:40)+'%';
|
||||||
|
if(score>=4){ addXp(40,'final'); bumpProgress('final',100); if(window.confetti)try{confetti();}catch(e){} } else bumpProgress('final',60); return; }
|
||||||
|
cur=bosses[i](); document.getElementById('fin-i').textContent=i+1; document.getElementById('fin-s').textContent=score;
|
||||||
|
document.getElementById('fin-name').textContent='Босс '+(i+1)+': '+cur.name; document.getElementById('fin-hp').style.width=(100-i*20)+'%';
|
||||||
|
document.getElementById('fin-q').innerHTML=cur.q; renderMath(document.getElementById('fin-q'));
|
||||||
|
document.getElementById('fin-a').value=''; document.getElementById('fin-fb').style.display='none'; }
|
||||||
|
function go(){ if(done||i>=5)return; var fb=document.getElementById('fin-fb'), v=parseInt(document.getElementById('fin-a').value,10);
|
||||||
|
if(isNaN(v)){ feedback(fb,false,'Введи число.'); return; }
|
||||||
|
if(v===cur.ans){ score++; feedback(fb,true,'✓ Босс повержен! Ответ '+cur.ans+'.'); } else feedback(fb,false,'✗ Босс устоял. Верно: '+cur.ans+'.');
|
||||||
|
document.getElementById('fin-s').textContent=score; i++; setTimeout(show,1400); }
|
||||||
|
document.getElementById('fin-go').addEventListener('click',go);
|
||||||
|
document.getElementById('fin-a').addEventListener('keydown',function(e){ if(e.key==='Enter')go(); }); show();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================== ДАННЫЕ ===================== */
|
||||||
|
var SIDEBARS = {
|
||||||
|
p1:{ title:'Шпаргалка § 1', rows:[ ['Множество','набор различных элементов'], ['$\\in$','принадлежит'], ['$\\notin$','не принадлежит'], ['$\\varnothing$','пустое множество'], ['Равенство','одни и те же элементы'] ]},
|
||||||
|
p2:{ title:'Шпаргалка § 2', rows:[ ['Перечисление','$\\{2;4;6\\}$'], ['Свойство','$\\{x\\mid x$ чётное$\\}$'], ['Читается','«$x$ такие, что …»'] ]},
|
||||||
|
p3:{ title:'Шпаргалка § 3', rows:[ ['$A\\cap B$','общие элементы (и в A, и в B)'], ['$A\\cup B$','все элементы (хотя бы в одном)'], ['Пример','$\\{1;2;3;4\\}\\cap\\{3;4;5\\}=\\{3;4\\}$'] ]},
|
||||||
|
p4:{ title:'Шпаргалка § 4', rows:[ ['Круги Эйлера','наглядное изображение множеств'], ['Объединение','$|A\\cup B|=|A|+|B|-|A\\cap B|$'], ['Только A','$|A|-|A\\cap B|$'] ]},
|
||||||
|
final:{ title:'Финал главы 3', rows:[ ['5 боссов','множества, операции, круги Эйлера'], ['Победа','4 из 5 и больше'], ['Награда','+40 XP и достижение «Глава 3 пройдена»'] ]}
|
||||||
|
};
|
||||||
|
var TIPS = [
|
||||||
|
{ sec:'p1', html:'В множестве элементы <b>различны</b> и порядок не важен: $\\{1;2;3\\}=\\{3;2;1\\}$, а повтор не добавляет нового элемента.' },
|
||||||
|
{ sec:'p2', html:'Характеристическое свойство $\\{x\\mid \\ldots\\}$ читается «множество всех $x$ таких, что …». Проверь признак для каждого числа.' },
|
||||||
|
{ sec:'p3', html:'Пересечение — «И» (элемент в обоих). Объединение — «ИЛИ» (хотя бы в одном). При объединении общие элементы пишут один раз.' },
|
||||||
|
{ sec:'p4', html:'Начинай с пересечения (центр). «Только A» = всё A минус пересечение. Не забывай тех, кто вне обоих кругов.' }
|
||||||
|
];
|
||||||
|
var GLOSSARY = [
|
||||||
|
{ term:'множество', def:'Набор различных объектов (элементов), объединённых признаком.', sec:'p1', aliases:['множество','множества','множеств','множестве','множеству'] },
|
||||||
|
{ term:'элемент', def:'Объект, входящий в множество. $a\\in A$ — $a$ принадлежит $A$.', sec:'p1', aliases:['элемент','элемента','элементы','элементов'] },
|
||||||
|
{ term:'пустое множество', def:'Множество без элементов, обозначается $\\varnothing$.', sec:'p1', aliases:['пустое множество','пустого множества','пустым множеством'] },
|
||||||
|
{ term:'пересечение', def:'$A\\cap B$ — элементы, принадлежащие обоим множествам.', sec:'p3', aliases:['пересечение','пересечения','пересечении'] },
|
||||||
|
{ term:'объединение', def:'$A\\cup B$ — элементы, принадлежащие хотя бы одному множеству.', sec:'p3', aliases:['объединение','объединения','объединении'] },
|
||||||
|
{ term:'круги Эйлера', def:'Изображение множеств кругами; пересечение — общая область.', sec:'p4', aliases:['круги эйлера','кругов эйлера','кругами эйлера','круги Эйлера'] }
|
||||||
|
];
|
||||||
|
var BUILDERS = { p1:buildP1, p2:buildP2, p3:buildP3, p4:buildP4, final:buildFinal };
|
||||||
|
Object.assign(window.M6, { sidebars:SIDEBARS, tips:TIPS, glossary:GLOSSARY, builders:BUILDERS });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user