81 lines
2 KiB
JavaScript
81 lines
2 KiB
JavaScript
import {GameOver} from "./Game.js";
|
|
|
|
export class Snake {
|
|
/**
|
|
* The snake of the game
|
|
* @param options
|
|
*/
|
|
constructor({startPos = [8,8], size = 4, startDirection = directions.RIGHT} = {}) {
|
|
if (startPos && Array.isArray(startPos) && startPos.length === 2 && startPos.filter(s => typeof s === "number" && s > 0 && s % 1 === 0).length === startPos.length)
|
|
this.body = [startPos];
|
|
else
|
|
throw new InvalidSnakeOption("startPos");
|
|
|
|
if (size && typeof size === "number" && size > 1 && size % 1 === 0)
|
|
this.size = size;
|
|
else
|
|
throw new InvalidSnakeOption("size");
|
|
|
|
// Generate snake body
|
|
for (let s = 1; s < this.size; s++) {
|
|
const pos = this.body[this.body.length - 1].map((n, i) => n - startDirection[i]);
|
|
if (pos.find(v => v < 0))
|
|
throw new InvalidSnakeOption("startPos too small");
|
|
this.body.push(pos);
|
|
}
|
|
|
|
this.eating = false;
|
|
this.narrowing = false;
|
|
}
|
|
|
|
/**
|
|
* Move the snake body
|
|
* @param {directions} direction
|
|
*/
|
|
move(direction) {
|
|
if (!this.eating)
|
|
this.body.pop();
|
|
else
|
|
this.eating = false;
|
|
|
|
const pos = this.body[0].map((n, i) => n + direction[i]);
|
|
|
|
if (this.body.find(([x,y]) => pos[0] === x && pos[1] === y))
|
|
throw new GameOver();
|
|
|
|
if (!this.narrowing)
|
|
this.body.unshift(pos);
|
|
else
|
|
this.narrowing = false;
|
|
}
|
|
|
|
/**
|
|
* Enable eating
|
|
*/
|
|
eat() {
|
|
this.eating = true;
|
|
}
|
|
|
|
/**
|
|
* Enable narrowing
|
|
*/
|
|
shrink() {
|
|
this.narrowing = true;
|
|
}
|
|
}
|
|
|
|
export const directions = {
|
|
UP: [0, -1],
|
|
RIGHT: [1, 0],
|
|
DOWN: [0, 1],
|
|
LEFT: [-1, 0]
|
|
};
|
|
|
|
export class InvalidSnakeOption extends Error {
|
|
/**
|
|
* @param {string} name
|
|
*/
|
|
constructor(name) {
|
|
super(`Invalid Snake option: ${name}`);
|
|
}
|
|
}
|