Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
285 lines (273 loc) · 23.7 KB
/
Copy pathindex.html
File metadata and controls
285 lines (273 loc) · 23.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PLAYZONE — 7 addictive mini games</title>
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root{--bg:#0a0a14;--card:#12121f;--line:#25253a;--ink:#f1f1fa;--mute:#8d8da8;--pink:#ff3d9a;--cyan:#22e4ff;--lime:#b6ff3b}
*{box-sizing:border-box;margin:0}
body{font:16px/1.6 Inter,system-ui,sans-serif;background:var(--bg);color:var(--ink);min-height:100vh;
background-image:radial-gradient(circle at 15% 0,#ff3d9a22,transparent 40%),radial-gradient(circle at 90% 10%,#22e4ff1f,transparent 40%)}
.w{max-width:1100px;margin:0 auto;padding:0 22px}
button{font:inherit;cursor:pointer;color:inherit}
.px{font-family:'Press Start 2P',monospace}
header{border-bottom:1px solid var(--line)}
header .w{display:flex;justify-content:space-between;align-items:center;height:62px;gap:14px}
.logo{font-size:14px;color:var(--lime)}
.tot{color:var(--mute);font-size:14px;margin-left:auto}.tot b{color:var(--cyan)}
.snd{background:none;border:1px solid var(--line);border-radius:8px;width:38px;height:36px;font-size:17px}
.hero{text-align:center;padding:60px 0 38px}
.hero h1{font-size:clamp(26px,6vw,54px);line-height:1.25;background:linear-gradient(90deg,var(--pink),var(--cyan),var(--lime));-webkit-background-clip:text;background-clip:text;color:transparent}
.hero p{color:var(--mute);max-width:48ch;margin:22px auto 26px;font-size:18px}
.btn{background:var(--lime);color:#0a0a14;border:0;border-radius:10px;padding:13px 26px;font-weight:600}
.btn:hover{filter:brightness(1.1)}
.ghost{background:var(--card);color:var(--ink);border:1px solid var(--line)}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:18px;padding-bottom:64px}
.card{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:22px;position:relative;overflow:hidden;transition:transform .2s,border-color .2s}
.card::before{content:"";position:absolute;inset:0 0 auto 0;height:3px;background:var(--c)}
.card:hover{transform:translateY(-4px);border-color:var(--c)}
.card .e{font-size:44px}
.card h3{font-size:20px;margin:6px 0 2px}
.card p{color:var(--mute);font-size:15px;margin-bottom:14px;min-height:48px}
.card .r{display:flex;justify-content:space-between;align-items:center}
.card .r span{font-size:13px;color:var(--mute)}.card .r b{color:var(--c)}
.new{position:absolute;top:14px;right:14px;background:var(--pink);color:#fff;font-size:11px;font-weight:600;padding:2px 10px;border-radius:99px}
.play{background:var(--c);color:#0a0a14;border:0;border-radius:8px;padding:8px 18px;font-weight:600}
#m{position:fixed;inset:0;background:#000b;display:none;align-items:center;justify-content:center;z-index:50;padding:14px}
#m.on{display:flex}
.box{background:var(--card);border:1px solid var(--line);border-radius:18px;width:min(460px,100%);max-height:96vh;overflow:auto;padding:20px}
.top{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px}
.top h2{font-size:12px;color:var(--lime)}
.x{background:none;border:1px solid var(--line);border-radius:8px;width:34px;height:34px;font-size:18px}
.hud{display:flex;justify-content:space-between;gap:8px;margin-bottom:10px;color:var(--mute);font-size:14px}.hud b{color:var(--ink)}
canvas{display:block;width:100%;max-width:300px;margin:0 auto 12px;border-radius:10px;border:1px solid var(--line);touch-action:none}
.pad{display:flex;flex-direction:column;align-items:center;gap:6px;margin-bottom:12px}
.pad div{display:flex;gap:6px}
.pad button,.lr button{width:56px;height:46px;background:var(--bg);border:1px solid var(--line);border-radius:8px}
.lr{display:flex;justify-content:center;gap:18px;margin-bottom:12px}.lr button{width:90px}
.mid{text-align:center}
.msg{text-align:center;min-height:30px;margin:10px 0;font-size:18px}
.mem{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:10px}
.mem button{aspect-ratio:1;background:var(--bg);border:1px solid var(--line);border-radius:10px;font-size:28px}
.mem button.up{background:#22e4ff22;border-color:var(--cyan)}
.mem button.ok{background:#b6ff3b22;border-color:var(--lime)}
.ttt{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;max-width:270px;margin:0 auto 10px}
.ttt button{aspect-ratio:1;background:var(--bg);border:1px solid var(--line);border-radius:10px;font:700 30px 'Press Start 2P'}
.ttt button.X{color:var(--cyan)}.ttt button.O{color:var(--pink)}
.rps{display:flex;justify-content:center;gap:10px;margin:14px 0}
.rps button{font-size:38px;background:var(--bg);border:1px solid var(--line);border-radius:12px;width:78px;height:78px}
.rps button:hover{border-color:var(--cyan)}
.big{font-size:56px;text-align:center}
.pd{height:190px;border-radius:14px;display:flex;align-items:center;justify-content:center;text-align:center;padding:10px;font-size:22px;font-weight:600;border:0;width:100%;color:#fff;margin-bottom:10px;user-select:none}
.in{display:flex;gap:8px}
.in input{flex:1;background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:10px 14px;color:var(--ink);font-size:18px;outline:0;min-width:0}
footer{border-top:1px solid var(--line);padding:22px 0;text-align:center;color:var(--mute);font-size:14px}
.cf{position:fixed;top:-20px;width:8px;height:14px;z-index:99;animation:fall 2s linear forwards;pointer-events:none}
@keyframes fall{to{transform:translateY(110vh) rotate(720deg)}}
.pop{animation:pop .3s}@keyframes pop{50%{transform:scale(1.15)}}
:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}
@media(prefers-reduced-motion:reduce){.card,.cf,.pop{transition:none;animation:none}}
</style>
</head>
<body>
<header><div class="w"><span class="px logo">PLAYZONE</span><span class="tot">Records: <b id="tot">0</b></span><button class="snd" id="snd" aria-label="Toggle sound">🔊</button></div></header>
<section class="hero w">
<h1 class="px">PLAY.<br>BEAT.<br>REPEAT.</h1>
<p>7 addictive mini games in one place. No downloads, works on phone and desktop. Your high scores are saved.</p>
<button class="btn" id="rnd">🎲 Surprise me</button>
</section>
<main class="w"><div class="grid" id="grid"></div></main>
<footer>PLAYZONE © 2026 · Good luck, player! 🎮</footer>
<div id="m" role="dialog" aria-modal="true"><div class="box">
<div class="top"><h2 class="px" id="mt"></h2><button class="x" id="mx" aria-label="Close">×</button></div>
<div id="body"></div>
</div></div>
<script>
var $=function(i){return document.getElementById(i)};
var rnd=function(n){return Math.floor(Math.random()*n)};
function store(k,v){try{if(v===undefined)return JSON.parse(localStorage.getItem(k));localStorage.setItem(k,JSON.stringify(v))}catch(e){return null}}
var best=store("best2")||{},muted=false,AC;
function beep(f,d,t){if(muted)return;try{AC=AC||new(window.AudioContext||window.webkitAudioContext)();var o=AC.createOscillator(),g=AC.createGain();o.type=t||"square";o.frequency.value=f;g.gain.value=.05;o.connect(g);g.connect(AC.destination);o.start();g.gain.exponentialRampToValueAtTime(.001,AC.currentTime+d);o.stop(AC.currentTime+d)}catch(e){}}
function boom(){var C=["#ff3d9a","#22e4ff","#b6ff3b","#ffb13b","#a46bff"];for(var i=0;i<46;i++){var d=document.createElement("i");d.className="cf";d.style.cssText="left:"+rnd(100)+"vw;background:"+C[rnd(5)]+";animation-delay:"+(Math.random()*.5)+"s";document.body.appendChild(d);setTimeout(function(x){x.remove()},2600,d)}beep(660,.12);setTimeout(function(){beep(880,.2)},120)}
function report(id,v,low){var b=best[id];if(v>0&&(b==null||(low?v<b:v>b))){best[id]=v;store("best2",best);cards();boom();return true}return false}
function sh(a){for(var i=a.length-1;i>0;i--){var j=rnd(i+1),t=a[i];a[i]=a[j];a[j]=t}return a}
/* ---------- 1. SNAKE ---------- */
function snake(el){
el.innerHTML='<div class="hud"><span>Score: <b id="sc">0</b></span><span>Level: <b id="lv">1</b></span><span>Best: <b>'+(best.snake||0)+'</b></span></div><canvas id="cv" width="300" height="300"></canvas><div class="pad"><button data-d="U" aria-label="Up">▲</button><div><button data-d="L" aria-label="Left">◀</button><button data-d="D" aria-label="Down">▼</button><button data-d="R" aria-label="Right">▶</button></div></div><p style="text-align:center;color:var(--mute);font-size:13px;margin-bottom:10px">🟡 Golden apples give +5 but vanish fast!</p><div class="mid"><button class="btn" id="go">Start</button></div>';
var c=$("cv").getContext("2d"),N=15,S=20,sn,dir,nd,food,gold,ate,sc,tm,run=false;
function free(){var p;do{p={x:rnd(N),y:rnd(N)}}while(sn.some(function(s){return s.x==p.x&&s.y==p.y})||(food&&food.x==p.x&&food.y==p.y));return p}
function draw(txt){c.fillStyle="#0a0a14";c.fillRect(0,0,300,300);
c.fillStyle="#ff3d9a";c.beginPath();c.arc(food.x*S+10,food.y*S+10,7,0,7);c.fill();
if(gold){c.fillStyle="#ffd43b";c.beginPath();c.arc(gold.x*S+10,gold.y*S+10,9,0,7);c.fill()}
sn.forEach(function(s,i){c.fillStyle=i?"#b6ff3b":"#22e4ff";c.fillRect(s.x*S+1,s.y*S+1,S-2,S-2)});
if(txt){c.fillStyle="#000a";c.fillRect(0,110,300,80);c.fillStyle="#fff";c.font="16px 'Press Start 2P'";c.textAlign="center";c.fillText(txt,150,157)}}
function start(){sn=[{x:7,y:7},{x:6,y:7},{x:5,y:7}];dir=nd={x:1,y:0};sc=0;ate=0;gold=null;food=null;food=free();$("sc").textContent=0;$("lv").textContent=1;draw();run=true;$("go").style.display="none";clearTimeout(tm);tick()}
function tick(){if(!run)return;dir=nd;var h={x:sn[0].x+dir.x,y:sn[0].y+dir.y};
if(h.x<0||h.y<0||h.x>=N||h.y>=N||sn.some(function(s){return s.x==h.x&&s.y==h.y})){run=false;beep(120,.4,"sawtooth");report("snake",sc);draw("GAME OVER");$("go").textContent="Play again";$("go").style.display="";return}
sn.unshift(h);var grow=false;
if(h.x==food.x&&h.y==food.y){sc++;ate++;grow=true;beep(520,.08);food=free();if(ate%5==0){gold=free();gold.t=40}}
else if(gold&&h.x==gold.x&&h.y==gold.y){sc+=5;grow=true;gold=null;beep(900,.15)}
if(!grow)sn.pop();
if(gold&&--gold.t<=0)gold=null;
$("sc").textContent=sc;$("lv").textContent=1+Math.floor(sc/8);
draw();tm=setTimeout(tick,Math.max(55,130-sc*3))}
function turn(x,y){if(!run)return;if(x==-dir.x&&y==-dir.y)return;nd={x:x,y:y}}
var M={U:[0,-1],D:[0,1],L:[-1,0],R:[1,0]};
function key(e){var k={ArrowUp:"U",ArrowDown:"D",ArrowLeft:"L",ArrowRight:"R",w:"U",s:"D",a:"L",d:"R"}[e.key];if(!k)return;e.preventDefault();if(!run)start();turn(M[k][0],M[k][1])}
document.addEventListener("keydown",key);
el.onclick=function(e){var d=e.target.dataset.d;if(d){if(!run)start();turn(M[d][0],M[d][1])}if(e.target.id=="go")start()};
var sx,sy;$("cv").ontouchstart=function(e){sx=e.touches[0].clientX;sy=e.touches[0].clientY};
$("cv").ontouchend=function(e){var dx=e.changedTouches[0].clientX-sx,dy=e.changedTouches[0].clientY-sy;if(!run)start();
if(Math.abs(dx)>Math.abs(dy))turn(dx>0?1:-1,0);else turn(0,dy>0?1:-1)};
sn=[{x:7,y:7},{x:6,y:7},{x:5,y:7}];dir=nd={x:1,y:0};food={x:11,y:7};draw();
return function(){run=false;clearTimeout(tm);document.removeEventListener("keydown",key)}
}
/* ---------- 2. CAR RACING ---------- */
function race(el){
el.innerHTML='<div class="hud"><span>Score: <b id="sc">0</b></span><span>Coins: <b id="cn">0</b></span><span>Best: <b>'+(best.race||0)+'</b></span></div><canvas id="cv" width="300" height="440"></canvas><div class="lr"><button id="bl" aria-label="Move left">◀</button><button id="br" aria-label="Move right">▶</button></div><p style="text-align:center;color:var(--mute);font-size:13px;margin-bottom:10px">Dodge traffic, grab coins 🪙. You get faster every second!</p><div class="mid"><button class="btn" id="go">Start engine</button></div>';
var c=$("cv").getContext("2d"),W=300,H=440,L=[50,150,250],run=false,raf,last,lane,px,en,co,dist,coins,spd,off,sp,cs;
function rr(x,y,w,h,r){c.beginPath();if(c.roundRect)c.roundRect(x,y,w,h,r);else c.rect(x,y,w,h);c.fill()}
function car(x,y,col){c.fillStyle="#000a";rr(x-15,y-26,34,60,8);c.fillStyle=col;rr(x-17,y-30,34,60,8);c.fillStyle="#0b1020";rr(x-12,y-16,24,12,3);rr(x-12,y+6,24,10,3);c.fillStyle="#ffe9a0";c.fillRect(x-14,y-30,7,4);c.fillRect(x+7,y-30,7,4)}
function draw(txt){c.fillStyle="#161628";c.fillRect(0,0,W,H);
c.fillStyle="#2c2c4a";c.fillRect(0,0,6,H);c.fillRect(W-6,0,6,H);
c.fillStyle="#3a3a5c";for(var y=-40+off;y<H;y+=40){c.fillRect(98,y,4,22);c.fillRect(198,y,4,22)}
co.forEach(function(k){c.fillStyle="#ffd43b";c.beginPath();c.arc(L[k.l],k.y,10,0,7);c.fill();c.fillStyle="#b88a00";c.font="bold 12px Inter";c.textAlign="center";c.fillText("$",L[k.l],k.y+4)});
en.forEach(function(e){car(L[e.l],e.y,e.c)});
car(px,380,"#b6ff3b");
if(txt){c.fillStyle="#000b";c.fillRect(0,180,W,80);c.fillStyle="#fff";c.font="16px 'Press Start 2P'";c.textAlign="center";c.fillText(txt,150,226)}}
function init(){lane=1;px=150;en=[];co=[];dist=0;coins=0;spd=4;off=0;sp=200;cs=300}
function start(){init();run=true;$("go").style.display="none";last=performance.now();raf=requestAnimationFrame(frame)}
function crash(){run=false;beep(90,.5,"sawtooth");var s=Math.floor(dist)+coins*10;report("race",s);draw("CRASH! "+s);$("go").textContent="Race again";$("go").style.display=""}
function frame(t){if(!run)return;var dt=Math.min(40,t-last)/16.67;last=t;
spd=4+Math.min(9,dist/350);off=(off+spd*dt)%40;dist+=spd*dt*.1;px+=(L[lane]-px)*Math.min(1,.25*dt);
sp-=spd*dt;if(sp<=0){en.push({l:rnd(3),y:-40,k:.45+Math.random()*.3,c:["#ff3d9a","#ffb13b","#a46bff","#22e4ff"][rnd(4)]});sp=170+rnd(110)-Math.min(60,dist/15)}
cs-=spd*dt;if(cs<=0){co.push({l:rnd(3),y:-20});cs=200+rnd(220)}
en.forEach(function(e){e.y+=spd*e.k*dt});co.forEach(function(k){k.y+=spd*dt});
for(var i=0;i<en.length;i++){if(Math.abs(en[i].y-380)<56&&Math.abs(L[en[i].l]-px)<30){crash();return}}
co=co.filter(function(k){if(Math.abs(k.y-380)<36&&Math.abs(L[k.l]-px)<28){coins++;beep(980,.07,"triangle");return false}return k.y<H+20});
en=en.filter(function(e){return e.y<H+50});
$("sc").textContent=Math.floor(dist)+coins*10;$("cn").textContent=coins;
draw();raf=requestAnimationFrame(frame)}
function mv(d){if(!run)return;lane=Math.max(0,Math.min(2,lane+d));beep(300,.04,"triangle")}
function key(e){var d={ArrowLeft:-1,a:-1,ArrowRight:1,d:1}[e.key];if(!d)return;e.preventDefault();if(!run)start();mv(d)}
document.addEventListener("keydown",key);
$("bl").onclick=function(){if(!run)start();mv(-1)};$("br").onclick=function(){if(!run)start();mv(1)};
$("go").onclick=start;
$("cv").onpointerdown=function(e){var r=this.getBoundingClientRect();if(!run)start();mv(e.clientX-r.left<r.width/2?-1:1)};
init();draw();
return function(){run=false;cancelAnimationFrame(raf);document.removeEventListener("keydown",key)}
}
/* ---------- 3. MEMORY ---------- */
function memory(el){
var E=["🐍","🚀","🎮","🍕","⚽","🎧","🔥","👾"],deck,open,lock,moves,found,t0,tm;
function init(){clearInterval(tm);deck=sh(E.concat(E));open=[];lock=false;moves=0;found=0;t0=0;
el.innerHTML='<div class="hud"><span>Moves: <b id="mv">0</b></span><span>Time: <b id="tt">0s</b></span><span>Best: <b>'+(best.memory||"—")+'</b></span></div><div class="mem">'+deck.map(function(_,i){return '<button data-i="'+i+'" aria-label="Card">?</button>'}).join("")+'</div><div class="msg" id="ms">Find all 8 pairs!</div><div class="mid"><button class="btn ghost" id="rs">Restart</button></div>'}
init();
el.onclick=function(e){
if(e.target.id=="rs"){init();return}
var b=e.target.closest("[data-i]");if(!b||lock||b.classList.contains("up")||b.classList.contains("ok"))return;
if(!t0){t0=Date.now();tm=setInterval(function(){$("tt").textContent=Math.floor((Date.now()-t0)/1000)+"s"},500)}
b.textContent=deck[b.dataset.i];b.classList.add("up");open.push(b);beep(440,.05);
if(open.length==2){moves++;$("mv").textContent=moves;lock=true;var a=open[0],c=open[1];
if(a.textContent==c.textContent){a.className=c.className="ok pop";open=[];lock=false;found++;beep(780,.12);
if(found==8){clearInterval(tm);var s=Math.floor((Date.now()-t0)/1000);$("ms").textContent=(report("memory",moves,true)?"🏆 New record! ":"🎉 Done! ")+moves+" moves in "+s+"s"}}
else setTimeout(function(){a.textContent=c.textContent="?";a.classList.remove("up");c.classList.remove("up");open=[];lock=false},700)}
};
return function(){clearInterval(tm)}
}
/* ---------- 4. TIC-TAC-TOE ---------- */
function ttt(el){
var L=[[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]],B,over,W=best.ttt||0,Lo=0,D=0;
function win(b){for(var i=0;i<8;i++){var l=L[i];if(b[l[0]]&&b[l[0]]==b[l[1]]&&b[l[0]]==b[l[2]])return b[l[0]]}return b.every(Boolean)?"d":null}
function mm(b,p){var w=win(b);if(w=="O")return 1;if(w=="X")return -1;if(w=="d")return 0;
var v=p=="O"?-2:2;for(var i=0;i<9;i++)if(!b[i]){b[i]="O"==p?"O":"X";var r=mm(b,p=="O"?"X":"O");b[i]="";v=p=="O"?Math.max(v,r):Math.min(v,r)}return v}
function ai(){var fr=[];for(var i=0;i<9;i++)if(!B[i])fr.push(i);
if(Math.random()<.3)return fr[rnd(fr.length)];
var bi=fr[0],bv=-2;fr.forEach(function(i){B[i]="O";var v=mm(B,"X");B[i]="";if(v>bv){bv=v;bi=i}});return bi}
function draw(m){el.innerHTML='<div class="hud"><span>You: ❌</span><span>Wins <b>'+W+'</b> · Losses <b>'+Lo+'</b> · Draws <b>'+D+'</b></span></div><div class="ttt">'+B.map(function(v,i){return '<button data-i="'+i+'" class="'+v+'" aria-label="Cell">'+v+'</button>'}).join("")+'</div><div class="msg">'+(m||"")+'</div><div class="mid"><button class="btn ghost" id="rs">New game</button></div>'}
function init(){B=Array(9).fill("");over=false;draw("Your move")}
init();
el.onclick=function(e){
if(e.target.id=="rs"){init();return}
var b=e.target.closest("[data-i]");if(!b||over||B[b.dataset.i])return;
B[b.dataset.i]="X";beep(500,.06);var w=win(B);
if(!w){B[ai()]="O";beep(330,.06);w=win(B)}
if(w){over=true;if(w=="X"){W++;report("ttt",W)}else if(w=="O")Lo++;else D++;
draw(w=="X"?"🎉 You win!":w=="O"?"🤖 Computer wins":"🤝 It’s a draw")}else draw("Your move")
};
return function(){}
}
/* ---------- 5. ROCK PAPER SCISSORS ---------- */
function rps(el){
var I=["✊","✋","✌️"],N=["Rock","Paper","Scissors"],st=0,T=["Nice try!","Too easy for me 😎","Ouch! Lucky shot.","Again?","I read your mind 🧠"];
function draw(m,a,b){el.innerHTML='<div class="hud"><span>Win streak: <b>'+st+'</b></span><span>Best: <b>'+(best.rps||0)+'</b></span></div><div class="big">'+(a!=null?I[a]+' vs '+I[b]:'🤔')+'</div><div class="msg">'+(m||"Pick your weapon!")+'</div><div class="rps">'+I.map(function(x,i){return '<button data-i="'+i+'" aria-label="'+N[i]+'">'+x+'</button>'}).join("")+'</div>'}
draw();
el.onclick=function(e){var b=e.target.closest("[data-i]");if(!b)return;var p=+b.dataset.i,c=rnd(3),m;
if(p==c)m="Draw. Again!";else if((p+1)%3==c){m="You lost. "+T[rnd(T.length)];st=0;beep(150,.2,"sawtooth")}else{st++;report("rps",st);m="You win! 🎉 Streak: "+st;beep(700,.1)}
draw(m,p,c)};
return function(){}
}
/* ---------- 6. REACTION (5 rounds) ---------- */
function react(el){
var tm,t0,state=0,R=[];
function rate(a){return a<200?"Lightning ⚡":a<260?"Fast 🚀":a<340?"Average 🙂":"Sleepy 😴"}
el.innerHTML='<div class="hud"><span id="rd">Round 1 / 5</span><span>Best avg: <b id="rb">'+(best.react?best.react+" ms":"—")+'</b></span></div><button class="pd" id="pd" style="background:#2a2a44">Tap to start</button><div class="msg" id="rm"></div>';
var pd=$("pd");
function wait(){state=1;pd.style.background="#c0263f";pd.textContent="Wait for green...";tm=setTimeout(function(){state=2;t0=performance.now();pd.style.background="#1fb85a";pd.textContent="TAP NOW!"},1400+rnd(2600))}
pd.onclick=function(){
if(state==0||state==3){if(state==3&&R.length>=5){R=[];}$("rm").textContent="";$("rd").textContent="Round "+(R.length+1)+" / 5";wait()}
else if(state==1){clearTimeout(tm);state=3;pd.style.background="#2a2a44";pd.textContent="Too early! Tap to retry";$("rm").textContent="Patience 😉"}
else{var ms=Math.round(performance.now()-t0);R.push(ms);state=3;pd.style.background="#2a2a44";beep(800,.05);
if(R.length<5){pd.textContent=ms+" ms — tap for next round";$("rm").textContent=R.join(" · ")+" ms"}
else{var avg=Math.round(R.reduce(function(a,b){return a+b},0)/5),nb=report("react",avg,true);$("rb").textContent=best.react+" ms";
pd.textContent="Average: "+avg+" ms";$("rm").textContent=rate(avg)+(nb?" 🏆 New record!":"")+" — tap to play again"}}
};
return function(){clearTimeout(tm)}
}
/* ---------- 7. NUMBER GUESS ---------- */
function guess(el){
var n,tries,over;
function init(){n=1+rnd(100);tries=0;over=false;
el.innerHTML='<div class="hud"><span>Tries: <b id="tr">0</b>/7</span><span>Best: <b>'+(best.guess||"—")+'</b></span></div><p style="text-align:center;color:var(--mute)">I’m thinking of a number from 1 to 100. You have 7 tries!</p><div class="msg" id="gm"></div><div class="in"><input id="gi" type="number" min="1" max="100" inputmode="numeric" placeholder="Your guess"><button class="btn" id="gb">Guess</button></div><div class="mid" style="margin-top:12px"><button class="btn ghost" id="rs">New number</button></div>'}
function go(){if(over)return;var v=+$("gi").value;if(!v||v<1||v>100){$("gm").textContent="Enter a number from 1 to 100";return}
tries++;$("tr").textContent=tries;$("gi").value="";var d=Math.abs(v-n);
if(v==n){over=true;beep(880,.2);$("gm").textContent=(report("guess",tries,true)?"🏆 Record! ":"🎉 ")+"Got it in "+tries+" tries"}
else if(tries>=7){over=true;beep(120,.4,"sawtooth");$("gm").textContent="Game over 😢 It was "+n}
else $("gm").textContent=(v<n?"⬆️ Higher":"⬇️ Lower")+" · "+(d<=5?"🔥 Burning!":d<=15?"🌡️ Warm":"❄️ Cold")}
init();
el.onclick=function(e){if(e.target.id=="gb")go();if(e.target.id=="rs")init()};
el.onkeydown=function(e){if(e.key=="Enter"&&e.target.id=="gi")go()};
return function(){}
}
/* ---------- MAIN ---------- */
var G=[
{id:"race",e:"🏎️",n:"Turbo Racer",d:"Dodge traffic, collect coins and survive as the speed keeps rising.",c:"#ff3d9a",f:race,u:" pts",low:0,nw:1},
{id:"snake",e:"🐍",n:"Snake",d:"Eat apples, grab golden bonuses and don’t hit the walls.",c:"#b6ff3b",f:snake,u:" pts",low:0},
{id:"memory",e:"🧠",n:"Memory Match",d:"Match 8 pairs against the clock. Fewer moves is better.",c:"#22e4ff",f:memory,u:" moves",low:1},
{id:"ttt",e:"❌",n:"Tic-Tac-Toe",d:"Play against the computer. Can you beat it?",c:"#ffb13b",f:ttt,u:" wins",low:0},
{id:"rps",e:"✊",n:"Rock Paper Scissors",d:"Build the longest win streak against a cheeky bot.",c:"#a46bff",f:rps,u:" streak",low:0},
{id:"react",e:"⚡",n:"Reaction Test",d:"5 rounds. Tap the moment it turns green. Lowest average wins.",c:"#1fb85a",f:react,u:" ms",low:1},
{id:"guess",e:"🔢",n:"Number Guess",d:"Find the secret number in 7 tries with hot and cold hints.",c:"#ff6b6b",f:guess,u:" tries",low:1}
];
function cards(){
$("grid").innerHTML=G.map(function(g){var b=best[g.id];
return '<article class="card" style="--c:'+g.c+'">'+(g.nw?'<span class="new">NEW</span>':'')+'<div class="e">'+g.e+'</div><h3>'+g.n+'</h3><p>'+g.d+'</p><div class="r"><span>Best: <b>'+(b!=null?b+g.u:"—")+'</b></span><button class="play" data-g="'+g.id+'">Play</button></div></article>'}).join("");
$("tot").textContent=G.filter(function(g){return best[g.id]!=null}).length+" / "+G.length;
}
var stop=null;
function openGame(id){var g=G.filter(function(x){return x.id==id})[0];
$("mt").textContent=g.n.toUpperCase();$("body").innerHTML="";$("m").classList.add("on");
stop=g.f($("body"));$("mx").focus()}
function closeGame(){$("m").classList.remove("on");if(stop){stop();stop=null}$("body").innerHTML=""}
$("grid").onclick=function(e){var g=e.target.dataset.g;if(g)openGame(g)};
$("rnd").onclick=function(){openGame(G[rnd(G.length)].id)};
$("mx").onclick=closeGame;
$("m").onclick=function(e){if(e.target.id=="m")closeGame()};
$("snd").onclick=function(){muted=!muted;this.textContent=muted?"🔇":"🔊"};
document.addEventListener("keydown",function(e){if(e.key=="Escape")closeGame()});
cards();
</script>
</body>
</html>