feat: snake game
feat: unit.random feat: ui.center
This commit is contained in:
@ -23,10 +23,13 @@ export default class Interface {
|
||||
|
||||
this.cursor = ansi(this.output).hide();
|
||||
|
||||
this.columns = this.output.columns;
|
||||
this.rows = this.output.rows;
|
||||
|
||||
this.input.addListener('data', data => {
|
||||
let always = listeners.filter(listener => {
|
||||
return listener.key === '';
|
||||
});
|
||||
|
||||
always.forEach(listener => listener.fn());
|
||||
|
||||
let key = Object.keys(keys).find((value, i) => {
|
||||
return keys[value] === data;
|
||||
});
|
||||
@ -41,6 +44,14 @@ export default class Interface {
|
||||
})
|
||||
}
|
||||
|
||||
get columns() {
|
||||
return this.output.columns;
|
||||
}
|
||||
|
||||
get rows() {
|
||||
return this.output.rows;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.output.write('\u001b[2J\u001b[0;0H');
|
||||
}
|
||||
@ -50,6 +61,17 @@ export default class Interface {
|
||||
}
|
||||
|
||||
onKey(key, fn) {
|
||||
if (typeof key === 'function') {
|
||||
fn = key;
|
||||
key = '';
|
||||
}
|
||||
listeners.push({ key, fn });
|
||||
}
|
||||
|
||||
get center() {
|
||||
return {
|
||||
x: this.output.columns / 2,
|
||||
y: this.output.rows / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,11 @@ export default class Unit {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
random() {
|
||||
this.x = Math.max(1, Math.floor(Math.random() * this.output.columns));
|
||||
this.y = Math.max(1, Math.floor(Math.random() * this.output.rows));
|
||||
}
|
||||
|
||||
speed() {
|
||||
let signs = [Math.random() > 0.5 ? -1 : 1, Math.random() > 0.5 ? -1 : 1];
|
||||
return [Math.random() * signs[0], Math.random() * signs[1]];
|
||||
|
169
src/snake.js
Normal file
169
src/snake.js
Normal file
@ -0,0 +1,169 @@
|
||||
import Unit from './classes/unit';
|
||||
import Interface from './classes/interface';
|
||||
import fs from 'fs';
|
||||
|
||||
let log = fs.createWriteStream(__dirname + '/log');
|
||||
|
||||
let FRAME = 100;
|
||||
|
||||
let ui = new Interface();
|
||||
|
||||
let snake = [];
|
||||
const UP = 0;
|
||||
const DOWN = 1;
|
||||
const LEFT = 2;
|
||||
const RIGHT = 3;
|
||||
let head = createPart();
|
||||
head.color = '#71da29';
|
||||
head.dieOnExit = true;
|
||||
createPart();
|
||||
createPart();
|
||||
|
||||
let point = new Unit(ui);
|
||||
point.shape = '+';
|
||||
point.color = '#f3ff6a';
|
||||
point.random();
|
||||
|
||||
let score = 0;
|
||||
|
||||
let stop = false;
|
||||
function loop() {
|
||||
if (stop) return;
|
||||
ui.clear();
|
||||
|
||||
point.draw();
|
||||
|
||||
snake.forEach((part, i) => {
|
||||
part.draw();
|
||||
|
||||
if (i > 0) part.findWay(i);
|
||||
|
||||
part.move();
|
||||
});
|
||||
|
||||
if (head.collides(point)) {
|
||||
point.random();
|
||||
createPart();
|
||||
score++;
|
||||
|
||||
FRAME--;
|
||||
}
|
||||
|
||||
if (head.collides(snake.slice(2))) {
|
||||
gameover();
|
||||
}
|
||||
|
||||
ui.cursor.goto(0, 0).yellow().write(`Score: ${score}`);
|
||||
ui.cursor.reset();
|
||||
|
||||
setTimeout(loop, FRAME);
|
||||
}
|
||||
|
||||
loop();
|
||||
|
||||
ui.onKey('right', () => {
|
||||
changeDirection(RIGHT);
|
||||
});
|
||||
ui.onKey('down', () => {
|
||||
changeDirection(DOWN);
|
||||
});
|
||||
ui.onKey('up', () => {
|
||||
changeDirection(UP);
|
||||
});
|
||||
ui.onKey('left', () => {
|
||||
changeDirection(LEFT);
|
||||
});
|
||||
|
||||
ui.onKey(() => {
|
||||
if (!stop) return;
|
||||
|
||||
stop = false;
|
||||
snake = [];
|
||||
head = createPart();
|
||||
head.color = '#71da29';
|
||||
createPart();
|
||||
createPart();
|
||||
|
||||
score = 0;
|
||||
|
||||
point.random();
|
||||
|
||||
loop();
|
||||
})
|
||||
|
||||
function changeDirection(dir) {
|
||||
head.direction = dir;
|
||||
}
|
||||
|
||||
function createPart() {
|
||||
let part = new Unit(ui);
|
||||
let last = snake[snake.length - 1];
|
||||
|
||||
let direction;
|
||||
if (!last) {
|
||||
direction = UP;
|
||||
} else {
|
||||
direction = last.direction;
|
||||
}
|
||||
|
||||
part.shape = '•';
|
||||
part.color = '#bdfe91';
|
||||
part.direction = direction;
|
||||
part.changeTo = null;
|
||||
|
||||
part.findWay = function(i) {
|
||||
let ahead = snake[i - 1];
|
||||
|
||||
if (this.changeTo !== null) {
|
||||
this.direction = this.changeTo;
|
||||
this.changeTo = null;
|
||||
}
|
||||
if (this.direction !== ahead.direction) {
|
||||
this.changeTo = ahead.direction;
|
||||
}
|
||||
}
|
||||
|
||||
part.speed = function(multiplier = 1) {
|
||||
let { direction } = part;
|
||||
let x = direction == LEFT ? -1 :
|
||||
direction == RIGHT ? 1 : 0;
|
||||
let y = direction == UP ? -1 :
|
||||
direction == DOWN ? 1 : 0;
|
||||
|
||||
return [x * multiplier, y * multiplier];
|
||||
}
|
||||
|
||||
let [dX, dY] = part.speed();
|
||||
dX *= -1;
|
||||
dY *= -1;
|
||||
|
||||
let x = last ? last.x + dX : ui.center.x;
|
||||
let y = last ? last.y + dY : ui.center.y;
|
||||
|
||||
part.go(x, y);
|
||||
|
||||
snake.push(part);
|
||||
return part;
|
||||
}
|
||||
|
||||
function gameover() {
|
||||
const MSG = 'Game Over!';
|
||||
ui.cursor.goto(ui.center.x - MSG.length / 2, ui.center.y);
|
||||
ui.cursor.red();
|
||||
ui.cursor.bold();
|
||||
ui.write(MSG);
|
||||
|
||||
ui.cursor.reset();
|
||||
ui.cursor.hex('#f65590');
|
||||
const RETRY = 'Press any key to play again';
|
||||
ui.cursor.goto(ui.center.x - RETRY.length / 2, ui.center.y + 2);
|
||||
ui.write(RETRY);
|
||||
|
||||
stop = true;
|
||||
}
|
||||
|
||||
process.on('exit', function() {
|
||||
ui.cursor.horizontalAbsolute(0).eraseLine()
|
||||
ui.cursor.show();
|
||||
log.end();
|
||||
});
|
@ -2,15 +2,17 @@ import Unit from './classes/unit';
|
||||
import Interface from './classes/interface';
|
||||
|
||||
const FRAME = 20;
|
||||
const ENEMY_SPAWN_RATE = 400;
|
||||
let ENEMY_SPAWN_RATE = 1000;
|
||||
let RELOAD_TIME = 200;
|
||||
|
||||
let ui = new Interface();
|
||||
|
||||
let player = new Unit(ui);
|
||||
player.go(1, ui.output.rows / 2);
|
||||
player.go(1, ui.center.y);
|
||||
player.shape = '=>';
|
||||
player.color = '#77d6ff';
|
||||
player.bold = true;
|
||||
player.canShoot = true;
|
||||
|
||||
let explosion = new Unit(ui);
|
||||
explosion.dead = true;
|
||||
@ -34,6 +36,8 @@ setInterval(() => {
|
||||
enemy.shape = '*';
|
||||
missle.dead = true;
|
||||
|
||||
ENEMY_SPAWN_RATE -= 5;
|
||||
|
||||
score++;
|
||||
}
|
||||
|
||||
@ -72,18 +76,25 @@ ui.onKey('up', () => {
|
||||
ui.onKey('left', () => {
|
||||
player.move(-1, 0);
|
||||
});
|
||||
|
||||
ui.onKey('space', () => {
|
||||
if (!player.canShoot) return;
|
||||
|
||||
player.canShoot = false;
|
||||
|
||||
let missle = new Unit(ui);
|
||||
missle.go(player.x, player.y);
|
||||
missle.shape = '+';
|
||||
missle.dieOnExit = true;
|
||||
|
||||
missles.push(missle);
|
||||
|
||||
setTimeout(() => {
|
||||
player.canShoot = true;
|
||||
}, RELOAD_TIME)
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
if (enemies.length > 5) return;
|
||||
|
||||
(function loop() {
|
||||
let enemy = new Unit(ui);
|
||||
enemy.go(Math.random() * ui.output.columns, 0);
|
||||
enemy.shape = 'o';
|
||||
@ -95,7 +106,9 @@ setInterval(() => {
|
||||
}
|
||||
|
||||
enemies.push(enemy);
|
||||
}, 1000);
|
||||
|
||||
setTimeout(loop, ENEMY_SPAWN_RATE);
|
||||
}());
|
||||
|
||||
process.on('exit', function() {
|
||||
ui.cursor.horizontalAbsolute(0).eraseLine()
|
||||
|
Reference in New Issue
Block a user