#!/usr/bin/env node /** * Publish sample games under the "Arcade Master" account * Run with: node scripts/publish-sample-games.js */ require('dotenv').config(); const { Pool } = require('pg'); const bcrypt = require('bcrypt'); const fs = require('fs'); const path = require('path'); 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 ARCADE_MASTER = { username: 'ArcadeMaster', email: 'arcademaster@gamercomp.com', password: 'ArcadeMaster2024!', displayName: 'Arcade Master' }; const GAMES_DIR = path.join(__dirname, '../../games/samples'); // Game metadata (title, description, prompt for each game) const GAME_METADATA = { 'brick-breaker.html': { title: 'Brick Breaker', description: 'Classic paddle and ball game! Move your paddle to bounce the ball and break all the bricks. Easy to learn, fun to play!', prompt: 'Create a classic brick breaker game with a wide paddle, slow ball, and 5 lives. Make it easy for kids to play.' }, 'pong.html': { title: 'Pong', description: 'The classic 2-player game! Play against the computer and try to score 7 points first. The computer makes mistakes so you can win!', prompt: 'Create a single-player Pong game vs AI. The AI should make mistakes sometimes so kids can win.' }, 'dino-runner.html': { title: 'Dino Runner', description: 'Run as far as you can! Tap or press space to jump over cacti. The game starts slow so you can learn the timing.', prompt: 'Create a Chrome Dino style endless runner. Start very slow with obstacles far apart.' }, 'asteroids.html': { title: 'Asteroids', description: 'Classic space shooter! Rotate your ship and blast the asteroids. You have shields that protect you when you get hit.', prompt: 'Create an Asteroids game with a large ship, slow asteroids, and invincibility after being hit.' }, 'whack-a-mole.html': { title: 'Whack-a-Mole', description: 'Tap the moles when they pop up! They stay visible for a long time so you can catch them. 60 seconds to get the high score!', prompt: 'Create a whack-a-mole game with a 3x3 grid. Moles should stay up for 2+ seconds.' }, 'block-drop.html': { title: 'Block Drop', description: 'Stack falling blocks to complete lines! Pieces fall slowly so you have time to think. A ghost piece shows where blocks will land.', prompt: 'Create a Tetris-style game with very slow falling pieces and helpful ghost piece preview.' }, 'twenty-forty-eight.html': { title: '2048', description: 'Swipe to combine numbers! 2+2=4, 4+4=8, and so on. No time limit - take as long as you need to plan your moves.', prompt: 'Create the 2048 puzzle game with no time pressure and clear directional arrows.' }, 'memory-match.html': { title: 'Memory Match', description: 'Find all the matching pairs! Flip cards to reveal pictures and remember where they are. Only 6 pairs to start.', prompt: 'Create a memory matching game with a small 3x4 grid (6 pairs) and cards that stay flipped for 1.5 seconds.' }, 'connect-four.html': { title: 'Connect Four', description: 'Drop your pieces to connect 4 in a row! Play against a friendly computer that lets you learn the game.', prompt: 'Create Connect Four vs AI. The AI should play randomly most of the time so kids can win.' }, 'lights-out.html': { title: 'Lights Out', description: 'Turn off all the lights to win! Clicking a light toggles it and its neighbors. Includes a helpful tutorial!', prompt: 'Create Lights Out puzzle with an easy 3x3 grid, interactive tutorial, and hover highlights.' }, 'geometry-runner.html': { title: 'Geometry Runner', description: 'Jump over obstacles in this rhythm-style runner! Starts very slow with obstacles far apart so you can learn the timing.', prompt: 'Create a Geometry Dash style runner that starts very slow with generous jump timing.' }, 'lunar-lander.html': { title: 'Lunar Lander', description: 'Land your spaceship safely on the moon! You have lots of fuel and the landing pad is big. Low gravity makes it easier.', prompt: 'Create a Lunar Lander with generous fuel, low gravity, and a large landing pad.' }, 'fruit-slicer.html': { title: 'Fruit Slicer', description: 'Swipe through the fruit to slice it! Fruit moves slowly and stays on screen longer. Watch out for bombs!', prompt: 'Create a Fruit Ninja style game with big slow fruit and rare bombs at the start.' }, 'maze-chomper.html': { title: 'Maze Chomper', description: 'Eat all the dots while avoiding ghosts! You move much faster than the ghosts, and power pellets last a long time.', prompt: 'Create a Pac-Man style game where the player is 3x faster than ghosts and power-ups last 10 seconds.' }, 'platformer.html': { title: 'Platformer', description: 'Jump and run to reach the flag! 5 lives, slow enemies, and big platforms make this adventure fun for everyone.', prompt: 'Create a simple platformer with 5 lives, slow enemies, and generous jump mechanics.' }, 'bridge-builder.html': { title: 'Bridge Builder', description: 'Build a bridge to get the car across! Strong beams and a light car make building easy. Includes a helpful tutorial.', prompt: 'Create a bridge building physics game with strong materials, light car, and visual tutorial.' } }; async function getOrCreateArcadeMaster() { // Check if user exists let result = await pool.query( 'SELECT * FROM users WHERE username = $1', [ARCADE_MASTER.username] ); if (result.rows.length > 0) { console.log(`Found existing Arcade Master account (ID: ${result.rows[0].id})`); return result.rows[0]; } // Create new user const passwordHash = await bcrypt.hash(ARCADE_MASTER.password, 10); result = await pool.query( `INSERT INTO users (username, email, password_hash, display_name, tokens_balance, role) VALUES ($1, $2, $3, $4, 9999, 'developer') RETURNING *`, [ARCADE_MASTER.username, ARCADE_MASTER.email, passwordHash, ARCADE_MASTER.displayName] ); console.log(`Created Arcade Master account (ID: ${result.rows[0].id})`); return result.rows[0]; } async function publishGame(creatorId, filename, gameCode) { const metadata = GAME_METADATA[filename]; if (!metadata) { console.log(` Skipping ${filename} - no metadata defined`); return null; } // Check if game already exists const existing = await pool.query( 'SELECT id FROM games WHERE title = $1 AND creator_id = $2', [metadata.title, creatorId] ); if (existing.rows.length > 0) { // Update existing game await pool.query( `UPDATE games SET game_code = $1, description = $2, prompt = $3 WHERE id = $4`, [gameCode, metadata.description, metadata.prompt, existing.rows[0].id] ); console.log(` Updated: ${metadata.title} (ID: ${existing.rows[0].id})`); return existing.rows[0].id; } // Insert new game as published const result = await pool.query( `INSERT INTO games (creator_id, title, description, game_code, prompt, status, published_at) VALUES ($1, $2, $3, $4, $5, 'published', NOW()) RETURNING id`, [creatorId, metadata.title, metadata.description, gameCode, metadata.prompt] ); console.log(` Published: ${metadata.title} (ID: ${result.rows[0].id})`); return result.rows[0].id; } async function main() { try { console.log('=== Publishing Sample Games ===\n'); // Get or create Arcade Master account const arcadeMaster = await getOrCreateArcadeMaster(); console.log(''); // Read all game files const files = fs.readdirSync(GAMES_DIR).filter(f => f.endsWith('.html')); console.log(`Found ${files.length} game files in ${GAMES_DIR}\n`); let published = 0; for (const filename of files) { const filepath = path.join(GAMES_DIR, filename); const gameCode = fs.readFileSync(filepath, 'utf8'); const gameId = await publishGame(arcadeMaster.id, filename, gameCode); if (gameId) published++; } console.log(`\n=== Complete! ===`); console.log(`Published ${published} games under ${ARCADE_MASTER.username}`); console.log(`\nArcade Master credentials:`); console.log(` Username: ${ARCADE_MASTER.username}`); console.log(` Password: ${ARCADE_MASTER.password}`); } catch (error) { console.error('Error:', error); process.exit(1); } finally { await pool.end(); } } main();