naider 1.24.4 → 1.24.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/naide.js +2 -2
- package/mcp/server.js +1 -1
- package/package.json +1 -1
- package/src/gen.js +217 -44
package/bin/naide.js
CHANGED
|
@@ -8,7 +8,7 @@ import { spawn } from 'child_process';
|
|
|
8
8
|
import { createRequire } from 'module';
|
|
9
9
|
const _require = createRequire(import.meta.url);
|
|
10
10
|
|
|
11
|
-
const NAIDE_VERSION = '1.24.
|
|
11
|
+
const NAIDE_VERSION = '1.24.5';
|
|
12
12
|
|
|
13
13
|
function crashReport(err, context = {}) {
|
|
14
14
|
const info = [
|
|
@@ -1093,7 +1093,7 @@ if (flags.help) {
|
|
|
1093
1093
|
~~ Keyword Cheat Sheet:
|
|
1094
1094
|
Intent server api rest bot cli page test database ai crud auth ws mail graphql game
|
|
1095
1095
|
Composite todo blog chat shop fullstack board
|
|
1096
|
-
Game tapping quiz memory snake typing reaction breakout
|
|
1096
|
+
Game tapping quiz memory snake typing reaction breakout dodge color pong
|
|
1097
1097
|
Platform discord slack telegram line
|
|
1098
1098
|
JP Intent サーバー 認証 ログイン 会員 CRUD 管理 データベース ボット テスト ページ ゲーム
|
|
1099
1099
|
JP Entity ユーザー 商品 記事 タスク 注文 コメント イベント 問い合わせ 掲示板 決済
|
package/mcp/server.js
CHANGED
|
@@ -192,7 +192,7 @@ async function handleMessage(msg) {
|
|
|
192
192
|
respond(msg.id, {
|
|
193
193
|
protocolVersion: '2024-11-05',
|
|
194
194
|
capabilities: { tools: {} },
|
|
195
|
-
serverInfo: { name: 'naide-mcp', version: '1.24.
|
|
195
|
+
serverInfo: { name: 'naide-mcp', version: '1.24.5' }
|
|
196
196
|
});
|
|
197
197
|
break;
|
|
198
198
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "naider",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.5",
|
|
4
4
|
"description": "NAIDE - Simpler than Python, compiles to 15 targets. AI-specialized language with 35+ built-in functions, syntax sugar (unless/until/repeat/swap/is/isnt), and 47 features. Targets: Node.js, Python, TypeScript, C, C++, Java, Go, Rust, PHP, Ruby, Kotlin, Swift, Dart, C#, Bun.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"exports": {
|
package/src/gen.js
CHANGED
|
@@ -100,6 +100,9 @@ const GAME_TYPES = {
|
|
|
100
100
|
reaction: ['reaction', 'reflex', 'リアクション', '反射', '反応'],
|
|
101
101
|
snake: ['snake', 'スネーク', 'ヘビ'],
|
|
102
102
|
breakout: ['breakout', 'brick', 'block', 'ブロック崩し', 'ブレイクアウト'],
|
|
103
|
+
dodge: ['dodge', 'avoid', 'escape', '避け', 'ドッジ', '回避'],
|
|
104
|
+
color: ['color', 'colour', 'stroop', 'カラー', '色'],
|
|
105
|
+
pong: ['pong', 'paddle', 'ポン', 'パドル', 'ピンポン'],
|
|
103
106
|
};
|
|
104
107
|
|
|
105
108
|
const NOISE = new Set([
|
|
@@ -111,6 +114,7 @@ const NOISE = new Set([
|
|
|
111
114
|
'game', 'play', 'player', 'score', 'level', 'arcade', 'highscore',
|
|
112
115
|
'tapping', 'tap', 'clicker', 'click', 'quiz', 'trivia', 'memory', 'matching', 'typing',
|
|
113
116
|
'reaction', 'reflex', 'snake', 'breakout', 'brick', 'block', 'pong', 'puzzle',
|
|
117
|
+
'dodge', 'avoid', 'escape', 'color', 'colour', 'stroop', 'paddle',
|
|
114
118
|
'の', 'を', 'に', 'で', 'は', 'が', 'と', 'も', 'から', 'まで', 'より', 'って',
|
|
115
119
|
'な', 'ような', 'みたいな', 'っぽい', 'ような感じ',
|
|
116
120
|
'作って', '作る', '作成', '生成', '追加', '実装', 'ください', 'して', 'つけて',
|
|
@@ -1791,6 +1795,9 @@ const GAME_PALETTES = {
|
|
|
1791
1795
|
typing: { bg:'#0a192f', sf:'#112240', pr:'#64FFDA', sc:'#00B4D8', ac:'#CCD6F6', tx:'#CCD6F6', mt:'#5c7a8a' },
|
|
1792
1796
|
reaction: { bg:'#18181b', sf:'#27272a', pr:'#F87171', sc:'#EF4444', ac:'#4ADE80', tx:'#fafafa', mt:'#71717a' },
|
|
1793
1797
|
breakout: { bg:'#0c0a1d', sf:'#1c1833', pr:'#A78BFA', sc:'#8B5CF6', ac:'#F59E0B', tx:'#f0f0ff', mt:'#8f85a3' },
|
|
1798
|
+
dodge: { bg:'#1a0005', sf:'#2d0a12', pr:'#FF4757', sc:'#FF6B81', ac:'#2ED573', tx:'#fff', mt:'#a88a8f' },
|
|
1799
|
+
color: { bg:'#0a1628', sf:'#132240', pr:'#FF6348', sc:'#FFA502', ac:'#7BED9F', tx:'#f5f6fa', mt:'#778ca3' },
|
|
1800
|
+
pong: { bg:'#0a0a0a', sf:'#1a1a2e', pr:'#00FF87', sc:'#60EFFF', ac:'#FFE66D', tx:'#f0f0f0', mt:'#666680' },
|
|
1794
1801
|
};
|
|
1795
1802
|
|
|
1796
1803
|
function extractGameOpts(raw) {
|
|
@@ -1812,7 +1819,7 @@ function cssVars(p) {
|
|
|
1812
1819
|
function cssBase(p) {
|
|
1813
1820
|
return `*{margin:0;padding:0;box-sizing:border-box}
|
|
1814
1821
|
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;background:${p.bg};color:${p.tx};min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;overflow:hidden;user-select:none;-webkit-user-select:none;-webkit-tap-highlight-color:transparent}
|
|
1815
|
-
.ct{width:min(420px,92vw);text-align:center}
|
|
1822
|
+
.ct{width:min(420px,92vw);text-align:center;position:relative}
|
|
1816
1823
|
h1{font-size:1.8rem;margin-bottom:.5rem;background:linear-gradient(135deg,${p.pr},${p.sc});-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
|
|
1817
1824
|
.sc{font-size:3rem;font-weight:800;margin:.5rem 0;font-variant-numeric:tabular-nums}
|
|
1818
1825
|
.btn{display:inline-block;padding:.75rem 2rem;border:none;border-radius:12px;font-size:1.1rem;font-weight:600;cursor:pointer;transition:transform .1s,box-shadow .2s;color:#fff;background:linear-gradient(135deg,${p.pr},${p.sc})}
|
|
@@ -1820,8 +1827,12 @@ h1{font-size:1.8rem;margin-bottom:.5rem;background:linear-gradient(135deg,${p.pr
|
|
|
1820
1827
|
.st{display:flex;gap:1.5rem;justify-content:center;margin:.75rem 0;font-size:.9rem;color:${p.mt}}
|
|
1821
1828
|
.st span{display:flex;flex-direction:column;align-items:center;gap:.2rem}.st b{color:${p.tx};font-size:1.1rem}
|
|
1822
1829
|
.hd{display:none!important}.hi{color:${p.ac}}
|
|
1830
|
+
.cdwn{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:6rem;font-weight:900;color:${p.pr};z-index:99;pointer-events:none;animation:cdPop .6s ease-out}
|
|
1831
|
+
@keyframes cdPop{0%{transform:scale(3);opacity:0}30%{opacity:1}100%{transform:scale(1);opacity:0}}
|
|
1823
1832
|
@keyframes pop{0%{transform:scale(1)}50%{transform:scale(1.15)}100%{transform:scale(1)}}
|
|
1824
|
-
@keyframes fadeUp{0%{opacity:1;transform:translateY(0)}100%{opacity:0;transform:translateY(-60px)}}
|
|
1833
|
+
@keyframes fadeUp{0%{opacity:1;transform:translateY(0)}100%{opacity:0;transform:translateY(-60px)}}
|
|
1834
|
+
@keyframes shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-4px)}75%{transform:translateX(4px)}}
|
|
1835
|
+
.pcv{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:90}`;
|
|
1825
1836
|
}
|
|
1826
1837
|
|
|
1827
1838
|
function cssTimerBar(p) {
|
|
@@ -1829,6 +1840,31 @@ function cssTimerBar(p) {
|
|
|
1829
1840
|
.bar .fl{height:100%;background:linear-gradient(90deg,${p.ac},${p.pr});transition:width .1s linear;border-radius:3px}`;
|
|
1830
1841
|
}
|
|
1831
1842
|
|
|
1843
|
+
function jsSfx() {
|
|
1844
|
+
return `const _ax=new(window.AudioContext||window.webkitAudioContext)();
|
|
1845
|
+
function sfx(freq,type,dur,vol){const o=_ax.createOscillator(),g=_ax.createGain();o.type=type||'sine';o.frequency.value=freq;g.gain.value=vol||.15;g.gain.exponentialRampToValueAtTime(.001,_ax.currentTime+(dur||.15));o.connect(g);g.connect(_ax.destination);o.start();o.stop(_ax.currentTime+(dur||.15))}
|
|
1846
|
+
function sfxTap(){sfx(800,'square',.06,.12)}
|
|
1847
|
+
function sfxHit(){sfx(520,'sine',.1,.15)}
|
|
1848
|
+
function sfxScore(){sfx(660,'sine',.08,.12);setTimeout(()=>sfx(880,'sine',.08,.12),60)}
|
|
1849
|
+
function sfxWin(){[523,659,784,1047].forEach((f,i)=>setTimeout(()=>sfx(f,'sine',.2,.1),i*100))}
|
|
1850
|
+
function sfxFail(){sfx(200,'sawtooth',.3,.1)}
|
|
1851
|
+
function sfxTick(){sfx(1200,'sine',.03,.08)}
|
|
1852
|
+
function sfxCombo(){sfx(1000,'square',.05,.1);setTimeout(()=>sfx(1400,'square',.05,.1),50)}`;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
function jsParticles(color1, color2) {
|
|
1856
|
+
return `const _pcv=document.createElement('canvas');_pcv.className='pcv';document.body.appendChild(_pcv);const _pctx=_pcv.getContext('2d');
|
|
1857
|
+
let _pts=[];function _pResize(){_pcv.width=innerWidth;_pcv.height=innerHeight}window.addEventListener('resize',_pResize);_pResize();
|
|
1858
|
+
function emitParticles(x,y,count,c1,c2){for(let i=0;i<(count||12);i++){const a=Math.random()*Math.PI*2,sp=2+Math.random()*4;_pts.push({x,y,vx:Math.cos(a)*sp,vy:Math.sin(a)*sp-2,life:1,sz:2+Math.random()*4,c:Math.random()>.5?(c1||'${color1}'):(c2||'${color2}')})}}
|
|
1859
|
+
function _pTick(){_pctx.clearRect(0,0,_pcv.width,_pcv.height);_pts=_pts.filter(p=>{p.x+=p.vx;p.y+=p.vy;p.vy+=.12;p.life-=.025;if(p.life<=0)return false;_pctx.globalAlpha=p.life;_pctx.fillStyle=p.c;_pctx.beginPath();_pctx.arc(p.x,p.y,p.sz*p.life,0,Math.PI*2);_pctx.fill();return true});_pctx.globalAlpha=1;requestAnimationFrame(_pTick)}_pTick();
|
|
1860
|
+
function burstAt(el,n){const r=el.getBoundingClientRect();emitParticles(r.left+r.width/2,r.top+r.height/2,n||16)}
|
|
1861
|
+
function shakeEl(el){el.style.animation='shake .2s';setTimeout(()=>el.style.animation='',200)}`;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
function jsCountdown(cb) {
|
|
1865
|
+
return `function countdown(fn){let n=3;const t=()=>{if(n>0){const d=document.createElement('div');d.className='cdwn';d.textContent=n;document.body.appendChild(d);sfxTick();setTimeout(()=>d.remove(),600);n--;setTimeout(t,600)}else{const d=document.createElement('div');d.className='cdwn';d.textContent='GO!';d.style.color='var(--ac)';document.body.appendChild(d);sfxScore();setTimeout(()=>d.remove(),500);fn()}};t()}`;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1832
1868
|
function jsCore(storageKey) {
|
|
1833
1869
|
return `const $=id=>document.getElementById(id);
|
|
1834
1870
|
let best=+(localStorage.getItem('${storageKey}')||0);
|
|
@@ -1867,7 +1903,7 @@ function buildTapping(title, opts) {
|
|
|
1867
1903
|
const dur = opts.duration || 10;
|
|
1868
1904
|
|
|
1869
1905
|
const css = [cssVars(p), cssBase(p), cssTimerBar(p),
|
|
1870
|
-
`.tap{width:min(300px,75vw);height:min(300px,75vw);border-radius:50%;background:radial-gradient(circle at 35% 35%,${p.pr},${p.sc});display:flex;align-items:center;justify-content:center;margin:1.5rem auto;cursor:pointer;position:relative;font-size:3.5rem;font-weight:900;transition:box-shadow .15s;letter-spacing:-.05em}`,
|
|
1906
|
+
`.tap{width:min(300px,75vw);height:min(300px,75vw);border-radius:50%;background:radial-gradient(circle at 35% 35%,${p.pr},${p.sc});display:flex;align-items:center;justify-content:center;margin:1.5rem auto;cursor:pointer;position:relative;font-size:3.5rem;font-weight:900;transition:box-shadow .15s;letter-spacing:-.05em;overflow:hidden}`,
|
|
1871
1907
|
`.tap:active{box-shadow:0 0 60px ${p.pr}88}`,
|
|
1872
1908
|
`.tap.on{animation:pulse 1s infinite}`,
|
|
1873
1909
|
`@keyframes pulse{0%,100%{box-shadow:0 0 0 0 ${p.pr}66}50%{box-shadow:0 0 0 20px ${p.pr}00}}`,
|
|
@@ -1890,12 +1926,13 @@ function buildTapping(title, opts) {
|
|
|
1890
1926
|
<div id="msg" style="font-size:1.2rem;line-height:2"></div>
|
|
1891
1927
|
<button class="btn" style="margin-top:1rem" onclick="go()">Again</button></div>`;
|
|
1892
1928
|
|
|
1893
|
-
const js = [jsCore('tap_best'),
|
|
1929
|
+
const js = [jsCore('tap_best'), jsSfx(), jsParticles(p.pr, p.ac), jsCountdown(),
|
|
1894
1930
|
`const DUR=${dur*1000};let taps,t0,raf,combo,lh;`,
|
|
1895
|
-
`function go(){
|
|
1931
|
+
`function go(){show('play');countdown(startGame)}`,
|
|
1932
|
+
`function startGame(){taps=0;combo=1;lh=0;t0=Date.now();$('cnt').textContent='0';$('cmb').textContent='x1';$('bar').style.width='100%';raf=requestAnimationFrame(tick)}`,
|
|
1896
1933
|
`function tick(){const e=Date.now()-t0,pc=Math.max(0,1-e/DUR)*100;$('bar').style.width=pc+'%';$('tps').textContent=(taps/Math.max(.1,e/1000)).toFixed(1);if(e>=DUR)return end();raf=requestAnimationFrame(tick)}`,
|
|
1897
|
-
`function hit(ev){ev.preventDefault();if(Date.now()-t0>DUR)return;taps++;const now=Date.now(),gap=now-lh;combo=gap<200?Math.min(combo+1,99):gap>500?1:combo;lh=now;$('cnt').textContent=taps;$('cmb').textContent='x'+combo;$('cnt').style.animation='none';void $('cnt').offsetWidth;$('cnt').style.animation='pop .15s';const el=$('tap'),rc=el.getBoundingClientRect(),cx=(ev.touches?ev.touches[0].clientX:ev.clientX)-rc.left,cy=(ev.touches?ev.touches[0].clientY:ev.clientY)-rc.top;const r=document.createElement('div');r.className='rp';r.style.cssText='left:'+(cx-100)+'px;top:'+(cy-100)+'px';el.appendChild(r);setTimeout(()=>r.remove(),500);if(combo>=3){const c=document.createElement('div');c.className='cb';c.textContent='x'+combo;c.style.cssText='left:'+Math.random()*60+20+'%;top:'+Math.random()*30+20+'%';document.body.appendChild(c);setTimeout(()=>c.remove(),600)}}`,
|
|
1898
|
-
`function end(){cancelAnimationFrame(raf);const tps=(taps/(DUR/1000)).toFixed(1);const nb=saveBest(taps);$('last').textContent=taps;$('fin').textContent=taps;const r=taps>=100?'Insane!':taps>=70?'Amazing!':taps>=50?'Great!':taps>=30?'Nice!':'Keep trying!';$('msg').innerHTML=r+'<br>'+tps+' taps/sec'+(nb?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
1934
|
+
`function hit(ev){ev.preventDefault();if(!t0||Date.now()-t0>DUR)return;taps++;sfxTap();const now=Date.now(),gap=now-lh;combo=gap<200?Math.min(combo+1,99):gap>500?1:combo;lh=now;$('cnt').textContent=taps;$('cmb').textContent='x'+combo;$('cnt').style.animation='none';void $('cnt').offsetWidth;$('cnt').style.animation='pop .15s';const el=$('tap'),rc=el.getBoundingClientRect(),cx=(ev.touches?ev.touches[0].clientX:ev.clientX)-rc.left,cy=(ev.touches?ev.touches[0].clientY:ev.clientY)-rc.top;const r=document.createElement('div');r.className='rp';r.style.cssText='left:'+(cx-100)+'px;top:'+(cy-100)+'px';el.appendChild(r);setTimeout(()=>r.remove(),500);emitParticles((ev.touches?ev.touches[0].clientX:ev.clientX),(ev.touches?ev.touches[0].clientY:ev.clientY),combo>=3?10:4);if(combo>=3){sfxCombo();shakeEl($('cnt'));const c=document.createElement('div');c.className='cb';c.textContent='x'+combo;c.style.cssText='left:'+Math.random()*60+20+'%;top:'+Math.random()*30+20+'%';document.body.appendChild(c);setTimeout(()=>c.remove(),600)}}`,
|
|
1935
|
+
`function end(){cancelAnimationFrame(raf);const tps=(taps/(DUR/1000)).toFixed(1);const nb=saveBest(taps);$('last').textContent=taps;$('fin').textContent=taps;if(nb&&taps>0){sfxWin();burstAt($('fin'),30)}else sfxFail();const r=taps>=100?'Insane!':taps>=70?'Amazing!':taps>=50?'Great!':taps>=30?'Nice!':'Keep trying!';$('msg').innerHTML=r+'<br>'+tps+' taps/sec'+(nb?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
1899
1936
|
].join('\n');
|
|
1900
1937
|
|
|
1901
1938
|
return htmlWrap(title, css, html, js);
|
|
@@ -1934,15 +1971,15 @@ function buildQuiz(title, opts) {
|
|
|
1934
1971
|
<div id="msg" style="font-size:1.1rem;margin:1rem 0"></div>
|
|
1935
1972
|
<button class="btn" onclick="go()">Play Again</button></div>`;
|
|
1936
1973
|
|
|
1937
|
-
const js = [jsCore('quiz_best'),
|
|
1938
|
-
`const QS=[{q:"What does HTML stand for?",a:["HyperText Markup Language","High Tech Modern Language","Home Tool Markup Language","Hyper Transfer Markup Language"],c:0},{q:"Which planet is closest to the Sun?",a:["Mercury","Venus","Mars","Earth"],c:0},{q:"What is the largest ocean on Earth?",a:["Pacific","Atlantic","Indian","Arctic"],c:0},{q:"In JavaScript, which keyword declares a constant?",a:["const","let","var","static"],c:0},{q:"What year did the World Wide Web go public?",a:["1991","1989","1995","2000"],c:0},{q:"What is the chemical symbol for gold?",a:["Au","Ag","Go","Gd"],c:0},{q:"How many bits are in a byte?",a:["8","4","16","32"],c:0},{q:"What does CSS stand for?",a:["Cascading Style Sheets","Computer Style Sheets","Creative Style System","Coded Style Sheets"],c:0},{q:"What is the speed of light (approx km/s)?",a:["300,000","150,000","500,000","1,000,000"],c:0},{q:"Which data structure uses FIFO?",a:["Queue","Stack","Tree","Graph"],c:0},{q:"What is the square root of 144?",a:["12","14","10","16"],c:0},{q:"Who painted the Mona Lisa?",a:["Da Vinci","Picasso","Van Gogh","Monet"],c:0},{q:"What gas do plants absorb?",a:["CO2","O2","N2","H2"],c:0},{q:"Which protocol is used for secure browsing?",a:["HTTPS","FTP","SMTP","SSH"],c:0},{q:"What is 2^10?",a:["1024","512","2048","4096"],c:0}];`,
|
|
1939
|
-
`const PER_Q=${perQ};let cur,score,pool,tmr,tl;`,
|
|
1974
|
+
const js = [jsCore('quiz_best'), jsSfx(), jsParticles(p.pr, p.ac),
|
|
1975
|
+
`const QS=[{q:"What does HTML stand for?",a:["HyperText Markup Language","High Tech Modern Language","Home Tool Markup Language","Hyper Transfer Markup Language"],c:0},{q:"Which planet is closest to the Sun?",a:["Mercury","Venus","Mars","Earth"],c:0},{q:"What is the largest ocean on Earth?",a:["Pacific","Atlantic","Indian","Arctic"],c:0},{q:"In JavaScript, which keyword declares a constant?",a:["const","let","var","static"],c:0},{q:"What year did the World Wide Web go public?",a:["1991","1989","1995","2000"],c:0},{q:"What is the chemical symbol for gold?",a:["Au","Ag","Go","Gd"],c:0},{q:"How many bits are in a byte?",a:["8","4","16","32"],c:0},{q:"What does CSS stand for?",a:["Cascading Style Sheets","Computer Style Sheets","Creative Style System","Coded Style Sheets"],c:0},{q:"What is the speed of light (approx km/s)?",a:["300,000","150,000","500,000","1,000,000"],c:0},{q:"Which data structure uses FIFO?",a:["Queue","Stack","Tree","Graph"],c:0},{q:"What is the square root of 144?",a:["12","14","10","16"],c:0},{q:"Who painted the Mona Lisa?",a:["Da Vinci","Picasso","Van Gogh","Monet"],c:0},{q:"What gas do plants absorb?",a:["CO2","O2","N2","H2"],c:0},{q:"Which protocol is used for secure browsing?",a:["HTTPS","FTP","SMTP","SSH"],c:0},{q:"What is 2^10?",a:["1024","512","2048","4096"],c:0},{q:"What is the binary representation of 10?",a:["1010","1100","1001","0110"],c:0},{q:"Which HTTP status code means Not Found?",a:["404","500","403","301"],c:0},{q:"What is the capital of Japan?",a:["Tokyo","Osaka","Kyoto","Nagoya"],c:0},{q:"How many continents are there?",a:["7","6","5","8"],c:0},{q:"What does API stand for?",a:["Application Programming Interface","Advanced Program Integration","Application Process Interface","Automated Programming Interface"],c:0}];`,
|
|
1976
|
+
`const PER_Q=${perQ};let cur,score,pool,tmr,tl,streak=0;`,
|
|
1940
1977
|
`function shuf(a){for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]]}return a}`,
|
|
1941
|
-
`function go(){pool=shuf([...QS]).slice(0,10);cur=0;score=0;$('dots').innerHTML=pool.map((_,i)=>'<div class="dot" id="d'+i+'"></div>').join('');show('play');next()}`,
|
|
1942
|
-
`function next(){if(cur>=pool.length)return end();$('d'+cur).classList.add('now');const q=pool[cur],mx=q.a.map((t,i)=>({t,ok:i===q.c}));shuf(mx);$('qtext').textContent=q.q;const ch=$('choices');ch.innerHTML='';mx.forEach(m=>{const d=document.createElement('div');d.textContent=m.t;d.onclick=()=>pick(d,m.ok,ch);ch.appendChild(d)});tl=PER_Q;$('timer').textContent=tl;clearInterval(tmr);tmr=setInterval(()=>{tl--;$('timer').textContent=Math.max(0,tl);if(tl<=0){clearInterval(tmr);timeout()}},1000)}`,
|
|
1943
|
-
`function pick(el,ok,ch){clearInterval(tmr);ch.querySelectorAll('div').forEach(c=>{c.onclick=null;if(c===el)c.classList.add(ok?'ok':'no');else if(!ok){const t=pool[cur].a[pool[cur].c];if(c.textContent===t)c.classList.add('ok')}});$('d'+cur).classList.remove('now');$('d'+cur).classList.add(ok?'pass':'fail');if(ok)score++;cur++;setTimeout(next,800)}`,
|
|
1944
|
-
`function timeout(){const ch=$('choices');ch.querySelectorAll('div').forEach(c=>{c.onclick=null;if(c.textContent===pool[cur].a[pool[cur].c])c.classList.add('ok')});$('d'+cur).classList.remove('now');$('d'+cur).classList.add('fail');cur++;setTimeout(next,800)}`,
|
|
1945
|
-
`function end(){clearInterval(tmr);const nb=saveBest(score);$('last').textContent=score;$('fin').textContent=score+'/'+pool.length;const pc=Math.round(score/pool.length*100);const r=pc===100?'Perfect!':pc>=80?'Excellent!':pc>=60?'Good!':pc>=40?'Not bad!':'Study more!';$('msg').innerHTML=r+'<br>'+pc+'% correct'+(nb?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
1978
|
+
`function go(){pool=shuf([...QS]).slice(0,10);cur=0;score=0;streak=0;$('dots').innerHTML=pool.map((_,i)=>'<div class="dot" id="d'+i+'"></div>').join('');show('play');next()}`,
|
|
1979
|
+
`function next(){if(cur>=pool.length)return end();$('d'+cur).classList.add('now');const q=pool[cur],mx=q.a.map((t,i)=>({t,ok:i===q.c}));shuf(mx);$('qtext').textContent=q.q;const ch=$('choices');ch.innerHTML='';mx.forEach(m=>{const d=document.createElement('div');d.textContent=m.t;d.onclick=()=>pick(d,m.ok,ch);ch.appendChild(d)});tl=PER_Q;$('timer').textContent=tl;clearInterval(tmr);tmr=setInterval(()=>{tl--;$('timer').textContent=Math.max(0,tl);if(tl<=3&&tl>0)sfxTick();if(tl<=0){clearInterval(tmr);timeout()}},1000)}`,
|
|
1980
|
+
`function pick(el,ok,ch){clearInterval(tmr);ch.querySelectorAll('div').forEach(c=>{c.onclick=null;if(c===el)c.classList.add(ok?'ok':'no');else if(!ok){const t=pool[cur].a[pool[cur].c];if(c.textContent===t)c.classList.add('ok')}});$('d'+cur).classList.remove('now');$('d'+cur).classList.add(ok?'pass':'fail');if(ok){score++;streak++;sfxScore();burstAt(el,streak>=3?20:8)}else{streak=0;sfxFail();shakeEl($('qtext'))}cur++;setTimeout(next,800)}`,
|
|
1981
|
+
`function timeout(){sfxFail();const ch=$('choices');ch.querySelectorAll('div').forEach(c=>{c.onclick=null;if(c.textContent===pool[cur].a[pool[cur].c])c.classList.add('ok')});$('d'+cur).classList.remove('now');$('d'+cur).classList.add('fail');streak=0;cur++;setTimeout(next,800)}`,
|
|
1982
|
+
`function end(){clearInterval(tmr);const nb=saveBest(score);$('last').textContent=score;$('fin').textContent=score+'/'+pool.length;if(nb&&score>0){sfxWin();burstAt($('fin'),30)}const pc=Math.round(score/pool.length*100);const r=pc===100?'Perfect!':pc>=80?'Excellent!':pc>=60?'Good!':pc>=40?'Not bad!':'Study more!';$('msg').innerHTML=r+'<br>'+pc+'% correct'+(nb?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
1946
1983
|
].join('\n');
|
|
1947
1984
|
|
|
1948
1985
|
return htmlWrap(title, css, html, js);
|
|
@@ -1980,13 +2017,13 @@ function buildMemory(title, opts) {
|
|
|
1980
2017
|
<div id="msg" style="font-size:1.1rem;margin:1rem 0"></div>
|
|
1981
2018
|
<button class="btn" onclick="go()">Play Again</button></div>`;
|
|
1982
2019
|
|
|
1983
|
-
const js = [jsCore('mem_best'),
|
|
2020
|
+
const js = [jsCore('mem_best'), jsSfx(), jsParticles(p.pr, p.ac),
|
|
1984
2021
|
`const EM=['🎮','🎯','🚀','💎','🔥','⚡','🌟','🎵','🎨','🏆','🍕','🌈','🦊','🐙','🎃','🍀','🎲','🌸'];`,
|
|
1985
|
-
`const PAIRS=${pairs};let cards,flp,done,moves,tmr,t0;`,
|
|
2022
|
+
`const PAIRS=${pairs};let cards,flp,done,moves,tmr,t0,cstrk=0;`,
|
|
1986
2023
|
`function shuf(a){for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]]}return a}`,
|
|
1987
|
-
`function go(){const pick=shuf([...EM]).slice(0,PAIRS);cards=shuf([...pick,...pick]);flp=[];done=0;moves=0;$('mv').textContent=0;$('pr').textContent='0/'+PAIRS;const g=$('grid');g.innerHTML='';cards.forEach((em,i)=>{const d=document.createElement('div');d.className='card';d.innerHTML='<div class="inn"><div class="fr">?</div><div class="bk">'+em+'</div></div>';d.onclick=()=>flip(d,i);g.appendChild(d)});t0=Date.now();tmr=setInterval(()=>{$('tm').textContent=Math.floor((Date.now()-t0)/1000)+'s'},200);show('play')}`,
|
|
1988
|
-
`function flip(el,i){if(flp.length>=2||el.classList.contains('flip')||el.classList.contains('done'))return;el.classList.add('flip');flp.push({el,i,v:cards[i]});if(flp.length===2){moves++;$('mv').textContent=moves;const[a,b]=flp;if(a.v===b.v){a.el.classList.add('done');b.el.classList.add('done');done++;$('pr').textContent=done+'/'+PAIRS;flp=[];if(done===PAIRS)end()}else{setTimeout(()=>{a.el.classList.remove('flip');b.el.classList.remove('flip');flp=[]},700)}}}`,
|
|
1989
|
-
`function end(){clearInterval(tmr);const sec=Math.floor((Date.now()-t0)/1000);const nb=saveBest(moves);$('fin').textContent=moves;const r=moves<=PAIRS+2?'Perfect memory!':moves<=PAIRS*2?'Great!':moves<=PAIRS*3?'Good!':'Keep practicing!';$('msg').innerHTML=r+'<br>Completed in '+sec+'s'+(nb?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
2024
|
+
`function go(){const pick=shuf([...EM]).slice(0,PAIRS);cards=shuf([...pick,...pick]);flp=[];done=0;moves=0;cstrk=0;$('mv').textContent=0;$('pr').textContent='0/'+PAIRS;const g=$('grid');g.innerHTML='';cards.forEach((em,i)=>{const d=document.createElement('div');d.className='card';d.innerHTML='<div class="inn"><div class="fr">?</div><div class="bk">'+em+'</div></div>';d.onclick=()=>flip(d,i);g.appendChild(d)});t0=Date.now();tmr=setInterval(()=>{$('tm').textContent=Math.floor((Date.now()-t0)/1000)+'s'},200);show('play')}`,
|
|
2025
|
+
`function flip(el,i){if(flp.length>=2||el.classList.contains('flip')||el.classList.contains('done'))return;sfxTap();el.classList.add('flip');flp.push({el,i,v:cards[i]});if(flp.length===2){moves++;$('mv').textContent=moves;const[a,b]=flp;if(a.v===b.v){cstrk++;if(cstrk>=2)sfxCombo();else sfxScore();a.el.classList.add('done');b.el.classList.add('done');burstAt(a.el,cstrk>=2?16:8);burstAt(b.el,cstrk>=2?16:8);done++;$('pr').textContent=done+'/'+PAIRS;flp=[];if(done===PAIRS)end()}else{cstrk=0;sfxHit();setTimeout(()=>{a.el.classList.remove('flip');b.el.classList.remove('flip');flp=[]},700)}}}`,
|
|
2026
|
+
`function end(){clearInterval(tmr);const sec=Math.floor((Date.now()-t0)/1000);const nb=saveBest(moves);if(nb)sfxWin();$('fin').textContent=moves;burstAt($('fin'),30);const r=moves<=PAIRS+2?'Perfect memory!':moves<=PAIRS*2?'Great!':moves<=PAIRS*3?'Good!':'Keep practicing!';$('msg').innerHTML=r+'<br>Completed in '+sec+'s'+(nb?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
1990
2027
|
].join('\n');
|
|
1991
2028
|
|
|
1992
2029
|
return htmlWrap(title, css, html, js);
|
|
@@ -2021,16 +2058,17 @@ function buildSnake(title, opts) {
|
|
|
2021
2058
|
<div id="msg" style="font-size:1.1rem;margin:1rem 0"></div>
|
|
2022
2059
|
<button class="btn" onclick="go()">Play Again</button></div>`;
|
|
2023
2060
|
|
|
2024
|
-
const js = [jsCore('snake_best'),
|
|
2025
|
-
`const GS=${S},GW=${W},cv=$('cv'),ctx=cv.getContext('2d');`,
|
|
2026
|
-
`let sn,dir,nd,food,sc,lp;$('hi').textContent=best;`,
|
|
2061
|
+
const js = [jsCore('snake_best'), jsSfx(), jsParticles(p.pr, p.ac), jsCountdown(),
|
|
2062
|
+
`const GS=${S},GW=${W},cv=$('cv'),ctx=cv.getContext('2d'),BASE_SPD=${spd};`,
|
|
2063
|
+
`let sn,dir,nd,food,sc,lp,spd;$('hi').textContent=best;`,
|
|
2027
2064
|
`function rnd(){let p;do{p={x:Math.floor(Math.random()*GW),y:Math.floor(Math.random()*GW)}}while(sn.some(s=>s.x===p.x&&s.y===p.y));return p}`,
|
|
2028
|
-
`function go(){
|
|
2065
|
+
`function go(){show('play');countdown(startGame)}`,
|
|
2066
|
+
`function startGame(){sn=[{x:8,y:8},{x:7,y:8},{x:6,y:8}];dir={x:1,y:0};nd={x:1,y:0};food=rnd();sc=0;spd=BASE_SPD;$('score').textContent=0;clearInterval(lp);lp=setInterval(tick,spd)}`,
|
|
2029
2067
|
`function sd(x,y){if(x===-dir.x&&y===-dir.y)return;nd={x,y}}`,
|
|
2030
|
-
`function tick(){dir=nd;const h={x:sn[0].x+dir.x,y:sn[0].y+dir.y};if(h.x<0||h.x>=GW||h.y<0||h.y>=GW||sn.some(s=>s.x===h.x&&s.y===h.y))return end();sn.unshift(h);if(h.x===food.x&&h.y===food.y){sc
|
|
2031
|
-
`function draw(){ctx.fillStyle='${p.sf}';ctx.fillRect(0,0,GW*GS,GW*GS);ctx.fillStyle='${p.ac}';ctx.beginPath();ctx.arc(food.x*GS+GS/2,food.y*GS+GS/2,GS/2-2,0,Math.PI*2);ctx.fill();sn.forEach((s,i)=>{const t=i/sn.length;ctx.fillStyle=i===0?'${p.pr}':'hsl('+(140+t*40)+',70%,'+(55-t*20)+'%)';ctx.beginPath();ctx.roundRect(s.x*GS+1,s.y*GS+1,GS-2,GS-2,4);ctx.fill()})}`,
|
|
2032
|
-
`function end(){clearInterval(lp);const nb=saveBest(sc);$('hi').textContent=best;$('fin').textContent=sc;const r=sc>=30?'Legend!':sc>=20?'Amazing!':sc>=10?'Great!':sc>=5?'Good!':'Try again!';$('msg').innerHTML=r+(nb&&sc>0?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
2033
|
-
`document.addEventListener('keydown',e=>{if(e.key==='ArrowUp')sd(0,-1)
|
|
2068
|
+
`function tick(){dir=nd;const h={x:sn[0].x+dir.x,y:sn[0].y+dir.y};if(h.x<0||h.x>=GW||h.y<0||h.y>=GW||sn.some(s=>s.x===h.x&&s.y===h.y))return end();sn.unshift(h);if(h.x===food.x&&h.y===food.y){sc++;sfxScore();$('score').textContent=sc;const rc=cv.getBoundingClientRect();emitParticles(rc.left+food.x*GS*(rc.width/(GW*GS))+GS/2,rc.top+food.y*GS*(rc.height/(GW*GS))+GS/2,10);food=rnd();if(sc%5===0&&spd>50){spd-=8;clearInterval(lp);lp=setInterval(tick,spd)}}else sn.pop();draw()}`,
|
|
2069
|
+
`function draw(){ctx.fillStyle='${p.sf}';ctx.fillRect(0,0,GW*GS,GW*GS);for(let x=0;x<GW;x++)for(let y=0;y<GW;y++){ctx.fillStyle='rgba(255,255,255,.015)';if((x+y)%2===0)ctx.fillRect(x*GS,y*GS,GS,GS)}ctx.fillStyle='${p.ac}';ctx.beginPath();ctx.arc(food.x*GS+GS/2,food.y*GS+GS/2,GS/2-2+Math.sin(Date.now()/200)*2,0,Math.PI*2);ctx.fill();ctx.shadowColor='${p.ac}';ctx.shadowBlur=8;ctx.fill();ctx.shadowBlur=0;sn.forEach((s,i)=>{const t=i/sn.length;ctx.fillStyle=i===0?'${p.pr}':'hsl('+(140+t*40)+',70%,'+(55-t*20)+'%)';ctx.beginPath();ctx.roundRect(s.x*GS+1,s.y*GS+1,GS-2,GS-2,4);ctx.fill();if(i===0){ctx.fillStyle='${p.bg}';const ex=dir.x===1?GS-6:dir.x===-1?4:dir.y===0?GS/2:dir.y===1?GS-6:4;const ey=dir.y===1?GS-6:dir.y===-1?4:dir.x===0?GS/2:dir.x===1?GS/2-2:GS/2-2;ctx.beginPath();ctx.arc(s.x*GS+ex,s.y*GS+ey,2,0,Math.PI*2);ctx.fill()}})}`,
|
|
2070
|
+
`function end(){clearInterval(lp);sfxFail();const nb=saveBest(sc);$('hi').textContent=best;$('fin').textContent=sc;if(nb&&sc>0){sfxWin();burstAt($('fin'),30)}const r=sc>=30?'Legend!':sc>=20?'Amazing!':sc>=10?'Great!':sc>=5?'Good!':'Try again!';$('msg').innerHTML=r+(nb&&sc>0?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
2071
|
+
`document.addEventListener('keydown',e=>{if(e.key==='ArrowUp'){e.preventDefault();sd(0,-1)}if(e.key==='ArrowDown'){e.preventDefault();sd(0,1)}if(e.key==='ArrowLeft')sd(-1,0);if(e.key==='ArrowRight')sd(1,0)});`,
|
|
2034
2072
|
`let tx,ty;cv.addEventListener('touchstart',e=>{tx=e.touches[0].clientX;ty=e.touches[0].clientY},{passive:true});cv.addEventListener('touchend',e=>{const dx=e.changedTouches[0].clientX-tx,dy=e.changedTouches[0].clientY-ty;if(Math.abs(dx)>Math.abs(dy))sd(dx>0?1:-1,0);else sd(0,dy>0?1:-1)},{passive:true});`,
|
|
2035
2073
|
].join('\n');
|
|
2036
2074
|
|
|
@@ -2069,13 +2107,13 @@ function buildReaction(title, opts) {
|
|
|
2069
2107
|
<div class="times" id="ftimes"></div>
|
|
2070
2108
|
<button class="btn" style="margin-top:1rem" onclick="go()">Try Again</button></div>`;
|
|
2071
2109
|
|
|
2072
|
-
const js = [jsCore('react_best'),
|
|
2110
|
+
const js = [jsCore('react_best'), jsSfx(), jsParticles(p.pr, p.ac),
|
|
2073
2111
|
`if(best)$('best').textContent=best+'ms';`,
|
|
2074
2112
|
`const RDS=${rounds};let rd,res,phase,gAt,tmr;`,
|
|
2075
2113
|
`function go(){rd=0;res=[];$('times').innerHTML='';show('play');nr()}`,
|
|
2076
|
-
`function nr(){if(rd>=RDS)return end();$('rnd').textContent=(rd+1)+'/'+RDS;const z=$('zone');z.className='zone ready';$('ztxt').textContent='Wait for green...';phase='wait';clearTimeout(tmr);tmr=setTimeout(()=>{z.className='zone go';$('ztxt').textContent='TAP NOW!';gAt=Date.now();phase='go'},1500+Math.random()*3000)}`,
|
|
2077
|
-
`function tap(){if(phase==='wait'){clearTimeout(tmr);$('zone').className='zone early';$('ztxt').textContent='Too early! Tap to retry';phase='early'}else if(phase==='early'){nr()}else if(phase==='go'){const ms=Date.now()-gAt;res.push(ms);const t=document.createElement('span');t.textContent=ms+'ms';$('times').appendChild(t);$('avg').textContent=Math.round(res.reduce((a,b)=>a+b,0)/res.length)+'ms';rd++;phase='done';setTimeout(nr,600)}}`,
|
|
2078
|
-
`function end(){const avg=Math.round(res.reduce((a,b)=>a+b,0)/res.length);const nb=saveBest(avg);if(nb)$('best').textContent=avg+'ms'
|
|
2114
|
+
`function nr(){if(rd>=RDS)return end();$('rnd').textContent=(rd+1)+'/'+RDS;const z=$('zone');z.className='zone ready';$('ztxt').textContent='Wait for green...';phase='wait';clearTimeout(tmr);tmr=setTimeout(()=>{z.className='zone go';$('ztxt').textContent='TAP NOW!';sfxTick();gAt=Date.now();phase='go'},1500+Math.random()*3000)}`,
|
|
2115
|
+
`function tap(){if(phase==='wait'){clearTimeout(tmr);sfxFail();shakeEl($('zone'));$('zone').className='zone early';$('ztxt').textContent='Too early! Tap to retry';phase='early'}else if(phase==='early'){nr()}else if(phase==='go'){const ms=Date.now()-gAt;res.push(ms);sfxScore();burstAt($('zone'),ms<=250?20:8);const t=document.createElement('span');t.textContent=ms+'ms';if(ms<=200)t.style.color='${p.ac}';$('times').appendChild(t);$('avg').textContent=Math.round(res.reduce((a,b)=>a+b,0)/res.length)+'ms';rd++;phase='done';setTimeout(nr,600)}}`,
|
|
2116
|
+
`function end(){const avg=Math.round(res.reduce((a,b)=>a+b,0)/res.length);const nb=saveBest(avg);if(nb){$('best').textContent=avg+'ms';sfxWin();burstAt($('fin'),30)}$('fin').textContent=avg;$('ftimes').innerHTML=res.map((r,i)=>'<span>R'+(i+1)+': '+r+'ms</span>').join('');const r=avg<=200?'Superhuman!':avg<=250?'Lightning!':avg<=300?'Fast!':avg<=400?'Average':'Keep practicing!';$('msg').innerHTML='<span style="font-size:1.2rem">'+r+'</span>'+(nb?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
2079
2117
|
].join('\n');
|
|
2080
2118
|
|
|
2081
2119
|
return htmlWrap(title, css, html, js);
|
|
@@ -2112,15 +2150,15 @@ function buildTyping(title, opts) {
|
|
|
2112
2150
|
<div id="msg" style="font-size:1.1rem;margin:1rem 0"></div>
|
|
2113
2151
|
<button class="btn" onclick="go()">Play Again</button></div>`;
|
|
2114
2152
|
|
|
2115
|
-
const js = [jsCore('type_best'),
|
|
2153
|
+
const js = [jsCore('type_best'), jsSfx(), jsParticles(p.pr, p.ac), jsCountdown(),
|
|
2116
2154
|
`const WDS=["the","be","to","and","in","that","have","for","not","with","you","this","but","from","they","say","she","or","will","my","one","all","would","there","what","so","up","out","if","about","who","get","which","go","when","make","can","like","time","just","know","take","people","into","year","good","some","could","them","see","other","than","then","now","look","only","come","its","over","think","also","back","after","use","two","how","work","first","well","even","new","want","give","most","find","here","thing","code","fast","type","game","play","next","open","read","test","data","file","line","word","text","long","name","part","best","help","made","move","hand","high","keep","last","same","tell","set","each","much","turn","real","show","full","form","left","start","might","run","need","home","life","old","big","end","point","still","call","live","away","right","hard","plan","team","late","mind","wait","stop","must","land","close","draw","press","mark","step","pick","rock","blue","deep","dark","hold","four","week","room","free","fall","base","rest","less","note","hear"];`,
|
|
2117
|
-
`const DUR=${dur*1000};let pool,ci,pos,done,t0,tmr;`,
|
|
2155
|
+
`const DUR=${dur*1000};let pool,ci,pos,done,t0,tmr,tried=0,streak=0;`,
|
|
2118
2156
|
`function shuf(a){for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]]}return a}`,
|
|
2119
|
-
`function go(){pool=shuf([...WDS]);ci=0;pos=0;done=0;t0=Date.now();$('wc').textContent=0;$('wpm').textContent=0;$('inp').value=''
|
|
2157
|
+
`function go(){show('play');countdown(()=>{pool=shuf([...WDS]);ci=0;pos=0;done=0;tried=0;streak=0;t0=Date.now();$('wc').textContent=0;$('wpm').textContent=0;$('inp').value='';$('inp').focus();render();tmr=setInterval(tick,100)})}`,
|
|
2120
2158
|
`function render(){const w=pool[ci%pool.length];const v=$('inp').value;$('disp').innerHTML=w.split('').map((c,i)=>{let cl=i<pos?(v[i]===c?'y':'n'):'';if(i===pos)cl+=' cur';return'<span class="c '+cl+'">'+c+'</span>'}).join('');$('queue').innerHTML='';for(let i=1;i<=5;i++)$('queue').innerHTML+='<span>'+pool[(ci+i)%pool.length]+'</span>'}`,
|
|
2121
|
-
`function tick(){const e=Date.now()-t0,pc=Math.max(0,1-e/DUR)*100;$('bar').style.width=pc+'%';$('timer').textContent=Math.max(0,Math.ceil((DUR-e)/1000));if(e>=DUR)end()}`,
|
|
2122
|
-
`$('inp').addEventListener('input',function(){if(Date.now()-t0>DUR)return;const w=pool[ci%pool.length],v=this.value;pos=v.length;if(v.endsWith(' ')||v.length>=w.length){if(v.trim()===w){done
|
|
2123
|
-
`function end(){clearInterval(tmr);$('inp').blur();const wpm=Math.round(done/(DUR/60000));const nb=saveBest(wpm);$('fin').textContent=wpm;const r=wpm>=80?'Blazing!':wpm>=60?'Excellent!':wpm>=40?'Good!':wpm>=20?'Decent!':'Keep practicing!';$('msg').innerHTML=r+'<br>'+done+' words in ${dur}s'+(nb&&wpm>0?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
2159
|
+
`function tick(){const e=Date.now()-t0,pc=Math.max(0,1-e/DUR)*100;$('bar').style.width=pc+'%';$('timer').textContent=Math.max(0,Math.ceil((DUR-e)/1000));$('wpm').textContent=Math.round(done/Math.max(.01,(e/60000)));if(e>=DUR)end()}`,
|
|
2160
|
+
`$('inp').addEventListener('input',function(){if(!t0||Date.now()-t0>DUR)return;const w=pool[ci%pool.length],v=this.value;pos=v.length;if(v.endsWith(' ')||v.length>=w.length){tried++;if(v.trim()===w){done++;streak++;sfxScore();if(streak>=3){sfxCombo();burstAt($('disp'),12)}$('wc').textContent=done}else{streak=0;sfxHit();shakeEl($('disp'))}ci++;pos=0;this.value=''}render()});`,
|
|
2161
|
+
`function end(){clearInterval(tmr);$('inp').blur();const wpm=Math.round(done/(DUR/60000));const acc=tried>0?Math.round(done/tried*100):0;const nb=saveBest(wpm);if(nb&&wpm>0){sfxWin();burstAt($('fin'),30)}else sfxFail();$('fin').textContent=wpm;const r=wpm>=80?'Blazing!':wpm>=60?'Excellent!':wpm>=40?'Good!':wpm>=20?'Decent!':'Keep practicing!';$('msg').innerHTML=r+'<br>'+done+' words in ${dur}s — '+acc+'% accuracy'+(nb&&wpm>0?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
2124
2162
|
].join('\n');
|
|
2125
2163
|
|
|
2126
2164
|
return htmlWrap(title, css, html, js);
|
|
@@ -2149,16 +2187,16 @@ function buildBreakout(title, opts) {
|
|
|
2149
2187
|
<button class="btn" onclick="go()">Play Again</button></div>`;
|
|
2150
2188
|
|
|
2151
2189
|
const brickColors = `['${p.pr}','${p.sc}','#A78BFA','${p.ac}','#EC4899']`;
|
|
2152
|
-
const js = [jsCore('brk_best'),
|
|
2190
|
+
const js = [jsCore('brk_best'), jsSfx(), jsParticles(p.pr, p.ac),
|
|
2153
2191
|
`const cv=$('cv'),ctx=cv.getContext('2d'),CW=360,CH=480,COLS=${COLS},ROWS=${ROWS},BW=CW/COLS-4,BH=18,PW=70,PH=12,BR=6;`,
|
|
2154
2192
|
`const BC=${brickColors};`,
|
|
2155
|
-
`let px,ball,dx,dy,bricks,sc,lives,raf;`,
|
|
2193
|
+
`let px,ball,dx,dy,bricks,sc,lives,raf,cmb=0;`,
|
|
2156
2194
|
`function mkBricks(){bricks=[];for(let r=0;r<ROWS;r++)for(let c=0;c<COLS;c++)bricks.push({x:c*(BW+4)+2,y:r*(BH+4)+40,w:BW,h:BH,color:BC[r%BC.length],alive:true})}`,
|
|
2157
|
-
`function go(){px=CW/2;ball={x:CW/2,y:CH-60};dx=3*(Math.random()>.5?1:-1);dy=-3.5;sc=0;lives=3;mkBricks();$('score').textContent=0;$('lives').textContent=3;show('play');cancelAnimationFrame(raf);tick()}`,
|
|
2195
|
+
`function go(){px=CW/2;ball={x:CW/2,y:CH-60};dx=3*(Math.random()>.5?1:-1);dy=-3.5;sc=0;lives=3;cmb=0;mkBricks();$('score').textContent=0;$('lives').textContent=3;show('play');cancelAnimationFrame(raf);tick()}`,
|
|
2158
2196
|
`function tick(){update();draw();if(lives>0&&bricks.some(b=>b.alive))raf=requestAnimationFrame(tick);else end()}`,
|
|
2159
|
-
`function update(){ball.x+=dx;ball.y+=dy;if(ball.x<=BR||ball.x>=CW-BR)dx=-dx;if(ball.y<=BR)dy=-dy;if(ball.y>=CH-PH-BR-8&&ball.y<=CH-8&&ball.x>=px-PW/2&&ball.x<=px+PW/2){dy=-Math.abs(dy);dx+=((ball.x-px)/(PW/2))*2}if(ball.y>CH){lives
|
|
2160
|
-
`function draw(){ctx.clearRect(0,0,CW,CH);for(const b of bricks){if(!b.alive)continue;ctx.fillStyle=b.color;ctx.beginPath();ctx.roundRect(b.x,b.y,b.w,b.h,4);ctx.fill()}ctx.fillStyle='${p.pr}';ctx.beginPath();ctx.roundRect(px-PW/2,CH-PH-8,PW,PH,6);ctx.fill();ctx.fillStyle='${p.ac}';ctx.beginPath();ctx.arc(ball.x,ball.y,BR,0,Math.PI*2);ctx.fill()}`,
|
|
2161
|
-
`function end(){cancelAnimationFrame(raf);const won=!bricks.some(b=>b.alive);const nb=saveBest(sc);$('fin').textContent=sc;$('msg').innerHTML=(won?'You cleared it!':'Game Over')+(nb&&sc>0?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
2197
|
+
`function update(){ball.x+=dx;ball.y+=dy;if(ball.x<=BR||ball.x>=CW-BR)dx=-dx;if(ball.y<=BR)dy=-dy;if(ball.y>=CH-PH-BR-8&&ball.y<=CH-8&&ball.x>=px-PW/2&&ball.x<=px+PW/2){dy=-Math.abs(dy);dx+=((ball.x-px)/(PW/2))*2;sfxTap();cmb=0}if(ball.y>CH){lives--;sfxFail();$('lives').textContent=lives;cmb=0;if(lives>0){ball={x:CW/2,y:CH-60};dx=3*(Math.random()>.5?1:-1);dy=-3.5}}let hitAny=false;for(const b of bricks){if(!b.alive)continue;if(ball.x+BR>b.x&&ball.x-BR<b.x+b.w&&ball.y+BR>b.y&&ball.y-BR<b.y+b.h){b.alive=false;dy=-dy;cmb++;const pts=10*(cmb>=5?3:cmb>=3?2:1);sc+=pts;hitAny=true;sfxHit();const rc=cv.getBoundingClientRect();emitParticles(rc.left+b.x*(rc.width/CW)+BW/2,rc.top+b.y*(rc.height/CH)+BH/2,cmb>=3?12:6,b.color);$('score').textContent=sc;if(cmb>=5){const sp=Math.sqrt(dx*dx+dy*dy);if(sp<7){dx*=1.05;dy*=1.05}}}}}`,
|
|
2198
|
+
`function draw(){ctx.clearRect(0,0,CW,CH);for(const b of bricks){if(!b.alive)continue;ctx.fillStyle=b.color;ctx.beginPath();ctx.roundRect(b.x,b.y,b.w,b.h,4);ctx.fill();ctx.fillStyle='rgba(255,255,255,.15)';ctx.fillRect(b.x+2,b.y+2,b.w-4,BH/3)}ctx.fillStyle='${p.pr}';ctx.shadowColor='${p.pr}';ctx.shadowBlur=10;ctx.beginPath();ctx.roundRect(px-PW/2,CH-PH-8,PW,PH,6);ctx.fill();ctx.shadowBlur=0;ctx.fillStyle='${p.ac}';ctx.shadowColor='${p.ac}';ctx.shadowBlur=6;ctx.beginPath();ctx.arc(ball.x,ball.y,BR,0,Math.PI*2);ctx.fill();ctx.shadowBlur=0}`,
|
|
2199
|
+
`function end(){cancelAnimationFrame(raf);const won=!bricks.some(b=>b.alive);const nb=saveBest(sc);if(won||nb)sfxWin();$('fin').textContent=sc;if(nb&&sc>0)burstAt($('fin'),30);$('msg').innerHTML=(won?'You cleared it!':'Game Over')+(nb&&sc>0?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
2162
2200
|
`cv.addEventListener('mousemove',e=>{const r=cv.getBoundingClientRect();px=Math.max(PW/2,Math.min(CW-PW/2,(e.clientX-r.left)*(CW/r.width)))});`,
|
|
2163
2201
|
`cv.addEventListener('touchmove',e=>{e.preventDefault();const r=cv.getBoundingClientRect();px=Math.max(PW/2,Math.min(CW-PW/2,(e.touches[0].clientX-r.left)*(CW/r.width)))},{passive:false});`,
|
|
2164
2202
|
`document.addEventListener('keydown',e=>{if(e.key==='ArrowLeft')px=Math.max(PW/2,px-20);if(e.key==='ArrowRight')px=Math.min(CW-PW/2,px+20)});`,
|
|
@@ -2167,11 +2205,146 @@ function buildBreakout(title, opts) {
|
|
|
2167
2205
|
return htmlWrap(title, css, html, js);
|
|
2168
2206
|
}
|
|
2169
2207
|
|
|
2208
|
+
// ── Dodge Game Builder ──
|
|
2209
|
+
|
|
2210
|
+
function buildDodge(title, opts) {
|
|
2211
|
+
const p = GAME_PALETTES.dodge;
|
|
2212
|
+
const spd = opts.difficulty === 'hard' ? 1.4 : opts.difficulty === 'easy' ? 0.7 : 1.0;
|
|
2213
|
+
|
|
2214
|
+
const css = [cssVars(p), cssBase(p),
|
|
2215
|
+
`canvas{border-radius:12px;background:${p.sf};display:block;margin:1rem auto;touch-action:none}`,
|
|
2216
|
+
].join('\n');
|
|
2217
|
+
|
|
2218
|
+
const html = `<div id="s-menu">
|
|
2219
|
+
<p style="color:${p.mt};margin:1rem 0">Move to dodge the falling obstacles!</p>
|
|
2220
|
+
<div class="st"><span>Best<b id="best">0</b></span></div>
|
|
2221
|
+
<button class="btn" onclick="go()">Play</button></div>
|
|
2222
|
+
<div id="s-play" class="hd">
|
|
2223
|
+
<div class="st"><span>Score<b id="score">0</b></span><span>Best<b id="hi">0</b></span></div>
|
|
2224
|
+
<canvas id="cv" width="360" height="520"></canvas></div>
|
|
2225
|
+
<div id="s-end" class="hd">
|
|
2226
|
+
<div class="sc" id="fin">0</div>
|
|
2227
|
+
<div id="msg" style="font-size:1.1rem;margin:1rem 0"></div>
|
|
2228
|
+
<button class="btn" onclick="go()">Play Again</button></div>`;
|
|
2229
|
+
|
|
2230
|
+
const js = [jsCore('dodge_best'), jsSfx(), jsParticles(p.pr, p.ac), jsCountdown(),
|
|
2231
|
+
`const cv=$('cv'),ctx=cv.getContext('2d'),CW=360,CH=520,PW=30,PH=30,SPD_MUL=${spd};`,
|
|
2232
|
+
`let px,py,obs,sc,raf,t0,spd,spawnT,alive;$('hi').textContent=best;`,
|
|
2233
|
+
`function go(){show('play');countdown(startGame)}`,
|
|
2234
|
+
`function startGame(){px=CW/2;py=CH-60;obs=[];sc=0;spd=2*SPD_MUL;spawnT=0;alive=true;t0=Date.now();$('score').textContent=0;cancelAnimationFrame(raf);tick()}`,
|
|
2235
|
+
`function tick(){if(!alive)return;update();draw();raf=requestAnimationFrame(tick)}`,
|
|
2236
|
+
`function update(){spawnT++;const diff=Math.min(30,Math.floor((Date.now()-t0)/5000));if(spawnT%(Math.max(8,30-diff*2))===0){const w=20+Math.random()*30;obs.push({x:Math.random()*(CW-w),y:-30,w,h:20+Math.random()*10,s:spd+Math.random()*2+diff*0.3,c:['${p.pr}','${p.sc}','#FF4757','#FF6B81','#FFA502'][Math.floor(Math.random()*5)]})}obs.forEach(o=>o.y+=o.s);obs=obs.filter(o=>{if(o.y>CH){sc++;$('score').textContent=sc;if(sc%10===0)sfxScore();return false}return true});for(const o of obs){if(px+PW/2>o.x&&px-PW/2<o.x+o.w&&py+PH/2>o.y&&py-PH/2<o.y+o.h){alive=false;sfxFail();const rc=cv.getBoundingClientRect();emitParticles(rc.left+px*(rc.width/CW),rc.top+py*(rc.height/CH),25);end();return}}spd=2*SPD_MUL+Math.floor((Date.now()-t0)/10000)*0.5}`,
|
|
2237
|
+
`function draw(){ctx.fillStyle='${p.sf}';ctx.fillRect(0,0,CW,CH);for(let i=0;i<CW;i+=40)for(let j=0;j<CH;j+=40){ctx.fillStyle='rgba(255,255,255,.01)';if((Math.floor(i/40)+Math.floor(j/40))%2===0)ctx.fillRect(i,j,40,40)}obs.forEach(o=>{ctx.fillStyle=o.c;ctx.beginPath();ctx.roundRect(o.x,o.y,o.w,o.h,4);ctx.fill()});ctx.fillStyle='${p.ac}';ctx.shadowColor='${p.ac}';ctx.shadowBlur=12;ctx.beginPath();ctx.arc(px,py,PW/2,0,Math.PI*2);ctx.fill();ctx.shadowBlur=0;ctx.fillStyle='${p.bg}';ctx.beginPath();ctx.arc(px-5,py-4,3,0,Math.PI*2);ctx.arc(px+5,py-4,3,0,Math.PI*2);ctx.fill();ctx.strokeStyle='${p.bg}';ctx.lineWidth=2;ctx.beginPath();ctx.arc(px,py+2,6,0,Math.PI);ctx.stroke()}`,
|
|
2238
|
+
`function end(){cancelAnimationFrame(raf);const nb=saveBest(sc);$('hi').textContent=best;$('fin').textContent=sc;if(nb&&sc>0){sfxWin();burstAt($('fin'),30)}const r=sc>=100?'Untouchable!':sc>=60?'Amazing!':sc>=30?'Great!':sc>=15?'Good!':'Keep dodging!';$('msg').innerHTML=r+(nb&&sc>0?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
2239
|
+
`let keys={};document.addEventListener('keydown',e=>{keys[e.key]=true;e.preventDefault()});document.addEventListener('keyup',e=>keys[e.key]=false);`,
|
|
2240
|
+
`setInterval(()=>{if(!alive)return;const s=5;if(keys.ArrowLeft||keys.a)px=Math.max(PW/2,px-s);if(keys.ArrowRight||keys.d)px=Math.min(CW-PW/2,px+s);if(keys.ArrowUp||keys.w)py=Math.max(PH/2,py-s);if(keys.ArrowDown||keys.s)py=Math.min(CH-PH/2,py+s)},16);`,
|
|
2241
|
+
`let dragging=false;cv.addEventListener('touchstart',e=>{dragging=true;const r=cv.getBoundingClientRect();px=Math.max(PW/2,Math.min(CW-PW/2,(e.touches[0].clientX-r.left)*(CW/r.width)));py=Math.max(PH/2,Math.min(CH-PH/2,(e.touches[0].clientY-r.top)*(CH/r.height)))},{passive:true});`,
|
|
2242
|
+
`cv.addEventListener('touchmove',e=>{e.preventDefault();if(!dragging)return;const r=cv.getBoundingClientRect();px=Math.max(PW/2,Math.min(CW-PW/2,(e.touches[0].clientX-r.left)*(CW/r.width)));py=Math.max(PH/2,Math.min(CH-PH/2,(e.touches[0].clientY-r.top)*(CH/r.height)))},{passive:false});`,
|
|
2243
|
+
`cv.addEventListener('touchend',()=>dragging=false,{passive:true});`,
|
|
2244
|
+
`cv.addEventListener('mousemove',e=>{if(!alive)return;const r=cv.getBoundingClientRect();px=Math.max(PW/2,Math.min(CW-PW/2,(e.clientX-r.left)*(CW/r.width)));py=Math.max(PH/2,Math.min(CH-PH/2,(e.clientY-r.top)*(CH/r.height)))});`,
|
|
2245
|
+
].join('\n');
|
|
2246
|
+
|
|
2247
|
+
return htmlWrap(title, css, html, js);
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
// ── Color Match Game Builder ──
|
|
2251
|
+
|
|
2252
|
+
function buildColor(title, opts) {
|
|
2253
|
+
const p = GAME_PALETTES.color;
|
|
2254
|
+
const dur = opts.duration || 30;
|
|
2255
|
+
const COLORS = [
|
|
2256
|
+
{name:'Red',hex:'#FF4757'},{name:'Blue',hex:'#3742FA'},{name:'Green',hex:'#2ED573'},
|
|
2257
|
+
{name:'Yellow',hex:'#FFA502'},{name:'Purple',hex:'#8854D0'},{name:'Orange',hex:'#FF6348'},
|
|
2258
|
+
{name:'Pink',hex:'#FF6B81'},{name:'Cyan',hex:'#18DCFF'},
|
|
2259
|
+
];
|
|
2260
|
+
|
|
2261
|
+
const css = [cssVars(p), cssBase(p), cssTimerBar(p),
|
|
2262
|
+
`.clr-name{font-size:2.8rem;font-weight:900;margin:1.5rem 0;letter-spacing:.05em;transition:transform .1s}`,
|
|
2263
|
+
`.opts{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:1rem 0}`,
|
|
2264
|
+
`.opts div{padding:1.2rem;border-radius:14px;cursor:pointer;font-size:1.1rem;font-weight:700;text-align:center;transition:transform .1s,box-shadow .2s;color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.4)}`,
|
|
2265
|
+
`.opts div:active{transform:scale(.93)}`,
|
|
2266
|
+
`.strk{font-size:.95rem;color:${p.ac};min-height:1.5em;font-weight:700}`,
|
|
2267
|
+
].join('\n');
|
|
2268
|
+
|
|
2269
|
+
const html = `<div id="s-menu">
|
|
2270
|
+
<p style="color:${p.mt};margin:1rem 0">Tap the color shown, not the word! ${dur}s</p>
|
|
2271
|
+
<div class="st"><span>Best<b id="best">0</b></span><span>Last<b id="last">-</b></span></div>
|
|
2272
|
+
<button class="btn" onclick="go()">Start</button></div>
|
|
2273
|
+
<div id="s-play" class="hd">
|
|
2274
|
+
<div class="st"><span>Score<b id="sc">0</b></span><span>Time<b id="timer">${dur}</b></span><span>Streak<b id="strk">0</b></span></div>
|
|
2275
|
+
<div class="bar"><div class="fl" id="bar" style="width:100%"></div></div>
|
|
2276
|
+
<div class="clr-name" id="word">-</div>
|
|
2277
|
+
<div class="strk" id="stxt"></div>
|
|
2278
|
+
<div class="opts" id="opts"></div></div>
|
|
2279
|
+
<div id="s-end" class="hd">
|
|
2280
|
+
<div class="sc" id="fin">0</div>
|
|
2281
|
+
<div id="msg" style="font-size:1.1rem;margin:1rem 0"></div>
|
|
2282
|
+
<button class="btn" onclick="go()">Play Again</button></div>`;
|
|
2283
|
+
|
|
2284
|
+
const colorsJS = JSON.stringify(COLORS);
|
|
2285
|
+
const js = [jsCore('color_best'), jsSfx(), jsParticles(p.pr, p.ac), jsCountdown(),
|
|
2286
|
+
`const CS=${colorsJS};`,
|
|
2287
|
+
`const DUR=${dur*1000};let sc,t0,tmr,streak,correct;`,
|
|
2288
|
+
`function shuf(a){for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]]}return a}`,
|
|
2289
|
+
`function go(){show('play');countdown(startGame)}`,
|
|
2290
|
+
`function startGame(){sc=0;streak=0;t0=Date.now();$('sc').textContent=0;$('strk').textContent=0;$('stxt').textContent='';tmr=setInterval(tick,100);nextRound()}`,
|
|
2291
|
+
`function tick(){const e=Date.now()-t0,pc=Math.max(0,1-e/DUR)*100;$('bar').style.width=pc+'%';$('timer').textContent=Math.max(0,Math.ceil((DUR-e)/1000));if(e>=DUR)end()}`,
|
|
2292
|
+
`function nextRound(){const pool=shuf([...CS]);const ans=pool[0];const fakeText=pool[1+Math.floor(Math.random()*(pool.length-1))];correct=ans.name;$('word').textContent=fakeText.name;$('word').style.color=ans.hex;const choices=shuf([ans,...shuf(pool.filter(c=>c.name!==ans.name)).slice(0,3)]);const o=$('opts');o.innerHTML='';choices.forEach(c=>{const d=document.createElement('div');d.textContent=c.name;d.style.background=c.hex;d.onclick=()=>pick(c.name===correct,d);o.appendChild(d)})}`,
|
|
2293
|
+
`function pick(ok,el){if(Date.now()-t0>DUR)return;$('opts').querySelectorAll('div').forEach(d=>d.onclick=null);if(ok){sc++;streak++;sfxScore();$('sc').textContent=sc;$('strk').textContent=streak;burstAt(el,streak>=5?20:8);if(streak>=3){sfxCombo();$('stxt').textContent=streak>=10?'ON FIRE!':streak>=5?'AMAZING!':'STREAK!'}}else{streak=0;sfxFail();shakeEl($('word'));$('strk').textContent=0;$('stxt').textContent=''}setTimeout(nextRound,ok?300:600)}`,
|
|
2294
|
+
`function end(){clearInterval(tmr);const nb=saveBest(sc);$('last').textContent=sc;$('fin').textContent=sc;if(nb&&sc>0){sfxWin();burstAt($('fin'),30)}const r=sc>=50?'Color Master!':sc>=35?'Amazing!':sc>=20?'Great!':sc>=10?'Good!':'Keep trying!';$('msg').innerHTML=r+'<br>'+sc+' in ${dur}s'+(nb?' <span class=hi>★ New Best!</span>':'');show('end')}`,
|
|
2295
|
+
].join('\n');
|
|
2296
|
+
|
|
2297
|
+
return htmlWrap(title, css, html, js);
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
// ── Pong Game Builder ──
|
|
2301
|
+
|
|
2302
|
+
function buildPong(title, opts) {
|
|
2303
|
+
const p = GAME_PALETTES.pong;
|
|
2304
|
+
const winScore = opts.difficulty === 'hard' ? 11 : 7;
|
|
2305
|
+
|
|
2306
|
+
const css = [cssVars(p), cssBase(p),
|
|
2307
|
+
`canvas{border-radius:12px;background:${p.bg};display:block;margin:1rem auto;touch-action:none;border:2px solid ${p.sf}}`,
|
|
2308
|
+
`.vs{display:flex;justify-content:center;gap:3rem;font-size:2.5rem;font-weight:900;font-variant-numeric:tabular-nums;margin:.5rem 0}`,
|
|
2309
|
+
`.vs span:first-child{color:${p.pr}}.vs span:last-child{color:${p.sc}}`,
|
|
2310
|
+
].join('\n');
|
|
2311
|
+
|
|
2312
|
+
const html = `<div id="s-menu">
|
|
2313
|
+
<p style="color:${p.mt};margin:1rem 0">First to ${winScore} wins — you vs AI</p>
|
|
2314
|
+
<div class="st"><span>Wins<b id="best">0</b></span></div>
|
|
2315
|
+
<button class="btn" onclick="go()">Play</button></div>
|
|
2316
|
+
<div id="s-play" class="hd">
|
|
2317
|
+
<div class="vs"><span id="p1">0</span><span id="p2">0</span></div>
|
|
2318
|
+
<canvas id="cv" width="360" height="420"></canvas>
|
|
2319
|
+
<p style="color:${p.mt};font-size:.8rem;margin:.5rem">Move mouse/touch or arrow keys</p></div>
|
|
2320
|
+
<div id="s-end" class="hd">
|
|
2321
|
+
<div class="sc" id="fin">WIN</div>
|
|
2322
|
+
<div id="msg" style="font-size:1.1rem;margin:1rem 0"></div>
|
|
2323
|
+
<button class="btn" onclick="go()">Rematch</button></div>`;
|
|
2324
|
+
|
|
2325
|
+
const js = [jsCore('pong_best'), jsSfx(), jsParticles(p.pr, p.ac),
|
|
2326
|
+
`const cv=$('cv'),ctx=cv.getContext('2d'),CW=360,CH=420,PW=70,PH=10,BR=7,WIN=${winScore};`,
|
|
2327
|
+
`let py,ay,bx,by,bdx,bdy,s1,s2,raf,aiSpd;`,
|
|
2328
|
+
`function go(){py=CW/2;ay=CW/2;bx=CW/2;by=CH/2;bdx=3*(Math.random()>.5?1:-1);bdy=3*(Math.random()>.5?1:-1);s1=0;s2=0;aiSpd=3;$('p1').textContent=0;$('p2').textContent=0;show('play');cancelAnimationFrame(raf);tick()}`,
|
|
2329
|
+
`function tick(){update();draw();if(s1<WIN&&s2<WIN)raf=requestAnimationFrame(tick);else end()}`,
|
|
2330
|
+
`function update(){bx+=bdx;by+=bdy;if(bx<=BR||bx>=CW-BR)bdx=-bdx;if(by<=PH+BR+4&&bx>=ay-PW/2&&bx<=ay+PW/2){bdy=Math.abs(bdy);bdx+=((bx-ay)/(PW/2))*1.5;sfxTap();const rc=cv.getBoundingClientRect();emitParticles(rc.left+bx*(rc.width/CW),rc.top+by*(rc.height/CH),4,'${p.sc}')}if(by>=CH-PH-BR-4&&bx>=py-PW/2&&bx<=py+PW/2){bdy=-Math.abs(bdy);bdx+=((bx-py)/(PW/2))*1.5;sfxHit();const rc=cv.getBoundingClientRect();emitParticles(rc.left+bx*(rc.width/CW),rc.top+by*(rc.height/CH),4,'${p.pr}')}if(by<0){s2++;sfxScore();$('p1').textContent=s2;reset()}if(by>CH){s1++;sfxFail();$('p2').textContent=s1;reset()}const diff=bx-ay;ay+=Math.min(aiSpd,Math.abs(diff))*(diff>0?1:-1)*(0.7+Math.random()*0.3);ay=Math.max(PW/2,Math.min(CW-PW/2,ay));aiSpd=3+Math.max(s1,s2)*0.3}`,
|
|
2331
|
+
`function reset(){bx=CW/2;by=CH/2;bdx=3*(Math.random()>.5?1:-1);bdy=3*(Math.random()>.5?1:-1)}`,
|
|
2332
|
+
`function draw(){ctx.fillStyle='${p.bg}';ctx.fillRect(0,0,CW,CH);ctx.setLineDash([6,6]);ctx.strokeStyle='${p.sf}';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(0,CH/2);ctx.lineTo(CW,CH/2);ctx.stroke();ctx.setLineDash([]);ctx.fillStyle='${p.sc}';ctx.shadowColor='${p.sc}';ctx.shadowBlur=8;ctx.beginPath();ctx.roundRect(ay-PW/2,4,PW,PH,4);ctx.fill();ctx.fillStyle='${p.pr}';ctx.shadowColor='${p.pr}';ctx.beginPath();ctx.roundRect(py-PW/2,CH-PH-4,PW,PH,4);ctx.fill();ctx.shadowBlur=0;ctx.fillStyle='${p.ac}';ctx.shadowColor='${p.ac}';ctx.shadowBlur=10;ctx.beginPath();ctx.arc(bx,by,BR,0,Math.PI*2);ctx.fill();ctx.shadowBlur=0}`,
|
|
2333
|
+
`function end(){cancelAnimationFrame(raf);const won=s1<s2;if(won){sfxWin();saveBest(best+1);$('fin').textContent='YOU WIN!';$('fin').style.color='${p.ac}';burstAt($('fin'),30)}else{sfxFail();$('fin').textContent='AI WINS';$('fin').style.color='${p.pr}'}$('msg').innerHTML=s2+' - '+s1+(won?' — Great match!':' — Try again!');show('end')}`,
|
|
2334
|
+
`cv.addEventListener('mousemove',e=>{const r=cv.getBoundingClientRect();py=Math.max(PW/2,Math.min(CW-PW/2,(e.clientX-r.left)*(CW/r.width)))});`,
|
|
2335
|
+
`cv.addEventListener('touchmove',e=>{e.preventDefault();const r=cv.getBoundingClientRect();py=Math.max(PW/2,Math.min(CW-PW/2,(e.touches[0].clientX-r.left)*(CW/r.width)))},{passive:false});`,
|
|
2336
|
+
`document.addEventListener('keydown',e=>{if(e.key==='ArrowLeft')py=Math.max(PW/2,py-20);if(e.key==='ArrowRight')py=Math.min(CW-PW/2,py+20)});`,
|
|
2337
|
+
].join('\n');
|
|
2338
|
+
|
|
2339
|
+
return htmlWrap(title, css, html, js);
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2170
2342
|
// ── Game Router ──
|
|
2171
2343
|
|
|
2172
2344
|
const GAME_BUILDERS = {
|
|
2173
2345
|
tapping: buildTapping, quiz: buildQuiz, memory: buildMemory,
|
|
2174
2346
|
snake: buildSnake, reaction: buildReaction, typing: buildTyping, breakout: buildBreakout,
|
|
2347
|
+
dodge: buildDodge, color: buildColor, pong: buildPong,
|
|
2175
2348
|
};
|
|
2176
2349
|
|
|
2177
2350
|
function gameHTML(type, title, rawInstruction) {
|