snake classic

In this example below you will see how to do a snake classic with some HTML / CSS and Javascript

just a quick vanilla html5 canvas/js mockup of the classic snake, inspired by ideas from dionyziz' version of snake (https://www.youtube.com/watch?v=f2Hi5xD5NAg)

Thumbnail
This awesome code was written by alan8r, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright alan8r ©
  • HTML
<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  <title>snake classic</title>
  
  
  
  
  
</head>

<body>

  <!DOCTYPE html>
<html>
<title>snake</title>
<style>
canvas {
	border: solid 1px #000;
}

#container {
	display: block;
	margin: 0 auto;
	text-align: center;
}
</style>
<body>

<div id="container">
	<h1>snake classic</h1>
	<div id="score">Score: </div>
	<canvas id="canvas" width=200 height=200></canvas>
	<br />arrow keys or wasd to move<br />
	r to restart<br /><br />
	comment your highscores :)
</div>

<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
ctx.lineWidth = 1;

var STROKE_OFFSET = .5;

var GAME_SPEED = 125;
var GAME_CELL_SIZE = 10;
var GAME_CELLS_WIDE = canvas.width / GAME_CELL_SIZE;
var GAME_CELLS_HIGH = canvas.height / GAME_CELL_SIZE;

var SNAKE_START_LENGTH = 3;
var SNAKE_START_X = 1;
var SNAKE_START_Y = GAME_CELLS_WIDE / 2;

var snake, snakeSpeed, fruits, score, running;

function init() {
	running = true;
	score = 0;
	gameSpeedMod = 0;
	snake = [];
	fruits = [];
	snakeSpeed = {
		x: 1,
		y: 0
	}
	
	for(var i = 0; i < SNAKE_START_LENGTH; i++) {
		snake.push({
					x: SNAKE_START_X + i, 
					y: SNAKE_START_Y
		});
	}
	generateFruit();
}

var keys = {
	37: 'left',
	38: 'up',
	39: 'right',
	40: 'down',
	65: 'left',
	68: 'right',
	82: 'r',
	83: 'down',
	87: 'up'
};

document.onkeydown = function(e) {
	var ox = snakeSpeed.x, 
		oy = snakeSpeed.y,
		dx = 0,
		dy = 0;
	switch(keys[e.keyCode]) {
		case 'up':
			if(oy != 1) dy = -1;
			else return;
			break;
		case 'down':
			if(oy != -1) dy = 1;
			else return;
			break;
		case 'left':
			if(ox != 1) dx = -1;
			else return;
			break;
		case 'right':
			if(ox != -1) dx = 1;
			else return;
			break;
		case 'r':
			restartGame();
		default:
			return;
	}
	
	snakeSpeed = {
		x: dx,
		y: dy
	}
}

function transformX(x) {
	return x * GAME_CELL_SIZE;
}

function transformY(y) {
	return y * GAME_CELL_SIZE;
}

function restartGame() {
	init();
	render();
	update();
}

function generateFruit() {
	var nx = Math.floor(Math.random() * (GAME_CELLS_WIDE - 1)) + 1;
	var ny = Math.floor(Math.random() * (GAME_CELLS_HIGH - 1)) + 1;
	if(!snakeAt(nx, ny)) {
		return fruits.push({
			x: nx,
			y: ny
		});
	} else {
		generateFruit();
	}
}

function snakeAt(x, y) {
	for(var i = 0; i < snake.length; i++) {
		if(x == snake[i].x && y == snake[i].y) {
			return true;
		}
	}
	return false;
}

function fruitAt(x, y) {
	for(var i = 0; i < fruits.length; i++) {
		fruit = fruits[i];
		if(x == fruit.x && y == fruit.y) {
			return true;
		} else {
			continue;
		}
	}
	return false;
}

function renderGrid() {
	var xx,	yy;
	for(var y = 0; y < GAME_CELLS_HIGH; y++) {
		for(var x = 0; x < GAME_CELLS_WIDE; x++) {
			xx = transformX(x) - STROKE_OFFSET;
			yy = transformY(y) - STROKE_OFFSET;
			ctx.strokeStyle = "#DDD";
			ctx.strokeRect(xx, yy, GAME_CELL_SIZE, GAME_CELL_SIZE);
		}
	}
}

function renderSnake() {
	if(running) ctx.fillStyle = "#090";
	else ctx.fillStyle = "#777";
	var size = GAME_CELL_SIZE;
	var xx = transformX(snake[snake.length-1].x) - STROKE_OFFSET;
	var yy = transformY(snake[snake.length-1].y) - STROKE_OFFSET;
	ctx.fillRect(xx, yy, size, size);
	if(running) ctx.strokeStyle = "#030";
	else ctx.strokeStyle = "#333";
	ctx.strokeRect(xx, yy, size, size);
	for(var i = 0; i < snake.length - 1; i++) {
		xx = transformX(snake[i].x) - STROKE_OFFSET;
		yy = transformY(snake[i].y) - STROKE_OFFSET;
		if(running) ctx.fillStyle = "#0F0";
		else ctx.fillStyle = "#AAA";
		ctx.fillRect(xx, yy, size, size);
		if(running) ctx.strokeStyle = "#090";
		else ctx.fillStyle = "#999";
		ctx.strokeRect(xx, yy, size, size);
	}
}

function renderFruit() {
	ctx.fillStyle = "#F00";
	if(fruits.length > 0) {
		for(var i = 0; i < fruits.length; i++) {
			var xx = transformX(fruits[i].x) - STROKE_OFFSET;
			var yy = transformY(fruits[i].y) - STROKE_OFFSET;
			ctx.fillStyle = "#F00";
			ctx.fillRect(xx, yy, GAME_CELL_SIZE, GAME_CELL_SIZE);
			ctx.strokeStyle = "#900";
			ctx.strokeRect(xx, yy, GAME_CELL_SIZE, GAME_CELL_SIZE);
		}
	}
}

function updateSnake() {
	var xx = snake[snake.length-1].x + snakeSpeed.x,
		yy = snake[snake.length-1].y + snakeSpeed.y;
		
	var cond =	xx >= 0 				&&
				xx < GAME_CELLS_WIDE 	&&
				yy >= 0					&&
				yy < GAME_CELLS_HIGH	&&
				!snakeAt(xx, yy);
	if(cond) {
		snake.push({
			x: xx,
			y: yy
		});
		if(!fruitAt(xx, yy)) {
			snake.shift();
		} else {
			score++;
			if(GAME_SPEED - gameSpeedMod > 50) gameSpeedMod = -1 * (score * 2);
			generateFruit();
			fruits.shift();
		}
	} else {
		gameover();
	}
}

function gameover() {
	running = false;
}

function renderGameover() {
	var offset = 5;
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	renderSnake();
	var msg1 = "GAME OVER";
	var msg2 = "press \'r\' to restart";
	ctx.font = "20px monospace";
	ctx.strokeStyle = "#FFF";
	ctx.fillStyle = "#000";
	ctx.lineWidth = 2;
	ctx.strokeText(msg1, canvas.width/4, canvas.height/2 - 5 + offset);
	ctx.fillText(msg1, canvas.width/4, canvas.height/2 - 5 + offset);
	ctx.font = "12px monospace";
	ctx.strokeText(msg2, canvas.width/7 + 6, canvas.height/2 + 5 + offset);
	ctx.fillText(msg2, canvas.width/7 + 6, canvas.height/2 + 5 + offset);
	ctx.lineWidth = 1;
}

function render() {
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	renderGrid();
	renderSnake();
	renderFruit();
	if(running) setTimeout(render, 17);
	else renderGameover();
}

function update() {
	updateSnake();
	document.getElementById("score").innerText = "Score: " + score;
	if(running) setTimeout(update, GAME_SPEED + gameSpeedMod);
}

restartGame();


</script>
</body>
</html>
  
  

</body>

</html>

Comments