Last active
April 3, 2023 18:54
-
-
Save justinledwards/36da919b49a7e7261a02ede858a6cabb to your computer and use it in GitHub Desktop.
CLI Snake with nodejs chalk and keypress
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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