UNPKG

claude-arcade

Version:

Add classic arcade games to your Claude Code workflow with Ctrl+G

269 lines (268 loc) 7.95 kB
#!/usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const readline = __importStar(require("readline")); // ANSI escape codes const CLEAR = '\x1b[2J'; const HIDE_CURSOR = '\x1b[?25l'; const SHOW_CURSOR = '\x1b[?25h'; const RESET = '\x1b[0m'; const YELLOW = '\x1b[33m'; const CYAN = '\x1b[36m'; const RED = '\x1b[31m'; const GREEN = '\x1b[32m'; const MAGENTA = '\x1b[35m'; const ALT_SCREEN = '\x1b[?1049h'; // Switch to alternate screen const MAIN_SCREEN = '\x1b[?1049l'; // Switch back to main screen function moveTo(x, y) { return `\x1b[${y};${x}H`; } // Game settings const WIDTH = 50; const HEIGHT = 20; const PADDLE_WIDTH = 7; // Game state let ball = { x: 25, y: 10, vx: 1, vy: 1 }; let paddle = { x: 21 }; let blocks = []; let score = 0; let playing = false; let gameLoop = null; // Color map const colors = { red: RED, yellow: YELLOW, green: GREEN, magenta: MAGENTA, }; // Initialize process.stdout.write(ALT_SCREEN + HIDE_CURSOR + CLEAR); // Create blocks function initBlocks() { blocks = []; const colorNames = ['red', 'yellow', 'green', 'magenta']; for (let row = 0; row < 4; row++) { for (let col = 0; col < 10; col++) { blocks.push({ x: col * 5, y: row + 2, active: true, color: colorNames[row], }); } } } // Reset game function reset() { ball = { x: Math.floor(WIDTH * 0.3 + Math.random() * WIDTH * 0.4), y: HEIGHT - 5, vx: Math.random() > 0.5 ? 1 : -1, vy: -1, }; paddle = { x: WIDTH / 2 - PADDLE_WIDTH / 2 }; score = 0; initBlocks(); } // Start game function start() { playing = true; reset(); if (gameLoop) clearInterval(gameLoop); gameLoop = setInterval(update, 100); } // Game loop function update() { ball.x += ball.vx; ball.y += ball.vy; // Wall bounce if (ball.x <= 0 || ball.x >= WIDTH - 1) { ball.vx *= -1; ball.x = Math.max(0, Math.min(WIDTH - 1, ball.x)); } if (ball.y <= 0) { ball.vy *= -1; ball.y = 0; } // Paddle hit const ballX = Math.round(ball.x); const ballY = Math.round(ball.y); if (ballY >= HEIGHT - 2 && ballX >= paddle.x && ballX < paddle.x + PADDLE_WIDTH && ball.vy > 0) { ball.vy *= -1; const hitPos = (ball.x - paddle.x) / PADDLE_WIDTH; if (hitPos < 0.3) ball.vx = -1; else if (hitPos > 0.7) ball.vx = 1; } // Block hit for (const block of blocks) { if (!block.active) continue; if (ballX >= block.x && ballX < block.x + 5 && ballY === block.y) { block.active = false; ball.vy *= -1; score += 10; break; } } // Win/Lose if (blocks.every(b => !b.active)) { endGame(true); return; } if (ball.y >= HEIGHT) { endGame(false); return; } draw(); } // Draw game - optimized function draw() { let output = moveTo(1, 1) + YELLOW + `Score: ${score}` + RESET; const ballX = Math.round(ball.x); const ballY = Math.round(ball.y); const paddleY = HEIGHT - 2; for (let y = 0; y < HEIGHT; y++) { output += moveTo(1, y + 3); let line = ''; for (let x = 0; x < WIDTH; x++) { const block = blocks.find(b => b.active && y === b.y && x >= b.x && x < b.x + 5); const isBall = ballX === x && ballY === y; const isPaddle = y === paddleY && x >= paddle.x && x < paddle.x + PADDLE_WIDTH; if (block) { line += colors[block.color] + '█' + RESET; } else if (isBall) { line += YELLOW + '●' + RESET; } else if (isPaddle) { line += CYAN + '═' + RESET; } else { line += ' '; } } output += line; } output += moveTo(1, HEIGHT + 4) + GREEN + 'Press P to play | ←→ to move | Q to quit' + RESET; process.stdout.write(output); } // Fast paddle redraw - single write! function drawPaddleOnly() { const paddleY = HEIGHT - 2; const ballX = Math.round(ball.x); const ballY = Math.round(ball.y); let line = ''; for (let x = 0; x < WIDTH; x++) { const isBall = ballX === x && ballY === paddleY; const isPaddle = x >= paddle.x && x < paddle.x + PADDLE_WIDTH; if (isBall) { line += YELLOW + '●' + RESET; } else if (isPaddle) { line += CYAN + '═' + RESET; } else { line += ' '; } } process.stdout.write(moveTo(1, paddleY + 3) + line); } // End game function endGame(won) { if (gameLoop) clearInterval(gameLoop); playing = false; let output = CLEAR + moveTo(1, 10); if (won) { output += GREEN + '🎉 YOU WIN! 🎉\n\n' + RESET; } else { output += RED + '💀 GAME OVER! 💀\n\n' + RESET; } output += YELLOW + `Final Score: ${score}\n\n` + RESET; output += GREEN + 'Press P to play again\n' + RESET; process.stdout.write(output); } // Show welcome function showWelcome() { const output = CLEAR + moveTo(1, 10) + CYAN + 'BREAKOUT GAME\n\n' + RESET + GREEN + 'Press P to start!\n' + RESET; process.stdout.write(output); } // Cleanup function cleanup() { if (gameLoop) clearInterval(gameLoop); process.stdout.write(SHOW_CURSOR + MAIN_SCREEN); if (process.stdin.isTTY) { process.stdin.setRawMode(false); } } // Keyboard input if (process.stdin.isTTY) { process.stdin.setRawMode(true); readline.emitKeypressEvents(process.stdin); process.stdin.on('keypress', (_str, key) => { if (!key) return; if (key.name === 'q' || (key.ctrl && key.name === 'c')) { cleanup(); process.exit(0); } if (key.name === 'p' && !playing) { start(); } if (playing) { if (key.name === 'left' || key.name === 'a') { paddle.x = Math.max(0, paddle.x - 5); drawPaddleOnly(); } else if (key.name === 'right' || key.name === 'd') { paddle.x = Math.min(WIDTH - PADDLE_WIDTH, paddle.x + 5); drawPaddleOnly(); } } }); } process.on('SIGINT', () => { cleanup(); process.exit(0); }); process.on('exit', cleanup); // Start showWelcome();