Skip to content

Instantly share code, notes, and snippets.

@justinledwards
Last active April 3, 2023 18:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justinledwards/36da919b49a7e7261a02ede858a6cabb to your computer and use it in GitHub Desktop.
Save justinledwards/36da919b49a7e7261a02ede858a6cabb to your computer and use it in GitHub Desktop.
CLI Snake with nodejs chalk and keypress
import chalk from 'chalk';
import keypress from 'keypress';
// npm i chalk keypress
keypress(process.stdin);
const width = process.stdout.columns;
const height = process.stdout.rows - 1;
let snake = [{ x: Math.floor(width / 2), y: Math.floor(height / 2) }];
let food = { x: Math.floor(Math.random() * width), y: Math.floor(Math.random() * height) };
let direction = 'right';
let speed = 100;
const hideCursor = () => {
process.stdout.write('\x1b[?25l');
};
const showCursor = () => {
process.stdout.write('\x1b[?25h');
};
const draw = (x, y, color) => {
process.stdout.write(`\x1b[${y + 1};${x + 1}H${color(' ')}`);
};
const move = () => {
let newX = snake[0].x;
let newY = snake[0].y;
switch (direction) {
case 'up':
newY--;
break;
case 'down':
newY++;
break;
case 'left':
newX--;
break;
case 'right':
newX++;
break;
}
if (
newX < 0 ||
newY < 0 ||
newX >= width ||
newY >= height ||
snake.slice(1).some((part) => part.x === newX && part.y === newY)
) {
clearInterval(gameLoop);
showCursor();
console.log(chalk.red('Game over!'));
process.exit(0);
}
// Clear tail
const tail = snake.pop();
draw(tail.x, tail.y, chalk.reset);
// Move head
snake.unshift({ x: newX, y: newY });
draw(newX, newY, chalk.bgGreen);
// Check for food
if (newX === food.x && newY === food.y) {
// Extend snake
snake.push(tail);
// Generate new food
food.x = Math.floor(Math.random() * width);
food.y = Math.floor(Math.random() * height);
speed *= 0.9;
// Draw new food
draw(food.x, food.y, chalk.bgRed);
}
};
process.stdin.on('keypress', (ch, key) => {
if (key && key.ctrl && key.name === 'c') {
showCursor();
process.exit();
}
switch (key.name) {
case 'up':
case 'down':
if (direction !== 'up' && direction !== 'down') {
direction = key.name;
}
break;
case 'left':
case 'right':
if (direction !== 'left' && direction !== 'right') {
direction = key.name;
}
break;
}
});
process.stdin.setRawMode(true);
process.stdin.resume();
hideCursor();
// Draw initial state
draw(snake[0].x, snake[0].y, chalk.bgGreen);
draw(food.x, food.y, chalk.bgRed);
const gameLoop = setInterval(move, speed);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment