Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: taniarascia/snek
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.0.0
Choose a base ref
...
head repository: taniarascia/snek
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Apr 9, 2019

  1. Copy the full SHA
    ac2ffac View commit details
  2. Copy the full SHA
    553872d View commit details
  3. 1.0.1

    taniarascia committed Apr 9, 2019
    Copy the full SHA
    d62dcd7 View commit details
  4. Copy the full SHA
    f1aa0f9 View commit details
  5. Fix Coord semantics

    taniarascia committed Apr 9, 2019
    Copy the full SHA
    1d00e9d View commit details
  6. use full URL for gif

    taniarascia committed Apr 9, 2019
    Copy the full SHA
    8bbd990 View commit details
  7. 1.0.2

    taniarascia committed Apr 9, 2019
    Copy the full SHA
    5157703 View commit details
  8. Fix dot generation typo

    taniarascia committed Apr 9, 2019
    Copy the full SHA
    c27634d View commit details
  9. Copy the full SHA
    f84a4e5 View commit details
  10. Copy the full SHA
    f18063b View commit details
  11. 1.0.3

    taniarascia committed Apr 9, 2019
    Copy the full SHA
    38d97ff View commit details

Commits on Apr 12, 2019

  1. Copy the full SHA
    dd6c5d0 View commit details
  2. 1.0.4

    taniarascia committed Apr 12, 2019
    Copy the full SHA
    f1fcec2 View commit details

Commits on Apr 16, 2019

  1. score number will be more visible if scoreBox color will be white

    black is not so visible on blue backgroud
    shootermv authored Apr 16, 2019
    Copy the full SHA
    25b2cfe View commit details

Commits on Apr 17, 2019

  1. Specify npx entry point

    corlaez committed Apr 17, 2019
    Copy the full SHA
    86c3eda View commit details
  2. Run this with node

    corlaez committed Apr 17, 2019
    Copy the full SHA
    596da10 View commit details
  3. Merge pull request #6 from shootermv/patch-1

    score number will be more visible if scoreBox color will be white
    taniarascia authored Apr 17, 2019
    Copy the full SHA
    99d347a View commit details
  4. Merge pull request #7 from corlaez/npx-snek

    Add npx support to run snek without previous install
    taniarascia authored Apr 17, 2019
    Copy the full SHA
    3ab9e67 View commit details

Commits on Jun 2, 2019

  1. Copy the full SHA
    7ef3daa View commit details

Commits on Jun 11, 2019

  1. Update README.md

    taniarascia authored Jun 11, 2019
    Copy the full SHA
    9e8ba43 View commit details

Commits on Aug 22, 2019

  1. yarn -> npm

    taniarascia committed Aug 22, 2019
    Copy the full SHA
    3d8207d View commit details
  2. remove yarn.lock

    taniarascia committed Aug 22, 2019
    Copy the full SHA
    2640a0a View commit details

Commits on Sep 4, 2019

  1. Update README.md

    taniarascia authored Sep 4, 2019
    Copy the full SHA
    c33f01a View commit details

Commits on Sep 11, 2019

  1. Update README.md

    taniarascia authored Sep 11, 2019
    Copy the full SHA
    bbd2bdd View commit details
  2. Update README.md

    taniarascia authored Sep 11, 2019
    Copy the full SHA
    233cec4 View commit details
  3. Update README.md

    taniarascia authored Sep 11, 2019
    Copy the full SHA
    7a9d467 View commit details
  4. Update README.md

    taniarascia authored Sep 11, 2019
    Copy the full SHA
    ab130df View commit details

Commits on Sep 26, 2019

  1. Update README.md

    taniarascia authored Sep 26, 2019
    Copy the full SHA
    e06b92f View commit details
  2. Update README.md

    taniarascia authored Sep 26, 2019
    Copy the full SHA
    06ba830 View commit details

Commits on Nov 12, 2019

  1. Copy the full SHA
    cac3a4e View commit details

Commits on Dec 10, 2019

  1. Add run on repl.it badge to README

    This pull request configures this repository to be run on Repl.it.      It adds a `.replit` configuration file and a Repl.it badge to the `README`.
         You can read more about running repos on Repl.it [here](https://docs.repl.it/repls/dot-replit), or view the Repl [here](https://repl.it/@dialogarithm/snek-1).
    Alexa B committed Dec 10, 2019
    Copy the full SHA
    44dcb79 View commit details

Commits on Mar 15, 2020

  1. Merge pull request #11 from runargs/replit-readme

    Add run on repl.it badge to README
    taniarascia authored Mar 15, 2020
    Copy the full SHA
    a2e1a90 View commit details
  2. Merge pull request #10 from inphomercial/consts-update

    Added new consts, replaced existing hard coded strings
    taniarascia authored Mar 15, 2020
    Copy the full SHA
    1ae39a9 View commit details

Commits on Jul 13, 2021

  1. Update package.json

    taniarascia authored Jul 13, 2021
    Copy the full SHA
    97429bb View commit details
Showing with 151 additions and 86 deletions.
  1. +2 −0 .replit
  2. +28 −11 README.md
  3. +13 −0 package-lock.json
  4. +11 −6 package.json
  5. +4 −4 play.js
  6. +41 −43 src/Game.js
  7. +25 −14 src/UserInterface.js
  8. +27 −0 src/constants.js
  9. +0 −8 yarn.lock
2 changes: 2 additions & 0 deletions .replit
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
language = "nodejs"
run = "npm i && npm run play"
39 changes: 28 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,53 @@
# 🐍 Snek.js [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![snekjs on NPM](https://img.shields.io/npm/v/snekjs.svg?color=green&label=snekjs)](https://www.npmjs.com/package/snekjs)
# 🐍 Snek.js

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![snekjs on NPM](https://img.shields.io/npm/v/snekjs.svg?color=green&label=snekjs)](https://www.npmjs.com/package/snekjs)

[![Run on Repl.it](https://repl.it/badge/github/taniarascia/snek)](https://repl.it/github/taniarascia/snek)

A terminal-based Snake implementation written in JavaScript (Node.js).

![snek.gif](snek.gif)
### [Read the tutorial](https://www.taniarascia.com/snake-game-in-javascript/)

![snek.gif](https://raw.githubusercontent.com/taniarascia/snek/master/snek.gif)

## Instructions

Use the arrow keys (``, ``, ``, ``) or `W` `A` `S` `D` to navigate the snake up, down, left, or right. Eat the red dot to gain points. If the snake collides with the wall or its own tail, it's game over. Press `ENTER` to restart, and `Q`, `ESCAPE` or `CTRL` + `C` to quit the game.

## Installation

### Run without installing

The easiest way to play the game is to just run it in the terminal without installing anything!

```bash
npx taniarascia/snek
```

### Clone from repository

```bash
git clone https://github.com/taniarascia/snek
cd snek
yarn && yarn play

# install and run via npm or yarn
npm i && npm run play
```

### npm module

Add the `snekjs` module.

```bash
yarn add snekjs
npm i snekjs
```

Create the game.

```js
// index.js

const blessed = require('blessed')
const { UserInterface, Game } = require('snekjs')
const ui = new UserInterface(blessed, blessed.screen())
const game = new Game(ui)
const game = new Game(new UserInterface())

// Begin game
game.start()
@@ -44,9 +61,9 @@ node index.js

## Acknowledgements

- [Vanya Sergeev](https://sergeev.io) for pointing out my snake collision bug, for advising me to make a single, reusable draw method, for showing me how to properly bind methods between classes, and for overall guidance and inspiration
- [Devin McIntyre](https://www.dev-eloper.com/) for general advice
- Panayiotis Nicolaou's [JavaScript Snake for Web](https://medium.freecodecamp.org/think-like-a-programmer-how-to-build-snake-using-only-javascript-html-and-css-7b1479c3339e) for initial logic
- [Vanya Sergeev](https://sergeev.io) for pointing out my snake collision bug, for advising me to make a single, reusable draw method, for showing me how to properly bind methods between classes, and for overall guidance and inspiration.
- [Devin McIntyre](https://www.dev-eloper.com/) for general advice.
- Panayiotis Nicolaou's [JavaScript Snake for Web](https://medium.freecodecamp.org/think-like-a-programmer-how-to-build-snake-using-only-javascript-html-and-css-7b1479c3339e) for initial logic.

## Author

13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 11 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
{
"name": "snekjs",
"version": "1.0.0",
"version": "1.0.4",
"description": "A terminal-based Snake implementation written in JavaScript (Node.js) 🐍",
"author": {
"name": "Tania Rascia",
"email": "me@taniarascia.com",
"url": "https://www.taniarascia.com"
},
"author": "Tania Rascia",
"main": "index.js",
"bin": {
"snek": "./play.js"
},
"scripts": {
"play": "node play.js"
},
"dependencies": {
"blessed": "^0.1.81"
},
"licence": "MIT",
"keywords": [
"snake",
"javascript",
"node"
],
"private": false
}
8 changes: 4 additions & 4 deletions play.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const blessed = require('blessed')
const { UserInterface } = require('./src/UserInterface')
#!/usr/bin/env node

const { Game } = require('./src/Game')
const ui = new UserInterface(blessed, blessed.screen())
const game = new Game(ui)
const { UserInterface } = require('./src/UserInterface')
const game = new Game(new UserInterface())

// Begin game
game.start()
84 changes: 41 additions & 43 deletions src/Game.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
const { performance, PerformanceObserver } = require('perf_hooks')
const {
GAME_SPEED,
DIRECTIONS,
INITIAL_SNAKE_SIZE,
SNAKE_COLOR,
DOT_COLOR,
DIRECTION_UP,
DIRECTION_RIGHT,
DIRECTION_DOWN,
DIRECTION_LEFT,
} = require('./constants')

/**
* @class Game
*
@@ -9,7 +20,7 @@ const { performance, PerformanceObserver } = require('perf_hooks')
* 3. The score
*
* The i/o of the game is handled by a separate UserInterface class, which is
* responsible for * detecting all event handlers (key press), creating the
* responsible for detecting all event handlers (key press), creating the
* screen, and drawing elements to the screen.
*/
class Game {
@@ -29,23 +40,15 @@ class Game {

reset() {
// Set up initial state
this.snake = [
{ x: 5, y: 0 },
{ x: 4, y: 0 },
{ x: 3, y: 0 },
{ x: 2, y: 0 },
{ x: 1, y: 0 },
{ x: 0, y: 0 },
]
this.snake = []

for (let i = INITIAL_SNAKE_SIZE; i >= 0; i--) {
this.snake[INITIAL_SNAKE_SIZE - i] = { x: i, y: 0 }
}

this.dot = {}
this.score = 0
this.currentDirection = 'right'
this.directions = {
up: { x: 0, y: -1 },
down: { x: 0, y: 1 },
right: { x: 1, y: 0 },
left: { x: -1, y: 0 },
}
this.currentDirection = DIRECTION_RIGHT
this.changingDirection = false
this.timer = null

@@ -60,17 +63,17 @@ class Game {
* do not allow reversal.
*/
changeDirection(_, key) {
if ((key.name === 'up' || key.name === 'w') && this.currentDirection !== 'down') {
this.currentDirection = 'up'
if ((key.name === DIRECTION_UP || key.name === 'w') && this.currentDirection !== DIRECTION_DOWN) {
this.currentDirection = DIRECTION_UP
}
if ((key.name === 'down' || key.name === 's') && this.currentDirection !== 'up') {
this.currentDirection = 'down'
if ((key.name === DIRECTION_DOWN || key.name === 's') && this.currentDirection !== DIRECTION_UP) {
this.currentDirection = DIRECTION_DOWN
}
if ((key.name === 'left' || key.name === 'a') && this.currentDirection !== 'right') {
this.currentDirection = 'left'
if ((key.name === DIRECTION_LEFT || key.name === 'a') && this.currentDirection !== DIRECTION_RIGHT) {
this.currentDirection = DIRECTION_LEFT
}
if ((key.name === 'right' || key.name === 'd') && this.currentDirection !== 'left') {
this.currentDirection = 'right'
if ((key.name === DIRECTION_RIGHT || key.name === 'd') && this.currentDirection !== DIRECTION_LEFT) {
this.currentDirection = DIRECTION_RIGHT
}
}

@@ -90,8 +93,8 @@ class Game {

// Move the head forward by one pixel based on velocity
const head = {
x: this.snake[0].x + this.directions[this.currentDirection].x,
y: this.snake[0].y + this.directions[this.currentDirection].y,
x: this.snake[0].x + DIRECTIONS[this.currentDirection].x,
y: this.snake[0].y + DIRECTIONS[this.currentDirection].y,
}

this.snake.unshift(head)
@@ -107,19 +110,19 @@ class Game {
}
}

generateRandomPixelCoords(min, max) {
generateRandomPixelCoord(min, max) {
// Get a random coordinate from 0 to max container height/width
return Math.round(Math.random() * (max - min) + min)
}

generateDot() {
// Generate a dot at a random x/y coordinate
this.dot.x = this.generateRandomPixelCoords(0, this.ui.gameContainer.width - 1)
this.dot.y = this.generateRandomPixelCoords(1, this.ui.gameContainer.height - 1)
this.dot.x = this.generateRandomPixelCoord(0, this.ui.gameContainer.width - 1)
this.dot.y = this.generateRandomPixelCoord(1, this.ui.gameContainer.height - 1)

// If the pixel is on a snake, regenerate the dot
this.snake.forEach(segment => {
if (segment.x === this.x && segment.y === this.y) {
if (segment.x === this.dot.x && segment.y === this.dot.y) {
this.generateDot()
}
})
@@ -128,16 +131,16 @@ class Game {
drawSnake() {
// Render each snake segment as a pixel
this.snake.forEach(segment => {
this.ui.draw(segment, 'green')
this.ui.draw(segment, SNAKE_COLOR)
})
}

drawDot() {
// Render the dot as a pixel
this.ui.draw(this.dot, 'red')
this.ui.draw(this.dot, DOT_COLOR)
}

gameOver() {
isGameOver() {
// If the snake collides with itself, end the game
const collide = this.snake
// Filter out the head
@@ -163,22 +166,17 @@ class Game {
this.ui.render()
}

// Set to initial direction and clear the screen
clear() {
this.changingDirection = false
this.ui.clearScreen()
}

tick() {
if (this.gameOver()) {
if (this.isGameOver()) {
this.showGameOverScreen()
clearInterval(this.timer)
this.timer = null

return
}

this.clear()
this.changingDirection = false
this.ui.clearScreen()
this.drawDot()
this.moveSnake()
this.drawSnake()
@@ -189,7 +187,7 @@ class Game {
if (!this.timer) {
this.reset()

this.timer = setInterval(this.tick.bind(this), 50)
this.timer = setInterval(this.tick.bind(this), GAME_SPEED)
}
}

39 changes: 25 additions & 14 deletions src/UserInterface.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const blessed = require('blessed')

/**
* @class UserInterface
*
@@ -7,17 +9,26 @@
* interfaces - web, terminal, etc.
*/
class UserInterface {
constructor(blessed, screen) {
constructor() {
// Blessed is the terminal library API that provides a screen, elements, and
// event handling
this.blessed = blessed
this.screen = screen
this.screen = blessed.screen()

// Game title
this.screen.title = 'Snek.js'

// Create the game container
this.gameBox = {
// Create the boxes
this.gameBox = this.createGameBox()
this.scoreBox = this.createScoreBox()
this.gameOverBox = this.createGameOverBox()

this.gameContainer = this.blessed.box(this.gameBox)
this.scoreContainer = this.blessed.box(this.scoreBox)
}

createGameBox() {
return {
parent: this.screen,
top: 1,
left: 0,
@@ -28,22 +39,25 @@ class UserInterface {
bg: 'black',
},
}
}

// Create the score container
this.scoreBox = {
createScoreBox() {
return {
parent: this.screen,
top: 0,
left: 'left',
width: '100%',
height: 1,
tags: true,
style: {
fg: 'black',
fg: 'white',
bg: 'blue',
},
}
}

this.gameOverBox = {
createGameOverBox() {
return {
parent: this.screen,
top: 'center',
left: 'center',
@@ -63,9 +77,6 @@ class UserInterface {
},
},
}

this.gameContainer = this.blessed.box(this.gameBox)
this.scoreContainer = this.blessed.box(this.scoreBox)
}

bindHandlers(keyPressHandler, quitHandler, enterHandler) {
@@ -76,11 +87,11 @@ class UserInterface {
}

// Draw a pixel
draw(coords, color) {
draw(coord, color) {
this.blessed.box({
parent: this.gameContainer,
top: coords.y,
left: coords.x,
top: coord.y,
left: coord.x,
width: 1,
height: 1,
style: {
Loading