|  |  |  | @ -1,5 +1,3 @@ | 
			
		
	
		
			
				
					|  |  |  |  | const assets=require('assets'); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | const [EMPTY, FOOD, WALL, SNAKE]=Array(4).keys(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | const ifNaN=(v, r) => isNaN(v)?r:v; | 
			
		
	
	
		
			
				
					|  |  |  | @ -51,7 +49,7 @@ class SnekGame { | 
			
		
	
		
			
				
					|  |  |  |  | 			} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 			// add the walls
 | 
			
		
	
		
			
				
					|  |  |  |  | 			settings.walls.forEach(([x, y]) => this.world[x][y]=WALL); | 
			
		
	
		
			
				
					|  |  |  |  | 			if(settings.walls) settings.walls.forEach(([x, y]) => this.world[x][y]=WALL); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 			// add the food
 | 
			
		
	
		
			
				
					|  |  |  |  | 			settings.food.forEach(([x, y]) => this.world[x][y]=FOOD); | 
			
		
	
	
		
			
				
					|  |  |  | @ -68,6 +66,7 @@ class SnekGame { | 
			
		
	
		
			
				
					|  |  |  |  | 			ifNaN(settings.snake[0][0]-settings.snake[1][0], 1), | 
			
		
	
		
			
				
					|  |  |  |  | 			ifNaN(settings.snake[0][1]-settings.snake[1][1], 0) | 
			
		
	
		
			
				
					|  |  |  |  | 		]; | 
			
		
	
		
			
				
					|  |  |  |  | 		this.lastDirection=this.direction | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		// store the snake
 | 
			
		
	
		
			
				
					|  |  |  |  | 		this.snake=[...settings.snake]; | 
			
		
	
	
		
			
				
					|  |  |  | @ -75,7 +74,6 @@ class SnekGame { | 
			
		
	
		
			
				
					|  |  |  |  | 		// get our canvas, like, if we want to actually draw
 | 
			
		
	
		
			
				
					|  |  |  |  | 		this.canvas=canvas; | 
			
		
	
		
			
				
					|  |  |  |  | 		this.ctx=canvas.getContext('2d'); | 
			
		
	
		
			
				
					|  |  |  |  | 		//TODO this.gl=canvas.getContext('webgl');
 | 
			
		
	
		
			
				
					|  |  |  |  | 		
 | 
			
		
	
		
			
				
					|  |  |  |  | 		// load the custom rules
 | 
			
		
	
		
			
				
					|  |  |  |  | 		this.rules=Object.assign({ | 
			
		
	
	
		
			
				
					|  |  |  | @ -93,6 +91,8 @@ class SnekGame { | 
			
		
	
		
			
				
					|  |  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	draw() { | 
			
		
	
		
			
				
					|  |  |  |  | 		const assets=require('assets'); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		// clear the canvas, because it's easier than having to deal with everything
 | 
			
		
	
		
			
				
					|  |  |  |  | 		this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -150,11 +150,22 @@ class SnekGame { | 
			
		
	
		
			
				
					|  |  |  |  | 		this.ctx.fill(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		this.ctx.beginPath(); | 
			
		
	
		
			
				
					|  |  |  |  | 		this.snake.forEach(([x, y], i) => { | 
			
		
	
		
			
				
					|  |  |  |  | 		this.snake.forEach(([x, y], i, a) => { | 
			
		
	
		
			
				
					|  |  |  |  | 			this.ctx.lineTo( | 
			
		
	
		
			
				
					|  |  |  |  | 				offsetX+cellSize*(x+1/2), | 
			
		
	
		
			
				
					|  |  |  |  | 				offsetY+cellSize*(y+1/2) | 
			
		
	
		
			
				
					|  |  |  |  | 			); | 
			
		
	
		
			
				
					|  |  |  |  | 			if(i!=0 && Math.hypot(x-a[i-1][0], y-a[i-1][1])>1) { | 
			
		
	
		
			
				
					|  |  |  |  | 				this.ctx.lineWidth=cellSize*snake.tailWrapSize; | 
			
		
	
		
			
				
					|  |  |  |  | 			} else { | 
			
		
	
		
			
				
					|  |  |  |  | 				this.ctx.lineWidth=cellSize*snake.tailSize; | 
			
		
	
		
			
				
					|  |  |  |  | 			} | 
			
		
	
		
			
				
					|  |  |  |  | 			this.ctx.stroke(); | 
			
		
	
		
			
				
					|  |  |  |  | 			this.ctx.beginPath() | 
			
		
	
		
			
				
					|  |  |  |  | 			this.ctx.moveTo( | 
			
		
	
		
			
				
					|  |  |  |  | 				offsetX+cellSize*(x+1/2), | 
			
		
	
		
			
				
					|  |  |  |  | 				offsetY+cellSize*(y+1/2) | 
			
		
	
		
			
				
					|  |  |  |  | 			); | 
			
		
	
		
			
				
					|  |  |  |  | 		}); | 
			
		
	
		
			
				
					|  |  |  |  | 		this.ctx.stroke(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -174,6 +185,9 @@ class SnekGame { | 
			
		
	
		
			
				
					|  |  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	step() { | 
			
		
	
		
			
				
					|  |  |  |  | 		this.tickId++; | 
			
		
	
		
			
				
					|  |  |  |  | 		this.lastDirection=this.direction; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		// compute our new head
 | 
			
		
	
		
			
				
					|  |  |  |  | 		const head=[ | 
			
		
	
		
			
				
					|  |  |  |  | 			this.snake[0][0]+this.direction[0], | 
			
		
	
	
		
			
				
					|  |  |  | @ -227,12 +241,27 @@ class SnekGame { | 
			
		
	
		
			
				
					|  |  |  |  | 					this.fruits.push(cell); | 
			
		
	
		
			
				
					|  |  |  |  | 					this.world[cell[0]][cell[1]]=FOOD; | 
			
		
	
		
			
				
					|  |  |  |  | 				} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 				if(this.rules.speedIncrease) { | 
			
		
	
		
			
				
					|  |  |  |  | 					this.delay*=this.rules.speedMultiplier; | 
			
		
	
		
			
				
					|  |  |  |  | 					if(this.delay<this.rules.speedCap) this.delay=this.rules.speedCap; | 
			
		
	
		
			
				
					|  |  |  |  | 				} | 
			
		
	
		
			
				
					|  |  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		// move our head forward
 | 
			
		
	
		
			
				
					|  |  |  |  | 		this.world[head[0]][head[1]]=SNAKE; | 
			
		
	
		
			
				
					|  |  |  |  | 		this.snake.unshift(head); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		// automatic speed increase
 | 
			
		
	
		
			
				
					|  |  |  |  | 		if(this.rules.autoSpeedIncrease) { | 
			
		
	
		
			
				
					|  |  |  |  | 			if(this.delay>50 && this.tickId%this.rules.autoSpeadIncreaseTicks==0) this.delay--; | 
			
		
	
		
			
				
					|  |  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		// automatic size grow
 | 
			
		
	
		
			
				
					|  |  |  |  | 		if(this.rules.autoSizeGrow) { | 
			
		
	
		
			
				
					|  |  |  |  | 			if(this.tickId%this.rules.autoSizeGrowTicks==0) this.snake.push(tail); | 
			
		
	
		
			
				
					|  |  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		// victory condition
 | 
			
		
	
		
			
				
					|  |  |  |  | 		if(this.rules.winCondition=='fruit') { | 
			
		
	
		
			
				
					|  |  |  |  | 			if(!this.fruits.length) return this.win(); | 
			
		
	
	
		
			
				
					|  |  |  | @ -271,23 +300,20 @@ class SnekGame { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	handleInputs(inputs) { | 
			
		
	
		
			
				
					|  |  |  |  | 		const trySet=(dir) => { | 
			
		
	
		
			
				
					|  |  |  |  | 			if(!(this.direction[0]==-dir[0] && this.direction[1]==-dir[1])) this.direction=dir; | 
			
		
	
		
			
				
					|  |  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  |  | 		if(inputs.left) { | 
			
		
	
		
			
				
					|  |  |  |  | 			trySet([-1, 0]); | 
			
		
	
		
			
				
					|  |  |  |  | 		} else if(inputs.right) { | 
			
		
	
		
			
				
					|  |  |  |  | 			trySet([ 1, 0]); | 
			
		
	
		
			
				
					|  |  |  |  | 		} else if(inputs.up) { | 
			
		
	
		
			
				
					|  |  |  |  | 			trySet([ 0,-1]); | 
			
		
	
		
			
				
					|  |  |  |  | 		} else if(inputs.down) { | 
			
		
	
		
			
				
					|  |  |  |  | 			trySet([ 0, 1]); | 
			
		
	
		
			
				
					|  |  |  |  | 			if(!dir.every((e, i) => e==this.lastDirection[i] || e==-this.lastDirection[i])) { | 
			
		
	
		
			
				
					|  |  |  |  | 				this.direction=dir; | 
			
		
	
		
			
				
					|  |  |  |  | 				return true; | 
			
		
	
		
			
				
					|  |  |  |  | 			} | 
			
		
	
		
			
				
					|  |  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 		Object.keys(inputs).forEach(k => delete inputs[k]); | 
			
		
	
		
			
				
					|  |  |  |  | 		if(inputs.left && trySet([-1, 0])) return delete inputs.left; | 
			
		
	
		
			
				
					|  |  |  |  | 		else if(inputs.right && trySet([ 1, 0])) return delete inputs.right; | 
			
		
	
		
			
				
					|  |  |  |  | 		else if(inputs.up && trySet([ 0,-1])) return delete inputs.up; | 
			
		
	
		
			
				
					|  |  |  |  | 		else if(inputs.down &&  trySet([ 0, 1])) return delete inputs.down; | 
			
		
	
		
			
				
					|  |  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	start() { | 
			
		
	
		
			
				
					|  |  |  |  | 		this.firstStep=Date.now(); | 
			
		
	
		
			
				
					|  |  |  |  | 		this.tickId=0; | 
			
		
	
		
			
				
					|  |  |  |  | 		this.playing=true; | 
			
		
	
		
			
				
					|  |  |  |  | 		requestAnimationFrame(() => this.tick()); | 
			
		
	
		
			
				
					|  |  |  |  | 	} | 
			
		
	
	
		
			
				
					|  |  |  | 
 |