Files
2026-04-30 02:14:25 +00:00

143 lines
38 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);