Files
gamercomp/backend/scripts/publish-sample-games.js
2026-04-30 02:14:25 +00:00

210 lines
8.4 KiB
JavaScript

#!/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();