Welcome Learner today in this blog post we will be Develop a greedy snake game in Browser With Javascript HTML5 and CSS3. All the Complete source code of the application is shown Down.
index.html
`
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="stylesheets/style.css">
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<!-- latest-->
<link href="https://fonts.googleapis.com/css?family=Press+Start+2P" rel="stylesheet">
<link href="https://unpkg.com/nes.css@2.3.0/css/nes.min.css" rel="stylesheet">
<!-- import boostrap-->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script>
<style>
html, body, pre, code, kbd, samp {
font-family: "Press Start 2P";
}
</style>
</head>
<div class="Container d-flex justify-content-center">
<div class="PlayGround row col-10 col-md-5">
<div class="nes-container with-title is-centered">
<div class="d-flex justify-content-between" style="margin:1em"></div>
<div class="GameBoard">
<div id="gameResult"></div>
<div class="alert alert-warning d-none" role="alert"></div>
<div class="Game" id="game"></div>
</div>
</div>
</div>
</div>
style.css
.Container {
margin-top: 5em;
height : 80vh;
width : 100%;
justify-content: center;
}
.PlayGround {
background-color: ghostwhite;
}
.GameBoard {
position: relative;
min-height: 80%;
background-color: gray;
margin: 1em;
}
.Game {
position: absolute;
height: 100%;
width: 100%;
}
table {
height: 100%;
width: 100%;
background-color: ghostwhite;
}
.table-bordered td {
border-width : 1px 1px !important;
}
script.js
const statics = {
game : {
Snake : {
field_width: 30,
field_height: 30,
init_speed: 150,
init_length: 3,
accelerate: 2.5,
point_weight : 5,
callback : ()=> { $("#gameResult").text('Win!')}
}
}
}
class Game {
initFields(x, y) {
// >> Clean game field and init a new one.
// ---
// >> ARGUMENTS :
// x (Integer): width of playground
// y (Integer): height of playground
// ---
// >> RETURN : null
let game = document.getElementById('game');
let gameboard = document.createElement('table');
for(let i = 0; i < y; i ++) {
let line = document.createElement('tr');
for(let j = 0; j < x; j ++) {
let block = document.createElement('td');
block.setAttribute('id', i + '_' + j);
line.appendChild(block);
}
gameboard.append(line);
}
game.appendChild(gameboard);
}
initBorder() {
// >> Optional parent method for adding border.
// ---
// >> ARGUMENTS :
// ---
// >> RETURN : null
let game = document.getElementById('game');
if (game.childElementCount) {
game.children[0].className += 'table table-bordered';
}
}
}
class Snake extends Game {
constructor(x_length, y_length) {
// >> Child object of Game, make yourself as long as possible by eating food.
// ---
// Arguments :
// x_length (Integer): width of this game.
// y_length (Integer): height of this game.
// ---
// RETURN : null
super();
this.x_length = x_length;
this.y_length = y_length;
this.initFields(x_length, y_length);
this.initBorder()
this.speed = statics.game.Snake.init_speed; // Snake move speed.
this.snake_length = statics.game.Snake.init_length; // initial length.
this.snake_body = Array(); // Queue for each piece position.
this.food = this.createFood();
this.initSnake();
this.direction = [0, 1];
this.speedUp();
this.callbackEvent()
}
getRandomIntInclusive(min, max) {
// >> Random number method.
// ---
// Arguments :
// min (Integer): bottom of random.
// max (Integer): cap of random.
// ---
// RETURN : Integer
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1) + min);
}
initSnake() {
// >> Initialize the snake with length on random position.
// ---
// Arguments :
// ---
// RETURN : null
let x = this.getRandomIntInclusive(this.snake_body, this.x_length-1);
let y = this.getRandomIntInclusive(this.snake_body, this.y_length-1);
for (let i = 0; i < this.snake_length ; i ++ ) {
let p = [y, x-i];
this.snake_body.push(p);
}
this.fillColor(this.snake_body);
}
callbackEvent() {
// >> Add keyboard event to change head direction.
// ---
// Arguments :
// ---
// RETURN : null
$(document).keydown((e) => {
let switch_to = null
switch (e.keyCode) {
case 37:
switch_to = [0, -1];
break
case 39 :
switch_to = [0, 1];
break
case 38 :
switch_to = [-1, 0];
break
case 40 :
switch_to = [1, 0];
break
default:
break;
}
if (switch_to) {
let current = [this.snake_body[0][0] - this.snake_body[1][0], this.snake_body[0][1] - this.snake_body[1][1]]
let c = switch_to.map((x)=> Math.abs(x));
if (c[0] != Math.abs(current[0]) || c[1]!= Math.abs(current[1])){
this.direction = switch_to;
}
}
}
)
}
fillColor() {
// >> Update view with latest .snake_body and food.
// ---
// Arguments :
// ---
// RETURN : null
$('td').css({backgroundColor: 'white'});
this.snake_body.forEach(element => {
$('#'+element.join('_')).css({backgroundColor: 'black'});
});
$('#'+this.food.join('_')).css({backgroundColor: 'red'});
}
crossBorder(next) {
// >> Convert the position when head reaching border.
// ---
// Arguments :
// next (Integer): head position.
// ---
// RETURN : null
if (next[0] < 0 || next[0] == this.x_length) {
next[0] = (next[0] + this.x_length)%this.x_length;
}
else if (next[1] < 0 || next[1] == this.y_length) {
next[1] = (next[1] + this.y_length)%this.y_length;
}
return next;
}
moveForward() {
// >> unshift next td in direction and pop the last in body, also determine below condition.
// >> * hit body : will call getResult() to stop the game and calculate points with length.
// >> * eat food : recover the drop to extend length and reset interval to speed up.
// ---
// Arguments :
// ---
// RETURN : null
let next = [this.snake_body[0][0]+this.direction[0], this.snake_body[0][1]+this.direction[1]];
next = this.crossBorder(next);
this.snake_body.unshift(next);
let drop = this.snake_body.pop();
let checker = this.snake_body.map((x)=>x.join('_'))
if (new Set(checker).size != checker.length) {
clearInterval(this.interval);
// toResult('Win', this.snake_length * statics.game.Snake.point_weight);
statics.game.Snake.callback();
}
else if (next.join('_') == this.food.join('_')) {
this.snake_length += 1;
this.speed -= statics.game.Snake.accelerate;
this.speedUp()
this.snake_body.push(drop);
this.food = this.createFood();
}
this.fillColor();
}
speedUp() {
// >> Reset interval with updated speed.
// ---
// Arguments :
// ---
// RETURN : null
clearInterval(this.interval);
this.interval = setInterval(() => {
this.moveForward()
}, this.speed);
}
createFood() {
// >> Create food in fields randomly.
// ---
// Arguments :
// ---
// RETURN : null
let food = [this.getRandomIntInclusive(0, this.x_length-1), this.getRandomIntInclusive(0, this.y_length-1)];
while (this.snake_body.map((x)=> x.join('_')).includes(food.join('_'))) {
food = [this.getRandomIntInclusive(0, this.x_length-1), this.getRandomIntInclusive(0, this.y_length-1)];
}
return food;
}
}
var game = new Snake(15, 15)