|
|
|
@ -6,20 +6,61 @@ const ifNaN=(v, r) => isNaN(v)?r:v; |
|
|
|
|
|
|
|
|
|
class SnekGame { |
|
|
|
|
constructor(settings, canvas, rules) { |
|
|
|
|
// build the world
|
|
|
|
|
this.dimensions=[...settings.dimensions]; |
|
|
|
|
this.world=Array(settings.dimensions[0]); |
|
|
|
|
for(let i=0; i<settings.dimensions[0]; i++) { |
|
|
|
|
this.world[i]=Array(settings.dimensions[1]); |
|
|
|
|
this.world[i].fill(EMPTY); |
|
|
|
|
// setup the delay
|
|
|
|
|
this.delay=settings.delay; |
|
|
|
|
|
|
|
|
|
// world is given in the level
|
|
|
|
|
if(settings.world) { // explicitly
|
|
|
|
|
|
|
|
|
|
// convert the world
|
|
|
|
|
this.world=Array(settings.world[0].length); |
|
|
|
|
for(let x=0; x<this.world.length; x++) { |
|
|
|
|
this.world[x]=Array(settings.world.length); |
|
|
|
|
for(let y=0; y<this.world[x].length; y++) { |
|
|
|
|
this.world[x][y]=(() => { |
|
|
|
|
switch(settings.world[y][x]) { |
|
|
|
|
case ' ': return EMPTY; |
|
|
|
|
case 'f': return FOOD; |
|
|
|
|
case 'w': return WALL; |
|
|
|
|
} |
|
|
|
|
})(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
//
|
|
|
|
|
// extract the dimensions
|
|
|
|
|
this.dimensions=[this.world.length, this.world[0].length]; |
|
|
|
|
|
|
|
|
|
// extract the fruits
|
|
|
|
|
this.fruits=[]; |
|
|
|
|
this.world |
|
|
|
|
.forEach((l, x) => l.forEach( |
|
|
|
|
(c, y) => { |
|
|
|
|
if(c==FOOD) this.fruits.push([x, y]); |
|
|
|
|
} |
|
|
|
|
)); |
|
|
|
|
} else { // dimension and objects
|
|
|
|
|
|
|
|
|
|
// get the dimensions
|
|
|
|
|
this.dimensions=[...settings.dimensions]; |
|
|
|
|
|
|
|
|
|
// build an empty world
|
|
|
|
|
this.world=Array(settings.dimensions[0]); |
|
|
|
|
for(let i=0; i<settings.dimensions[0]; i++) { |
|
|
|
|
this.world[i]=Array(settings.dimensions[1]); |
|
|
|
|
this.world[i].fill(EMPTY); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// add the walls
|
|
|
|
|
settings.walls.forEach(([x, y]) => this.world[x][y]=WALL); |
|
|
|
|
|
|
|
|
|
// add the food
|
|
|
|
|
settings.food.forEach(([x, y]) => this.world[x][y]=FOOD); |
|
|
|
|
this.fruits=[...settings.food]; |
|
|
|
|
} |
|
|
|
|
console.log(this); |
|
|
|
|
settings.walls.forEach(([x, y]) => this.world[x][y]=WALL); |
|
|
|
|
settings.food.forEach(([x, y]) => this.world[x][y]=FOOD); |
|
|
|
|
|
|
|
|
|
// add the snake to the world
|
|
|
|
|
settings.snake.forEach(([x, y]) => this.world[x][y]=SNAKE); |
|
|
|
|
|
|
|
|
|
// setup the delay
|
|
|
|
|
this.delay=settings.delay; |
|
|
|
|
|
|
|
|
|
// get the head and initial direction
|
|
|
|
|
this.head=[...settings.snake[0]]; |
|
|
|
@ -28,9 +69,8 @@ class SnekGame { |
|
|
|
|
ifNaN(settings.snake[0][1]-settings.snake[1][1], 0) |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
// get the snake and the fruits themselves
|
|
|
|
|
// store the snake
|
|
|
|
|
this.snake=[...settings.snake]; |
|
|
|
|
this.fruits=[...settings.food]; |
|
|
|
|
|
|
|
|
|
// get our canvas, like, if we want to actually draw
|
|
|
|
|
this.canvas=canvas; |
|
|
|
@ -43,8 +83,13 @@ class SnekGame { |
|
|
|
|
speedIncrease: true, |
|
|
|
|
worldWrap: true, |
|
|
|
|
winCondition: 'none', |
|
|
|
|
scoreSystem: 'fruit' |
|
|
|
|
}, rules, settings); |
|
|
|
|
scoreSystem: 'fruit', |
|
|
|
|
netPlay: false |
|
|
|
|
}, rules, settings.rules || {}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get playTime() { |
|
|
|
|
return Date.now()-this.firstStep; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
draw() { |
|
|
|
@ -121,7 +166,7 @@ class SnekGame { |
|
|
|
|
this.ctx.drawImage( |
|
|
|
|
fruit, |
|
|
|
|
offsetX+cellSize*x+(1-fruitScale)*cellSize/2, |
|
|
|
|
offsetY+cellSize*x+(1-fruitScale)*cellSize/2, |
|
|
|
|
offsetY+cellSize*y+(1-fruitScale)*cellSize/2, |
|
|
|
|
cellSize*fruitScale, |
|
|
|
|
cellSize*fruitScale |
|
|
|
|
); |
|
|
|
@ -163,11 +208,8 @@ class SnekGame { |
|
|
|
|
|
|
|
|
|
// remove the fruit from existence
|
|
|
|
|
this.world[head[0]][head[1]]=SNAKE; |
|
|
|
|
this.fruits.splice( |
|
|
|
|
this.fruits.find( |
|
|
|
|
([x, y]) => x==head[0] && y==head[1] |
|
|
|
|
), |
|
|
|
|
1 |
|
|
|
|
this.fruits=this.fruits.filter( |
|
|
|
|
([x, y]) => !(x==head[0] && y==head[1]) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// custom rules
|
|
|
|
@ -192,14 +234,22 @@ class SnekGame { |
|
|
|
|
this.snake.unshift(head); |
|
|
|
|
|
|
|
|
|
// victory condition
|
|
|
|
|
if(!this.fruits.length) return this.win(); |
|
|
|
|
if(this.rules.winCondition=='fruit') { |
|
|
|
|
if(!this.fruits.length) return this.win(); |
|
|
|
|
} |
|
|
|
|
if(this.rules.winCondition=='time') { |
|
|
|
|
if(this.playTime>=this.rules.gameDuration) return this.win(); |
|
|
|
|
} |
|
|
|
|
if(this.rules.winCondition=='score') { |
|
|
|
|
if(this.score>=this.rules.scoreObjective) return this.win(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tick() { |
|
|
|
|
if(!this.playing) return; |
|
|
|
|
if(!this.lastStep) this.lastStep=this.firstStep; |
|
|
|
|
this.draw(); |
|
|
|
|
if(this.callback) this.callback(); |
|
|
|
|
if(this.callback) this.callback('tick'); |
|
|
|
|
if(this.lastStep+this.delay<Date.now()) { |
|
|
|
|
this.lastStep+=this.delay; |
|
|
|
|
this.step(); |
|
|
|
@ -209,15 +259,14 @@ class SnekGame { |
|
|
|
|
|
|
|
|
|
win() { |
|
|
|
|
this.playing=false; |
|
|
|
|
// you gud lol
|
|
|
|
|
console.log("You gud lol"); |
|
|
|
|
console.log(`Won in ${(Date.now()-this.firstStep)/1000} seconds`); |
|
|
|
|
this.endPlayTime=this.playTime; |
|
|
|
|
if(this.callback) this.callback('win'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
die() { |
|
|
|
|
this.playing=false; |
|
|
|
|
// you bad lol
|
|
|
|
|
console.log("You bad lol"); |
|
|
|
|
this.endPlayTime=this.playTime; |
|
|
|
|
if(this.callback) this.callback('die'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
handleInputs(inputs) { |
|
|
|
@ -244,4 +293,5 @@ class SnekGame { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
module.exports=SnekGame; |
|
|
|
|
return SnekGame; |
|
|
|
|