Skip to content

Instantly share code, notes, and snippets.

@katjad
Created November 29, 2019 00:09
Show Gist options
  • Save katjad/1f5c9a2d8982777e3fe7f1d286ab6818 to your computer and use it in GitHub Desktop.
Save katjad/1f5c9a2d8982777e3fe7f1d286ab6818 to your computer and use it in GitHub Desktop.
Space Invaders
const term = require( 'terminal-kit' ).terminal ;
const termkit = require( 'terminal-kit' )
const ScreenBuffer = termkit.ScreenBuffer;
const INVADERS_WIDTH = 80;
const NO_PER_ROW = 11;
const ROWS = 5;
const COLUMN_WIDTH = 6;
const ROW_HEIGHT = 4;
const FRAMES_TILL_MOVE = 10;
let countframes = 0;
let viewport, sprites = {};
let x, y, startx, xshoot;
let startState, moveRight = true;
// /òó\ /òó\
// \/ /\
// v v
// M M M M
// < > > <
// dôb dôb
// ^ ^ v v
// ṁ
// d[_]b
const drawSprite = (sprite, x, y) => {
sprite.draw({ dst: sprites.invaders, x: x, y: y })
}
const drawSpaceInvaders = () => {
const inv1string = (startState) ? ' \n/òó\\\n \\/ ' : ' \n/òó\\\n /\\ ';
const inv2string = (startState) ? ' v \nM M \n> < ' : ' v \nM M \n< > ';
const inv3string = (startState) ? ' \ndôb \n^ ^ ' : ' \ndôb \nv v ';
sprites.invader1 = ScreenBuffer.createFromString(
{ attr: { color: 'blue' } }, inv1string
)
sprites.invader2 = ScreenBuffer.createFromString(
{ attr: { color: 'white' } }, inv2string
)
sprites.invader3 = ScreenBuffer.createFromString(
{ attr: { color: 'green' } }, inv3string
)
const spriteArray = [sprites.invader1,
sprites.invader2,
sprites.invader2,
sprites.invader3,
sprites.invader3 ]
ypos = 0
spriteArray.forEach(sprite => {
let xpos = 0;
[...Array(NO_PER_ROW)].forEach((_, i) => {drawSprite(sprite, xpos, ypos); xpos += COLUMN_WIDTH })
ypos += ROW_HEIGHT
})
}
const createInvadersBuffer = () => {
sprites.invaders = new ScreenBuffer({
dst: viewport,
width: INVADERS_WIDTH,
height: ROW_HEIGHT * ROWS,
x: x,
y: y
})
}
const createShooter = () => {
sprites.shooter = ScreenBuffer.createFromString(
{ attr: { color: 'white' } }, ' ^ \nd[_]b'
)
}
const drawShooter = () => {
sprites.invaders.draw();
sprites.shooter.draw({
dst: viewport,
x: xshoot,
y: viewport.height - 2
})
}
terminate = () => {
term.hideCursor( false ) ;
term.grabInput( false ) ;
setTimeout( function() {
term.moveTo( 1 , term.height , '\n\n' ) ;
process.exit() ;
} , 100 ) ;
}
inputs = key =>
{
switch ( key )
{
case 'LEFT' :
if (xshoot >= startx + 5) {
xshoot -= 2;
}
break ;
case 'RIGHT' :
if (xshoot < startx + INVADERS_WIDTH -10) {
xshoot += 2;
}
break ;
case 'UP' :
shootBullets()
break;
case 'q':
case 'CTRL_C':
terminate() ;
break ;
}
}
const nextPosition = () => {
if (moveRight) {
if (sprites.invaders.x < startx + INVADERS_WIDTH - (COLUMN_WIDTH*NO_PER_ROW)) {
sprites.invaders.x += 2;
} else {
sprites.invaders.y += ROW_HEIGHT / 2;
moveRight = false;
}
} else {
if (sprites.invaders.x >= startx) {
sprites.invaders.x -= 2;
} else {
sprites.invaders.y += ROW_HEIGHT / 2;
moveRight = true;
}
}
}
const showText = () => {
term.moveTo(startx + 5).eraseLine.white(
'Left and Right Arrow keys: move the player - Q/Ctrl-C: Quit'
);
}
const animate = () => {
term.clear();
viewport.clear();
drawShooter();
if (countframes % FRAMES_TILL_MOVE === 0) {
nextPosition();
startState = !startState;
drawSpaceInvaders();
}
viewport.draw();
showText();
countframes += 1;
setTimeout( animate, 1000 / 60);
}
const init = callback => {
termkit.getDetectedTerminal((error, detectedTerm) => {
if (error) {
throw new Error('Cannot detect terminal.')
}
terminal = detectedTerm;
twidth = terminal.width;
theight = terminal.height;
x = (twidth > INVADERS_WIDTH) ? (twidth - INVADERS_WIDTH) / 2 : 10;
startx = x;
xshoot = twidth / 2;
y = (theight > ROW_HEIGHT*ROWS) ? theight - (ROW_HEIGHT*ROWS + 25) : 2;
term.clear();
inputs()
viewport = new ScreenBuffer({
dst: terminal,
width: Math.min( terminal.width ),
height: Math.min( terminal.height - 1),
y: 2,
noFill: true
});
createInvadersBuffer();
drawSpaceInvaders();
createShooter();
term.grabInput() ;
term.on( 'key' , inputs ) ;
callback();
})
}
init(() => animate())
{
"name": "space_invaders",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@cronvel/get-pixels": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@cronvel/get-pixels/-/get-pixels-3.3.1.tgz",
"integrity": "sha512-jgDb8vGPkpjRDbiYyHTI2Bna4HJysjPNSiERzBnRJjCR/YqC3u0idTae0tmNECsaZLOpAWmlK9wiIwnLGIT9Bg==",
"requires": {
"jpeg-js": "^0.1.1",
"ndarray": "^1.0.13",
"ndarray-pack": "^1.1.1",
"node-bitmap": "0.0.1",
"omggif": "^1.0.5",
"pngjs": "^2.0.0"
}
},
"chroma-js": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.1.0.tgz",
"integrity": "sha512-uiRdh4ZZy+UTPSrAdp8hqEdVb1EllLtTHOt5TMaOjJUvi+O54/83Fc5K2ld1P+TJX+dw5B+8/sCgzI6eaur/lg==",
"requires": {
"cross-env": "^6.0.3"
}
},
"cross-env": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz",
"integrity": "sha512-+KqxF6LCvfhWvADcDPqo64yVIB31gv/jQulX2NGzKS/g3GEVz6/pt4wjHFtFWsHMddebWD/sDthJemzM4MaAag==",
"requires": {
"cross-spawn": "^7.0.0"
}
},
"cross-spawn": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz",
"integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==",
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
}
},
"cwise-compiler": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz",
"integrity": "sha1-9NZnQQ6FDToxOn0tt7HlBbsDTMU=",
"requires": {
"uniq": "^1.0.0"
}
},
"iota-array": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz",
"integrity": "sha1-ge9X/l0FgUzVjCSDYyqZwwoOgIc="
},
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
"jpeg-js": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.1.2.tgz",
"integrity": "sha1-E1uZLAV1yYXPoPSUoyJ+0jhYPs4="
},
"lazyness": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/lazyness/-/lazyness-1.1.1.tgz",
"integrity": "sha512-rYHC6l6LeRlJSt5jxpqN8z/49gZ0CqLi89HAGzJjHahCFlqEjFGFN9O15hmzSzUGFl7zN/vOWduv/+0af3r/kQ=="
},
"ndarray": {
"version": "1.0.18",
"resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.18.tgz",
"integrity": "sha1-tg06cyJOxVXQ+qeXEeUCRI/T95M=",
"requires": {
"iota-array": "^1.0.0",
"is-buffer": "^1.0.2"
}
},
"ndarray-pack": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ndarray-pack/-/ndarray-pack-1.2.1.tgz",
"integrity": "sha1-jK6+qqJNXs9w/4YCBjeXfajuWFo=",
"requires": {
"cwise-compiler": "^1.1.2",
"ndarray": "^1.0.13"
}
},
"nextgen-events": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/nextgen-events/-/nextgen-events-1.3.0.tgz",
"integrity": "sha512-eBz5mrO4Hw2eenPVm0AVPHuAzg/RZetAWMI547RH8O9+a0UYhCysiZ3KoNWslnWNlHetb9kzowEshsKsmFo2YQ=="
},
"node-bitmap": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/node-bitmap/-/node-bitmap-0.0.1.tgz",
"integrity": "sha1-GA6scAPgxwdhjvMTaPYvhLKmkJE="
},
"omggif": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz",
"integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"pngjs": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-2.3.1.tgz",
"integrity": "sha1-EdHhK5y2TWPjDBQ6Mw9MH1Z9qF8="
},
"setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
},
"seventh": {
"version": "0.7.30",
"resolved": "https://registry.npmjs.org/seventh/-/seventh-0.7.30.tgz",
"integrity": "sha512-GDX4eZEZXQFqURkUA802R3GkawzGA8zm2QS9AfFqPcJKakoytxhI0soTRfhEqNhqh0RrRFO/EraffrAULaxiQQ==",
"requires": {
"setimmediate": "^1.0.5"
}
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"requires": {
"shebang-regex": "^3.0.0"
}
},
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
"string-kit": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/string-kit/-/string-kit-0.10.3.tgz",
"integrity": "sha512-Pn7887wnmb9N0uAYKzqKGj1FWN2GBryiZBkM8mGqQWHoZsTUKDY574RT7ajXbRz4kf0TRKJm1JY5b0bDW0B4cw=="
},
"terminal-kit": {
"version": "1.31.7",
"resolved": "https://registry.npmjs.org/terminal-kit/-/terminal-kit-1.31.7.tgz",
"integrity": "sha512-n5WkVXaPnJC7CfqC424lwPt2/ILWks7yFZi24fjiyaT+L23E1urRdjvALHtX1F3Iyjxso54vp86VHvdbXnf84A==",
"requires": {
"@cronvel/get-pixels": "^3.3.1",
"chroma-js": "^2.0.6",
"lazyness": "^1.1.1",
"ndarray": "^1.0.18",
"nextgen-events": "^1.3.0",
"seventh": "^0.7.30",
"string-kit": "^0.10.2",
"tree-kit": "^0.6.1"
}
},
"tree-kit": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/tree-kit/-/tree-kit-0.6.1.tgz",
"integrity": "sha512-7mV4KbsLMuA6ths3J1wpVUj2PLmLdoNEGnP9fm3kxef4UXYC/A0rL5gKsqtkUaCMuRYUMORyioy8IpBWUBQ1Ig=="
},
"uniq": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
"integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8="
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"requires": {
"isexe": "^2.0.0"
}
}
}
}
{
"name": "space_invaders",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"terminal-kit": "^1.31.7"
}
}
@katjad
Copy link
Author

katjad commented Nov 29, 2019

Run yarn or npm install to install terminal-kit module, then node index.js to start the game

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment