Skip to content

Instantly share code, notes, and snippets.

@devshark
Last active October 29, 2018 16:36
Show Gist options
  • Save devshark/ec4e5e41593f8faa7ed513fe9a3c21d8 to your computer and use it in GitHub Desktop.
Save devshark/ec4e5e41593f8faa7ed513fe9a3c21d8 to your computer and use it in GitHub Desktop.
Higad - a batang 90s game. #NokiaForever
/*
* Reference: https://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript/14853974
*/
// Warn if overriding existing method
if(Array.prototype.equals)
console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
// Hide method from for-in loops
Object.defineProperty(Array.prototype, "equals", {enumerable: false});
module.exports = Object.freeze({
DIRECTION_UP: 'up',
DIRECTION_DOWN: 'down',
DIRECTION_LEFT: 'left',
DIRECTION_RIGHT: 'right',
CHAR_HIGAD: '#',
CHAR_FOOD: "\u2764",
CHAR_SPACE: ' ',
})
class Debugger {
constructor (options) {
this.options = Object.assign({
yPosition: 0,
maxWidth: 0,
padding: null,
enable: true,
program: null,
}, options)
}
log (obj) {
if (!this.options.enable) return;
const pos = [0, this.options.yPosition];
this.write(this.normalize(obj), pos)
}
normalize (obj) {
if (obj instanceof Object) {
obj = JSON.stringify(obj)
} else {
obj = obj.toString()
}
if (obj.length > this.options.maxWidth) {
return obj.slice(0, this.options.maxWidth)
}
const padRight = this.options.maxWidth - obj.length
return `${obj}${Array(padRight).fill(this.options.padding).join('')}`
}
write(char, loc) {
if (!loc instanceof Array || loc.length !== 2) {
return console.error('Error! Cannot find coordinates.')
}
if (!this.options.program) {
return console.log(char)
}
this.options.program.move(loc[0], loc[1])
this.options.program.write(char)
}
}
module.exports = Debugger
/*
* Higad
* A snake-like game inspired by Engineer-man's Python-based snake game
* Author: Anthony Lim
*/
const blessed = require('blessed')
const cursor = require('ansi')(process.stdout)
require('./Array.prototype.equals')
const Debugger = require('./debugger')
const constants = require('./constants')
const program = blessed.program()
const screen = blessed.screen({
useBCE: true,
smartCSR: true,
title: 'Higad Paxenxia'
})
class Higad {
constructor (debug) {
this.debug = debug
this.logger = new Debugger({
yPosition: screen.height - 1,
maxWidth: screen.width,
padding: constants.CHAR_SPACE,
enable: debug,
program,
})
program.clear()
program.disableMouse()
this.opposites = Object.freeze({
left: constants.DIRECTION_RIGHT,
right: constants.DIRECTION_LEFT,
up: constants.DIRECTION_DOWN,
down: constants.DIRECTION_UP,
})
this.exitKeys = ['escape', 'q', 'C-c']
this.state = {
sh: null,
sw: null,
score: 0,
snake: [],
food: null,
key: constants.DIRECTION_RIGHT,
interval: 100,
timer: null,
}
this.onInit()
this.initEvents = this.initEvents.bind(this)
this.startGame = this.startGame.bind(this)
this.getState = this.getState.bind(this)
this.setState = this.setState.bind(this)
this.initEvents();
const {interval} = this.getState()
this.setState({
timer: setInterval(this.startGame, interval),
})
}
onInit () {
const sh = screen.height - (this.debug ? 2 : 1)
const sw = screen.width;
const snk_x = parseInt(sw/4);
const snk_y = parseInt(sh/2);
const higad = [
[snk_x, snk_y],
[snk_x-1, snk_y],
[snk_x-2, snk_y],
]
this.setState({
sh,
sw,
higad,
})
cursor.hide()
}
initEvents () {
screen.key([constants.DIRECTION_DOWN,
constants.DIRECTION_LEFT,
constants.DIRECTION_RIGHT,
constants.DIRECTION_UP], (e, key) => {
const currentKey = this.getState().key;
if (this.opposites[currentKey] != key.name) {
this.setState({key: key.name})
}
})
screen.key(this.exitKeys, () => {
process.exit(0)
})
}
getFood () {
const {sh, sw} = this.getState()
const { random } = Math
return [
(parseInt(random() * sw)),
(parseInt(random() * sh)),
]
}
startGame () {
let {higad, sh, sw, food, key, score} = this.getState()
if (food == null) {
while (food == null) {
const newFood = this.getFood()
food = higad.includes(newFood) ? null : newFood
}
this.logger.write(constants.CHAR_FOOD, food)
this.logger.log({food})
}
this.logger.log({higad: higad[0], body: higad.slice(1), includes: higad.slice(1).includes(higad[0])})
this.showScore()
if ([0, sw].includes(higad[0][0]) || // it hit the sides
[0, sh].includes(higad[0][1]) || // it hit the floor/ceiling
higad.slice(1).find(s=>s.equals(higad[0])) // it hit itself
) {
return this.gameOver();
}
const newHead = higad[0].slice()
switch (key) {
case constants.DIRECTION_UP:
newHead[1] -= 1;
break;
case constants.DIRECTION_DOWN:
newHead[1] += 1;
break;
case constants.DIRECTION_LEFT:
newHead[0] -= 1;
break;
case constants.DIRECTION_RIGHT:
newHead[0] += 1;
break;
}
higad.unshift(newHead)
if (food.equals(newHead)) {
food = null
score++;
if (score % 10 == 0) {
this.speedUp()
}
} else {
const tail = higad.pop()
this.logger.write(constants.CHAR_SPACE, tail)
}
this.logger.write(constants.CHAR_HIGAD, newHead)
this.setState({higad, food, score})
}
speedUp () {
let {timer, interval} = this.getState()
interval -= 10
clearInterval(timer);
this.setState({
interval,
timer: setInterval(this.startGame, interval)
})
}
gameOver () {
const {sh, timer} = this.getState()
clearInterval(timer);
this.logger.write(this.logger.normalize(`Game Over! Your Score: ${this.getState().score}. Press space for new game, Ctrl+C to quit.`), [0, sh]);
screen.onceKey(['space'], function(){
(new Higad())
})
cursor.show()
}
showScore () {
const { sh } = this.getState()
this.logger.write(this.logger.normalize(`Score: ${this.getState().score}. Press Ctrl+C to quit.`), [0, sh])
}
setState (newState) {
this.state = Object.assign({}, this.state, newState);
}
getState () {
return this.state;
}
}
module.exports = Higad
const Higad = require('./higad')
const debug = false
new Higad(debug)
{
"name": "higad",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"author": "devshark/anthony.lim",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"ansi": "^0.3.1",
"blessed": "^0.1.81"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment