Initial commit

This commit is contained in:
Allen
2026-04-30 02:14:25 +00:00
commit 7090e6f01d
243 changed files with 115122 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
require('dotenv').config();
const { Pool } = require('pg');
const pool = new Pool({
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
});
const games = [
{
title: "Star Catcher",
description: "Catch falling stars while avoiding meteors! Move left and right to collect as many stars as you can.",
code: `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Star Catcher</title>
<style>*{margin:0;padding:0;box-sizing:border-box}body{background:#1a1a2e;display:flex;justify-content:center;align-items:center;min-height:100vh;font-family:Arial,sans-serif}#game{position:relative;background:linear-gradient(to bottom,#16213e,#0f3460);border-radius:10px;overflow:hidden;touch-action:none}#score{position:absolute;top:10px;left:10px;color:#fff;font-size:20px;z-index:10}#player{position:absolute;bottom:20px;width:60px;height:60px;font-size:50px;text-align:center;transition:left 0.1s}.item{position:absolute;font-size:30px;text-align:center}#gameOver{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(0,0,0,0.8);color:#fff;padding:30px;border-radius:10px;text-align:center;display:none}#gameOver button{margin-top:15px;padding:10px 30px;font-size:18px;background:#4CAF50;color:#fff;border:none;border-radius:5px;cursor:pointer}#instructions{position:absolute;bottom:10px;left:50%;transform:translateX(-50%);color:rgba(255,255,255,0.7);font-size:12px}</style></head>
<body><div id="game"><div id="score">Score: 0</div><div id="player">🧺</div><div id="gameOver"><h2>Game Over!</h2><p>Final Score: <span id="finalScore">0</span></p><button onclick="restart()">Play Again</button></div><div id="instructions">← → Arrow keys or touch to move</div></div>
<script>const game=document.getElementById('game'),player=document.getElementById('player'),scoreEl=document.getElementById('score'),gameOverEl=document.getElementById('gameOver'),finalScoreEl=document.getElementById('finalScore');let width=Math.min(400,window.innerWidth-20),height=500,playerX=width/2-30,score=0,gameActive=true,items=[],speed=3;window.gameScore=0;game.style.width=width+'px';game.style.height=height+'px';player.style.left=playerX+'px';function createItem(){if(!gameActive)return;const item=document.createElement('div');item.className='item';item.textContent=Math.random()>0.3?'⭐':'☄️';item.dataset.type=item.textContent==='⭐'?'star':'meteor';item.style.left=Math.random()*(width-30)+'px';item.style.top='-30px';game.appendChild(item);items.push({el:item,y:-30,x:parseFloat(item.style.left)})}function update(){if(!gameActive)return;items.forEach((item,i)=>{item.y+=speed;item.el.style.top=item.y+'px';if(item.y>height){item.el.remove();items.splice(i,1)}else if(item.y>height-100&&item.y<height-20){const itemX=item.x,itemW=30;if(playerX<itemX+itemW&&playerX+60>itemX){if(item.el.dataset.type==='star'){score+=10;window.gameScore=score;scoreEl.textContent='Score: '+score;item.el.remove();items.splice(i,1)}else{endGame()}}}});speed=3+Math.floor(score/50)*0.5;requestAnimationFrame(update)}function endGame(){gameActive=false;finalScoreEl.textContent=score;gameOverEl.style.display='block'}function restart(){items.forEach(i=>i.el.remove());items=[];score=0;window.gameScore=0;speed=3;scoreEl.textContent='Score: 0';gameOverEl.style.display='none';gameActive=true;update()}document.addEventListener('keydown',e=>{if(e.key==='ArrowLeft')playerX=Math.max(0,playerX-20);if(e.key==='ArrowRight')playerX=Math.min(width-60,playerX+20);player.style.left=playerX+'px'});game.addEventListener('touchmove',e=>{e.preventDefault();const touch=e.touches[0],rect=game.getBoundingClientRect();playerX=Math.max(0,Math.min(width-60,touch.clientX-rect.left-30));player.style.left=playerX+'px'});setInterval(createItem,1000);update();</script></body></html>`
},
{
title: "Balloon Pop Frenzy",
description: "Pop as many balloons as you can before they float away! Click or tap the balloons to pop them and score points.",
code: `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Balloon Pop</title>
<style>*{margin:0;padding:0}body{background:linear-gradient(to bottom,#87CEEB,#98FB98);min-height:100vh;font-family:Arial,sans-serif;overflow:hidden}#game{position:relative;width:100%;height:100vh;max-width:500px;margin:0 auto}#score{position:fixed;top:10px;left:50%;transform:translateX(-50%);background:rgba(255,255,255,0.9);padding:10px 20px;border-radius:20px;font-size:20px;font-weight:bold;color:#333;z-index:100}.balloon{position:absolute;font-size:50px;cursor:pointer;transition:transform 0.1s;user-select:none;animation:float 0.5s ease-out}.balloon:hover{transform:scale(1.1)}.balloon:active{transform:scale(0.9)}@keyframes float{from{opacity:0;transform:scale(0.5)}to{opacity:1;transform:scale(1)}}.pop{animation:pop 0.3s ease-out forwards}@keyframes pop{to{transform:scale(1.5);opacity:0}}#timer{position:fixed;top:10px;right:20px;background:rgba(255,255,255,0.9);padding:10px 15px;border-radius:20px;font-size:18px;color:#e74c3c}#gameOver{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:white;padding:40px;border-radius:20px;text-align:center;box-shadow:0 10px 30px rgba(0,0,0,0.2);display:none;z-index:200}#gameOver h2{color:#333;margin-bottom:10px}#gameOver p{font-size:24px;color:#4CAF50;margin-bottom:20px}#gameOver button{padding:15px 40px;font-size:18px;background:#4CAF50;color:white;border:none;border-radius:10px;cursor:pointer}</style></head>
<body><div id="game"><div id="score">Score: 0</div><div id="timer">Time: 30</div><div id="gameOver"><h2>Time's Up!</h2><p>Score: <span id="finalScore">0</span></p><button onclick="restart()">Play Again</button></div></div>
<script>const game=document.getElementById('game'),scoreEl=document.getElementById('score'),timerEl=document.getElementById('timer'),gameOverEl=document.getElementById('gameOver'),finalScoreEl=document.getElementById('finalScore');let score=0,timeLeft=30,gameActive=true,balloonInterval,timerInterval;window.gameScore=0;const colors=['🎈','🔴','🟡','🟢','🔵','🟣','🟠'];function createBalloon(){if(!gameActive)return;const balloon=document.createElement('div');balloon.className='balloon';balloon.textContent=colors[Math.floor(Math.random()*colors.length)];balloon.style.left=Math.random()*(window.innerWidth-60)+'px';balloon.style.bottom='-60px';game.appendChild(balloon);let y=-60,speed=2+Math.random()*2;function rise(){if(!gameActive){balloon.remove();return}y+=speed;balloon.style.bottom=y+'px';if(y>window.innerHeight+60){balloon.remove()}else{requestAnimationFrame(rise)}}rise();balloon.onclick=()=>{if(!gameActive)return;balloon.classList.add('pop');score+=10;window.gameScore=score;scoreEl.textContent='Score: '+score;setTimeout(()=>balloon.remove(),300)}}function startGame(){gameActive=true;score=0;timeLeft=30;window.gameScore=0;scoreEl.textContent='Score: 0';timerEl.textContent='Time: 30';gameOverEl.style.display='none';balloonInterval=setInterval(createBalloon,600);timerInterval=setInterval(()=>{timeLeft--;timerEl.textContent='Time: '+timeLeft;if(timeLeft<=0)endGame()},1000)}function endGame(){gameActive=false;clearInterval(balloonInterval);clearInterval(timerInterval);finalScoreEl.textContent=score;gameOverEl.style.display='block'}function restart(){document.querySelectorAll('.balloon').forEach(b=>b.remove());startGame()}startGame();</script></body></html>`
},
{
title: "Snake Classic",
description: "The classic snake game! Eat the apples to grow longer but don't hit the walls or yourself. Use arrow keys or swipe to control.",
code: `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Snake</title>
<style>*{margin:0;padding:0;box-sizing:border-box}body{background:#1a1a2e;display:flex;flex-direction:column;justify-content:center;align-items:center;min-height:100vh;font-family:Arial,sans-serif}#score{color:#4CAF50;font-size:24px;margin-bottom:10px}canvas{background:#16213e;border-radius:10px;touch-action:none}#gameOver{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(0,0,0,0.9);color:#fff;padding:30px;border-radius:10px;text-align:center;display:none}#gameOver button{margin-top:15px;padding:10px 30px;font-size:18px;background:#4CAF50;color:#fff;border:none;border-radius:5px;cursor:pointer}#controls{margin-top:10px;color:rgba(255,255,255,0.6);font-size:12px}</style></head>
<body><div id="score">Score: 0</div><canvas id="game"></canvas><div id="controls">Arrow keys or swipe to move</div><div id="gameOver"><h2>Game Over!</h2><p>Score: <span id="finalScore">0</span></p><button onclick="restart()">Play Again</button></div>
<script>const canvas=document.getElementById('game'),ctx=canvas.getContext('2d'),scoreEl=document.getElementById('score'),gameOverEl=document.getElementById('gameOver'),finalScoreEl=document.getElementById('finalScore');let size=Math.min(400,window.innerWidth-20),gridSize=20,tileCount=size/gridSize;canvas.width=canvas.height=size;let snake=[{x:10,y:10}],food={x:15,y:15},dx=0,dy=0,score=0,gameActive=true,speed=100;window.gameScore=0;function draw(){if(!gameActive)return;ctx.fillStyle='#16213e';ctx.fillRect(0,0,size,size);ctx.fillStyle='#4CAF50';snake.forEach((seg,i)=>{ctx.fillStyle=i===0?'#8BC34A':'#4CAF50';ctx.fillRect(seg.x*gridSize+1,seg.y*gridSize+1,gridSize-2,gridSize-2)});ctx.fillStyle='#e74c3c';ctx.font=gridSize-4+'px Arial';ctx.fillText('🍎',food.x*gridSize+2,food.y*gridSize+gridSize-4)}function update(){if(!gameActive||dx===0&&dy===0)return;const head={x:snake[0].x+dx,y:snake[0].y+dy};if(head.x<0||head.x>=tileCount||head.y<0||head.y>=tileCount||snake.some(s=>s.x===head.x&&s.y===head.y)){endGame();return}snake.unshift(head);if(head.x===food.x&&head.y===food.y){score+=10;window.gameScore=score;scoreEl.textContent='Score: '+score;food={x:Math.floor(Math.random()*tileCount),y:Math.floor(Math.random()*tileCount)};speed=Math.max(50,speed-2)}else{snake.pop()}}function endGame(){gameActive=false;finalScoreEl.textContent=score;gameOverEl.style.display='block'}function restart(){snake=[{x:10,y:10}];food={x:15,y:15};dx=dy=0;score=0;speed=100;window.gameScore=0;scoreEl.textContent='Score: 0';gameOverEl.style.display='none';gameActive=true}function gameLoop(){update();draw();setTimeout(()=>requestAnimationFrame(gameLoop),speed)}document.addEventListener('keydown',e=>{if(e.key==='ArrowUp'&&dy!==1){dx=0;dy=-1}if(e.key==='ArrowDown'&&dy!==-1){dx=0;dy=1}if(e.key==='ArrowLeft'&&dx!==1){dx=-1;dy=0}if(e.key==='ArrowRight'&&dx!==-1){dx=1;dy=0}});let touchStartX,touchStartY;canvas.addEventListener('touchstart',e=>{touchStartX=e.touches[0].clientX;touchStartY=e.touches[0].clientY});canvas.addEventListener('touchend',e=>{const diffX=e.changedTouches[0].clientX-touchStartX,diffY=e.changedTouches[0].clientY-touchStartY;if(Math.abs(diffX)>Math.abs(diffY)){if(diffX>0&&dx!==1){dx=1;dy=0}else if(diffX<0&&dx!==1){dx=-1;dy=0}}else{if(diffY>0&&dy!==-1){dx=0;dy=1}else if(diffY<0&&dy!==1){dx=0;dy=-1}}});gameLoop();</script></body></html>`
},
{
title: "Memory Match",
description: "Test your memory! Flip cards to find matching pairs. Complete the game with as few moves as possible.",
code: `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Memory Match</title>
<style>*{margin:0;padding:0;box-sizing:border-box}body{background:linear-gradient(135deg,#667eea,#764ba2);min-height:100vh;display:flex;flex-direction:column;align-items:center;padding:20px;font-family:Arial,sans-serif}h1{color:white;margin-bottom:10px}#stats{color:white;margin-bottom:20px;font-size:18px}#grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;max-width:400px}.card{width:80px;height:80px;background:white;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:40px;cursor:pointer;transition:transform 0.3s,background 0.3s;user-select:none}.card:hover{transform:scale(1.05)}.card.hidden{background:linear-gradient(135deg,#4CAF50,#45a049)}.card.hidden::after{content:'?';font-size:30px;color:white}.card.matched{background:#98FB98;transform:scale(0.95)}.card.wrong{animation:shake 0.3s}@keyframes shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-5px)}75%{transform:translateX(5px)}}#gameOver{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:white;padding:40px;border-radius:20px;text-align:center;display:none;z-index:100}#gameOver h2{color:#4CAF50;margin-bottom:10px}#gameOver button{margin-top:15px;padding:12px 30px;font-size:16px;background:#4CAF50;color:white;border:none;border-radius:10px;cursor:pointer}</style></head>
<body><h1>Memory Match</h1><div id="stats">Moves: 0 | Pairs: 0/8</div><div id="grid"></div><div id="gameOver"><h2>You Win!</h2><p>Completed in <span id="finalMoves">0</span> moves</p><button onclick="restart()">Play Again</button></div>
<script>const grid=document.getElementById('grid'),statsEl=document.getElementById('stats'),gameOverEl=document.getElementById('gameOver'),finalMovesEl=document.getElementById('finalMoves');const emojis=['🎮','🎯','🎪','🎨','🎭','🎪','🎸','🎺'];let cards=[],flipped=[],matched=0,moves=0,canFlip=true;window.gameScore=0;function shuffle(arr){for(let i=arr.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[arr[i],arr[j]]=[arr[j],arr[i]]}return arr}function createCards(){const pairs=[...emojis,...emojis];shuffle(pairs);grid.innerHTML='';cards=pairs.map((emoji,i)=>{const card=document.createElement('div');card.className='card hidden';card.dataset.emoji=emoji;card.dataset.index=i;card.onclick=()=>flipCard(card);grid.appendChild(card);return card})}function flipCard(card){if(!canFlip||flipped.includes(card)||card.classList.contains('matched'))return;card.classList.remove('hidden');card.textContent=card.dataset.emoji;flipped.push(card);if(flipped.length===2){moves++;canFlip=false;checkMatch()}}function checkMatch(){const[a,b]=flipped;if(a.dataset.emoji===b.dataset.emoji){a.classList.add('matched');b.classList.add('matched');matched++;window.gameScore=matched*10;updateStats();flipped=[];canFlip=true;if(matched===8)endGame()}else{a.classList.add('wrong');b.classList.add('wrong');setTimeout(()=>{a.classList.add('hidden');a.classList.remove('wrong');a.textContent='';b.classList.add('hidden');b.classList.remove('wrong');b.textContent='';flipped=[];canFlip=true},800)}updateStats()}function updateStats(){statsEl.textContent='Moves: '+moves+' | Pairs: '+matched+'/8'}function endGame(){finalMovesEl.textContent=moves;gameOverEl.style.display='block'}function restart(){matched=moves=0;window.gameScore=0;flipped=[];canFlip=true;gameOverEl.style.display='none';updateStats();createCards()}createCards();</script></body></html>`
},
{
title: "Whack-a-Mole",
description: "Quick! Whack the moles before they hide! Test your reflexes in this classic arcade game.",
code: `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Whack-a-Mole</title>
<style>*{margin:0;padding:0;box-sizing:border-box}body{background:linear-gradient(to bottom,#87CEEB,#90EE90);min-height:100vh;display:flex;flex-direction:column;align-items:center;padding:20px;font-family:Arial,sans-serif}h1{color:#333;margin-bottom:10px}#stats{display:flex;gap:30px;margin-bottom:20px;font-size:20px;color:#333}#grid{display:grid;grid-template-columns:repeat(3,1fr);gap:15px}.hole{width:100px;height:100px;background:#8B4513;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;position:relative;box-shadow:inset 0 -10px 20px rgba(0,0,0,0.3)}.mole{font-size:60px;opacity:0;transform:translateY(20px);transition:all 0.2s}.mole.up{opacity:1;transform:translateY(-10px)}.hole:active .mole.up{transform:scale(0.8)}#gameOver{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:white;padding:40px;border-radius:20px;text-align:center;display:none;box-shadow:0 10px 30px rgba(0,0,0,0.3)}#gameOver button{margin-top:15px;padding:12px 30px;font-size:16px;background:#4CAF50;color:white;border:none;border-radius:10px;cursor:pointer}</style></head>
<body><h1>Whack-a-Mole!</h1><div id="stats"><span>Score: <span id="score">0</span></span><span>Time: <span id="timer">30</span></span></div><div id="grid"></div><div id="gameOver"><h2>Time's Up!</h2><p>Final Score: <span id="finalScore">0</span></p><button onclick="restart()">Play Again</button></div>
<script>const grid=document.getElementById('grid'),scoreEl=document.getElementById('score'),timerEl=document.getElementById('timer'),gameOverEl=document.getElementById('gameOver'),finalScoreEl=document.getElementById('finalScore');let score=0,timeLeft=30,gameActive=true,moleTimeout,gameInterval;window.gameScore=0;for(let i=0;i<9;i++){const hole=document.createElement('div');hole.className='hole';hole.innerHTML='<span class="mole">🐹</span>';hole.onclick=()=>whack(hole);grid.appendChild(hole)}const holes=[...document.querySelectorAll('.hole')],moles=[...document.querySelectorAll('.mole')];function showMole(){if(!gameActive)return;moles.forEach(m=>m.classList.remove('up'));const idx=Math.floor(Math.random()*9);moles[idx].classList.add('up');moleTimeout=setTimeout(showMole,Math.max(400,1000-score*10))}function whack(hole){const mole=hole.querySelector('.mole');if(mole.classList.contains('up')){mole.classList.remove('up');score+=10;window.gameScore=score;scoreEl.textContent=score}}function startGame(){score=0;timeLeft=30;gameActive=true;window.gameScore=0;scoreEl.textContent='0';timerEl.textContent='30';gameOverEl.style.display='none';showMole();gameInterval=setInterval(()=>{timeLeft--;timerEl.textContent=timeLeft;if(timeLeft<=0)endGame()},1000)}function endGame(){gameActive=false;clearTimeout(moleTimeout);clearInterval(gameInterval);moles.forEach(m=>m.classList.remove('up'));finalScoreEl.textContent=score;gameOverEl.style.display='block'}function restart(){startGame()}startGame();</script></body></html>`
},
{
title: "Color Match",
description: "Match the color to the word! But be careful - the colors are tricky. How fast can you react?",
code: `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Color Match</title>
<style>*{margin:0;padding:0;box-sizing:border-box}body{background:#1a1a2e;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px;font-family:Arial,sans-serif;color:white}#stats{display:flex;gap:40px;margin-bottom:30px;font-size:20px}#word{font-size:60px;font-weight:bold;margin-bottom:10px;text-transform:uppercase}#question{font-size:18px;margin-bottom:30px;color:#ccc}.buttons{display:flex;gap:15px;flex-wrap:wrap;justify-content:center}button{padding:15px 35px;font-size:18px;border:none;border-radius:10px;cursor:pointer;transition:transform 0.1s}button:hover{transform:scale(1.05)}button:active{transform:scale(0.95)}#result{margin-top:20px;font-size:24px;height:30px}#gameOver{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:white;color:#333;padding:40px;border-radius:20px;text-align:center;display:none}#gameOver h2{color:#4CAF50}#gameOver button{margin-top:15px;background:#4CAF50;color:white}</style></head>
<body><div id="stats"><span>Score: <span id="score">0</span></span><span>Time: <span id="timer">30</span></span></div><div id="word">RED</div><div id="question">Does the COLOR match the WORD?</div><div class="buttons"><button id="yesBtn" style="background:#4CAF50;color:white" onclick="answer(true)">YES</button><button id="noBtn" style="background:#e74c3c;color:white" onclick="answer(false)">NO</button></div><div id="result"></div><div id="gameOver"><h2>Game Over!</h2><p>Final Score: <span id="finalScore">0</span></p><button onclick="restart()">Play Again</button></div>
<script>const wordEl=document.getElementById('word'),scoreEl=document.getElementById('score'),timerEl=document.getElementById('timer'),resultEl=document.getElementById('result'),gameOverEl=document.getElementById('gameOver'),finalScoreEl=document.getElementById('finalScore');const colors=[{name:'RED',hex:'#e74c3c'},{name:'BLUE',hex:'#3498db'},{name:'GREEN',hex:'#2ecc71'},{name:'YELLOW',hex:'#f1c40f'},{name:'PURPLE',hex:'#9b59b6'},{name:'ORANGE',hex:'#e67e22'}];let score=0,timeLeft=30,currentMatch,gameActive=true;window.gameScore=0;function newRound(){const wordColor=colors[Math.floor(Math.random()*colors.length)];const textColor=colors[Math.floor(Math.random()*colors.length)];currentMatch=wordColor.name===textColor.name;wordEl.textContent=wordColor.name;wordEl.style.color=textColor.hex}function answer(yes){if(!gameActive)return;if(yes===currentMatch){score+=10;window.gameScore=score;scoreEl.textContent=score;resultEl.textContent='✓ Correct!';resultEl.style.color='#4CAF50'}else{score=Math.max(0,score-5);window.gameScore=score;scoreEl.textContent=score;resultEl.textContent='✗ Wrong!';resultEl.style.color='#e74c3c'}setTimeout(()=>{resultEl.textContent='';newRound()},500)}function startGame(){score=0;timeLeft=30;gameActive=true;window.gameScore=0;scoreEl.textContent='0';timerEl.textContent='30';gameOverEl.style.display='none';newRound();const timer=setInterval(()=>{timeLeft--;timerEl.textContent=timeLeft;if(timeLeft<=0){gameActive=false;clearInterval(timer);finalScoreEl.textContent=score;gameOverEl.style.display='block'}},1000)}function restart(){startGame()}startGame();</script></body></html>`
},
{
title: "Fruit Ninja",
description: "Slice the fruits as they fly across the screen! Avoid the bombs or it's game over.",
code: `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Fruit Ninja</title>
<style>*{margin:0;padding:0}body{background:linear-gradient(to bottom,#2c3e50,#1a252f);min-height:100vh;overflow:hidden;font-family:Arial,sans-serif}#game{position:relative;width:100%;height:100vh;touch-action:none}#score{position:fixed;top:20px;left:20px;color:white;font-size:24px;z-index:100}#lives{position:fixed;top:20px;right:20px;color:white;font-size:24px}.fruit{position:absolute;font-size:50px;cursor:crosshair;user-select:none;transition:opacity 0.2s}.sliced{opacity:0;transform:scale(1.5)rotate(180deg);transition:all 0.3s}#gameOver{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(0,0,0,0.9);color:white;padding:40px;border-radius:20px;text-align:center;display:none;z-index:200}#gameOver button{margin-top:15px;padding:12px 30px;font-size:16px;background:#e74c3c;color:white;border:none;border-radius:10px;cursor:pointer}</style></head>
<body><div id="game"><div id="score">Score: 0</div><div id="lives">❤️❤️❤️</div><div id="gameOver"><h2>Game Over!</h2><p>Final Score: <span id="finalScore">0</span></p><button onclick="restart()">Play Again</button></div></div>
<script>const game=document.getElementById('game'),scoreEl=document.getElementById('score'),livesEl=document.getElementById('lives'),gameOverEl=document.getElementById('gameOver'),finalScoreEl=document.getElementById('finalScore');const fruits=['🍎','🍊','🍋','🍇','🍉','🍓','🥝','🍑'];let score=0,lives=3,gameActive=true;window.gameScore=0;function createFruit(){if(!gameActive)return;const fruit=document.createElement('div');fruit.className='fruit';const isBomb=Math.random()<0.15;fruit.textContent=isBomb?'💣':fruits[Math.floor(Math.random()*fruits.length)];fruit.dataset.bomb=isBomb;const startX=Math.random()*window.innerWidth;fruit.style.left=startX+'px';fruit.style.bottom='-60px';game.appendChild(fruit);const angle=(Math.random()-0.5)*0.5,speed=15+Math.random()*5;let x=startX,y=-60,vy=speed,vx=angle*10,gravity=0.3;function animate(){if(!gameActive){fruit.remove();return}vy-=gravity;y+=vy;x+=vx;fruit.style.bottom=y+'px';fruit.style.left=x+'px';if(y<-100){fruit.remove();if(!fruit.dataset.sliced&&!isBomb){lives--;updateLives();if(lives<=0)endGame()}}else{requestAnimationFrame(animate)}}animate();fruit.onmousedown=fruit.ontouchstart=(e)=>{e.preventDefault();if(!gameActive)return;fruit.dataset.sliced='true';fruit.classList.add('sliced');if(isBomb){lives=0;endGame()}else{score+=10;window.gameScore=score;scoreEl.textContent='Score: '+score}setTimeout(()=>fruit.remove(),300)}}function updateLives(){livesEl.textContent='❤️'.repeat(lives)}function endGame(){gameActive=false;finalScoreEl.textContent=score;gameOverEl.style.display='block'}function restart(){score=0;lives=3;gameActive=true;window.gameScore=0;scoreEl.textContent='Score: 0';updateLives();gameOverEl.style.display='none';document.querySelectorAll('.fruit').forEach(f=>f.remove())}setInterval(createFruit,800);restart();</script></body></html>`
},
{
title: "Math Blaster",
description: "Solve math problems as fast as you can! Each correct answer earns points, but wrong answers cost time.",
code: `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Math Blaster</title>
<style>*{margin:0;padding:0;box-sizing:border-box}body{background:linear-gradient(135deg,#667eea,#764ba2);min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px;font-family:Arial,sans-serif;color:white}#stats{display:flex;gap:40px;margin-bottom:30px;font-size:22px}#problem{font-size:48px;margin-bottom:30px;background:rgba(255,255,255,0.1);padding:20px 40px;border-radius:15px}#answer{font-size:32px;padding:15px 30px;border:none;border-radius:10px;text-align:center;width:150px;outline:none}#result{margin-top:20px;font-size:28px;height:40px}.answers{display:flex;gap:15px;flex-wrap:wrap;justify-content:center}.answer-btn{padding:15px 30px;font-size:24px;border:none;border-radius:10px;cursor:pointer;background:rgba(255,255,255,0.9);color:#333;min-width:80px;transition:transform 0.1s}.answer-btn:hover{transform:scale(1.1)}.answer-btn.correct{background:#4CAF50;color:white}.answer-btn.wrong{background:#e74c3c;color:white}#gameOver{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:white;color:#333;padding:40px;border-radius:20px;text-align:center;display:none}#gameOver h2{color:#4CAF50}#gameOver button{margin-top:15px;padding:12px 30px;font-size:16px;background:#4CAF50;color:white;border:none;border-radius:10px;cursor:pointer}</style></head>
<body><div id="stats"><span>Score: <span id="score">0</span></span><span>Time: <span id="timer">60</span></span></div><div id="problem">5 + 3 = ?</div><div class="answers" id="answers"></div><div id="result"></div><div id="gameOver"><h2>Time's Up!</h2><p>Final Score: <span id="finalScore">0</span></p><p>Problems Solved: <span id="solved">0</span></p><button onclick="restart()">Play Again</button></div>
<script>const problemEl=document.getElementById('problem'),answersEl=document.getElementById('answers'),scoreEl=document.getElementById('score'),timerEl=document.getElementById('timer'),resultEl=document.getElementById('result'),gameOverEl=document.getElementById('gameOver'),finalScoreEl=document.getElementById('finalScore'),solvedEl=document.getElementById('solved');let score=0,timeLeft=60,solved=0,correctAnswer,gameActive=true,timer;window.gameScore=0;function newProblem(){const ops=['+','-','×'];const op=ops[Math.floor(Math.random()*ops.length)];let a,b,answer;if(op==='+'){a=Math.floor(Math.random()*20)+1;b=Math.floor(Math.random()*20)+1;answer=a+b}else if(op==='-'){a=Math.floor(Math.random()*20)+10;b=Math.floor(Math.random()*a)+1;answer=a-b}else{a=Math.floor(Math.random()*12)+1;b=Math.floor(Math.random()*12)+1;answer=a*b}correctAnswer=answer;problemEl.textContent=a+' '+op+' '+b+' = ?';const answers=[answer];while(answers.length<4){const wrong=answer+Math.floor(Math.random()*11)-5;if(wrong!==answer&&wrong>0&&!answers.includes(wrong))answers.push(wrong)}answers.sort(()=>Math.random()-0.5);answersEl.innerHTML='';answers.forEach(ans=>{const btn=document.createElement('button');btn.className='answer-btn';btn.textContent=ans;btn.onclick=()=>checkAnswer(ans,btn);answersEl.appendChild(btn)})}function checkAnswer(ans,btn){if(!gameActive)return;if(ans===correctAnswer){btn.classList.add('correct');score+=10;solved++;window.gameScore=score;scoreEl.textContent=score;resultEl.textContent='✓ Correct!';resultEl.style.color='#4CAF50'}else{btn.classList.add('wrong');timeLeft=Math.max(0,timeLeft-3);timerEl.textContent=timeLeft;resultEl.textContent='✗ -3 seconds!';resultEl.style.color='#e74c3c'}setTimeout(()=>{resultEl.textContent='';newProblem()},600)}function startGame(){score=0;timeLeft=60;solved=0;gameActive=true;window.gameScore=0;scoreEl.textContent='0';timerEl.textContent='60';gameOverEl.style.display='none';newProblem();timer=setInterval(()=>{timeLeft--;timerEl.textContent=timeLeft;if(timeLeft<=0){gameActive=false;clearInterval(timer);finalScoreEl.textContent=score;solvedEl.textContent=solved;gameOverEl.style.display='block'}},1000)}function restart(){startGame()}startGame();</script></body></html>`
},
{
title: "Brick Breaker",
description: "Classic brick breaking action! Bounce the ball to destroy all bricks. Don't let the ball fall!",
code: `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Brick Breaker</title>
<style>*{margin:0;padding:0}body{background:#1a1a2e;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;font-family:Arial,sans-serif}#score{color:white;font-size:20px;margin-bottom:10px}canvas{background:#16213e;border-radius:10px;touch-action:none}#gameOver{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(0,0,0,0.9);color:white;padding:30px;border-radius:15px;text-align:center;display:none}#gameOver button{margin-top:15px;padding:10px 30px;font-size:16px;background:#4CAF50;color:white;border:none;border-radius:5px;cursor:pointer}#controls{color:rgba(255,255,255,0.6);margin-top:10px;font-size:12px}</style></head>
<body><div id="score">Score: 0 | Lives: 3</div><canvas id="game"></canvas><div id="controls">← → or touch to move</div><div id="gameOver"><h2 id="endTitle">Game Over</h2><p>Score: <span id="finalScore">0</span></p><button onclick="restart()">Play Again</button></div>
<script>const canvas=document.getElementById('game'),ctx=canvas.getContext('2d'),scoreEl=document.getElementById('score'),gameOverEl=document.getElementById('gameOver'),endTitleEl=document.getElementById('endTitle'),finalScoreEl=document.getElementById('finalScore');let W=Math.min(400,window.innerWidth-20),H=500;canvas.width=W;canvas.height=H;const paddleW=80,paddleH=12,ballR=8,brickRows=5,brickCols=8,brickW=(W-20)/brickCols,brickH=20;let paddleX=(W-paddleW)/2,ballX=W/2,ballY=H-50,dx=4,dy=-4,score=0,lives=3,bricks=[],gameActive=true;window.gameScore=0;function createBricks(){bricks=[];const colors=['#e74c3c','#e67e22','#f1c40f','#2ecc71','#3498db'];for(let r=0;r<brickRows;r++){for(let c=0;c<brickCols;c++){bricks.push({x:10+c*brickW,y:30+r*(brickH+5),w:brickW-4,h:brickH,color:colors[r],alive:true})}}}function draw(){ctx.clearRect(0,0,W,H);ctx.fillStyle='#4CAF50';ctx.fillRect(paddleX,H-paddleH-10,paddleW,paddleH);ctx.beginPath();ctx.arc(ballX,ballY,ballR,0,Math.PI*2);ctx.fillStyle='white';ctx.fill();bricks.forEach(b=>{if(b.alive){ctx.fillStyle=b.color;ctx.fillRect(b.x,b.y,b.w,b.h)}})}function update(){if(!gameActive)return;ballX+=dx;ballY+=dy;if(ballX<ballR||ballX>W-ballR)dx=-dx;if(ballY<ballR)dy=-dy;if(ballY>H-paddleH-10-ballR&&ballX>paddleX&&ballX<paddleX+paddleW){dy=-dy;dx+=(ballX-(paddleX+paddleW/2))*0.1}if(ballY>H){lives--;if(lives<=0){endGame(false)}else{ballX=W/2;ballY=H-50;dx=4;dy=-4}updateScore()}bricks.forEach(b=>{if(b.alive&&ballX>b.x&&ballX<b.x+b.w&&ballY>b.y&&ballY<b.y+b.h){b.alive=false;dy=-dy;score+=10;window.gameScore=score;updateScore();if(bricks.every(br=>!br.alive))endGame(true)}})}function updateScore(){scoreEl.textContent='Score: '+score+' | Lives: '+lives}function endGame(won){gameActive=false;endTitleEl.textContent=won?'You Win!':'Game Over';finalScoreEl.textContent=score;gameOverEl.style.display='block'}function restart(){score=0;lives=3;window.gameScore=0;paddleX=(W-paddleW)/2;ballX=W/2;ballY=H-50;dx=4;dy=-4;gameActive=true;createBricks();updateScore();gameOverEl.style.display='none'}function gameLoop(){update();draw();requestAnimationFrame(gameLoop)}document.addEventListener('keydown',e=>{if(e.key==='ArrowLeft')paddleX=Math.max(0,paddleX-20);if(e.key==='ArrowRight')paddleX=Math.min(W-paddleW,paddleX+20)});canvas.addEventListener('touchmove',e=>{e.preventDefault();const rect=canvas.getBoundingClientRect();paddleX=Math.max(0,Math.min(W-paddleW,e.touches[0].clientX-rect.left-paddleW/2))});createBricks();gameLoop();</script></body></html>`
},
{
title: "Space Invaders",
description: "Defend Earth from alien invaders! Shoot them down before they reach the bottom. Classic arcade action!",
code: `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Space Invaders</title>
<style>*{margin:0;padding:0}body{background:#000;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;font-family:Arial,sans-serif}#score{color:#0f0;font-size:20px;margin-bottom:10px;font-family:monospace}canvas{background:#000;border:2px solid #0f0}#gameOver{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(0,50,0,0.95);color:#0f0;padding:30px;border:2px solid #0f0;text-align:center;display:none;font-family:monospace}#gameOver button{margin-top:15px;padding:10px 30px;font-size:16px;background:#0f0;color:#000;border:none;cursor:pointer;font-family:monospace}#controls{color:#0f0;margin-top:10px;font-size:12px;font-family:monospace}</style></head>
<body><div id="score">SCORE: 0 | LIVES: 3</div><canvas id="game"></canvas><div id="controls">← → MOVE | SPACE FIRE | TAP TO SHOOT</div><div id="gameOver"><h2 id="endTitle">GAME OVER</h2><p>SCORE: <span id="finalScore">0</span></p><button onclick="restart()">PLAY AGAIN</button></div>
<script>const canvas=document.getElementById('game'),ctx=canvas.getContext('2d'),scoreEl=document.getElementById('score'),gameOverEl=document.getElementById('gameOver'),endTitleEl=document.getElementById('endTitle'),finalScoreEl=document.getElementById('finalScore');let W=Math.min(400,window.innerWidth-20),H=500;canvas.width=W;canvas.height=H;const playerW=40,playerH=20;let playerX=W/2-playerW/2,bullets=[],enemies=[],enemyBullets=[],score=0,lives=3,gameActive=true,enemyDir=1,enemySpeed=1;window.gameScore=0;function createEnemies(){enemies=[];for(let r=0;r<4;r++){for(let c=0;c<8;c++){enemies.push({x:30+c*40,y:50+r*35,w:30,h:20,alive:true})}}}function draw(){ctx.fillStyle='#000';ctx.fillRect(0,0,W,H);ctx.fillStyle='#0f0';ctx.fillRect(playerX,H-playerH-10,playerW,playerH);ctx.fillStyle='#0f0';bullets.forEach(b=>{ctx.fillRect(b.x-2,b.y,4,10)});ctx.fillStyle='#fff';enemies.forEach(e=>{if(e.alive){ctx.font='25px Arial';ctx.fillText('👾',e.x,e.y+20)}});ctx.fillStyle='#f00';enemyBullets.forEach(b=>{ctx.fillRect(b.x-2,b.y,4,10)})}function update(){if(!gameActive)return;bullets.forEach((b,i)=>{b.y-=8;if(b.y<0)bullets.splice(i,1)});enemyBullets.forEach((b,i)=>{b.y+=5;if(b.y>H)enemyBullets.splice(i,1);if(b.y>H-playerH-10&&b.x>playerX&&b.x<playerX+playerW){lives--;enemyBullets.splice(i,1);if(lives<=0)endGame(false);updateScore()}});let hitEdge=false;enemies.forEach(e=>{if(e.alive){e.x+=enemyDir*enemySpeed;if(e.x<10||e.x>W-40)hitEdge=true;if(e.y>H-60)endGame(false)}});if(hitEdge){enemyDir*=-1;enemies.forEach(e=>e.y+=20)}if(Math.random()<0.01){const shooters=enemies.filter(e=>e.alive);if(shooters.length){const e=shooters[Math.floor(Math.random()*shooters.length)];enemyBullets.push({x:e.x+15,y:e.y+20})}}bullets.forEach((b,bi)=>{enemies.forEach(e=>{if(e.alive&&b.x>e.x&&b.x<e.x+e.w&&b.y>e.y&&b.y<e.y+e.h){e.alive=false;bullets.splice(bi,1);score+=10;window.gameScore=score;updateScore();if(enemies.every(en=>!en.alive))endGame(true)}})});enemySpeed=1+Math.floor(score/100)*0.2}function updateScore(){scoreEl.textContent='SCORE: '+score+' | LIVES: '+lives}function shoot(){if(gameActive)bullets.push({x:playerX+playerW/2,y:H-playerH-15})}function endGame(won){gameActive=false;endTitleEl.textContent=won?'YOU WIN!':'GAME OVER';finalScoreEl.textContent=score;gameOverEl.style.display='block'}function restart(){score=0;lives=3;window.gameScore=0;bullets=[];enemyBullets=[];enemyDir=1;enemySpeed=1;playerX=W/2-playerW/2;gameActive=true;createEnemies();updateScore();gameOverEl.style.display='none'}function gameLoop(){update();draw();requestAnimationFrame(gameLoop)}document.addEventListener('keydown',e=>{if(e.key==='ArrowLeft')playerX=Math.max(0,playerX-15);if(e.key==='ArrowRight')playerX=Math.min(W-playerW,playerX+15);if(e.key===' ')shoot()});canvas.addEventListener('touchstart',e=>{const rect=canvas.getBoundingClientRect(),tx=e.touches[0].clientX-rect.left;if(tx<W/3)playerX=Math.max(0,playerX-20);else if(tx>W*2/3)playerX=Math.min(W-playerW,playerX+20);else shoot()});createEnemies();gameLoop();</script></body></html>`
}
];
async function insertGames() {
const client = await pool.connect();
try {
// Get admin user ID
const adminResult = await client.query(
"SELECT id FROM users WHERE role = 'admin' LIMIT 1"
);
if (adminResult.rows.length === 0) {
console.error('No admin user found!');
return;
}
const adminId = adminResult.rows[0].id;
console.log('Using admin ID:', adminId);
for (const game of games) {
const result = await client.query(
`INSERT INTO games (creator_id, title, description, game_code, status, published_at, play_count)
VALUES ($1, $2, $3, $4, 'published', NOW(), $5)
ON CONFLICT DO NOTHING
RETURNING id, title`,
[adminId, game.title, game.description, game.code, Math.floor(Math.random() * 100) + 10]
);
if (result.rows[0]) {
console.log('Created game:', result.rows[0].title);
}
}
console.log('All games created successfully!');
} finally {
client.release();
await pool.end();
}
}
insertGames().catch(console.error);