feat(math6): курсовой финал на хабе + звание «Математик 6 класса»

Капстоун-бой из 6 испытаний (по одному из каждой главы: десятичные,
проценты, множества, рациональные, координаты, геометрия) с HP-баром.
Победа 5/6 → +150 XP (LS.xp) + звание «Математик 6 класса» (зажигается
ачивка-strip, флаг localStorage math6_course_done). Тесты math6: 17/17.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-02 20:11:15 +03:00
parent 21853bdc27
commit 0bb48d3f04
2 changed files with 95 additions and 1 deletions
+92
View File
@@ -110,6 +110,32 @@ main{max-width:1100px;margin:0 auto;padding:32px 24px 60px}
.ach-sub{font-size:.85rem;color:var(--muted);margin-top:2px}
.ach-strip.lit .ach-title{color:#92400e}
/* COURSE FINAL */
.cf-wrap{background:var(--card);border:1.5px solid var(--border);border-radius:18px;overflow:hidden;margin-bottom:28px;box-shadow:var(--sh)}
.cf-head{padding:18px 22px;background:linear-gradient(135deg,#312e81,#4f46e5 55%,#7c3aed);color:#fff;cursor:pointer;display:flex;align-items:center;gap:14px;user-select:none}
.cf-head:hover{filter:brightness(1.07)}
.cf-hi{width:46px;height:46px;border-radius:12px;background:rgba(255,255,255,.2);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.cf-hi svg{width:26px;height:26px;stroke:#fff;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.cf-ht{flex:1;min-width:0}
.cf-tag{display:inline-block;padding:3px 9px;background:rgba(255,255,255,.24);border-radius:99px;font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:4px}
.cf-title{font-family:'Outfit',sans-serif;font-size:1.18rem;font-weight:800;line-height:1.25}
.cf-sub{font-size:.84rem;opacity:.9;margin-top:2px}
.cf-chev{flex-shrink:0;transition:transform .25s}
.cf-chev svg{width:24px;height:24px;stroke:#fff;fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.cf-wrap.open .cf-chev{transform:rotate(180deg)}
.cf-body{display:none;padding:22px}
.cf-wrap.open .cf-body{display:block}
.cf-hp{height:14px;background:rgba(220,38,38,.12);border-radius:9px;overflow:hidden;border:1px solid #fecaca;margin:6px 0 12px}
.cf-hp-fill{height:100%;background:linear-gradient(90deg,#dc2626,#f59e0b);border-radius:9px;transition:width .5s}
.cf-meta{display:flex;gap:16px;font-size:.9rem;color:var(--muted);margin-bottom:10px;font-weight:600}
.cf-name{font-weight:800;color:#4338ca;text-align:center;margin-bottom:6px}
.cf-q{font-size:1.05rem;line-height:1.5;padding:12px 14px;background:var(--pri-soft);border-radius:9px;margin-bottom:10px;text-align:center}
.cf-row{display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap}
.cf-inp{padding:9px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);width:140px;text-align:center;font-family:'Outfit',monospace;font-size:1rem}
.cf-btn{padding:9px 18px;border-radius:9px;background:linear-gradient(135deg,#4f46e5,#7c3aed);color:#fff;font-weight:700;border:none;cursor:pointer}
.cf-fb{margin-top:10px;padding:10px 14px;border-radius:9px;font-weight:600;font-size:.9rem;display:none}
.cf-fb.ok{display:block;background:#d1fae5;color:#065f46}
.cf-fb.bad{display:block;background:#fee2e2;color:#7f1d1d}
.foot{text-align:center;padding:24px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border)}
</style>
</head>
@@ -230,6 +256,26 @@ main{max-width:1100px;margin:0 auto;padding:32px 24px 60px}
</div>
<div class="cf-wrap" id="cf-wrap">
<div class="cf-head" id="cf-head">
<div class="cf-hi"><svg viewBox="0 0 24 24"><polygon points="12 2 15 9 22 9 17 14 19 22 12 17 5 22 7 14 2 9 9 9"/></svg></div>
<div class="cf-ht">
<span class="cf-tag">Курсовой финал</span>
<div class="cf-title">6 испытаний — по одному из каждой главы</div>
<div class="cf-sub">Победи 5 из 6 и получи звание «Математик 6 класса» (+150 XP)</div>
</div>
<div class="cf-chev"><svg viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"/></svg></div>
</div>
<div class="cf-body">
<div class="cf-hp"><div class="cf-hp-fill" id="cf-hp" style="width:100%"></div></div>
<div class="cf-meta"><span>Испытание <b id="cf-i">1</b> / 6</span><span>Пройдено: <b id="cf-s">0</b> / 6</span></div>
<div class="cf-name" id="cf-name"></div>
<div class="cf-q" id="cf-q"></div>
<div class="cf-row"><input type="text" id="cf-a" class="cf-inp" placeholder="ответ"><button class="cf-btn" id="cf-go">Ответить</button></div>
<div class="cf-fb" id="cf-fb"></div>
</div>
</div>
<div class="ach-strip" id="ach-strip">
<div class="ach-icon">
<svg viewBox="0 0 24 24"><path d="M6 9H4l-1-3h18l-1 3h-2M6 9l1 6h10l1-6M6 9h12"/><path d="M9 21h6M12 15v6"/></svg>
@@ -284,6 +330,52 @@ function loadProgress(){
}
if(document.readyState==='loading') document.addEventListener('DOMContentLoaded', loadProgress); else loadProgress();
window.addEventListener('focus', loadProgress);
/* ===================== КУРСОВОЙ ФИНАЛ ===================== */
(function(){
function _ri(a,b){ return a + Math.floor(Math.random()*(b-a+1)); }
function _pick(a){ return a[_ri(0,a.length-1)]; }
function comma(x){ return String(x).replace('.',','); }
var head=document.getElementById('cf-head'), wrap=document.getElementById('cf-wrap');
if(!head) return;
head.addEventListener('click', function(){ wrap.classList.toggle('open'); });
function litAch(text){ var s=document.getElementById('ach-strip'), sub=document.getElementById('ach-sub'); if(s)s.classList.add('lit'); if(sub)sub.textContent=text||'Звание «Математик 6 класса» получено!'; }
if(localStorage.getItem('math6_course_done')==='1') litAch();
var bosses=[
function(){ var p=_pick([[1.2,0.5,0.6],[2.5,0.4,1],[0.3,6,1.8],[0.2,0.4,0.08],[1.5,0.2,0.3]]); return {name:'Глава 1 · Десятичные дроби', q:'Вычисли '+comma(p[0])+' · '+comma(p[1])+'.', ans:p[2]}; },
function(){ var n=_pick([40,60,80,200]), m=_pick([10,20,25,5]); return {name:'Глава 2 · Проценты', q:'Найди '+m+'% от '+n+'.', ans:n*m/100}; },
function(){ var c=_ri(2,6), a=c+_ri(3,8), b=c+_ri(3,8); return {name:'Глава 3 · Множество', q:'|A|='+a+', |B|='+b+', |A∩B|='+c+'. Найди |AB|.', ans:a+b-c}; },
function(){ var a=_pick([-7,-4,-9,8,-12]), b=_pick([12,15,-3,-6,20]); return {name:'Глава 4 · Рациональные числа', q:'Вычисли '+a+' + ('+b+').', ans:a+b}; },
function(){ var k=_pick([2,3,4,5]), a=_pick([2,3,4]); return {name:'Глава 5 · Координаты', q:'Прямая y=kx проходит через ('+a+'; '+(k*a)+'). Найди k.', ans:k}; },
function(){ var r=_pick([2,3,4,5]); return {name:'Глава 6 · Геометрия', q:'Площадь круга S=πr² при r='+r+' (π=3,14).', ans:Math.round(3.14*r*r*100)/100}; }
];
var i=0, score=0, cur=null, done=false;
function show(){
if(i>=6){ done=true;
document.getElementById('cf-name').textContent='';
document.getElementById('cf-q').innerHTML=(score>=5?'<b>Победа!</b> Звание «Математик 6 класса» ваше!':'<b>Бой окончен.</b> Пройдено '+score+' / 6. Попробуйте ещё раз!');
document.getElementById('cf-hp').style.width=(score>=5?0:35)+'%';
document.querySelector('.cf-row').style.display='none';
if(score>=5){ if(localStorage.getItem('math6_course_done')!=='1'){ localStorage.setItem('math6_course_done','1'); if(window.LS&&window.LS.xp)window.LS.xp.add(150,'math6-course'); } litAch(); }
return; }
cur=bosses[i](); document.getElementById('cf-i').textContent=i+1; document.getElementById('cf-s').textContent=score;
document.getElementById('cf-name').textContent=cur.name;
document.getElementById('cf-hp').style.width=(100-i*100/6)+'%';
document.getElementById('cf-q').textContent=cur.q;
document.getElementById('cf-a').value=''; document.getElementById('cf-fb').style.display='none';
}
function go(){ if(done||i>=6)return; var fb=document.getElementById('cf-fb'), v=parseFloat(document.getElementById('cf-a').value.replace(',','.').trim());
if(isNaN(v)){ fb.className='cf-fb bad'; fb.textContent='Введите число.'; return; }
if(Math.abs(v-cur.ans)<0.011){ score++; fb.className='cf-fb ok'; fb.textContent='✓ Верно! Ответ '+comma(cur.ans)+'.'; }
else { fb.className='cf-fb bad'; fb.textContent='✗ Неверно. Правильно: '+comma(cur.ans)+'.'; }
document.getElementById('cf-s').textContent=score; i++; setTimeout(show,1300);
}
document.getElementById('cf-go').addEventListener('click', go);
document.getElementById('cf-a').addEventListener('keydown', function(e){ if(e.key==='Enter')go(); });
show();
})();
</script>
</body>