claude-arcade
Version:
Add classic arcade games to your Claude Code workflow with Ctrl+G
130 lines (129 loc) • 4.55 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SnakeGame = void 0;
class SnakeGame {
constructor() {
this.snake = [{ x: 10, y: 10 }];
this.food = { x: 15, y: 15 };
this.direction = { x: 1, y: 0 };
this.score = 0;
this.playing = false;
this.gameOver = false;
this.WIDTH = 40;
this.HEIGHT = 20;
}
isPlaying() {
return this.playing;
}
isGameOver() {
return this.gameOver;
}
getScore() {
return this.score;
}
generateFood() {
let newFood;
do {
newFood = {
x: Math.floor(Math.random() * this.WIDTH),
y: Math.floor(Math.random() * this.HEIGHT)
};
} while (this.snake.some(segment => segment.x === newFood.x && segment.y === newFood.y));
this.food = newFood;
}
reset() {
this.snake = [{ x: Math.floor(this.WIDTH / 2), y: Math.floor(this.HEIGHT / 2) }];
this.direction = { x: 1, y: 0 };
this.score = 0;
this.gameOver = false;
this.generateFood();
}
start() {
this.playing = true;
this.reset();
}
stop() {
this.playing = false;
}
update() {
if (this.gameOver)
return;
const head = { ...this.snake[0] };
head.x += this.direction.x;
head.y += this.direction.y;
// Wall collision
if (head.x < 0 || head.x >= this.WIDTH || head.y < 0 || head.y >= this.HEIGHT) {
this.gameOver = true;
this.playing = false;
return;
}
// Self collision
if (this.snake.some(segment => segment.x === head.x && segment.y === head.y)) {
this.gameOver = true;
this.playing = false;
return;
}
this.snake.unshift(head);
// Food collision
if (head.x === this.food.x && head.y === this.food.y) {
this.score += 10;
this.generateFood();
}
else {
this.snake.pop();
}
}
setDirection(dx, dy) {
// Prevent reversing
if (this.direction.x === -dx && this.direction.y === -dy)
return;
this.direction = { x: dx, y: dy };
}
draw() {
const GREEN = '\x1b[32m', YELLOW = '\x1b[33m', RED = '\x1b[31m', RESET = '\x1b[0m';
const WHITE = '\x1b[37m';
let output = '\x1b[2J\x1b[1;1H' + YELLOW + `Score: ${this.score} | Length: ${this.snake.length}` + RESET;
// Top border
output += `\x1b[2;1H` + WHITE + '┌' + '─'.repeat(this.WIDTH) + '┐' + RESET;
for (let y = 0; y < this.HEIGHT; y++) {
output += `\x1b[${y + 3};1H` + WHITE + '│' + RESET;
for (let x = 0; x < this.WIDTH; x++) {
const isSnakeHead = this.snake[0].x === x && this.snake[0].y === y;
const isSnakeBody = this.snake.slice(1).some(segment => segment.x === x && segment.y === y);
const isFood = this.food.x === x && this.food.y === y;
if (isSnakeHead) {
output += GREEN + '●' + RESET;
}
else if (isSnakeBody) {
output += GREEN + '█' + RESET;
}
else if (isFood) {
output += RED + '♦' + RESET;
}
else {
output += ' ';
}
}
output += WHITE + '│' + RESET;
}
// Bottom border
output += `\x1b[${this.HEIGHT + 3};1H` + WHITE + '└' + '─'.repeat(this.WIDTH) + '┘' + RESET;
output += `\x1b[${this.HEIGHT + 5};1H` + GREEN + 'Q/Ctrl+C: Exit | WASD/Arrows: Move | P: Restart' + RESET;
return output;
}
drawGameOver() {
const RED = '\x1b[31m', YELLOW = '\x1b[33m', RESET = '\x1b[0m';
let output = '\x1b[2J\x1b[10;1H';
output += RED + '🐍 GAME OVER! 🐍\n\n' + RESET;
output += YELLOW + `Final Score: ${this.score}\n` + RESET;
output += YELLOW + `Snake Length: ${this.snake.length}\n` + RESET;
return output;
}
drawWelcome() {
const GREEN = '\x1b[32m', YELLOW = '\x1b[33m', RESET = '\x1b[0m';
return '\x1b[2J\x1b[10;1H' + GREEN + '🐍 SNAKE GAME 🐍\n\n' + RESET +
YELLOW + 'Press P to start!\n' +
YELLOW + 'Press Q or Ctrl+C to exit back to Claude\n' + RESET;
}
}
exports.SnakeGame = SnakeGame;